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