1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-21 06:53:53 +00:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Pieter van der Kloet 2011-08-03 08:28:57 +02:00
commit 0a04008fc9
132 changed files with 5280 additions and 426 deletions

4
.gitignore vendored
View file

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

View file

@ -2,8 +2,23 @@ project(OpenMW)
IF (APPLE) IF (APPLE)
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app") set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
# using 10.6 sdk
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
ENDIF (APPLE) ENDIF (APPLE)
# Version
set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 11)
set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
# doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp")
# Sound source selection # Sound source selection
option(USE_AUDIERE "use Audiere for sound" OFF) option(USE_AUDIERE "use Audiere for sound" OFF)
option(USE_FFMPEG "use ffmpeg for sound" OFF) option(USE_FFMPEG "use ffmpeg for sound" OFF)
@ -17,9 +32,8 @@ if(DPKG_PROGRAM)
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files") set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
else() else()
if (APPLE) if (APPLE)
# set path inside bundle set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files") set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources files")
else() else()
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files") set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
@ -57,6 +71,7 @@ IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1")
set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK") set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK")
set(BULLET_ROOT "${PREBUILT_DIR}/bullet")
ELSE() ELSE()
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.") message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
ENDIF() ENDIF()
@ -365,7 +380,7 @@ endif()
if (APPLE) if (APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
"${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg") "${OpenMW_BINARY_DIR}/plugins.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY) "${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
@ -384,9 +399,6 @@ if (APPLE)
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY) "${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
"${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg")
endif (APPLE) endif (APPLE)
@ -397,8 +409,9 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC)
# Apple bundling # Apple bundling
# TODO REWRITE!
if (APPLE) if (APPLE)
set(MISC_FILES set(MISC_FILES
${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg ${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg
${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg) ${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg)
@ -435,8 +448,7 @@ if(DPKG_PROGRAM)
exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
else() else()
#FIXME this should probably be read from some file like ${CMAKE_CURRENT_SOURCE_DIR}/VERSION or something that gets updated when changing version set(VERSION_STRING "${OPENMW_VERSION}")
set(VERSION_STRING "0.10.0")
set(PACKAGE_MAINTAINER "unknown") set(PACKAGE_MAINTAINER "unknown")
endif() endif()
@ -483,6 +495,8 @@ if (BUILD_ESMTOOL)
add_subdirectory( apps/esmtool ) add_subdirectory( apps/esmtool )
endif() endif()
add_subdirectory( apps/launcher )
if (WIN32) if (WIN32)
if (MSVC) if (MSVC)
if (USE_DEBUG_CONSOLE) if (USE_DEBUG_CONSOLE)

View file

@ -576,6 +576,7 @@ WARN_LOGFILE =
INPUT = apps \ INPUT = apps \
components \ components \
libs \ libs \
Docs
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View file

@ -576,6 +576,7 @@ WARN_LOGFILE =
INPUT = apps \ INPUT = apps \
components \ components \
libs \ libs \
Docs
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

5
Docs/mainpage.hpp.cmake Normal file
View file

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

View file

@ -0,0 +1,79 @@
set(LAUNCHER
datafilespage.cpp
graphicspage.cpp
lineedit.cpp
main.cpp
maindialog.cpp
naturalsort.cpp
playpage.cpp
pluginsmodel.cpp
pluginsview.cpp
)
set(LAUNCHER_HEADER
combobox.hpp
datafilespage.hpp
graphicspage.hpp
lineedit.hpp
maindialog.hpp
naturalsort.hpp
playpage.hpp
pluginsmodel.hpp
pluginsview.hpp
)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
combobox.hpp
datafilespage.hpp
graphicspage.hpp
lineedit.hpp
maindialog.hpp
playpage.hpp
pluginsmodel.hpp
pluginsview.hpp
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
find_package(PNG REQUIRED)
include_directories(${PNG_INCLUDE_DIR})
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
include(${QT_USE_FILE})
# Main executable
add_executable(omwlauncher
${LAUNCHER}
${MISC} ${MISC_HEADER}
${FILES} ${FILES_HEADER}
${TO_UTF8}
${ESM}
${RCC_SRCS}
${MOC_SRCS}
)
target_link_libraries(omwlauncher
${Boost_LIBRARIES}
${OGRE_LIBRARIES}
${QT_LIBRARIES}
${PNG_LIBRARY}
)
if (APPLE)
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${APP_BUNDLE_DIR}/../launcher.qss")
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${APP_BUNDLE_DIR}/../launcher.cfg")
else()
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
endif()

View file

@ -0,0 +1,28 @@
#ifndef COMBOBOX_H
#define COMBOBOX_H
#include <QComboBox>
class ComboBox : public QComboBox
{
Q_OBJECT
private:
QString oldText;
public:
ComboBox(QWidget *parent=0) : QComboBox(parent), oldText()
{
connect(this,SIGNAL(editTextChanged(const QString&)), this,
SLOT(textChangedSlot(const QString&)));
connect(this,SIGNAL(currentIndexChanged(const QString&)), this,
SLOT(textChangedSlot(const QString&)));
}
private slots:
void textChangedSlot(const QString &newText)
{
emit textChanged(oldText, newText);
oldText = newText;
}
signals:
void textChanged(const QString &oldText, const QString &newText);
};
#endif

View file

@ -0,0 +1,979 @@
#include <QtGui>
#include <components/esm/esm_reader.hpp>
#include <components/files/path.hpp>
#include <components/files/collections.hpp>
#include <components/files/multidircollection.hpp>
#include "datafilespage.hpp"
#include "lineedit.hpp"
#include "naturalsort.hpp"
#include "pluginsmodel.hpp"
#include "pluginsview.hpp"
using namespace ESM;
using namespace std;
//sort QModelIndexList ascending
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
{
return index1.row() >= index2.row();
}
//sort QModelIndexList descending
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
{
return index1.row() <= index2.row();
}
DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent)
{
mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters
mPluginsModel = new PluginsModel(); // Contains selectable plugins
mPluginsProxyModel = new QSortFilterProxyModel();
mPluginsProxyModel->setDynamicSortFilter(true);
mPluginsProxyModel->setSourceModel(mPluginsModel);
QLabel *filterLabel = new QLabel(tr("&Filter:"), this);
LineEdit *filterLineEdit = new LineEdit(this);
filterLabel->setBuddy(filterLineEdit);
QToolBar *filterToolBar = new QToolBar(this);
filterToolBar->setMovable(false);
// Create a container widget and a layout to get the spacer to work
QWidget *filterWidget = new QWidget(this);
QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget);
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
filterLayout->addItem(hSpacer1);
filterLayout->addWidget(filterLabel);
filterLayout->addWidget(filterLineEdit);
filterToolBar->addWidget(filterWidget);
mMastersWidget = new QTableWidget(this); // Contains the available masters
mMastersWidget->setObjectName("MastersWidget");
mMastersWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
mMastersWidget->setSelectionMode(QAbstractItemView::MultiSelection);
mMastersWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
mMastersWidget->setAlternatingRowColors(true);
mMastersWidget->horizontalHeader()->setStretchLastSection(true);
mMastersWidget->horizontalHeader()->hide();
mMastersWidget->verticalHeader()->hide();
mMastersWidget->insertColumn(0);
mPluginsTable = new PluginsView(this);
mPluginsTable->setModel(mPluginsProxyModel);
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
mPluginsTable->horizontalHeader()->hide();
// Set the row height to the size of the checkboxes
QCheckBox checkBox;
unsigned int height = checkBox.sizeHint().height() + 4;
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
// Add both tables to a splitter
QSplitter *splitter = new QSplitter(this);
splitter->setOrientation(Qt::Horizontal);
splitter->addWidget(mMastersWidget);
splitter->addWidget(mPluginsTable);
// Adjust the default widget widths inside the splitter
QList<int> sizeList;
sizeList << 100 << 300;
splitter->setSizes(sizeList);
// Bottom part with profile options
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
mProfilesComboBox = new ComboBox(this);
mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
mProfilesComboBox->setInsertPolicy(QComboBox::InsertAtBottom);
mProfileToolBar = new QToolBar(this);
mProfileToolBar->setMovable(false);
mProfileToolBar->setIconSize(QSize(16, 16));
mProfileToolBar->addWidget(profileLabel);
mProfileToolBar->addWidget(mProfilesComboBox);
QVBoxLayout *pageLayout = new QVBoxLayout(this);
pageLayout->addWidget(filterToolBar);
pageLayout->addWidget(splitter);
pageLayout->addWidget(mProfileToolBar);
connect(mMastersWidget->selectionModel(),
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
this, SLOT(masterSelectionChanged(const QItemSelection&, const QItemSelection&)));
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(const QString)));
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)));
setupConfig();
createActions();
}
void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict)
{
// Put the paths in a boost::filesystem vector to use with Files::Collections
std::vector<boost::filesystem::path> dataDirs;
foreach (const QString &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();
}

View file

@ -0,0 +1,93 @@
#ifndef DATAFILESPAGE_H
#define DATAFILESPAGE_H
#include <QWidget>
#include <QModelIndex>
#include "combobox.hpp"
class QTableWidget;
class QStandardItemModel;
class QItemSelection;
class QItemSelectionModel;
class QSortFilterProxyModel;
class QStringListModel;
class QSettings;
class QAction;
class QToolBar;
class QMenu;
class PluginsModel;
class PluginsView;
class ComboBox;
class DataFilesPage : public QWidget
{
Q_OBJECT
public:
DataFilesPage(QWidget *parent = 0);
ComboBox *mProfilesComboBox;
QSettings *mLauncherConfig;
const QStringList checkedPlugins();
const QStringList selectedMasters();
void setupConfig();
void readConfig();
void writeConfig(QString profile = QString());
void setupDataFiles(const QStringList &paths, bool strict);
public slots:
void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
void setCheckState(QModelIndex index);
void filterChanged(const QString filter);
void showContextMenu(const QPoint &point);
void profileChanged(const QString &previous, const QString &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

View file

@ -0,0 +1,484 @@
#include <QtGui>
#include <components/files/path.hpp>
#include "graphicspage.hpp"
GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent)
{
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
mRendererComboBox = new QComboBox(rendererGroup);
// Layout for the combobox and label
QGridLayout *renderSystemLayout = new QGridLayout();
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
mRendererStackedWidget = new QStackedWidget(rendererGroup);
QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup);
rendererGroupLayout->addLayout(renderSystemLayout);
rendererGroupLayout->addWidget(mRendererStackedWidget);
// Display
QGroupBox *displayGroup = new QGroupBox(tr("Display"), this);
mDisplayStackedWidget = new QStackedWidget(displayGroup);
QVBoxLayout *displayGroupLayout = new QVBoxLayout(displayGroup);
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
displayGroupLayout->addWidget(mDisplayStackedWidget);
displayGroupLayout->addItem(vSpacer3);
// Layout for the whole page
QVBoxLayout *pageLayout = new QVBoxLayout(this);
pageLayout->addWidget(rendererGroup);
pageLayout->addWidget(displayGroup);
connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
createPages();
setupConfig();
setupOgre();
readConfig();
}
void GraphicsPage::createPages()
{
// OpenGL rendering settings
QWidget *mOGLRendererPage = new QWidget();
QLabel *OGLRTTLabel = new QLabel(tr("Preferred RTT Mode:"), mOGLRendererPage);
mOGLRTTComboBox = new QComboBox(mOGLRendererPage);
QLabel *OGLAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mOGLRendererPage);
mOGLAntiAliasingComboBox = new QComboBox(mOGLRendererPage);
QGridLayout *OGLRendererLayout = new QGridLayout(mOGLRendererPage);
QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
OGLRendererLayout->addWidget(OGLRTTLabel, 0, 0, 1, 1);
OGLRendererLayout->addWidget(mOGLRTTComboBox, 0, 1, 1, 1);
OGLRendererLayout->addWidget(OGLAntiAliasingLabel, 1, 0, 1, 1);
OGLRendererLayout->addWidget(mOGLAntiAliasingComboBox, 1, 1, 1, 1);
OGLRendererLayout->addItem(vSpacer1, 2, 1, 1, 1);
// OpenGL display settings
QWidget *mOGLDisplayPage = new QWidget();
QLabel *OGLResolutionLabel = new QLabel(tr("Resolution:"), mOGLDisplayPage);
mOGLResolutionComboBox = new QComboBox(mOGLDisplayPage);
QLabel *OGLFrequencyLabel = new QLabel(tr("Display Frequency:"), mOGLDisplayPage);
mOGLFrequencyComboBox = new QComboBox(mOGLDisplayPage);
mOGLVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mOGLDisplayPage);
mOGLFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mOGLDisplayPage);
QGridLayout *OGLDisplayLayout = new QGridLayout(mOGLDisplayPage);
QSpacerItem *vSpacer2 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
OGLDisplayLayout->addWidget(OGLResolutionLabel, 0, 0, 1, 1);
OGLDisplayLayout->addWidget(mOGLResolutionComboBox, 0, 1, 1, 1);
OGLDisplayLayout->addWidget(OGLFrequencyLabel, 1, 0, 1, 1);
OGLDisplayLayout->addWidget(mOGLFrequencyComboBox, 1, 1, 1, 1);
OGLDisplayLayout->addItem(vSpacer2, 2, 1, 1, 1);
OGLDisplayLayout->addWidget(mOGLVSyncCheckBox, 3, 0, 1, 1);
OGLDisplayLayout->addWidget(mOGLFullScreenCheckBox, 6, 0, 1, 1);
// Direct3D rendering settings
QWidget *mD3DRendererPage = new QWidget();
QLabel *D3DRenderDeviceLabel = new QLabel(tr("Rendering Device:"), mD3DRendererPage);
mD3DRenderDeviceComboBox = new QComboBox(mD3DRendererPage);
QLabel *D3DAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mD3DRendererPage);
mD3DAntiAliasingComboBox = new QComboBox(mD3DRendererPage);
QLabel *D3DFloatingPointLabel = new QLabel(tr("Floating-point Mode:"), mD3DRendererPage);
mD3DFloatingPointComboBox = new QComboBox(mD3DRendererPage);
mD3DNvPerfCheckBox = new QCheckBox(tr("Allow NVPerfHUD"), mD3DRendererPage);
QGridLayout *D3DRendererLayout = new QGridLayout(mD3DRendererPage);
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
QSpacerItem *vSpacer4 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
D3DRendererLayout->addWidget(D3DRenderDeviceLabel, 0, 0, 1, 1);
D3DRendererLayout->addWidget(mD3DRenderDeviceComboBox, 0, 1, 1, 1);
D3DRendererLayout->addWidget(D3DAntiAliasingLabel, 1, 0, 1, 1);
D3DRendererLayout->addWidget(mD3DAntiAliasingComboBox, 1, 1, 1, 1);
D3DRendererLayout->addWidget(D3DFloatingPointLabel, 2, 0, 1, 1);
D3DRendererLayout->addWidget(mD3DFloatingPointComboBox, 2, 1, 1, 1);
D3DRendererLayout->addItem(vSpacer3, 3, 1, 1, 1);
D3DRendererLayout->addWidget(mD3DNvPerfCheckBox, 4, 0, 1, 1);
D3DRendererLayout->addItem(vSpacer4, 5, 1, 1, 1);
// Direct3D display settings
QWidget *mD3DDisplayPage = new QWidget();
QLabel *D3DResolutionLabel = new QLabel(tr("Resolution:"), mD3DDisplayPage);
mD3DResolutionComboBox = new QComboBox(mD3DDisplayPage);
mD3DVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mD3DDisplayPage);
mD3DFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mD3DDisplayPage);
QGridLayout *mD3DDisplayLayout = new QGridLayout(mD3DDisplayPage);
QSpacerItem *vSpacer5 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
mD3DDisplayLayout->addWidget(D3DResolutionLabel, 0, 0, 1, 1);
mD3DDisplayLayout->addWidget(mD3DResolutionComboBox, 0, 1, 1, 1);
mD3DDisplayLayout->addItem(vSpacer5, 1, 1, 1, 1);
mD3DDisplayLayout->addWidget(mD3DVSyncCheckBox, 2, 0, 1, 1);
mD3DDisplayLayout->addWidget(mD3DFullScreenCheckBox, 5, 0, 1, 1);
// Add the created pages
mRendererStackedWidget->addWidget(mOGLRendererPage);
mRendererStackedWidget->addWidget(mD3DRendererPage);
mDisplayStackedWidget->addWidget(mOGLDisplayPage);
mDisplayStackedWidget->addWidget(mD3DDisplayPage);
}
void GraphicsPage::setupConfig()
{
QString ogreCfg = "./ogre.cfg";
QFile file(ogreCfg);
if (!file.exists()) {
ogreCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
"openmw", "ogre.cfg"));
}
mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat);
}
void GraphicsPage::setupOgre()
{
QString pluginCfg = "./plugins.cfg";
QFile file(pluginCfg);
if (!file.exists()) {
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
"openmw", "plugins.cfg"));
}
// Reopen the file from user directory
file.setFileName(pluginCfg);
if (!file.exists()) {
// There's no plugins.cfg in the user directory, use global directory
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigGlobal,
"openmw", "plugins.cfg"));
}
// Create a log manager so we can surpress debug text to stdout/stderr
Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
logMgr->createLog("launcherOgre.log", true, false, false);
try
{
mOgre = new Ogre::Root(pluginCfg.toStdString());
}
catch(Ogre::Exception &ex)
{
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str());
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating Ogre::Root");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Failed to create the Ogre::Root object</b><br><br> \
Make sure the plugins.cfg is present and valid.<br><br> \
Press \"Show Details...\" for more information.<br>"));
msgBox.setDetailedText(ogreError);
msgBox.exec();
qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError));
std::exit(1);
}
// Get the available renderers and put them in the combobox
const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
mSelectedRenderSystem = *r;
mRendererComboBox->addItem((*r)->getName().c_str());
}
int index = mRendererComboBox->findText(mOgreConfig->value("Render System").toString());
if ( index != -1) {
mRendererComboBox->setCurrentIndex(index);
}
// Create separate rendersystems
QString openGLName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("OpenGL"), Qt::MatchStartsWith));
QString direct3DName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("Direct3D"), Qt::MatchStartsWith));
mOpenGLRenderSystem = mOgre->getRenderSystemByName(openGLName.toStdString());
mDirect3DRenderSystem = mOgre->getRenderSystemByName(direct3DName.toStdString());
if (!mOpenGLRenderSystem && !mDirect3DRenderSystem) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating renderer");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
msgBox.exec();
std::exit(1);
}
// Now fill the GUI elements
// OpenGL
if (mOpenGLRenderSystem) {
mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem));
mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem));
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem);
// Remove extraneous spaces
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
mOGLResolutionComboBox->addItems(videoModes);
mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem));
}
// Direct3D
if (mDirect3DRenderSystem) {
mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem));
mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mDirect3DRenderSystem));
mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem));
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem);
// Remove extraneous spaces
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
mD3DResolutionComboBox->addItems(videoModes);
}
}
void GraphicsPage::readConfig()
{
// Read the config file settings
if (mOpenGLRenderSystem) {
int index = mOGLRTTComboBox->findText(getConfigValue("RTT Preferred Mode", mOpenGLRenderSystem));
if ( index != -1) {
mOGLRTTComboBox->setCurrentIndex(index);
}
index = mOGLAntiAliasingComboBox->findText(getConfigValue("FSAA", mOpenGLRenderSystem));
if ( index != -1){
mOGLAntiAliasingComboBox->setCurrentIndex(index);
}
index = mOGLResolutionComboBox->findText(getConfigValue("Video Mode", mOpenGLRenderSystem));
if ( index != -1) {
mOGLResolutionComboBox->setCurrentIndex(index);
}
index = mOGLFrequencyComboBox->findText(getConfigValue("Display Frequency", mOpenGLRenderSystem));
if ( index != -1) {
mOGLFrequencyComboBox->setCurrentIndex(index);
}
// Now we do the same for the checkboxes
if (getConfigValue("VSync", mOpenGLRenderSystem) == QLatin1String("Yes")) {
mOGLVSyncCheckBox->setCheckState(Qt::Checked);
}
if (getConfigValue("Full Screen", mOpenGLRenderSystem) == QLatin1String("Yes")) {
mOGLFullScreenCheckBox->setCheckState(Qt::Checked);
}
}
if (mDirect3DRenderSystem) {
int index = mD3DRenderDeviceComboBox->findText(getConfigValue("Rendering Device", mDirect3DRenderSystem));
if ( index != -1) {
mD3DRenderDeviceComboBox->setCurrentIndex(index);
}
index = mD3DAntiAliasingComboBox->findText(getConfigValue("FSAA", mDirect3DRenderSystem));
if ( index != -1) {
mD3DAntiAliasingComboBox->setCurrentIndex(index);
}
index = mD3DFloatingPointComboBox->findText(getConfigValue("Floating-point mode", mDirect3DRenderSystem));
if ( index != -1) {
mD3DFloatingPointComboBox->setCurrentIndex(index);
}
index = mD3DResolutionComboBox->findText(getConfigValue("Video Mode", mDirect3DRenderSystem));
if ( index != -1) {
mD3DResolutionComboBox->setCurrentIndex(index);
}
if (getConfigValue("Allow NVPerfHUD", mDirect3DRenderSystem) == QLatin1String("Yes")) {
mD3DNvPerfCheckBox->setCheckState(Qt::Checked);
}
if (getConfigValue("VSync", mDirect3DRenderSystem) == QLatin1String("Yes")) {
mD3DVSyncCheckBox->setCheckState(Qt::Checked);
}
if (getConfigValue("Full Screen", mDirect3DRenderSystem) == QLatin1String("Yes")) {
mD3DFullScreenCheckBox->setCheckState(Qt::Checked);
}
}
}
void GraphicsPage::writeConfig()
{
// Write the config file settings
// Custom write method: We cannot use QSettings because it does not accept spaces
QFile file(mOgreConfig->fileName());
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
// File could not be opened,
QMessageBox msgBox;
msgBox.setWindowTitle("Error opening Ogre configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
QTextStream out(&file);
out << "Render System=" << mSelectedRenderSystem->getName().c_str() << endl << endl;
if (mDirect3DRenderSystem) {
QString direct3DName = mDirect3DRenderSystem->getName().c_str();
direct3DName.prepend("[");
direct3DName.append("]");
out << direct3DName << endl;
if (mD3DNvPerfCheckBox->checkState() == Qt::Checked) {
out << "Allow NVPerfHUD=Yes" << endl;
} else {
out << "Allow NVPerfHUD=No" << endl;
}
out << "FSAA=" << mD3DAntiAliasingComboBox->currentText() << endl;
out << "Floating-point mode=" << mD3DFloatingPointComboBox->currentText() << endl;
if (mD3DFullScreenCheckBox->checkState() == Qt::Checked) {
out << "Full Screen=Yes" << endl;
} else {
out << "Full Screen=No" << endl;
}
out << "Rendering Device=" << mD3DRenderDeviceComboBox->currentText() << endl;
out << "Resource Creation Policy=Create on all devices" << endl;
if (mD3DVSyncCheckBox->checkState() == Qt::Checked) {
out << "VSync=Yes" << endl;
} else {
out << "VSync=No" << endl;
}
out << "VSync Interval=1" << endl;
out << "Video Mode=" << mD3DResolutionComboBox->currentText() << endl;
out << "sRGB Gamma Conversion=No" << endl;
}
out << endl;
if (mOpenGLRenderSystem) {
QString openGLName = mOpenGLRenderSystem->getName().c_str();
openGLName.prepend("[");
openGLName.append("]");
out << openGLName << endl;
out << "Colour Depth=32" << endl;
out << "Display Frequency=" << mOGLFrequencyComboBox->currentText() << endl;
out << "FSAA=" << mOGLAntiAliasingComboBox->currentText() << endl;
if (mOGLFullScreenCheckBox->checkState() == Qt::Checked) {
out << "Full Screen=Yes" << endl;
} else {
out << "Full Screen=No" << endl;
}
out << "RTT Preferred Mode=" << mOGLRTTComboBox->currentText() << endl;
if (mOGLVSyncCheckBox->checkState() == Qt::Checked) {
out << "VSync=Yes" << endl;
} else {
out << "VSync=No" << endl;
}
out << "VSync Interval=1" << endl;
out << "Video Mode=" << mOGLResolutionComboBox->currentText() << endl;
out << "sRGB Gamma Conversion=No" << endl;
}
file.close();
}
QString GraphicsPage::getConfigValue(const QString &key, Ogre::RenderSystem *renderer)
{
QString result;
mOgreConfig->beginGroup(renderer->getName().c_str());
result = mOgreConfig->value(key).toString();
mOgreConfig->endGroup();
return result;
}
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
{
QStringList result;
uint row = 0;
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++)
{
Ogre::StringVector::iterator opt_it;
uint idx = 0;
for (opt_it = i->second.possibleValues.begin ();
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
{
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0)
result << (*opt_it).c_str();
}
}
return result;
}
void GraphicsPage::rendererChanged(const QString &renderer)
{
if (renderer.contains("Direct3D")) {
mRendererStackedWidget->setCurrentIndex(1);
mDisplayStackedWidget->setCurrentIndex(1);
}
if (renderer.contains("OpenGL")) {
mRendererStackedWidget->setCurrentIndex(0);
mDisplayStackedWidget->setCurrentIndex(0);
}
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
}

View file

@ -0,0 +1,69 @@
#ifndef GRAPHICSPAGE_H
#define GRAPHICSPAGE_H
#include <QWidget>
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgreConfigFile.h>
#include <OgreConfigDialog.h>
class QComboBox;
class QCheckBox;
class QStackedWidget;
class QSettings;
class GraphicsPage : public QWidget
{
Q_OBJECT
public:
GraphicsPage(QWidget *parent = 0);
QSettings *mOgreConfig;
void writeConfig();
public slots:
void rendererChanged(const QString &renderer);
private:
Ogre::Root *mOgre;
Ogre::RenderSystem *mSelectedRenderSystem;
Ogre::RenderSystem *mOpenGLRenderSystem;
Ogre::RenderSystem *mDirect3DRenderSystem;
QComboBox *mRendererComboBox;
QStackedWidget *mRendererStackedWidget;
QStackedWidget *mDisplayStackedWidget;
// OpenGL
QComboBox *mOGLRTTComboBox;
QComboBox *mOGLAntiAliasingComboBox;
QComboBox *mOGLResolutionComboBox;
QComboBox *mOGLFrequencyComboBox;
QCheckBox *mOGLVSyncCheckBox;
QCheckBox *mOGLFullScreenCheckBox;
// Direct3D
QComboBox *mD3DRenderDeviceComboBox;
QComboBox *mD3DAntiAliasingComboBox;
QComboBox *mD3DFloatingPointComboBox;
QComboBox *mD3DResolutionComboBox;
QCheckBox *mD3DNvPerfCheckBox;
QCheckBox *mD3DVSyncCheckBox;
QCheckBox *mD3DFullScreenCheckBox;
QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
void createPages();
void setupConfig();
void setupOgre();
void readConfig();
};
#endif

View file

@ -0,0 +1,30 @@
######################################################################
# Automatically generated by qmake (2.01a) Fri Jun 24 21:14:15 2011
######################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
HEADERS += combobox.hpp \
datafilespage.hpp \
graphicspage.hpp \
lineedit.hpp \
maindialog.hpp \
naturalsort.hpp \
playpage.hpp \
pluginsmodel.hpp \
pluginsview.hpp
SOURCES += datafilespage.cpp \
graphicspage.cpp \
lineedit.cpp \
main.cpp \
maindialog.cpp \
naturalsort.cpp \
playpage.cpp \
pluginsmodel.cpp \
pluginsview.cpp
RESOURCES += resources.qrc
win32:RC_FILE = launcher.rc

View file

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

View file

@ -0,0 +1,46 @@
/****************************************************************************
**
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/
#include "lineedit.hpp"
#include <QToolButton>
#include <QStyle>
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
{
clearButton = new QToolButton(this);
QPixmap pixmap(":images/clear.png");
clearButton->setIcon(QIcon(pixmap));
clearButton->setIconSize(pixmap.size());
clearButton->setCursor(Qt::ArrowCursor);
clearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
clearButton->hide();
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(clearButton->sizeHint().width() + frameWidth + 1));
QSize msz = minimumSizeHint();
setMinimumSize(qMax(msz.width(), clearButton->sizeHint().height() + frameWidth * 2 + 2),
qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2));
}
void LineEdit::resizeEvent(QResizeEvent *)
{
QSize sz = clearButton->sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
clearButton->move(rect().right() - frameWidth - sz.width(),
(rect().bottom() + 1 - sz.height())/2);
}
void LineEdit::updateCloseButton(const QString& text)
{
clearButton->setVisible(!text.isEmpty());
}

View file

@ -0,0 +1,35 @@
/****************************************************************************
**
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
**
** Use, modification and distribution is allowed without limitation,
** warranty, liability or support of any kind.
**
****************************************************************************/
#ifndef LINEEDIT_H
#define LINEEDIT_H
#include <QLineEdit>
class QToolButton;
class LineEdit : public QLineEdit
{
Q_OBJECT
public:
LineEdit(QWidget *parent = 0);
protected:
void resizeEvent(QResizeEvent *);
private slots:
void updateCloseButton(const QString &text);
private:
QToolButton *clearButton;
};
#endif // LIENEDIT_H

35
apps/launcher/main.cpp Normal file
View file

@ -0,0 +1,35 @@
#include <QApplication>
#include <QDir>
#include <QFile>
#include "maindialog.hpp"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
#if defined(Q_OS_MAC)
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
#endif
QDir::setCurrent(dir.absolutePath());
// Load the stylesheet
QFile file("./launcher.qss");
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
app.setStyleSheet(styleSheet);
MainDialog dialog;
return dialog.exec();
}

View file

@ -0,0 +1,355 @@
#include <QtGui>
#include <components/files/path.hpp>
#include "maindialog.hpp"
#include "playpage.hpp"
#include "graphicspage.hpp"
#include "datafilespage.hpp"
MainDialog::MainDialog()
{
mIconWidget = new QListWidget;
mIconWidget->setObjectName("IconWidget");
mIconWidget->setViewMode(QListView::IconMode);
mIconWidget->setWrapping(false);
mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
mIconWidget->setIconSize(QSize(48, 48));
mIconWidget->setMovement(QListView::Static);
mIconWidget->setMinimumWidth(400);
mIconWidget->setFixedHeight(80);
mIconWidget->setSpacing(4);
mIconWidget->setCurrentRow(0);
mIconWidget->setFlow(QListView::LeftToRight);
QGroupBox *groupBox = new QGroupBox(this);
QVBoxLayout *groupLayout = new QVBoxLayout(groupBox);
mPagesWidget = new QStackedWidget(groupBox);
groupLayout->addWidget(mPagesWidget);
QPushButton *playButton = new QPushButton(tr("Play"));
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
buttonBox->setStandardButtons(QDialogButtonBox::Close);
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(mIconWidget);
dialogLayout->addWidget(groupBox);
dialogLayout->addWidget(buttonBox);
setWindowTitle(tr("OpenMW Launcher"));
setWindowIcon(QIcon(":/images/openmw.png"));
// Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
setMinimumSize(QSize(575, 575));
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
setupConfig();
createIcons();
createPages();
}
void MainDialog::createIcons()
{
if (!QIcon::hasThemeIcon("document-new")) {
QIcon::setThemeName("tango");
}
// We create a fallback icon because the default fallback doesn't work
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
QListWidgetItem *playButton = new QListWidgetItem(mIconWidget);
playButton->setIcon(QIcon(":/images/openmw.png"));
playButton->setText(tr("Play"));
playButton->setTextAlignment(Qt::AlignCenter);
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget);
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
graphicsButton->setText(tr("Graphics"));
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget);
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
dataFilesButton->setText(tr("Data Files"));
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
connect(mIconWidget,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
}
void MainDialog::createPages()
{
// Various pages
mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(this);
mDataFilesPage = new DataFilesPage(this);
// First we retrieve all data= keys from the config
// We can't use QSettings directly because it
// does not support multiple keys with the same name
QFile file(mGameConfig->fileName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error opening OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
QApplication::exit(); // File cannot be opened or created
}
QTextStream in(&file);
QStringList dataDirs;
// Add each data= value
while (!in.atEnd()) {
QString line = in.readLine();
if (line.startsWith("data=")) {
dataDirs.append(line.remove("data="));
}
}
// Add the data-local= key
QString dataLocal = mGameConfig->value("data-local").toString();
if (!dataLocal.isEmpty()) {
dataDirs.append(dataLocal);
}
if (!dataDirs.isEmpty()) {
// Now pass the datadirs on to the DataFilesPage
mDataFilesPage->setupDataFiles(dataDirs, mGameConfig->value("fs-strict").toBool());
} else {
QMessageBox msgBox;
msgBox.setWindowTitle("Error reading OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not read the location of the data files</b><br><br> \
Please make sure OpenMW is correctly configured and try again.<br>"));
msgBox.exec();
QApplication::exit(); // No data or data-local entries in openmw.cfg
}
// Set the combobox of the play page to imitate the comobox on the datafilespage
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
// Add the pages to the stacked widget
mPagesWidget->addWidget(mPlayPage);
mPagesWidget->addWidget(mGraphicsPage);
mPagesWidget->addWidget(mDataFilesPage);
// Select the first page
mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select);
connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play()));
connect(mPlayPage->mProfilesComboBox,
SIGNAL(currentIndexChanged(int)),
this, SLOT(profileChanged(int)));
connect(mDataFilesPage->mProfilesComboBox,
SIGNAL(currentIndexChanged(int)),
this, SLOT(profileChanged(int)));
}
void MainDialog::profileChanged(int index)
{
// Just to be sure, should always have a selection
if (!mIconWidget->selectionModel()->hasSelection()) {
return;
}
QString currentPage = mIconWidget->currentItem()->data(Qt::DisplayRole).toString();
if (currentPage == QLatin1String("Play")) {
mDataFilesPage->mProfilesComboBox->setCurrentIndex(index);
}
if (currentPage == QLatin1String("Data Files")) {
mPlayPage->mProfilesComboBox->setCurrentIndex(index);
}
}
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
{
if (!current)
current = previous;
mPagesWidget->setCurrentIndex(mIconWidget->row(current));
}
void MainDialog::closeEvent(QCloseEvent *event)
{
// Now write all config files
writeConfig();
event->accept();
}
void MainDialog::play()
{
// First do a write of all the configs, just to be sure
writeConfig();
#ifdef Q_WS_WIN
QString game = "./openmw.exe";
QFile file(game);
#elif defined(Q_WS_MAC)
QDir dir(QCoreApplication::applicationDirPath());
QString game = dir.absoluteFilePath("openmw");
QFile file(game);
#else
QString game = "./openmw";
QFile file(game);
#endif
QProcess process;
QFileInfo info(file);
if (!file.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not find OpenMW</b><br><br> \
The OpenMW application is not found.<br> \
Please make sure OpenMW is installed correctly and try again.<br>"));
msgBox.exec();
return;
}
if (!info.isExecutable()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
The OpenMW application is not executable.<br> \
Please make sure you have the right permissions and try again.<br>"));
msgBox.exec();
return;
}
// Start the game
if (!process.startDetached(game)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
An error occurred while starting OpenMW.<br><br> \
Press \"Show Details...\" for more information.<br>"));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
return;
} else {
close();
}
}
void MainDialog::setupConfig()
{
// First we read the OpenMW config
QString config = "./openmw.cfg";
QFile file(config);
if (!file.exists()) {
config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
"openmw", "openmw.cfg"));
}
file.close();
// Open our config file
mGameConfig = new QSettings(config, QSettings::IniFormat);
}
void MainDialog::writeConfig()
{
// Write the profiles
mDataFilesPage->writeConfig();
mDataFilesPage->mLauncherConfig->sync();
// Write the graphics settings
mGraphicsPage->writeConfig();
mGraphicsPage->mOgreConfig->sync();
QStringList dataFiles = mDataFilesPage->selectedMasters();
dataFiles.append(mDataFilesPage->checkedPlugins());
// Open the config as a QFile
QFile file(mGameConfig->fileName());
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle("Error opening OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
QTextStream in(&file);
QByteArray buffer;
// Remove all previous master/plugin entries from config
while (!in.atEnd()) {
QString line = in.readLine();
if (!line.contains("master") && !line.contains("plugin")) {
buffer += line += "\n";
}
}
file.close();
// Now we write back the other config entries
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error writing OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not write to %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
file.write(buffer);
QTextStream out(&file);
// Write the list of game files to the config
foreach (const QString &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();
}

View file

@ -0,0 +1,46 @@
#ifndef MAINDIALOG_H
#define MAINDIALOG_H
#include <QDialog>
class QListWidget;
class QListWidgetItem;
class QStackedWidget;
class QStringListModel;
class QSettings;
class PlayPage;
class GraphicsPage;
class DataFilesPage;
class MainDialog : public QDialog
{
Q_OBJECT
public:
MainDialog();
public slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void play();
void profileChanged(int index);
private:
void createIcons();
void createPages();
void setupConfig();
void writeConfig();
void closeEvent(QCloseEvent *event);
QListWidget *mIconWidget;
QStackedWidget *mPagesWidget;
PlayPage *mPlayPage;
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
QSettings *mGameConfig;
};
#endif

View file

@ -0,0 +1,95 @@
/*
* This file contains code found in the QtGui module of the Qt Toolkit.
* See Qt's qfilesystemmodel source files for more information
*/
#include "naturalsort.hpp"
static inline QChar getNextChar(const QString &s, int location)
{
return (location < s.length()) ? s.at(location) : QChar();
}
/*!
* Natural number sort, skips spaces.
*
* Examples:
* 1, 2, 10, 55, 100
* 01.jpg, 2.jpg, 10.jpg
*
* Note on the algorithm:
* Only as many characters as necessary are looked at and at most they all
* are looked at once.
*
* Slower then QString::compare() (of course)
*/
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
{
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {
// skip spaces, tabs and 0's
QChar c1 = getNextChar(s1, l1);
while (c1.isSpace())
c1 = getNextChar(s1, ++l1);
QChar c2 = getNextChar(s2, l2);
while (c2.isSpace())
c2 = getNextChar(s2, ++l2);
if (c1.isDigit() && c2.isDigit()) {
while (c1.digitValue() == 0)
c1 = getNextChar(s1, ++l1);
while (c2.digitValue() == 0)
c2 = getNextChar(s2, ++l2);
int lookAheadLocation1 = l1;
int lookAheadLocation2 = l2;
int currentReturnValue = 0;
// find the last digit, setting currentReturnValue as we go if it isn't equal
for (
QChar lookAhead1 = c1, lookAhead2 = c2;
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
lookAhead2 = getNextChar(s2, ++lookAheadLocation2)
) {
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
if (!is1ADigit && !is2ADigit)
break;
if (!is1ADigit)
return -1;
if (!is2ADigit)
return 1;
if (currentReturnValue == 0) {
if (lookAhead1 < lookAhead2) {
currentReturnValue = -1;
} else if (lookAhead1 > lookAhead2) {
currentReturnValue = 1;
}
}
}
if (currentReturnValue != 0)
return currentReturnValue;
}
if (cs == Qt::CaseInsensitive) {
if (!c1.isLower()) c1 = c1.toLower();
if (!c2.isLower()) c2 = c2.toLower();
}
int r = QString::localeAwareCompare(c1, c2);
if (r < 0)
return -1;
if (r > 0)
return 1;
}
// The two strings are the same (02 == 2) so fall back to the normal sort
return QString::compare(s1, s2, cs);
}
bool naturalSortLessThanCS( const QString &left, const QString &right )
{
return (naturalCompare( left, right, Qt::CaseSensitive ) < 0);
}
bool naturalSortLessThanCI( const QString &left, const QString &right )
{
return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0);
}

View file

@ -0,0 +1,9 @@
#ifndef NATURALSORT_H
#define NATURALSORT_H
#include <QString>
bool naturalSortLessThanCS( const QString &left, const QString &right );
bool naturalSortLessThanCI( const QString &left, const QString &right );
#endif

View file

@ -0,0 +1,43 @@
#include <QtGui>
#include "playpage.hpp"
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
{
QWidget *playWidget = new QWidget(this);
playWidget->setObjectName("PlayGroup");
playWidget->setFixedSize(QSize(425, 375));
mPlayButton = new QPushButton(tr("Play"), playWidget);
mPlayButton->setObjectName("PlayButton");
mPlayButton->setMinimumSize(QSize(200, 50));
QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget);
profileLabel->setObjectName("ProfileLabel");
QPlastiqueStyle *style = new QPlastiqueStyle;
mProfilesComboBox = new QComboBox(playWidget);
mProfilesComboBox->setObjectName("ProfilesComboBox");
mProfilesComboBox->setStyle(style);
QGridLayout *playLayout = new QGridLayout(playWidget);
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
playLayout->addWidget(mPlayButton, 1, 1, 1, 1);
playLayout->addWidget(profileLabel, 2, 1, 1, 1);
playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1);
playLayout->addItem(hSpacer1, 2, 0, 1, 1);
playLayout->addItem(hSpacer2, 2, 2, 1, 1);
playLayout->addItem(vSpacer1, 0, 1, 1, 1);
playLayout->addItem(vSpacer2, 4, 1, 1, 1);
QHBoxLayout *pageLayout = new QHBoxLayout(this);
pageLayout->addWidget(playWidget);
}

View file

@ -0,0 +1,21 @@
#ifndef PLAYPAGE_H
#define PLAYPAGE_H
#include <QWidget>
class QComboBox;
class QPushButton;
class PlayPage : public QWidget
{
Q_OBJECT
public:
PlayPage(QWidget *parent = 0);
QComboBox *mProfilesComboBox;
QPushButton *mPlayButton;
};
#endif

View file

@ -0,0 +1,149 @@
#include <QMimeData>
#include <QBitArray>
#include <limits>
#include "pluginsmodel.hpp"
PluginsModel::PluginsModel(QObject *parent) : QStandardItemModel(parent)
{
}
void decodeDataRecursive(QDataStream &stream, QStandardItem *item)
{
int colCount, childCount;
stream >> *item;
stream >> colCount >> childCount;
item->setColumnCount(colCount);
int childPos = childCount;
while(childPos > 0) {
childPos--;
QStandardItem *child = new QStandardItem();
decodeDataRecursive(stream, child);
item->setChild( childPos / colCount, childPos % colCount, child);
}
}
bool PluginsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent)
{
// Code largely based on QStandardItemModel::dropMimeData
// check if the action is supported
if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction))
return false;
// check if the format is supported
QString format = QLatin1String("application/x-qstandarditemmodeldatalist");
if (!data->hasFormat(format))
return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
if (row > rowCount(parent))
row = rowCount(parent);
if (row == -1)
row = rowCount(parent);
if (column == -1)
column = 0;
// decode and insert
QByteArray encoded = data->data(format);
QDataStream stream(&encoded, QIODevice::ReadOnly);
//code based on QAbstractItemModel::decodeData
// adapted to work with QStandardItem
int top = std::numeric_limits<int>::max();
int left = std::numeric_limits<int>::max();
int bottom = 0;
int right = 0;
QVector<int> rows, columns;
QVector<QStandardItem *> items;
while (!stream.atEnd()) {
int r, c;
QStandardItem *item = new QStandardItem();
stream >> r >> c;
decodeDataRecursive(stream, item);
rows.append(r);
columns.append(c);
items.append(item);
top = qMin(r, top);
left = qMin(c, left);
bottom = qMax(r, bottom);
right = qMax(c, right);
}
// insert the dragged items into the table, use a bit array to avoid overwriting items,
// since items from different tables can have the same row and column
int dragRowCount = 0;
int dragColumnCount = right - left + 1;
// Compute the number of continuous rows upon insertion and modify the rows to match
QVector<int> rowsToInsert(bottom + 1);
for (int i = 0; i < rows.count(); ++i)
rowsToInsert[rows.at(i)] = 1;
for (int i = 0; i < rowsToInsert.count(); ++i) {
if (rowsToInsert[i] == 1){
rowsToInsert[i] = dragRowCount;
++dragRowCount;
}
}
for (int i = 0; i < rows.count(); ++i)
rows[i] = top + rowsToInsert[rows[i]];
QBitArray isWrittenTo(dragRowCount * dragColumnCount);
// make space in the table for the dropped data
int colCount = columnCount(parent);
if (colCount < dragColumnCount + column) {
insertColumns(colCount, dragColumnCount + column - colCount, parent);
colCount = columnCount(parent);
}
insertRows(row, dragRowCount, parent);
row = qMax(0, row);
column = qMax(0, column);
QStandardItem *parentItem = itemFromIndex (parent);
if (!parentItem)
parentItem = invisibleRootItem();
QVector<QPersistentModelIndex> newIndexes(items.size());
// set the data in the table
for (int j = 0; j < items.size(); ++j) {
int relativeRow = rows.at(j) - top;
int relativeColumn = columns.at(j) - left;
int destinationRow = relativeRow + row;
int destinationColumn = relativeColumn + column;
int flat = (relativeRow * dragColumnCount) + relativeColumn;
// if the item was already written to, or we just can't fit it in the table, create a new row
if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) {
destinationColumn = qBound(column, destinationColumn, colCount - 1);
destinationRow = row + dragRowCount;
insertRows(row + dragRowCount, 1, parent);
flat = (dragRowCount * dragColumnCount) + relativeColumn;
isWrittenTo.resize(++dragRowCount * dragColumnCount);
}
if (!isWrittenTo.testBit(flat)) {
newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index());
isWrittenTo.setBit(flat);
}
}
for(int k = 0; k < newIndexes.size(); k++) {
if (newIndexes.at(k).isValid()) {
parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k));
} else {
delete items.at(k);
}
}
// The important part, tell the view what is dropped
emit indexesDropped(newIndexes);
return true;
}

View file

@ -0,0 +1,21 @@
#ifndef PLUGINSMODEL_H
#define PLUGINSMODEL_H
#include <QStandardItemModel>
class PluginsModel : public QStandardItemModel
{
Q_OBJECT
public:
PluginsModel(QObject *parent = 0);
~PluginsModel() {};
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
signals:
void indexesDropped(QVector<QPersistentModelIndex> indexes);
};
#endif

View file

@ -0,0 +1,42 @@
#include <QDebug>
#include <QSortFilterProxyModel>
#include "pluginsview.hpp"
PluginsView::PluginsView(QWidget *parent) : QTableView(parent)
{
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setAlternatingRowColors(true);
setDragEnabled(true);
setDragDropMode(QAbstractItemView::InternalMove);
setDropIndicatorShown(true);
setDragDropOverwriteMode(false);
setContextMenuPolicy(Qt::CustomContextMenu);
}
void PluginsView::startDrag(Qt::DropActions supportedActions)
{
selectionModel()->select( selectionModel()->selection(),
QItemSelectionModel::Select | QItemSelectionModel::Rows );
QAbstractItemView::startDrag( supportedActions );
}
void PluginsView::setModel(QSortFilterProxyModel *model)
{
QTableView::setModel(model);
qRegisterMetaType< QVector<QPersistentModelIndex> >();
connect(model->sourceModel(), SIGNAL(indexesDropped(QVector<QPersistentModelIndex>)),
this, SLOT(selectIndexes(QVector<QPersistentModelIndex>)), Qt::QueuedConnection);
}
void PluginsView::selectIndexes( QVector<QPersistentModelIndex> aIndexes )
{
selectionModel()->clearSelection();
foreach( QPersistentModelIndex pIndex, aIndexes )
selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows );
}

View file

@ -0,0 +1,29 @@
#ifndef PLUGINSVIEW_H
#define PLUGINSVIEW_H
#include <QTableView>
#include "pluginsmodel.hpp"
class QSortFilterProxyModel;
class PluginsView : public QTableView
{
Q_OBJECT
public:
PluginsView(QWidget *parent = 0);
PluginsModel* model() const
{ return qobject_cast<PluginsModel*>(QAbstractItemView::model()); }
void startDrag(Qt::DropActions supportedActions);
void setModel(QSortFilterProxyModel *model);
public slots:
void selectIndexes(QVector<QPersistentModelIndex> aIndexes);
};
Q_DECLARE_METATYPE(QVector<QPersistentModelIndex>);
#endif

View file

@ -0,0 +1,21 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/images">
<file alias="clear.png">resources/images/clear.png</file>
<file alias="down.png">resources/images/down.png</file>
<file alias="openmw.png">resources/images/openmw.png</file>
<file alias="openmw-plugin.png">resources/images/openmw-plugin.png</file>
<file alias="openmw-header.png">resources/images/openmw-header.png</file>
<file alias="playpage-background.png">resources/images/playpage-background.png</file>
</qresource>
<qresource prefix="icons/tango">
<file alias="index.theme">resources/icons/tango/index.theme</file>
<file alias="video-display.png">resources/icons/tango/video-display.png</file>
<file alias="16x16/document-new.png">resources/icons/tango/document-new.png</file>
<file alias="16x16/edit-copy.png">resources/icons/tango/edit-copy.png</file>
<file alias="16x16/edit-delete.png">resources/icons/tango/edit-delete.png</file>
<file alias="16x16/go-bottom.png">resources/icons/tango/go-bottom.png</file>
<file alias="16x16/go-down.png">resources/icons/tango/go-down.png</file>
<file alias="16x16/go-top.png">resources/icons/tango/go-top.png</file>
<file alias="16x16/go-up.png">resources/icons/tango/go-up.png</file>
</qresource>
</RCC>

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

View file

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

View file

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

View file

@ -0,0 +1,9 @@
#ifndef CONFIG_H
#define CONFIG_H
#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@
#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@
#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@
#define OPENMW_VERSION "@OPENMW_VERSION@"
#endif

View file

@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include <OgreVector3.h> #include <OgreVector3.h>
#include <Ogre.h>
#include "components/esm/records.hpp" #include "components/esm/records.hpp"
#include <components/esm_store/cell_store.hpp> #include <components/esm_store/cell_store.hpp>
@ -44,10 +45,6 @@
#include <OgreRoot.h> #include <OgreRoot.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OSX/macUtils.h>
#endif
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include "mwgui/class.hpp" #include "mwgui/class.hpp"
@ -76,7 +73,7 @@ void OMW::Engine::executeLocalScripts()
} }
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt) bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
{ {
if(mShowFPS) if(mShowFPS)
{ {
@ -92,6 +89,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
std::string effect; std::string effect;
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell(); MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
//If the region has changed //If the region has changed
if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){ if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){
timer.restart(); timer.restart();
@ -153,6 +152,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
{ {
mEnvironment.mFrameDuration = evt.timeSinceLastFrame; mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
//
mEnvironment.mWindowManager->onFrame(mEnvironment.mFrameDuration);
// global scripts // global scripts
mEnvironment.mGlobalScripts->run (mEnvironment); mEnvironment.mGlobalScripts->run (mEnvironment);
@ -246,8 +248,12 @@ void OMW::Engine::loadBSA()
for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter) for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
{ {
std::cout << "Adding " << iter->second.string() << std::endl; std::cout << "Adding " << iter->second.string() << std::endl;
addBSA (iter->second.string()); Bsa::addBSA (iter->second.string());
} }
std::string m = mDataDir.string();
std::cout << "Data dir" << m << "\n";
Bsa::addDir(m, mFSStrict);
} }
// add resources directory // add resources directory
@ -338,15 +344,12 @@ void OMW::Engine::go()
ogreCfg.insert(0, cfgUserDir); ogreCfg.insert(0, cfgUserDir);
//A local plugins.cfg will be used if it exist, otherwise look in the default path //A local plugins.cfg will be used if it exist, otherwise look in the default path
if(!isFile(plugCfg.c_str())) if(!Misc::isFile(plugCfg.c_str()))
{ {
plugCfg.insert(0, cfgDir); plugCfg.insert(0, cfgDir);
} }
mOgre.configure(!isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false); mOgre.configure(!Misc::isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false);
addResourcesDirectory (mDataDir / "Meshes");
addResourcesDirectory (mDataDir / "Textures");
// This has to be added BEFORE MyGUI is initialized, as it needs // This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here. // to find core.xml here.
@ -358,12 +361,12 @@ void OMW::Engine::go()
loadBSA(); loadBSA();
// Create physics. shapeLoader is deleted by the physic engine // Create physics. shapeLoader is deleted by the physic engine
ManualBulletShapeLoader* shapeLoader = new ManualBulletShapeLoader(); NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader); mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
// Create the world // Create the world
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster, mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
mResDir, mNewGame, mEnvironment); mResDir, mNewGame, mEnvironment, mEncoding);
// Set up the GUI system // Set up the GUI system
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir); mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
@ -384,7 +387,7 @@ void OMW::Engine::go()
mOgre.getCamera(), mOgre.getCamera(),
mEnvironment.mWorld->getStore(), mEnvironment.mWorld->getStore(),
(mDataDir), (mDataDir),
mUseSound); mUseSound, mFSStrict);
// Create script system // Create script system
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full, mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
@ -512,3 +515,8 @@ void OMW::Engine::setCompileAll (bool all)
{ {
mCompileAll = all; mCompileAll = all;
} }
void OMW::Engine::setEncoding(const std::string& encoding)
{
mEncoding = encoding;
}

View file

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

View file

@ -26,6 +26,13 @@
#endif #endif
// for Ogre::macBundlePath
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OSX/macUtils.h>
#endif
#include "config.hpp"
using namespace std; using namespace std;
/// Parse command line options and openmw.cfg file (if one exists). Results are directly /// Parse command line options and openmw.cfg file (if one exists). Results are directly
@ -41,7 +48,8 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
"Syntax: openmw <options>\nAllowed options"); "Syntax: openmw <options>\nAllowed options");
desc.add_options() desc.add_options()
("help", "print help message") ("help", "print help message and quit")
("version", "print version information and quit")
("data", bpo::value<std::vector<std::string> >() ("data", bpo::value<std::vector<std::string> >()
->default_value (std::vector<std::string>(), "data") ->default_value (std::vector<std::string>(), "data")
->multitoken(), ->multitoken(),
@ -77,6 +85,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
( "fs-strict", boost::program_options::value<bool>()-> ( "fs-strict", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false), implicit_value (true)->default_value (false),
"strict file system handling (no case folding)") "strict file system handling (no case folding)")
( "encoding", boost::program_options::value<std::string>()->
default_value("win1252"),
"Character encoding used in OpenMW game messages:\n"
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
; ;
bpo::variables_map variables; bpo::variables_map variables;
@ -84,7 +99,7 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
//If there is an openmw.cfg in the current path use that as global config //If there is an openmw.cfg in the current path use that as global config
//Otherwise try getPath //Otherwise try getPath
std::string cfgFile = "openmw.cfg"; std::string cfgFile = "openmw.cfg";
if(!isFile(cfgFile.c_str())) if(!Misc::isFile(cfgFile.c_str()))
{ {
cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg"); cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg");
} }
@ -105,10 +120,39 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
if (globalConfigFile.is_open()) if (globalConfigFile.is_open())
bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables); bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables);
bool run = true;
if (variables.count ("help")) if (variables.count ("help"))
{ {
std::cout << desc << std::endl; std::cout << desc << std::endl;
run = false;
}
if (variables.count ("version"))
{
std::cout << "OpenMW version " << OPENMW_VERSION << std::endl;
run = false;
}
if (!run)
return false; return false;
// Font encoding settings
std::string encoding(variables["encoding"].as<std::string>());
if (encoding == "win1250")
{
std::cout << "Using Central and Eastern European font encoding." << std::endl;
engine.setEncoding(encoding);
}
else if (encoding == "win1251")
{
std::cout << "Using Cyrillic font encoding." << std::endl;
engine.setEncoding(encoding);
}
else
{
std::cout << "Using default (English) font encoding." << std::endl;
engine.setEncoding("win1252");
} }
// directory settings // directory settings
@ -177,7 +221,7 @@ int main(int argc, char**argv)
{ {
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// set current dir to bundle path // set current dir to bundle path
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()); boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
boost::filesystem::current_path(bundlePath); boost::filesystem::current_path(bundlePath);
#endif #endif

View file

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

View file

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

View file

@ -14,11 +14,11 @@
namespace MWClass namespace MWClass
{ {
void Misc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender, void Miscellaneous::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
MWWorld::Environment& environment) const MWWorld::Environment& environment) const
{ {
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref = ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Misc>(); ptr.get<ESM::Miscellaneous>();
assert (ref->base != NULL); assert (ref->base != NULL);
const std::string &model = ref->base->model; const std::string &model = ref->base->model;
@ -31,39 +31,39 @@ namespace MWClass
} }
} }
std::string Misc::getName (const MWWorld::Ptr& ptr) const std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const
{ {
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref = ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Misc>(); ptr.get<ESM::Miscellaneous>();
return ref->base->name; return ref->base->name;
} }
boost::shared_ptr<MWWorld::Action> Misc::activate (const MWWorld::Ptr& ptr, boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
{ {
return boost::shared_ptr<MWWorld::Action> ( return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr)); new MWWorld::ActionTake (ptr));
} }
void Misc::insertIntoContainer (const MWWorld::Ptr& ptr, void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{ {
insertIntoContainerStore (ptr, containerStore.miscItems); insertIntoContainerStore (ptr, containerStore.miscItems);
} }
std::string Misc::getScript (const MWWorld::Ptr& ptr) const std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const
{ {
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref = ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Misc>(); ptr.get<ESM::Miscellaneous>();
return ref->base->script; return ref->base->script;
} }
void Misc::registerSelf() void Miscellaneous::registerSelf()
{ {
boost::shared_ptr<Class> instance (new Misc); boost::shared_ptr<Class> instance (new Miscellaneous);
registerClass (typeid (ESM::Misc).name(), instance); registerClass (typeid (ESM::Miscellaneous).name(), instance);
} }
} }

View file

@ -5,7 +5,7 @@
namespace MWClass namespace MWClass
{ {
class Misc : public MWWorld::Class class Miscellaneous : public MWWorld::Class
{ {
public: public:

View file

@ -95,8 +95,7 @@ namespace MWClass
upperright[uppernumbers++] = npcName + "chest"; upperright[uppernumbers++] = npcName + "chest";
neckandup[neckNumbers++] = npcName + "chest"; neckandup[neckNumbers++] = npcName + "chest";
} }
//std::cout << "GETTING NPC PART";
//Orgre::SceneNode test = cellRender.getNpcPart();
const ESM::BodyPart *upperleg = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg"); const ESM::BodyPart *upperleg = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg");
const ESM::BodyPart *groin = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin"); const ESM::BodyPart *groin = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin");
@ -113,8 +112,6 @@ namespace MWClass
const ESM::BodyPart *hands = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands.1st"); const ESM::BodyPart *hands = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands.1st");
//std::cout << "RACE" << bodyRaceID << "\n";
Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75); Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75);
if (groin){ if (groin){
@ -124,10 +121,9 @@ namespace MWClass
} }
if (tail) { if (tail) {
cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail"); cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail");
//std::cout << "TAIL\n";
} }
//addresses[1] = npcName + "groin";
if(upperleg){ if(upperleg){
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18 cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( -6, 0, -16), axis, Ogre::Radian(0), npcName + "upper leg2", addresses2, numbers); cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( -6, 0, -16), axis, Ogre::Radian(0), npcName + "upper leg2", addresses2, numbers);
@ -218,9 +214,6 @@ namespace MWClass
if(hand) if(hand)
{ {
//std::cout << "WE FOUND A HAND\n";
//-50, 0, -120
//std::cout << "WE FOUND HANDS\n";
std::string pass; std::string pass;
if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0) if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0)
pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF"; pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF";
@ -230,7 +223,6 @@ namespace MWClass
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0,0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120 cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0,0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
upperleft[uppernumbers] = npcName + "hand"; upperleft[uppernumbers] = npcName + "hand";
upperright[uppernumbers++] = npcName + "hand2"; upperright[uppernumbers++] = npcName + "hand2";
//cellRender.rotateMesh(Ogre::Vector3(0, 0,0), kOgrePi, upperleft, uppernumbers);
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperleft, uppernumbers); cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperleft, uppernumbers);
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperright, uppernumbers); cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperright, uppernumbers);
} }
@ -244,7 +236,6 @@ namespace MWClass
else else
pass =hands->model; //-50, 0, -120 pass =hands->model; //-50, 0, -120
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1,-110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110 cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1,-110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110
//cellRender.insertMesh("meshes\\" + hands->model, Ogre::Vector3(42, 0,110), Ogre::Vector3(1, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120 cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
upperleft[uppernumbers] = npcName + "hand"; upperleft[uppernumbers] = npcName + "hand";
upperright[uppernumbers++] = npcName + "hand2"; upperright[uppernumbers++] = npcName + "hand2";

View file

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

View file

@ -171,6 +171,22 @@ namespace MWGui
MyGUI::KeyCode key, MyGUI::KeyCode key,
MyGUI::Char _char) MyGUI::Char _char)
{ {
if( key == MyGUI::KeyCode::Tab)
{
std::vector<std::string> matches;
listNames();
command->setCaption(complete( command->getCaption(), matches ));
#if 0
int i = 0;
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); it++,i++ )
{
printOK( *it );
if( i == 50 )
break;
}
#endif
}
if(command_history.empty()) return; if(command_history.empty()) return;
// Traverse history with up and down arrows // Traverse history with up and down arrows
@ -223,11 +239,11 @@ namespace MWGui
try try
{ {
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr()); ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
Interpreter::Interpreter interpreter (interpreterContext); Interpreter::Interpreter interpreter;
MWScript::installOpcodes (interpreter); MWScript::installOpcodes (interpreter);
std::vector<Interpreter::Type_Code> code; std::vector<Interpreter::Type_Code> code;
output.getCode (code); output.getCode (code);
interpreter.run (&code[0], code.size()); interpreter.run (&code[0], code.size(), interpreterContext);
} }
catch (const std::exception& error) catch (const std::exception& error)
{ {
@ -237,4 +253,119 @@ namespace MWGui
command->setCaption(""); command->setCaption("");
} }
std::string Console::complete( std::string input, std::vector<std::string> &matches )
{
using namespace std;
string output=input;
string tmp=input;
bool has_front_quote = false;
/* Does the input string contain things that don't have to be completed? If yes erase them. */
/* Are there quotation marks? */
if( tmp.find('"') != string::npos ) {
int numquotes=0;
for(string::iterator it=tmp.begin(); it < tmp.end(); it++) {
if( *it == '"' )
numquotes++;
}
/* Is it terminated?*/
if( numquotes % 2 ) {
tmp.erase( 0, tmp.rfind('"')+1 );
has_front_quote = true;
}
else {
size_t pos;
if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) {
tmp.erase( 0, tmp.rfind(' ')+1);
}
else {
tmp.clear();
}
has_front_quote = false;
}
}
/* No quotation marks. Are there spaces?*/
else {
size_t rpos;
if( (rpos=tmp.rfind(' ')) != string::npos ) {
if( rpos == 0 ) {
tmp.clear();
}
else {
tmp.erase(0, rpos+1);
}
}
}
/* Erase the input from the output string so we can easily append the completed form later. */
output.erase(output.end()-tmp.length(), output.end());
/* Is there still something in the input string? If not just display all commands and return the unchanged input. */
if( tmp.length() == 0 ) {
matches=mNames;
return input;
}
/* Iterate through the vector. */
for(vector<string>::iterator it=mNames.begin(); it < mNames.end();it++) {
bool string_different=false;
/* Is the string shorter than the input string? If yes skip it. */
if( (*it).length() < tmp.length() )
continue;
/* Is the beginning of the string different from the input string? If yes skip it. */
for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) {
if( tolower(*iter) != tolower(*iter2) ) {
string_different=true;
break;
}
}
if( string_different )
continue;
/* The beginning of the string matches the input string, save it for the next test. */
matches.push_back(*it);
}
/* There are no matches. Return the unchanged input. */
if( matches.empty() )
{
return input;
}
/* Only one match. We're done. */
if( matches.size() == 1 ) {
/* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/
if( ( matches.front().find(' ') != string::npos ) ) {
if( !has_front_quote )
output.append(string("\""));
return output.append(matches.front() + string("\" "));
}
else if( has_front_quote ) {
return output.append(matches.front() + string("\" "));
}
else {
return output.append(matches.front() + string(" "));
}
}
/* Check if all matching strings match further than input. If yes complete to this match. */
int i = tmp.length();
for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) {
for(vector<string>::iterator it=matches.begin(); it < matches.end();it++) {
if( tolower((*it)[i]) != tolower(*iter) ) {
/* Append the longest match to the end of the output string*/
output.append(matches.front().substr( 0, i));
return output;
}
}
}
/* All keywords match with the shortest. Append it to the output string and return it. */
return output.append(matches.front());
}
} }

View file

@ -80,6 +80,8 @@ namespace MWGui
MyGUI::Char _char); MyGUI::Char _char);
void acceptCommand(MyGUI::EditPtr _sender); void acceptCommand(MyGUI::EditPtr _sender);
std::string complete( std::string input, std::vector<std::string> &matches );
}; };
} }
#endif #endif

View file

@ -0,0 +1,394 @@
#include "messagebox.hpp"
using namespace MWGui;
MessageBoxManager::MessageBoxManager (WindowManager *windowManager)
{
mWindowManager = windowManager;
// defines
mMessageBoxSpeed = 0.1;
mInterMessageBoxe = NULL;
}
void MessageBoxManager::onFrame (float frameDuration)
{
std::vector<MessageBoxManagerTimer>::iterator it;
for(it = mTimers.begin(); it != mTimers.end();)
{
it->current += frameDuration;
if(it->current >= it->max)
{
it->messageBox->mMarkedToDelete = true;
if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one
{
// collect all with mMarkedToDelete and delete them.
// and place the other messageboxes on the right position
int height = 0;
std::vector<MessageBox*>::iterator it2 = mMessageBoxes.begin();
while(it2 != mMessageBoxes.end())
{
if((*it2)->mMarkedToDelete)
{
delete (*it2);
it2 = mMessageBoxes.erase(it2);
}
else {
(*it2)->update(height);
height += (*it2)->getHeight();
it2++;
}
}
}
it = mTimers.erase(it);
}
else
{
it++;
}
}
if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) {
delete mInterMessageBoxe;
mInterMessageBoxe = NULL;
mWindowManager->setNextMode(GM_Game);
}
}
void MessageBoxManager::createMessageBox (const std::string& message)
{
std::cout << "MessageBox: " << message << std::endl;
MessageBox *box = new MessageBox(*this, message);
removeMessageBox(message.length()*mMessageBoxSpeed, box);
mMessageBoxes.push_back(box);
std::vector<MessageBox*>::iterator it;
if(mMessageBoxes.size() > 3) {
delete *mMessageBoxes.begin();
mMessageBoxes.erase(mMessageBoxes.begin());
}
int height = 0;
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
{
(*it)->update(height);
height += (*it)->getHeight();
}
}
bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons)
{
if(mInterMessageBoxe != NULL) {
std::cout << "there is a MessageBox already" << std::endl;
return false;
}
std::cout << "interactive MessageBox: " << message << " - ";
std::copy (buttons.begin(), buttons.end(), std::ostream_iterator<std::string> (std::cout, ", "));
std::cout << std::endl;
mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);
return true;
}
bool MessageBoxManager::isInteractiveMessageBox ()
{
return mInterMessageBoxe != NULL;
}
void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox)
{
MessageBoxManagerTimer timer;
timer.current = 0;
timer.max = time;
timer.messageBox = msgbox;
mTimers.insert(mTimers.end(), timer);
}
bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)
{
std::vector<MessageBox*>::iterator it;
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
{
if((*it) == msgbox)
{
delete (*it);
mMessageBoxes.erase(it);
return true;
}
}
return false;
}
void MessageBoxManager::setMessageBoxSpeed (int speed)
{
mMessageBoxSpeed = speed;
}
int MessageBoxManager::readPressedButton ()
{
if(mInterMessageBoxe != NULL)
{
return mInterMessageBoxe->readPressedButton();
}
return -1;
}
MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message)
: Layout("openmw_messagebox_layout.xml")
, mMessageBoxManager(parMessageBoxManager)
, cMessage(message)
{
// defines
mFixedWidth = 300;
mBottomPadding = 20;
mNextBoxPadding = 20;
mMarkedToDelete = false;
getWidget(mMessageWidget, "message");
mMessageWidget->setOverflowToTheLeft(true);
mMessageWidget->addText(cMessage);
MyGUI::IntSize size;
size.width = mFixedWidth;
size.height = 100; // dummy
MyGUI::IntCoord coord;
coord.left = 10; // dummy
coord.top = 10; // dummy
mMessageWidget->setSize(size);
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box
mMainWidget->setSize(size);
size.width -= 15; // this is to center the text (see messagebox_layout.xml, Widget type="Edit" position="-2 -3 0 0")
mMessageWidget->setSize(size);
}
void MessageBox::update (int height)
{
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
MyGUI::IntCoord coord;
coord.left = (gameWindowSize.width - mFixedWidth)/2;
coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding);
MyGUI::IntSize size;
size.width = mFixedWidth;
size.height = mHeight;
mMainWidget->setCoord(coord);
mMainWidget->setSize(size);
mMainWidget->setVisible(true);
}
int MessageBox::getHeight ()
{
return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox
}
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
: Layout("openmw_interactive_messagebox_layout.xml")
, mMessageBoxManager(parMessageBoxManager)
, mButtonPressed(-1)
{
int fixedWidth = 500;
int textPadding = 10; // padding between text-widget and main-widget
int textButtonPadding = 20; // padding between the text-widget und the button-widget
int buttonLeftPadding = 10; // padding between the buttons if horizontal
int buttonTopPadding = 5; // ^-- if vertical
int buttonPadding = 5; // padding between button label and button itself
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
mMarkedToDelete = false;
getWidget(mMessageWidget, "message");
getWidget(mButtonsWidget, "buttons");
mMessageWidget->setOverflowToTheLeft(true);
mMessageWidget->addText(message);
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
int biggestButtonWidth = 0;
int buttonWidth = 0;
int buttonsWidth = 0;
int buttonHeight = 0;
MyGUI::IntCoord dummyCoord(0, 0, 0, 0);
std::vector<std::string>::const_iterator it;
for(it = buttons.begin(); it != buttons.end(); ++it)
{
MyGUI::ButtonPtr button = mButtonsWidget->createWidget<MyGUI::Button>(
MyGUI::WidgetStyle::Child,
std::string("MW_Button"),
dummyCoord,
MyGUI::Align::Default);
button->setCaption(*it);
button->eventMouseButtonClick = MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
mButtons.push_back(button);
buttonWidth = button->_getTextSize().width + 2*buttonPadding + buttonLeftPadding;
buttonsWidth += buttonWidth;
buttonHeight = button->_getTextSize().height + 2*buttonPadding + buttonTopPadding;
if(buttonWidth > biggestButtonWidth)
{
biggestButtonWidth = buttonWidth;
}
}
buttonsWidth += buttonLeftPadding;
MyGUI::IntSize mainWidgetSize;
if(buttonsWidth < fixedWidth)
{
// on one line
std::cout << "on one line" << std::endl;
if(textSize.width + 2*textPadding < buttonsWidth)
{
std::cout << "width = buttonsWidth" << std::endl;
mainWidgetSize.width = buttonsWidth;
}
else
{
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
MyGUI::IntCoord absCoord;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
std::cout << "left " << absCoord.left << " top " << absCoord.top << std::endl;
mMainWidget->setCoord(absCoord);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
std::vector<MyGUI::ButtonPtr>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
buttonCord.left = left;
buttonCord.top = textSize.height + textButtonPadding;
buttonSize.width = (*button)->_getTextSize().width + 2*buttonPadding;
buttonSize.height = (*button)->_getTextSize().height + 2*buttonPadding;
(*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize);
left += buttonSize.width + buttonLeftPadding;
}
}
else
{
// among each other
if(biggestButtonWidth > textSize.width) {
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
}
else {
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
std::cout << "biggestButtonWidth " << biggestButtonWidth << " textSize.width " << textSize.width << std::endl;
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord absCoord;
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setCoord(absCoord);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
int top = textButtonPadding + buttonTopPadding + textSize.height;
std::vector<MyGUI::ButtonPtr>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
buttonSize.width = (*button)->_getTextSize().width + buttonPadding*2;
buttonSize.height = (*button)->_getTextSize().height + buttonPadding*2;
buttonCord.top = top;
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
(*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize);
top += buttonSize.height + 2*buttonTopPadding;
}
}
}
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
{
mMarkedToDelete = true;
int index = 0;
std::vector<MyGUI::ButtonPtr>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
if(*button == pressed)
{
mButtonPressed = index;
return;
}
index++;
}
std::cout << "Cant be possible :/" << std::endl;
}
int InteractiveMessageBox::readPressedButton ()
{
int pressed = mButtonPressed;
mButtonPressed = -1;
return pressed;
}

View file

@ -0,0 +1,89 @@
#ifndef MWGUI_MESSAGE_BOX_H
#define MWGUI_MESSAGE_BOX_H
#include <openengine/gui/layout.hpp>
#include <MyGUI.h>
#include "window_base.hpp"
#include "window_manager.hpp"
namespace MWGui
{
class InteractiveMessageBox;
class MessageBoxManager;
class MessageBox;
struct MessageBoxManagerTimer {
float current;
float max;
MessageBox *messageBox;
};
class MessageBoxManager
{
public:
MessageBoxManager (WindowManager* windowManager);
void onFrame (float frameDuration);
void createMessageBox (const std::string& message);
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
bool isInteractiveMessageBox ();
void removeMessageBox (float time, MessageBox *msgbox);
bool removeMessageBox (MessageBox *msgbox);
void setMessageBoxSpeed (int speed);
int readPressedButton ();
WindowManager *mWindowManager;
private:
std::vector<MessageBox*> mMessageBoxes;
InteractiveMessageBox* mInterMessageBoxe;
std::vector<MessageBoxManagerTimer> mTimers;
float mMessageBoxSpeed;
};
class MessageBox : public OEngine::GUI::Layout
{
public:
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
void setMessage (const std::string& message);
int getHeight ();
void update (int height);
bool mMarkedToDelete;
protected:
MessageBoxManager& mMessageBoxManager;
int mHeight;
const std::string& cMessage;
MyGUI::EditPtr mMessageWidget;
int mFixedWidth;
int mBottomPadding;
int mNextBoxPadding;
};
class InteractiveMessageBox : public OEngine::GUI::Layout
{
public:
InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
void mousePressed (MyGUI::Widget* _widget);
int readPressedButton ();
bool mMarkedToDelete;
private:
MessageBoxManager& mMessageBoxManager;
MyGUI::EditPtr mMessageWidget;
MyGUI::WidgetPtr mButtonsWidget;
std::vector<MyGUI::ButtonPtr> mButtons;
int mTextButtonPadding;
int mButtonPressed;
};
}
#endif

View file

@ -26,7 +26,10 @@ namespace MWGui
GM_ClassGenerate, GM_ClassGenerate,
GM_ClassPick, GM_ClassPick,
GM_ClassCreate, GM_ClassCreate,
GM_Review GM_Review,
// interactive MessageBox
GM_InterMessageBox
}; };
// Windows shown in inventory mode // Windows shown in inventory mode

View file

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

View file

@ -62,6 +62,7 @@ namespace MWGui
class CreateClassDialog; class CreateClassDialog;
class BirthDialog; class BirthDialog;
class ReviewDialog; class ReviewDialog;
class MessageBoxManager;
struct ClassPoint struct ClassPoint
{ {
@ -84,6 +85,7 @@ namespace MWGui
MapWindow *map; MapWindow *map;
MainMenu *menu; MainMenu *menu;
StatsWindow *stats; StatsWindow *stats;
MessageBoxManager *mMessageBoxManager;
#if 0 #if 0
InventoryWindow *inventory; InventoryWindow *inventory;
#endif #endif
@ -245,8 +247,13 @@ namespace MWGui
void removeDialog(OEngine::GUI::Layout* dialog); void removeDialog(OEngine::GUI::Layout* dialog);
///< Hides dialog and schedules dialog to be deleted. ///< Hides dialog and schedules dialog to be deleted.
void messageBox (const std::string& message, const std::vector<std::string>& buttons); void messageBox (const std::string& message, const std::vector<std::string>& buttons);
int readPressedButton ();
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
void onFrame (float frameDuration);
/** /**
* Fetches a GMST string from the store, if there is no setting with the given * Fetches a GMST string from the store, if there is no setting with the given

View file

@ -258,7 +258,7 @@ namespace MWInput
} }
//NOTE: Used to check for movement keys //NOTE: Used to check for movement keys
bool frameStarted(const Ogre::FrameEvent &evt) bool frameRenderingQueued (const Ogre::FrameEvent &evt)
{ {
// Tell OIS to handle all input events // Tell OIS to handle all input events
input.capture(); input.capture();

View file

@ -35,7 +35,7 @@ namespace MWRender
virtual ~CellRenderImp() {} virtual ~CellRenderImp() {}
/// start inserting a new reference. /// start inserting a new reference.
virtual void insertBegin (ESM::CellRef &ref) = 0; virtual void insertBegin (ESM::CellRef &ref, bool static_ = false) = 0;
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements) = 0; virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements) = 0;
/// insert a mesh related to the most recent insertBegin call. /// insert a mesh related to the most recent insertBegin call.
@ -71,10 +71,10 @@ namespace MWRender
public: public:
Rendering (CellRenderImp& cellRender, ESM::CellRef &ref) Rendering (CellRenderImp& cellRender, ESM::CellRef &ref, bool static_ = false)
: mCellRender (cellRender), mEnd (false) : mCellRender (cellRender), mEnd (false)
{ {
mCellRender.insertBegin (ref); mCellRender.insertBegin (ref, static_);
} }
~Rendering() ~Rendering()

View file

@ -31,12 +31,24 @@ float ExteriorCellRender::lightQuadraticRadiusMult = 1;
bool ExteriorCellRender::lightOutQuadInLin = false; bool ExteriorCellRender::lightOutQuadInLin = false;
// start inserting a new reference. int ExteriorCellRender::uniqueID = 0;
void ExteriorCellRender::insertBegin (ESM::CellRef &ref) ExteriorCellRender::ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
MWScene &_scene)
: mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0)
{
uniqueID = uniqueID +1;
sg = mScene.getMgr()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
}
void ExteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_)
{ {
assert (!mInsert); assert (!mInsert);
isStatic = static_;
// Create and place scene node for this object // Create and place scene node for this object
mInsert = mBase->createChildSceneNode(); mInsert = mBase->createChildSceneNode();
@ -59,7 +71,7 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref)
// Rotates first around z, then y, then x // Rotates first around z, then y, then x
mInsert->setOrientation(xr*yr*zr); mInsert->setOrientation(xr*yr*zr);
mInsertMesh.clear(); mInsertMesh.clear();
} }
@ -112,7 +124,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
} }
mNpcPart = parent->createChildSceneNode(sceneNodeName); mNpcPart = parent->createChildSceneNode(sceneNodeName);
MeshPtr good2 = NIFLoader::load(mesh); MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh); MovableObject *ent = mScene.getMgr()->createEntity(mesh);
@ -201,12 +213,21 @@ void ExteriorCellRender::insertMesh(const std::string &mesh)
{ {
assert (mInsert); assert (mInsert);
NIFLoader::load(mesh); NifOgre::NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh); Entity *ent = mScene.getMgr()->createEntity(mesh);
mInsert->attachObject(ent);
if (mInsertMesh.empty()) if(!isStatic)
mInsertMesh = mesh; {
mInsert->attachObject(ent);
}
else
{
sg->addEntity(ent,mInsert->_getDerivedPosition(),mInsert->_getDerivedOrientation(),mInsert->_getDerivedScale());
sg->setRegionDimensions(Ogre::Vector3(100000,10000,100000));
mScene.getMgr()->destroyEntity(ent);
}
if (mInsertMesh.empty())
mInsertMesh = mesh;
} }
void ExteriorCellRender::insertObjectPhysics() void ExteriorCellRender::insertObjectPhysics()
@ -333,6 +354,8 @@ void ExteriorCellRender::show()
configureFog(); configureFog();
insertCell(mCell, mEnvironment); insertCell(mCell, mEnvironment);
sg->build();
} }
void ExteriorCellRender::hide() void ExteriorCellRender::hide()
@ -341,15 +364,49 @@ void ExteriorCellRender::hide()
mBase->setVisible(false); mBase->setVisible(false);
} }
void ExteriorCellRender::destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode)
{
if ( !i_pSceneNode )
{
assert( false );
return;
}
// Destroy all the attached objects
SceneNode::ObjectIterator itObject = i_pSceneNode->getAttachedObjectIterator();
while ( itObject.hasMoreElements() )
{
MovableObject* pObject = static_cast<MovableObject*>(itObject.getNext());
i_pSceneNode->getCreator()->destroyMovableObject( pObject );
}
// Recurse to child SceneNodes
SceneNode::ChildNodeIterator itChild = i_pSceneNode->getChildIterator();
while ( itChild.hasMoreElements() )
{
SceneNode* pChildNode = static_cast<SceneNode*>(itChild.getNext());
destroyAllAttachedMovableObjects( pChildNode );
}
}
void ExteriorCellRender::destroy() void ExteriorCellRender::destroy()
{ {
if(mBase) if(mBase)
{ {
destroyAllAttachedMovableObjects(mBase);
mBase->removeAndDestroyAllChildren(); mBase->removeAndDestroyAllChildren();
mScene.getMgr()->destroySceneNode(mBase); mScene.getMgr()->destroySceneNode(mBase);
} }
mBase = NULL; mBase = 0;
if (sg)
{
mScene.getMgr()->destroyStaticGeometry (sg);
sg = 0;
}
} }
// Switch through lighting modes. // Switch through lighting modes.

View file

@ -6,6 +6,7 @@
#include "OgreColourValue.h" #include "OgreColourValue.h"
#include <OgreMath.h> #include <OgreMath.h>
#include <Ogre.h>
namespace Ogre namespace Ogre
{ {
@ -57,19 +58,24 @@ namespace MWRender
std::string mInsertMesh; std::string mInsertMesh;
Ogre::SceneNode *mNpcPart; Ogre::SceneNode *mNpcPart;
//the static geometry
Ogre::StaticGeometry *sg;
bool isStatic;
// 0 normal, 1 more bright, 2 max // 0 normal, 1 more bright, 2 max
int mAmbientMode; int mAmbientMode;
Ogre::ColourValue mAmbientColor; Ogre::ColourValue mAmbientColor;
/// start inserting a new reference. /// start inserting a new reference.
virtual void insertBegin (ESM::CellRef &ref); virtual void insertBegin (ESM::CellRef &ref, bool static_ = false);
/// insert a mesh related to the most recent insertBegin call. /// insert a mesh related to the most recent insertBegin call.
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements);
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst); virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst);
virtual void insertMesh(const std::string &mesh); virtual void insertMesh(const std::string &mesh);
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements); virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements); virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
@ -95,8 +101,7 @@ namespace MWRender
public: public:
ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment, ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
MWScene &_scene) MWScene &_scene);
: mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0) {}
virtual ~ExteriorCellRender() { destroy(); } virtual ~ExteriorCellRender() { destroy(); }
@ -121,6 +126,10 @@ namespace MWRender
/// Remove the reference with the given handle permanently from the scene. /// Remove the reference with the given handle permanently from the scene.
virtual void deleteObject (const std::string& handle); virtual void deleteObject (const std::string& handle);
void destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode);
static int uniqueID;
}; };
} }

View file

@ -35,7 +35,7 @@ bool InteriorCellRender::lightOutQuadInLin = false;
// start inserting a new reference. // start inserting a new reference.
void InteriorCellRender::insertBegin (ESM::CellRef &ref) void InteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_)
{ {
assert (!insert); assert (!insert);
@ -106,7 +106,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
npcPart = parent->createChildSceneNode(sceneNodeName); npcPart = parent->createChildSceneNode(sceneNodeName);
//npcPart->showBoundingBox(true); //npcPart->showBoundingBox(true);
MeshPtr good2 = NIFLoader::load(mesh); MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh); MovableObject *ent = scene.getMgr()->createEntity(mesh);
//ent->extr //ent->extr
@ -182,11 +182,11 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
void InteriorCellRender::insertMesh(const std::string &mesh) void InteriorCellRender::insertMesh(const std::string &mesh)
{ {
assert (insert); assert (insert);
NIFLoader::load(mesh); NifOgre::NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh); MovableObject *ent = scene.getMgr()->createEntity(mesh);
insert->attachObject(ent); insert->attachObject(ent);
if (mInsertMesh.empty()) if (mInsertMesh.empty())
mInsertMesh = mesh; mInsertMesh = mesh;

View file

@ -63,7 +63,7 @@ namespace MWRender
Ogre::ColourValue ambientColor; Ogre::ColourValue ambientColor;
/// start inserting a new reference. /// start inserting a new reference.
virtual void insertBegin (ESM::CellRef &ref); virtual void insertBegin (ESM::CellRef &ref, bool static_ = false);
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements); virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements); virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
/// insert a mesh related to the most recent insertBegin call. /// insert a mesh related to the most recent insertBegin call.

View file

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

View file

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

View file

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

View file

@ -8,6 +8,7 @@
#include <components/compiler/streamerrorhandler.hpp> #include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/fileparser.hpp> #include <components/compiler/fileparser.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/types.hpp> #include <components/interpreter/types.hpp>
namespace ESMS namespace ESMS
@ -35,6 +36,8 @@ namespace MWScript
bool mVerbose; bool mVerbose;
Compiler::Context& mCompilerContext; Compiler::Context& mCompilerContext;
Compiler::FileParser mParser; Compiler::FileParser mParser;
Interpreter::Interpreter mInterpreter;
bool mOpcodesInstalled;
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts; std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;

View file

@ -84,20 +84,25 @@ namespace MWSound
// finding. It takes DOS paths (any case, \\ slashes or / slashes) // finding. It takes DOS paths (any case, \\ slashes or / slashes)
// relative to the sound dir, and translates them into full paths // relative to the sound dir, and translates them into full paths
// of existing files in the filesystem, if they exist. // of existing files in the filesystem, if they exist.
bool FSstrict;
FileFinder::FileFinder files; FileFinder::FileFinder files;
FileFinder::FileFinderStrict strict;
FileFinder::FileFinder musicpath;
FileFinder::FileFinderStrict musicpathStrict;
SoundImpl(Ogre::Root *root, Ogre::Camera *camera, SoundImpl(Ogre::Root *root, Ogre::Camera *camera,
const ESMS::ESMStore &str, const ESMS::ESMStore &str,
const std::string &soundDir) const std::string &soundDir, const std::string &musicDir, bool fsstrict)
: mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY))) : mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
, updater(mgr) , updater(mgr)
, cameraTracker(mgr) , cameraTracker(mgr)
, store(str) , store(str)
, files(soundDir) , files(soundDir), strict(soundDir)
,musicpath(musicDir), musicpathStrict(musicDir)
{ {
FSstrict = fsstrict;
cout << "Sound output: " << SOUND_OUT << endl; cout << "Sound output: " << SOUND_OUT << endl;
cout << "Sound decoder: " << SOUND_IN << endl; cout << "Sound decoder: " << SOUND_IN << endl;
// Attach the camera to the camera tracker // Attach the camera to the camera tracker
cameraTracker.followCamera(camera); cameraTracker.followCamera(camera);
@ -111,6 +116,8 @@ namespace MWSound
cameraTracker.unfollowCamera(); cameraTracker.unfollowCamera();
} }
static std::string toMp3(std::string str) static std::string toMp3(std::string str)
{ {
std::string::size_type i = str.rfind('.'); std::string::size_type i = str.rfind('.');
@ -122,28 +129,76 @@ namespace MWSound
return str; return str;
} }
bool hasFile(const std::string &str) bool hasFile(const std::string &str, bool music = false)
{ {
if(files.has(str)) return true; if(FSstrict == false)
// Not found? Try with .mp3 {
return files.has(toMp3(str)); if(music)
{
if(musicpath.has(str)) return true;
// Not found? Try with .mp3
return musicpath.has(toMp3(str));
}
else
{
if(files.has(str)) return true;
return files.has(toMp3(str));
}
}
else
{
if(music)
{
if(musicpathStrict.has(str)) return true;
// Not found? Try with .mp3
return musicpathStrict.has(toMp3(str));
}
else
{
if(strict.has(str)) return true;
return strict.has(toMp3(str));
}
}
} }
// Convert a Morrowind sound path (eg. Fx\funny.wav) to full path // Convert a Morrowind sound path (eg. Fx\funny.wav) to full path
// with proper slash conversion (eg. datadir/Sound/Fx/funny.wav) // with proper slash conversion (eg. datadir/Sound/Fx/funny.wav)
std::string convertPath(const std::string &str) std::string convertPath(const std::string &str, bool music = false)
{ {
// Search and return if(FSstrict == false)
if(files.has(str)) {
return files.lookup(str); // Search and return
if(music && musicpath.has(str))
return musicpath.lookup(str);
else if(files.has(str))
return files.lookup(str);
// Try mp3 if the wav wasn't found // Try mp3 if the wav wasn't found
std::string mp3 = toMp3(str); std::string mp3 = toMp3(str);
if(files.has(mp3)) if(music && musicpath.has(mp3))
return files.lookup(mp3); return musicpath.lookup(mp3);
else if(files.has(mp3))
return files.lookup(mp3);
}
else
{
if(music && musicpathStrict.has(str))
return musicpathStrict.lookup(str);
else if(strict.has(str))
return strict.lookup(str);
// Give up // Try mp3 if the wav wasn't found
return ""; std::string mp3 = toMp3(str);
if(music && musicpathStrict.has(mp3))
return musicpathStrict.lookup(mp3);
else if(strict.has(str))
return strict.lookup(mp3);
}
// Give up
return "";
} }
// Convert a soundId to file name, and modify the volume // Convert a soundId to file name, and modify the volume
@ -304,15 +359,31 @@ namespace MWSound
} }
}; };
void SoundManager::streamMusicFull (const std::string& filename)
{
if(!mData) return;
// Play the sound and tell it to stream, if possible. TODO:
// Store the reference, the jukebox will need to check status,
// control volume etc.
if (mData->music)
mData->music->stop();
mData->music = mData->mgr->load(filename);
mData->music->setStreaming(true);
mData->music->setVolume(0.4);
mData->music->play();
}
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera, SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
const ESMS::ESMStore &store, const ESMS::ESMStore &store,
boost::filesystem::path dataDir, boost::filesystem::path dataDir,
bool useSound) bool useSound, bool fsstrict)
: mData(NULL) : mData(NULL), fsStrict (fsstrict)
{ {
MP3Lookup(dataDir / "Music/Explore/"); MP3Lookup(dataDir / "Music/Explore/");
if(useSound) if(useSound)
mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string()); mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string(), (dataDir / "Music").string(), fsstrict);
} }
SoundManager::~SoundManager() SoundManager::~SoundManager()
@ -321,6 +392,16 @@ namespace MWSound
delete mData; delete mData;
} }
void SoundManager::streamMusic(const std::string& filename)
{
if(mData->hasFile(filename, true))
{
std::string fullpath = mData->convertPath(filename, true);
streamMusicFull(fullpath);
}
}
void SoundManager::MP3Lookup(boost::filesystem::path dir) void SoundManager::MP3Lookup(boost::filesystem::path dir)
{ {
boost::filesystem::directory_iterator dir_iter(dir), dir_end; boost::filesystem::directory_iterator dir_iter(dir), dir_end;
@ -353,7 +434,7 @@ namespace MWSound
try try
{ {
std::cout << "Playing " << music << "\n"; std::cout << "Playing " << music << "\n";
streamMusic(music); streamMusicFull(music);
} }
catch(std::exception &e) catch(std::exception &e)
{ {
@ -397,21 +478,6 @@ namespace MWSound
return !mData->isPlaying(ptr, "_say_sound"); return !mData->isPlaying(ptr, "_say_sound");
} }
void SoundManager::streamMusic (const std::string& filename)
{
if(!mData) return;
// Play the sound and tell it to stream, if possible. TODO:
// Store the reference, the jukebox will need to check status,
// control volume etc.
if (mData->music)
mData->music->stop();
mData->music = mData->mgr->load(filename);
mData->music->setStreaming(true);
mData->music->setVolume(0.4);
mData->music->play();
}
void SoundManager::playSound (const std::string& soundId, float volume, float pitch) void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
{ {

View file

@ -27,19 +27,28 @@ namespace MWSound
// Hide implementation details - engine.cpp is compiling // Hide implementation details - engine.cpp is compiling
// enough as it is. // enough as it is.
struct SoundImpl; struct SoundImpl;
SoundImpl *mData; SoundImpl *mData;
std::vector<boost::filesystem::path> files; std::vector<boost::filesystem::path> files;
bool fsStrict;
void streamMusicFull (const std::string& filename);
///< Play a soundifle
/// \param absolute filename
public: public:
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store, SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
boost::filesystem::path dataDir, bool useSound); boost::filesystem::path dataDir, bool useSound, bool fsstrict);
~SoundManager(); ~SoundManager();
void streamMusic(const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void startRandomTitle(); void startRandomTitle();
void MP3Lookup(boost::filesystem::path dir); void MP3Lookup(boost::filesystem::path dir);
//struct SoundImpl;
bool isMusicPlaying(); bool isMusicPlaying();
SoundImpl getMData(); SoundImpl getMData();
@ -51,9 +60,7 @@ namespace MWSound
bool sayDone (MWWorld::Ptr reference) const; bool sayDone (MWWorld::Ptr reference) const;
///< Is actor not speaking? ///< Is actor not speaking?
void streamMusic (const std::string& filename);
///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory.
void playSound (const std::string& soundId, float volume, float pitch); void playSound (const std::string& soundId, float volume, float pitch);
///< Play a sound, independently of 3D-position ///< Play a sound, independently of 3D-position

View file

@ -16,7 +16,7 @@ namespace MWWorld
ESMS::CellRefList<ESM::Ingredient, D> ingreds; ESMS::CellRefList<ESM::Ingredient, D> ingreds;
ESMS::CellRefList<ESM::Light, D> lights; ESMS::CellRefList<ESM::Light, D> lights;
ESMS::CellRefList<ESM::Tool, D> lockpicks; ESMS::CellRefList<ESM::Tool, D> lockpicks;
ESMS::CellRefList<ESM::Misc, D> miscItems; ESMS::CellRefList<ESM::Miscellaneous, D> miscItems;
ESMS::CellRefList<ESM::Probe, D> probes; ESMS::CellRefList<ESM::Probe, D> probes;
ESMS::CellRefList<ESM::Repair, D> repairs; ESMS::CellRefList<ESM::Repair, D> repairs;
ESMS::CellRefList<ESM::Weapon, D> weapons; ESMS::CellRefList<ESM::Weapon, D> weapons;

View file

@ -135,7 +135,7 @@ namespace MWWorld
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = cell.lockpicks.find (name)) if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = cell.lockpicks.find (name))
return Ptr (ref, &cell); return Ptr (ref, &cell);
if (ESMS::LiveCellRef<ESM::Misc, RefData> *ref = cell.miscItems.find (name)) if (ESMS::LiveCellRef<ESM::Miscellaneous, RefData> *ref = cell.miscItems.find (name))
return Ptr (ref, &cell); return Ptr (ref, &cell);
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = cell.npcs.find (name)) if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = cell.npcs.find (name))
@ -198,7 +198,7 @@ namespace MWWorld
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = searchViaHandle (handle, cell.lockpicks)) if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = searchViaHandle (handle, cell.lockpicks))
return Ptr (ref, &cell); return Ptr (ref, &cell);
if (ESMS::LiveCellRef<ESM::Misc, RefData> *ref = searchViaHandle (handle, cell.miscItems)) if (ESMS::LiveCellRef<ESM::Miscellaneous, RefData> *ref = searchViaHandle (handle, cell.miscItems))
return Ptr (ref, &cell); return Ptr (ref, &cell);
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = searchViaHandle (handle, cell.npcs)) if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = searchViaHandle (handle, cell.npcs))
@ -284,7 +284,6 @@ namespace MWWorld
removeScripts (iter->first); removeScripts (iter->first);
mEnvironment.mMechanicsManager->dropActors (iter->first); mEnvironment.mMechanicsManager->dropActors (iter->first);
iter->second->destroy();
mEnvironment.mSoundManager->stopSound (iter->first); mEnvironment.mSoundManager->stopSound (iter->first);
delete iter->second; delete iter->second;
mActiveCells.erase (iter); mActiveCells.erase (iter);
@ -410,9 +409,9 @@ namespace MWWorld
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, const std::string& master, const boost::filesystem::path& resDir,
bool newGame, Environment& environment) bool newGame, Environment& environment, const std::string& encoding)
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0), : mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
mSky (false), mCellChanged (false), mEnvironment (environment) mSky (false), mCellChanged (false), mEnvironment (environment), mNextDynamicRecord (0)
{ {
mPhysEngine = physEng; mPhysEngine = physEng;
@ -421,6 +420,7 @@ namespace MWWorld
std::cout << "Loading ESM " << masterPath.string() << "\n"; std::cout << "Loading ESM " << masterPath.string() << "\n";
// This parses the ESM file and loads a sample cell // This parses the ESM file and loads a sample cell
mEsm.setEncoding(encoding);
mEsm.open (masterPath.string()); mEsm.open (masterPath.string());
mStore.load (mEsm); mStore.load (mEsm);
@ -871,4 +871,36 @@ namespace MWWorld
{ {
return mScene.toggleRenderMode (mode); return mScene.toggleRenderMode (mode);
} }
std::pair<std::string, const ESM::Potion *> World::createRecord (const ESM::Potion& record)
{
/// \todo Rewrite the ESMStore so that a dynamic 2nd ESMStore can be attached to it.
/// This function should then insert the record into the 2nd store (the code for this
/// should also be moved to the ESMStore class). It might be a good idea to review
/// the STL-container usage of the ESMStore before the rewrite.
std::ostringstream stream;
stream << "$dynamic" << mNextDynamicRecord++;
const ESM::Potion *created =
&mStore.potions.list.insert (std::make_pair (stream.str(), record)).first->second;
mStore.all.insert (std::make_pair (stream.str(), ESM::REC_ALCH));
return std::make_pair (stream.str(), created);
}
std::pair<std::string, const ESM::Class *> World::createRecord (const ESM::Class& record)
{
/// \todo See function above.
std::ostringstream stream;
stream << "$dynamic" << mNextDynamicRecord++;
const ESM::Class *created =
&mStore.classes.list.insert (std::make_pair (stream.str(), record)).first->second;
mStore.all.insert (std::make_pair (stream.str(), ESM::REC_CLAS));
return std::make_pair (stream.str(), created);
}
} }

View file

@ -79,6 +79,7 @@ namespace MWWorld
bool mSky; bool mSky;
bool mCellChanged; bool mCellChanged;
Environment& mEnvironment; Environment& mEnvironment;
int mNextDynamicRecord;
OEngine::Physic::PhysicEngine* mPhysEngine; OEngine::Physic::PhysicEngine* mPhysEngine;
@ -115,7 +116,7 @@ namespace MWWorld
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng, World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections, const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, bool newGame, const std::string& master, const boost::filesystem::path& resDir, bool newGame,
Environment& environment); Environment& environment, const std::string& encoding);
~World(); ~World();
@ -202,6 +203,14 @@ namespace MWWorld
bool toggleRenderMode (RenderMode mode); bool toggleRenderMode (RenderMode mode);
///< Toggle a render mode. ///< Toggle a render mode.
///< \return Resulting mode ///< \return Resulting mode
std::pair<std::string, const ESM::Potion *> createRecord (const ESM::Potion& record);
///< Create a new recrod (of type potion) in the ESM store.
/// \return ID, pointer to created record
std::pair<std::string, const ESM::Class *> createRecord (const ESM::Class& record);
///< Create a new recrod (of type class) in the ESM store.
/// \return ID, pointer to created record
}; };
} }

View file

@ -23,16 +23,200 @@
#include "bsa_archive.hpp" #include "bsa_archive.hpp"
#include <OgreFileSystem.h>
#include <OgreArchive.h> #include <OgreArchive.h>
#include <OgreArchiveFactory.h> #include <OgreArchiveFactory.h>
#include <OgreArchiveManager.h> #include <OgreArchiveManager.h>
#include "bsa_file.hpp" #include "bsa_file.hpp"
#include <libs/mangle/stream/clients/ogre_datastream.hpp> #include <libs/mangle/stream/clients/ogre_datastream.hpp>
namespace
{
using namespace Ogre; using namespace Ogre;
using namespace Mangle::Stream; using namespace Mangle::Stream;
using namespace Bsa;
struct ciLessBoost : std::binary_function<std::string, std::string, bool>
{
bool operator() (const std::string & s1, const std::string & s2) const {
//case insensitive version of is_less
return lexicographical_compare(s1, s2, boost::algorithm::is_iless());
}
};
static bool fsstrict = false;
/// An OGRE Archive wrapping a BSAFile archive /// An OGRE Archive wrapping a BSAFile archive
class DirArchive: public Ogre::FileSystemArchive
{
boost::filesystem::path currentdir;
std::map<std::string, std::vector<std::string>, ciLessBoost> m;
unsigned int cutoff;
bool comparePortion(std::string file1, std::string file2, int start, int size) const
{
for(int i = start; i < start+size; i++)
{
char one = file1.at(i);
char two = file2.at(i);
if(tolower(one) != tolower(two) )
return false;
}
return true;
}
public:
DirArchive(const String& name)
: FileSystemArchive(name, "Dir"), currentdir (name)
{
mType = "Dir";
std::string s = name;
cutoff = s.size() + 1;
if(fsstrict == false)
populateMap(currentdir);
}
void populateMap(boost::filesystem::path d){
//need to cut off first
boost::filesystem::directory_iterator dir_iter(d), dir_end;
std::vector<std::string> filesind;
boost::filesystem::path f;
for(;dir_iter != dir_end; dir_iter++)
{
if(boost::filesystem::is_directory(*dir_iter))
populateMap(*dir_iter);
else
{
f = *dir_iter;
std::string s = f.string();
std::string small;
if(cutoff < s.size())
small = s.substr(cutoff, s.size() - cutoff);
else
small = s.substr(cutoff - 1, s.size() - cutoff);
filesind.push_back(small);
}
}
std::string small;
std::string original = d.string();
if(cutoff < original.size())
small = original.substr(cutoff, original.size() - cutoff);
else
small = original.substr(cutoff - 1, original.size() - cutoff);
m[small] = filesind;
}
bool isCaseSensitive() const { return fsstrict; }
// The archive is loaded in the constructor, and never unloaded.
void load() {}
void unload() {}
bool exists(const String& filename) {
std::string copy = filename;
for (unsigned int i = 0; i < filename.size(); i++)
{
if(copy.at(i) == '\\' ){
copy.replace(i, 1, "/");
}
}
if(copy.at(0) == '\\' || copy.at(0) == '/')
{
copy.erase(0, 1);
}
if(fsstrict == true)
{
//std::cout << "fsstrict " << copy << "\n";
return FileSystemArchive::exists(copy);
}
int last = copy.size() - 1;
int i = last;
for (;last >= 0; i--)
{
if(copy.at(i) == '/' || copy.at(i) == '\\')
break;
}
std::string folder = copy.substr(0, i); //folder with no slash
std::vector<std::string>& current = m[folder];
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
{
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
return FileSystemArchive::exists(*iter);
}
}
return false;
}
DataStreamPtr open(const String& filename, bool readonly = true) const
{
std::map<std::string, std::vector<std::string>, ciLessBoost> mlocal = m;
std::string copy = filename;
for (unsigned int i = 0; i < filename.size(); i++)
{
if(copy.at(i) == '\\' ){
copy.replace(i, 1, "/");
}
}
if(copy.at(0) == '\\' || copy.at(0) == '/')
{
copy.erase(0, 1);
}
if(fsstrict == true)
{
return FileSystemArchive::open(copy, readonly);
}
int last = copy.size() - 1;
int i = last;
for (;last >= 0; i--)
{
if(copy.at(i) == '/' || copy.at(i) == '\\')
break;
}
std::string folder = copy.substr(0, i); //folder with no slash
std::vector<std::string> current = mlocal[folder];
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
{
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
return FileSystemArchive::open(*iter, readonly);
}
}
DataStreamPtr p;
return p;
}
};
class BSAArchive : public Archive class BSAArchive : public Archive
{ {
BSAFile arc; BSAFile arc;
@ -145,7 +329,27 @@ public:
void destroyInstance( Archive* arch) { delete arch; } void destroyInstance( Archive* arch) { delete arch; }
}; };
class DirArchiveFactory : public FileSystemArchiveFactory
{
public:
const String& getType() const
{
static String name = "Dir";
return name;
}
Archive *createInstance( const String& name )
{
return new DirArchive(name);
}
void destroyInstance( Archive* arch) { delete arch; }
};
static bool init = false; static bool init = false;
static bool init2 = false;
static void insertBSAFactory() static void insertBSAFactory()
{ {
if(!init) if(!init)
@ -155,6 +359,20 @@ static void insertBSAFactory()
} }
} }
static void insertDirFactory()
{
if(!init2)
{
ArchiveManager::getSingleton().addArchiveFactory( new DirArchiveFactory );
init2 = true;
}
}
}
namespace Bsa
{
// The function below is the only publicly exposed part of this file // The function below is the only publicly exposed part of this file
void addBSA(const std::string& name, const std::string& group) void addBSA(const std::string& name, const std::string& group)
@ -163,3 +381,14 @@ void addBSA(const std::string& name, const std::string& group)
ResourceGroupManager::getSingleton(). ResourceGroupManager::getSingleton().
addResourceLocation(name, "BSA", group); addResourceLocation(name, "BSA", group);
} }
void addDir(const std::string& name, const bool& fs, const std::string& group)
{
fsstrict = fs;
insertDirFactory();
ResourceGroupManager::getSingleton().
addResourceLocation(name, "Dir", group);
}
}

View file

@ -22,12 +22,21 @@
*/ */
#include <string> #include <string>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#ifndef _BSA_ARCHIVE_H_ #ifndef BSA_BSA_ARCHIVE_H
#define _BSA_ARCHIVE_H_ #define BSA_BSA_ARCHIVE_H
namespace Bsa
{
/// Add the given BSA file as an input archive in the Ogre resource /// Add the given BSA file as an input archive in the Ogre resource
/// system. /// system.
void addBSA(const std::string& file, const std::string& group="General"); void addBSA(const std::string& file, const std::string& group="General");
void addDir(const std::string& file, const bool& fs, const std::string& group="General");
}
#endif #endif

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,8 @@
namespace ESM namespace ESM
{ {
using namespace Misc;
ESM_Context ESMReader::getContext() ESM_Context ESMReader::getContext()
{ {
// Update the file position before returning // Update the file position before returning
@ -324,7 +326,7 @@ std::string ESMReader::getString(int size)
mEsm->read(ptr, size); mEsm->read(ptr, size);
// Convert to UTF8 and return // Convert to UTF8 and return
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252); return ToUTF8::getUtf8(mEncoding);
} }
void ESMReader::fail(const std::string &msg) void ESMReader::fail(const std::string &msg)
@ -342,4 +344,21 @@ void ESMReader::fail(const std::string &msg)
throw std::runtime_error(ss.str()); throw std::runtime_error(ss.str());
} }
void ESMReader::setEncoding(const std::string& encoding)
{
if (encoding == "win1250")
{
mEncoding = ToUTF8::WINDOWS_1250;
}
else if (encoding == "win1251")
{
mEncoding = ToUTF8::WINDOWS_1251;
}
else
{
// Default Latin encoding
mEncoding = ToUTF8::WINDOWS_1252;
}
}
} }

View file

@ -350,6 +350,9 @@ public:
/// Used for error handling /// Used for error handling
void fail(const std::string &msg); void fail(const std::string &msg);
/// Sets font encoding for ESM strings
void setEncoding(const std::string& encoding);
private: private:
Mangle::Stream::StreamPtr mEsm; Mangle::Stream::StreamPtr mEsm;
@ -360,6 +363,7 @@ private:
SaveData mSaveData; SaveData mSaveData;
MasterList mMasters; MasterList mMasters;
ToUTF8::FromType mEncoding;
}; };
} }
#endif #endif

View file

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

View file

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

View file

@ -17,6 +17,7 @@
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <algorithm>
namespace ESMS namespace ESMS
{ {
@ -99,7 +100,7 @@ namespace ESMS
CellRefList<ItemLevList, D> itemLists; CellRefList<ItemLevList, D> itemLists;
CellRefList<ESM::Light, D> lights; CellRefList<ESM::Light, D> lights;
CellRefList<Tool, D> lockpicks; CellRefList<Tool, D> lockpicks;
CellRefList<Misc, D> miscItems; CellRefList<Miscellaneous, D> miscItems;
CellRefList<NPC, D> npcs; CellRefList<NPC, D> npcs;
CellRefList<Probe, D> probes; CellRefList<Probe, D> probes;
CellRefList<Repair, D> repairs; CellRefList<Repair, D> repairs;
@ -187,7 +188,14 @@ namespace ESMS
// Get each reference in turn // Get each reference in turn
while(cell->getNextRef(esm, ref)) while(cell->getNextRef(esm, ref))
{ {
int rec = store.find(ref.refID); std::string lowerCase;
std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase),
(int(*)(int)) std::tolower);
int rec = store.find(ref.refID);
ref.refID = lowerCase;
/* We can optimize this further by storing the pointer to the /* We can optimize this further by storing the pointer to the
record itself in store.all, so that we don't need to look it record itself in store.all, so that we don't need to look it

View file

@ -85,7 +85,13 @@ void ESMStore::load(ESMReader &esm)
dialogue = 0; dialogue = 0;
// Insert the reference into the global lookup // Insert the reference into the global lookup
if(!id.empty()) if(!id.empty() &&
(n.val==REC_ACTI || n.val==REC_ALCH || n.val==REC_APPA || n.val==REC_ARMO ||
n.val==REC_BOOK || n.val==REC_CLOT || n.val==REC_CONT || n.val==REC_CREA ||
n.val==REC_DOOR || n.val==REC_INGR || n.val==REC_LEVC || n.val==REC_LEVI ||
n.val==REC_LIGH || n.val==REC_LOCK || n.val==REC_MISC || n.val==REC_NPC_ ||
n.val==REC_PROB || n.val==REC_REPA || n.val==REC_STAT || n.val==REC_WEAP)
)
all[id] = n.val; all[id] = n.val;
} }
} }

View file

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

View file

@ -20,9 +20,9 @@ class FileFinderT
void add(const boost::filesystem::path &pth) void add(const boost::filesystem::path &pth)
{ {
std::string file = pth.string(); std::string file = pth.string();
std::string key = file.substr(cut); std::string key = file.substr(cut);
owner->table[key] = file; owner->table[key] = file;
} }
}; };
@ -50,17 +50,18 @@ public:
bool has(const std::string& file) const bool has(const std::string& file) const
{ {
return table.find(file) != table.end(); return table.find(file) != table.end();
} }
// Find the full path from a relative path. // Find the full path from a relative path.
const std::string &lookup(const std::string& file) const const std::string &lookup(const std::string& file) const
{ {
return table.find(file)->second; return table.find(file)->second;
} }
}; };
// The default is to use path_less for equality checks // The default is to use path_less for equality checks
typedef FileFinderT<path_less> FileFinder; typedef FileFinderT<path_less> FileFinder;
typedef FileFinderT<path_slash> FileFinderStrict;
} }
#endif #endif

View file

@ -44,6 +44,41 @@ struct path_less
return compareString(a.c_str(), b.c_str()) < 0; return compareString(a.c_str(), b.c_str()) < 0;
} }
}; };
struct path_slash
{
int compareChar(char a, char b) const
{
if(a>b) return 1;
else if(a<b) return -1;
return 0;
}
int comparePathChar(char a, char b) const
{
if(a == '\\') a = '/';
if(b == '\\') b = '/';
return compareChar(a,b);
}
int compareString(const char *a, const char *b) const
{
while(*a && *b)
{
int i = comparePathChar(*a,*b);
if(i != 0) return i;
a++; b++;
}
// At this point, one or both of the chars is a null terminator.
// Normal char comparison will get the correct final result here.
return compareChar(*a,*b);
}
bool operator() (const std::string& a, const std::string& b) const
{
return compareString(a.c_str(), b.c_str()) < 0;
}
};
} }
#endif #endif

View file

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

View file

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

View file

@ -134,8 +134,7 @@ namespace Interpreter
throw std::runtime_error (error.str()); throw std::runtime_error (error.str());
} }
Interpreter::Interpreter (Context& context) Interpreter::Interpreter()
: mRuntime (context)
{} {}
Interpreter::~Interpreter() Interpreter::~Interpreter()
@ -195,11 +194,11 @@ namespace Interpreter
mSegment5.insert (std::make_pair (code, opcode)); mSegment5.insert (std::make_pair (code, opcode));
} }
void Interpreter::run (const Type_Code *code, int codeSize) void Interpreter::run (const Type_Code *code, int codeSize, Context& context)
{ {
assert (codeSize>=4); assert (codeSize>=4);
mRuntime.configure (code, codeSize); mRuntime.configure (code, codeSize, context);
int opcodes = static_cast<int> (code[0]); int opcodes = static_cast<int> (code[0]);

View file

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

View file

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

View file

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

View file

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

View file

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

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