1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-20 07:23:51 +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

2
.gitignore vendored
View file

@ -2,3 +2,5 @@ build
*~
Doxygen
prebuilt
apps/openmw/config.hpp
Docs/mainpage.hpp

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

@ -62,6 +62,7 @@ namespace MWGui
class CreateClassDialog;
class BirthDialog;
class ReviewDialog;
class MessageBoxManager;
struct ClassPoint
{
@ -84,6 +85,7 @@ namespace MWGui
MapWindow *map;
MainMenu *menu;
StatsWindow *stats;
MessageBoxManager *mMessageBoxManager;
#if 0
InventoryWindow *inventory;
#endif
@ -248,6 +250,11 @@ namespace MWGui
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
* ID or it is not a string the default string is returned.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -52,6 +52,21 @@ namespace MWScript
}
};
class OpGetButtonPressed : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
MWWorld::Ptr ptr = context.getReference();
runtime.push (context.getWindowManager().readPressedButton());
}
};
const int opcodeEnableBirthMenu = 0x200000e;
const int opcodeEnableClassMenu = 0x200000f;
const int opcodeEnableNameMenu = 0x2000010;
@ -63,6 +78,7 @@ namespace MWScript
const int opcodeEnableStatsMenu = 0x2000016;
const int opcodeEnableRest = 0x2000017;
const int opcodeShowRestMenu = 0x2000018;
const int opcodeGetButtonPressed = 0x2000137;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -82,6 +98,8 @@ namespace MWScript
extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest);
extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu);
extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -115,6 +133,8 @@ namespace MWScript
interpreter.installSegment5 (opcodeShowRestMenu,
new OpShowDialogue (MWGui::GM_Rest));
interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -62,5 +62,6 @@ public:
// The default is to use path_less for equality checks
typedef FileFinderT<path_less> FileFinder;
typedef FileFinderT<path_slash> FileFinderStrict;
}
#endif

View file

@ -45,5 +45,40 @@ struct path_less
}
};
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

View file

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

View file

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

View file

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

View file

@ -34,7 +34,7 @@ namespace Interpreter
public:
Interpreter (Context& context);
Interpreter();
~Interpreter();
@ -56,9 +56,8 @@ namespace Interpreter
void installSegment5 (int code, Opcode0 *opcode);
///< ownership of \a opcode is transferred to *this.
void run (const Type_Code *code, int codeSize);
void run (const Type_Code *code, int codeSize, Context& context);
};
}
#endif

View file

@ -7,7 +7,7 @@
namespace Interpreter
{
Runtime::Runtime (Context& context) : mContext (context), mCode (0), mPC (0) {}
Runtime::Runtime() : mContext (0), mCode (0), mPC (0) {}
int Runtime::getPC() const
{
@ -47,10 +47,11 @@ namespace Interpreter
return literalBlock;
}
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize)
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context)
{
clear();
mContext = &context;
mCode = code;
mCodeSize = codeSize;
mPC = 0;
@ -58,6 +59,7 @@ namespace Interpreter
void Runtime::clear()
{
mContext = 0;
mCode = 0;
mCodeSize = 0;
mStack.clear();
@ -105,7 +107,7 @@ namespace Interpreter
Context& Runtime::getContext()
{
return mContext;
assert (mContext);
return *mContext;
}
}

View file

@ -14,7 +14,7 @@ namespace Interpreter
class Runtime
{
Context& mContext;
Context *mContext;
const Type_Code *mCode;
int mCodeSize;
int mPC;
@ -22,7 +22,7 @@ namespace Interpreter
public:
Runtime (Context& context);
Runtime ();
int getPC() const;
///< return program counter.
@ -33,7 +33,7 @@ namespace Interpreter
std::string getStringLiteral (int index) const;
void configure (const Type_Code *code, int codeSize);
void configure (const Type_Code *code, int codeSize, Context& context);
///< \a context and \a code must exist as least until either configure, clear or
/// the destructor is called. \a codeSize is given in 32-bit words.

View file

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

View file

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

View file

@ -21,14 +21,17 @@
*/
#ifndef _SLICE_ARRAY_H_
#define _SLICE_ARRAY_H_
#ifndef MISC_SLICE_ARRAY_H
#define MISC_SLICE_ARRAY_H
// A simple array implementation containing a pointer and a
// length. Used for holding slices into a data buffer.
#include <string.h>
#include <string>
namespace Misc
{
template <class T>
struct SliceArray
{
@ -74,4 +77,6 @@ typedef SliceArray<char> SString;
typedef SliceArray<int> IntArray;
typedef SliceArray<float> FloatArray;
}
#endif

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