mirror of https://github.com/OpenMW/openmw.git
Merge branch 'master' of https://github.com/zinnschlag/openmw into saveOnClose
commit
c17821e6c8
@ -0,0 +1,19 @@
|
||||
set(BSATOOL
|
||||
bsatool.cpp
|
||||
)
|
||||
source_group(apps\\bsatool FILES ${BSATOOL})
|
||||
|
||||
# Main executable
|
||||
add_executable(bsatool
|
||||
${BSATOOL}
|
||||
)
|
||||
|
||||
target_link_libraries(bsatool
|
||||
${Boost_LIBRARIES}
|
||||
components
|
||||
)
|
||||
|
||||
if (BUILD_WITH_CODE_COVERAGE)
|
||||
add_definitions (--coverage)
|
||||
target_link_libraries(bsatool gcov)
|
||||
endif()
|
@ -0,0 +1,288 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <exception>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <components/bsa/bsa_file.hpp>
|
||||
|
||||
#define BSATOOL_VERSION 1.1
|
||||
|
||||
// Create local aliases for brevity
|
||||
namespace bpo = boost::program_options;
|
||||
namespace bfs = boost::filesystem;
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
std::string mode;
|
||||
std::string filename;
|
||||
std::string extractfile;
|
||||
std::string outdir;
|
||||
|
||||
bool longformat;
|
||||
bool fullpath;
|
||||
};
|
||||
|
||||
void replaceAll(std::string& str, const std::string& needle, const std::string& substitute)
|
||||
{
|
||||
int pos = str.find(needle);
|
||||
while(pos != -1)
|
||||
{
|
||||
str.replace(pos, needle.size(), substitute);
|
||||
pos = str.find(needle);
|
||||
}
|
||||
}
|
||||
|
||||
bool parseOptions (int argc, char** argv, Arguments &info)
|
||||
{
|
||||
bpo::options_description desc("Inspect and extract files from Bethesda BSA archives\n\n"
|
||||
"Usages:\n"
|
||||
" bsatool list [-l] archivefile\n"
|
||||
" List the files presents in the input archive.\n\n"
|
||||
" bsatool extract [-f] archivefile [file_to_extract] [output_directory]\n"
|
||||
" Extract a file from the input archive.\n\n"
|
||||
" bsatool extractall archivefile [output_directory]\n"
|
||||
" Extract all files from the input archive.\n\n"
|
||||
"Allowed options");
|
||||
|
||||
desc.add_options()
|
||||
("help,h", "print help message.")
|
||||
("version,v", "print version information and quit.")
|
||||
("long,l", "Include extra information in archive listing.")
|
||||
("full-path,f", "Create diretory hierarchy on file extraction "
|
||||
"(always true for extractall).")
|
||||
;
|
||||
|
||||
// input-file is hidden and used as a positional argument
|
||||
bpo::options_description hidden("Hidden Options");
|
||||
|
||||
hidden.add_options()
|
||||
( "mode,m", bpo::value<std::string>(), "bsatool mode")
|
||||
( "input-file,i", bpo::value< std::vector<std::string> >(), "input file")
|
||||
;
|
||||
|
||||
bpo::positional_options_description p;
|
||||
p.add("mode", 1).add("input-file", 3);
|
||||
|
||||
// there might be a better way to do this
|
||||
bpo::options_description all;
|
||||
all.add(desc).add(hidden);
|
||||
|
||||
bpo::variables_map variables;
|
||||
try
|
||||
{
|
||||
bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv)
|
||||
.options(all).positional(p).run();
|
||||
bpo::store(valid_opts, variables);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "ERROR parsing arguments: " << e.what() << "\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bpo::notify(variables);
|
||||
|
||||
if (variables.count ("help"))
|
||||
{
|
||||
std::cout << desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables.count ("version"))
|
||||
{
|
||||
std::cout << "BSATool version " << BSATOOL_VERSION << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!variables.count("mode"))
|
||||
{
|
||||
std::cout << "ERROR: no mode specified!\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
info.mode = variables["mode"].as<std::string>();
|
||||
if (!(info.mode == "list" || info.mode == "extract" || info.mode == "extractall"))
|
||||
{
|
||||
std::cout << std::endl << "ERROR: invalid mode \"" << info.mode << "\"\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!variables.count("input-file"))
|
||||
{
|
||||
std::cout << "\nERROR: missing BSA archive\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
info.filename = variables["input-file"].as< std::vector<std::string> >()[0];
|
||||
|
||||
// Default output to the working directory
|
||||
info.outdir = ".";
|
||||
|
||||
if (info.mode == "extract")
|
||||
{
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() < 2)
|
||||
{
|
||||
std::cout << "\nERROR: file to extract unspecified\n\n"
|
||||
<< desc << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.extractfile = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
if (variables["input-file"].as< std::vector<std::string> >().size() > 2)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[2];
|
||||
}
|
||||
else if (variables["input-file"].as< std::vector<std::string> >().size() > 1)
|
||||
info.outdir = variables["input-file"].as< std::vector<std::string> >()[1];
|
||||
|
||||
info.longformat = variables.count("long");
|
||||
info.fullpath = variables.count("full-path");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int list(Bsa::BSAFile& bsa, Arguments& info);
|
||||
int extract(Bsa::BSAFile& bsa, Arguments& info);
|
||||
int extractAll(Bsa::BSAFile& bsa, Arguments& info);
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Arguments info;
|
||||
if(!parseOptions (argc, argv, info))
|
||||
return 1;
|
||||
|
||||
// Open file
|
||||
Bsa::BSAFile bsa;
|
||||
try
|
||||
{
|
||||
bsa.open(info.filename);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
std::cout << "ERROR reading BSA archive '" << info.filename
|
||||
<< "'\nDetails:\n" << e.what() << std::endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (info.mode == "list")
|
||||
return list(bsa, info);
|
||||
else if (info.mode == "extract")
|
||||
return extract(bsa, info);
|
||||
else if (info.mode == "extractall")
|
||||
return extractAll(bsa, info);
|
||||
else
|
||||
{
|
||||
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int list(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
// List all files
|
||||
const Bsa::BSAFile::FileList &files = bsa.getList();
|
||||
for(int i=0; i<files.size(); i++)
|
||||
{
|
||||
if(info.longformat)
|
||||
{
|
||||
// Long format
|
||||
std::cout << std::setw(50) << std::left << files[i].name;
|
||||
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
|
||||
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
|
||||
}
|
||||
else
|
||||
std::cout << files[i].name << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extract(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
std::string archivePath = info.extractfile;
|
||||
replaceAll(archivePath, "/", "\\");
|
||||
|
||||
std::string extractPath = info.extractfile;
|
||||
replaceAll(extractPath, "\\", "/");
|
||||
|
||||
if (!bsa.exists(archivePath.c_str()))
|
||||
{
|
||||
std::cout << "ERROR: file '" << archivePath << "' not found\n";
|
||||
std::cout << "In archive: " << info.filename << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path relPath (extractPath);
|
||||
bfs::path outdir (info.outdir);
|
||||
|
||||
bfs::path target;
|
||||
if (info.fullpath)
|
||||
target = outdir / relPath;
|
||||
else
|
||||
target = outdir / relPath.filename();
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
Ogre::DataStreamPtr data = bsa.getFile(archivePath.c_str());
|
||||
bfs::ofstream out(target, std::ios::binary);
|
||||
|
||||
// Write the file to disk
|
||||
std::cout << "Extracting " << info.extractfile << " to " << target << std::endl;
|
||||
out.write(data->getAsString().c_str(), data->size());
|
||||
out.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int extractAll(Bsa::BSAFile& bsa, Arguments& info)
|
||||
{
|
||||
// Get the list of files present in the archive
|
||||
Bsa::BSAFile::FileList list = bsa.getList();
|
||||
|
||||
// Iter on the list
|
||||
for(Bsa::BSAFile::FileList::iterator it = list.begin(); it != list.end(); ++it) {
|
||||
const char* archivePath = it->name;
|
||||
|
||||
std::string extractPath (archivePath);
|
||||
replaceAll(extractPath, "\\", "/");
|
||||
|
||||
// Get the target path (the path the file will be extracted to)
|
||||
bfs::path target (info.outdir);
|
||||
target /= extractPath;
|
||||
|
||||
// Create the directory hierarchy
|
||||
bfs::create_directories(target.parent_path());
|
||||
|
||||
bfs::file_status s = bfs::status(target.parent_path());
|
||||
if (!bfs::is_directory(s))
|
||||
{
|
||||
std::cout << "ERROR: " << target.parent_path() << " is not a directory." << std::endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Get a stream for the file to extract
|
||||
// (inefficient because getFile iter on the list again)
|
||||
Ogre::DataStreamPtr data = bsa.getFile(archivePath);
|
||||
bfs::ofstream out(target, std::ios::binary);
|
||||
|
||||
// Write the file to disk
|
||||
std::cout << "Extracting " << target << std::endl;
|
||||
out.write(data->getAsString().c_str(), data->size());
|
||||
out.close();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,43 +1,40 @@
|
||||
#include <QtGui>
|
||||
|
||||
#include "playpage.hpp"
|
||||
|
||||
#include <QtGui>
|
||||
|
||||
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");
|
||||
setupUi(this);
|
||||
|
||||
// Hacks to get the stylesheet look properly on different platforms
|
||||
QPlastiqueStyle *style = new QPlastiqueStyle;
|
||||
mProfilesComboBox = new QComboBox(playWidget);
|
||||
mProfilesComboBox->setObjectName("ProfilesComboBox");
|
||||
mProfilesComboBox->setStyle(style);
|
||||
QFont font = QApplication::font();
|
||||
font.setPointSize(12); // Fixes problem with overlapping items
|
||||
|
||||
QGridLayout *playLayout = new QGridLayout(playWidget);
|
||||
profilesComboBox->setStyle(style);
|
||||
profilesComboBox->setFont(font);
|
||||
|
||||
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
|
||||
connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
|
||||
|
||||
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);
|
||||
void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model)
|
||||
{
|
||||
profilesComboBox->setModel(model);
|
||||
}
|
||||
|
||||
QHBoxLayout *pageLayout = new QHBoxLayout(this);
|
||||
void PlayPage::setProfilesComboBoxIndex(int index)
|
||||
{
|
||||
profilesComboBox->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
pageLayout->addWidget(playWidget);
|
||||
void PlayPage::slotCurrentIndexChanged(int index)
|
||||
{
|
||||
emit profileChanged(index);
|
||||
}
|
||||
|
||||
}
|
||||
void PlayPage::slotPlayClicked()
|
||||
{
|
||||
emit playButtonClicked();
|
||||
}
|
||||
|
@ -1,21 +0,0 @@
|
||||
<!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>
|
@ -0,0 +1,177 @@
|
||||
#include "gamesettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <components/files/configurationmanager.hpp>
|
||||
|
||||
#include <boost/version.hpp>
|
||||
/**
|
||||
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||
*/
|
||||
#if (BOOST_VERSION <= 104600)
|
||||
namespace boost
|
||||
{
|
||||
|
||||
template<>
|
||||
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
|
||||
{
|
||||
return boost::filesystem::path(arg);
|
||||
}
|
||||
|
||||
} /* namespace boost */
|
||||
#endif /* (BOOST_VERSION <= 104600) */
|
||||
|
||||
|
||||
GameSettings::GameSettings(Files::ConfigurationManager &cfg)
|
||||
: mCfgMgr(cfg)
|
||||
{
|
||||
}
|
||||
|
||||
GameSettings::~GameSettings()
|
||||
{
|
||||
}
|
||||
|
||||
void GameSettings::validatePaths()
|
||||
{
|
||||
if (mSettings.isEmpty() || !mDataDirs.isEmpty())
|
||||
return; // Don't re-validate paths if they are already parsed
|
||||
|
||||
QStringList paths = mSettings.values(QString("data"));
|
||||
Files::PathContainer dataDirs;
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
dataDirs.push_back(Files::PathContainer::value_type(path.toStdString()));
|
||||
}
|
||||
|
||||
// Parse the data dirs to convert the tokenized paths
|
||||
mCfgMgr.processPaths(dataDirs);
|
||||
mDataDirs.clear();
|
||||
|
||||
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) {
|
||||
QString path = QString::fromStdString(it->string());
|
||||
path.remove(QChar('\"'));
|
||||
|
||||
QDir dir(path);
|
||||
if (dir.exists())
|
||||
mDataDirs.append(path);
|
||||
}
|
||||
|
||||
// Do the same for data-local
|
||||
QString local = mSettings.value(QString("data-local"));
|
||||
|
||||
if (local.isEmpty())
|
||||
return;
|
||||
|
||||
dataDirs.clear();
|
||||
dataDirs.push_back(Files::PathContainer::value_type(local.toStdString()));
|
||||
|
||||
mCfgMgr.processPaths(dataDirs);
|
||||
|
||||
if (!dataDirs.empty()) {
|
||||
QString path = QString::fromStdString(dataDirs.front().string());
|
||||
path.remove(QChar('\"'));
|
||||
|
||||
QDir dir(path);
|
||||
if (dir.exists())
|
||||
mDataLocal = path;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList GameSettings::values(const QString &key, const QStringList &defaultValues)
|
||||
{
|
||||
if (!mSettings.values(key).isEmpty())
|
||||
return mSettings.values(key);
|
||||
return defaultValues;
|
||||
}
|
||||
|
||||
bool GameSettings::readFile(QTextStream &stream)
|
||||
{
|
||||
QMap<QString, QString> cache;
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
|
||||
// Don't remove existing data entries
|
||||
if (key != QLatin1String("data"))
|
||||
mSettings.remove(key);
|
||||
|
||||
QStringList values = cache.values(key);
|
||||
values.append(mSettings.values(key));
|
||||
|
||||
if (!values.contains(value)) {
|
||||
cache.insertMulti(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = cache; // This is the first time we read a file
|
||||
validatePaths();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(cache);
|
||||
validatePaths();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GameSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
// Iterate in reverse order to preserve insertion order
|
||||
QMapIterator<QString, QString> i(mSettings);
|
||||
i.toBack();
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
|
||||
if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin"))
|
||||
continue;
|
||||
|
||||
// Quote paths with spaces
|
||||
if (i.key() == QLatin1String("data")
|
||||
|| i.key() == QLatin1String("data-local")
|
||||
|| i.key() == QLatin1String("resources"))
|
||||
{
|
||||
if (i.value().contains(QChar(' ')))
|
||||
{
|
||||
QString stripped = i.value();
|
||||
stripped.remove(QChar('\"')); // Remove quotes
|
||||
|
||||
stream << i.key() << "=\"" << stripped << "\"\n";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
stream << i.key() << "=" << i.value() << "\n";
|
||||
|
||||
}
|
||||
|
||||
QStringList masters = mSettings.values(QString("master"));
|
||||
for (int i = masters.count(); i--;) {
|
||||
stream << "master=" << masters.at(i) << "\n";
|
||||
}
|
||||
|
||||
QStringList plugins = mSettings.values(QString("plugin"));
|
||||
for (int i = plugins.count(); i--;) {
|
||||
stream << "plugin=" << plugins.at(i) << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
#ifndef GAMESETTINGS_HPP
|
||||
#define GAMESETTINGS_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer;
|
||||
struct ConfigurationManager;}
|
||||
|
||||
class GameSettings
|
||||
{
|
||||
public:
|
||||
GameSettings(Files::ConfigurationManager &cfg);
|
||||
~GameSettings();
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
mSettings.clear();
|
||||
}
|
||||
|
||||
inline QStringList getDataDirs() { return mDataDirs; }
|
||||
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
|
||||
inline QString getDataLocal() {return mDataLocal; }
|
||||
|
||||
QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
|
||||
bool readFile(QTextStream &stream);
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
private:
|
||||
Files::ConfigurationManager &mCfgMgr;
|
||||
|
||||
void validatePaths();
|
||||
QMap<QString, QString> mSettings;
|
||||
|
||||
QStringList mDataDirs;
|
||||
QString mDataLocal;
|
||||
};
|
||||
|
||||
#endif // GAMESETTINGS_HPP
|
@ -0,0 +1,44 @@
|
||||
#include "graphicssettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
GraphicsSettings::GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
GraphicsSettings::~GraphicsSettings()
|
||||
{
|
||||
}
|
||||
|
||||
bool GraphicsSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
QMapIterator<QString, QString> i(settings);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
|
||||
QString prefix;
|
||||
QString key;
|
||||
|
||||
if (sectionRe.exactMatch(i.key())) {
|
||||
prefix = sectionRe.cap(1);
|
||||
key = sectionRe.cap(2);
|
||||
}
|
||||
|
||||
if (sectionPrefix != prefix) {
|
||||
sectionPrefix = prefix;
|
||||
stream << "\n[" << prefix << "]\n";
|
||||
}
|
||||
|
||||
stream << key << " = " << i.value() << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
#ifndef GRAPHICSSETTINGS_HPP
|
||||
#define GRAPHICSSETTINGS_HPP
|
||||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
GraphicsSettings();
|
||||
~GraphicsSettings();
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
|
||||
#endif // GRAPHICSSETTINGS_HPP
|
@ -0,0 +1,101 @@
|
||||
#include "launchersettings.hpp"
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
LauncherSettings::LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
LauncherSettings::~LauncherSettings()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
if (flags == Qt::MatchExactly)
|
||||
return settings.values(key);
|
||||
|
||||
QStringList result;
|
||||
|
||||
if (flags == Qt::MatchStartsWith) {
|
||||
QStringList keys = settings.keys();
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
if (currentKey.startsWith(key))
|
||||
result.append(settings.value(currentKey));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList LauncherSettings::subKeys(const QString &key)
|
||||
{
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
QStringList keys = settings.uniqueKeys();
|
||||
|
||||
QRegExp keyRe("(.+)/");
|
||||
|
||||
QStringList result;
|
||||
|
||||
foreach (const QString ¤tKey, keys) {
|
||||
|
||||
if (keyRe.indexIn(currentKey) != -1) {
|
||||
|
||||
QString prefixedKey = keyRe.cap(1);
|
||||
if(prefixedKey.startsWith(key)) {
|
||||
|
||||
QString subKey = prefixedKey.remove(key);
|
||||
if (!subKey.isEmpty())
|
||||
result.append(subKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.removeDuplicates();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LauncherSettings::writeFile(QTextStream &stream)
|
||||
{
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("([^/]+)/(.+)$");
|
||||
QMap<QString, QString> settings = SettingsBase::getSettings();
|
||||
|
||||
QMapIterator<QString, QString> i(settings);
|
||||
i.toBack();
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
|
||||
QString prefix;
|
||||
QString key;
|
||||
|
||||
if (sectionRe.exactMatch(i.key())) {
|
||||
prefix = sectionRe.cap(1);
|
||||
key = sectionRe.cap(2);
|
||||
}
|
||||
|
||||
// Get rid of legacy settings
|
||||
if (key.contains(QChar('\\')))
|
||||
continue;
|
||||
|
||||
if (key == QLatin1String("CurrentProfile"))
|
||||
continue;
|
||||
|
||||
if (sectionPrefix != prefix) {
|
||||
sectionPrefix = prefix;
|
||||
stream << "\n[" << prefix << "]\n";
|
||||
}
|
||||
|
||||
stream << key << "=" << i.value() << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
#ifndef LAUNCHERSETTINGS_HPP
|
||||
#define LAUNCHERSETTINGS_HPP
|
||||
|
||||
#include "settingsbase.hpp"
|
||||
|
||||
class LauncherSettings : public SettingsBase<QMap<QString, QString> >
|
||||
{
|
||||
public:
|
||||
LauncherSettings();
|
||||
~LauncherSettings();
|
||||
|
||||
QStringList subKeys(const QString &key);
|
||||
QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly);
|
||||
|
||||
bool writeFile(QTextStream &stream);
|
||||
|
||||
};
|
||||
|
||||
#endif // LAUNCHERSETTINGS_HPP
|
@ -0,0 +1,98 @@
|
||||
#ifndef SETTINGSBASE_HPP
|
||||
#define SETTINGSBASE_HPP
|
||||
|
||||
#include <QTextStream>
|
||||
#include <QStringList>
|
||||
#include <QString>
|
||||
#include <QRegExp>
|
||||
#include <QMap>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
template <class Map>
|
||||
class SettingsBase
|
||||
{
|
||||
|
||||
public:
|
||||
SettingsBase() {}
|
||||
~SettingsBase() {}
|
||||
|
||||
inline QString value(const QString &key, const QString &defaultValue = QString())
|
||||
{
|
||||
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
|
||||
}
|
||||
|
||||
inline void setValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insert(key, value);
|
||||
}
|
||||
|
||||
inline void setMultiValue(const QString &key, const QString &value)
|
||||
{
|
||||
QStringList values = mSettings.values(key);
|
||||
if (!values.contains(value))
|
||||
mSettings.insertMulti(key, value);
|
||||
}
|
||||
|
||||
inline void remove(const QString &key)
|
||||
{
|
||||
mSettings.remove(key);
|
||||
}
|
||||
|
||||
Map getSettings() {return mSettings;}
|
||||
|
||||
bool readFile(QTextStream &stream)
|
||||
{
|
||||
mCache.clear();
|
||||
|
||||
QString sectionPrefix;
|
||||
QRegExp sectionRe("^\\[([^]]+)\\]");
|
||||
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
QString line = stream.readLine().simplified();
|
||||
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
|
||||
if (sectionRe.exactMatch(line)) {
|
||||
sectionPrefix = sectionRe.cap(1);
|
||||
sectionPrefix.append("/");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keyRe.indexIn(line) != -1) {
|
||||
|
||||
QString key = keyRe.cap(1).simplified();
|
||||
QString value = keyRe.cap(2).simplified();
|
||||
|
||||
if (!sectionPrefix.isEmpty())
|
||||
key.prepend(sectionPrefix);
|
||||
|
||||
mSettings.remove(key);
|
||||
|
||||
QStringList values = mCache.values(key);
|
||||
if (!values.contains(value)) {
|
||||
mCache.insertMulti(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mSettings.isEmpty()) {
|
||||
mSettings = mCache; // This is the first time we read a file
|
||||
return true;
|
||||
}
|
||||
|
||||
// Merge the changed keys with those which didn't
|
||||
mSettings.unite(mCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Map mSettings;
|
||||
Map mCache;
|
||||
};
|
||||
|
||||
#endif // SETTINGSBASE_HPP
|
@ -0,0 +1,269 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "checkablemessagebox.hpp"
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QHeaderView>
|
||||
#include <QLabel>
|
||||
#include <QSpacerItem>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
/*!
|
||||
\class Utils::CheckableMessageBox
|
||||
|
||||
\brief A messagebox suitable for questions with a
|
||||
"Do not ask me again" checkbox.
|
||||
|
||||
Emulates the QMessageBox API with
|
||||
static conveniences. The message label can open external URLs.
|
||||
*/
|
||||
|
||||
class CheckableMessageBoxPrivate
|
||||
{
|
||||
public:
|
||||
CheckableMessageBoxPrivate(QDialog *q)
|
||||
: clickedButton(0)
|
||||
{
|
||||
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
|
||||
|
||||
pixmapLabel = new QLabel(q);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
|
||||
pixmapLabel->setSizePolicy(sizePolicy);
|
||||
pixmapLabel->setVisible(false);
|
||||
|
||||
QSpacerItem *pixmapSpacer =
|
||||
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
|
||||
|
||||
messageLabel = new QLabel(q);
|
||||
messageLabel->setMinimumSize(QSize(300, 0));
|
||||
messageLabel->setWordWrap(true);
|
||||
messageLabel->setOpenExternalLinks(true);
|
||||
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
|
||||
|
||||
QSpacerItem *checkBoxRightSpacer =
|
||||
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
QSpacerItem *buttonSpacer =
|
||||
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
checkBox = new QCheckBox(q);
|
||||
checkBox->setText(CheckableMessageBox::tr("Do not ask again"));
|
||||
|
||||
buttonBox = new QDialogButtonBox(q);
|
||||
buttonBox->setOrientation(Qt::Horizontal);
|
||||
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
|
||||
|
||||
QVBoxLayout *verticalLayout = new QVBoxLayout();
|
||||
verticalLayout->addWidget(pixmapLabel);
|
||||
verticalLayout->addItem(pixmapSpacer);
|
||||
|
||||
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
|
||||
horizontalLayout_2->addLayout(verticalLayout);
|
||||
horizontalLayout_2->addWidget(messageLabel);
|
||||
|
||||
QHBoxLayout *horizontalLayout = new QHBoxLayout();
|
||||
horizontalLayout->addWidget(checkBox);
|
||||
horizontalLayout->addItem(checkBoxRightSpacer);
|
||||
|
||||
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
|
||||
verticalLayout_2->addLayout(horizontalLayout_2);
|
||||
verticalLayout_2->addLayout(horizontalLayout);
|
||||
verticalLayout_2->addItem(buttonSpacer);
|
||||
verticalLayout_2->addWidget(buttonBox);
|
||||
}
|
||||
|
||||
QLabel *pixmapLabel;
|
||||
QLabel *messageLabel;
|
||||
QCheckBox *checkBox;
|
||||
QDialogButtonBox *buttonBox;
|
||||
QAbstractButton *clickedButton;
|
||||
};
|
||||
|
||||
CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
d(new CheckableMessageBoxPrivate(this))
|
||||
{
|
||||
setModal(true);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept()));
|
||||
connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject()));
|
||||
connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)),
|
||||
SLOT(slotClicked(QAbstractButton*)));
|
||||
}
|
||||
|
||||
CheckableMessageBox::~CheckableMessageBox()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::slotClicked(QAbstractButton *b)
|
||||
{
|
||||
d->clickedButton = b;
|
||||
}
|
||||
|
||||
QAbstractButton *CheckableMessageBox::clickedButton() const
|
||||
{
|
||||
return d->clickedButton;
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const
|
||||
{
|
||||
if (d->clickedButton)
|
||||
return d->buttonBox->standardButton(d->clickedButton);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::text() const
|
||||
{
|
||||
return d->messageLabel->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setText(const QString &t)
|
||||
{
|
||||
d->messageLabel->setText(t);
|
||||
}
|
||||
|
||||
QPixmap CheckableMessageBox::iconPixmap() const
|
||||
{
|
||||
if (const QPixmap *p = d->pixmapLabel->pixmap())
|
||||
return QPixmap(*p);
|
||||
return QPixmap();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setIconPixmap(const QPixmap &p)
|
||||
{
|
||||
d->pixmapLabel->setPixmap(p);
|
||||
d->pixmapLabel->setVisible(!p.isNull());
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isChecked() const
|
||||
{
|
||||
return d->checkBox->isChecked();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setChecked(bool s)
|
||||
{
|
||||
d->checkBox->setChecked(s);
|
||||
}
|
||||
|
||||
QString CheckableMessageBox::checkBoxText() const
|
||||
{
|
||||
return d->checkBox->text();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxText(const QString &t)
|
||||
{
|
||||
d->checkBox->setText(t);
|
||||
}
|
||||
|
||||
bool CheckableMessageBox::isCheckBoxVisible() const
|
||||
{
|
||||
return d->checkBox->isVisible();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setCheckBoxVisible(bool v)
|
||||
{
|
||||
d->checkBox->setVisible(v);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const
|
||||
{
|
||||
return d->buttonBox->standardButtons();
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
|
||||
{
|
||||
d->buttonBox->setStandardButtons(s);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const
|
||||
{
|
||||
return d->buttonBox->button(b);
|
||||
}
|
||||
|
||||
QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role)
|
||||
{
|
||||
return d->buttonBox->addButton(text, role);
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
|
||||
{
|
||||
foreach (QAbstractButton *b, d->buttonBox->buttons())
|
||||
if (QPushButton *pb = qobject_cast<QPushButton *>(b))
|
||||
if (pb->isDefault())
|
||||
return d->buttonBox->standardButton(pb);
|
||||
return QDialogButtonBox::NoButton;
|
||||
}
|
||||
|
||||
void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
|
||||
{
|
||||
if (QPushButton *b = d->buttonBox->button(s)) {
|
||||
b->setDefault(true);
|
||||
b->setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
QDialogButtonBox::StandardButton
|
||||
CheckableMessageBox::question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons,
|
||||
QDialogButtonBox::StandardButton defaultButton)
|
||||
{
|
||||
CheckableMessageBox mb(parent);
|
||||
mb.setWindowTitle(title);
|
||||
mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question));
|
||||
mb.setText(question);
|
||||
mb.setCheckBoxText(checkBoxText);
|
||||
mb.setChecked(*checkBoxSetting);
|
||||
mb.setStandardButtons(buttons);
|
||||
mb.setDefaultButton(defaultButton);
|
||||
mb.exec();
|
||||
*checkBoxSetting = mb.isChecked();
|
||||
return mb.clickedStandardButton();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
|
||||
{
|
||||
return static_cast<QMessageBox::StandardButton>(int(db));
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CHECKABLEMESSAGEBOX_HPP
|
||||
#define CHECKABLEMESSAGEBOX_HPP
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
|
||||
class CheckableMessageBoxPrivate;
|
||||
|
||||
class CheckableMessageBox : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString text READ text WRITE setText)
|
||||
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
|
||||
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
|
||||
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
|
||||
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
|
||||
|
||||
public:
|
||||
explicit CheckableMessageBox(QWidget *parent);
|
||||
virtual ~CheckableMessageBox();
|
||||
|
||||
static QDialogButtonBox::StandardButton
|
||||
question(QWidget *parent,
|
||||
const QString &title,
|
||||
const QString &question,
|
||||
const QString &checkBoxText,
|
||||
bool *checkBoxSetting,
|
||||
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
|
||||
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &);
|
||||
|
||||
bool isChecked() const;
|
||||
void setChecked(bool s);
|
||||
|
||||
QString checkBoxText() const;
|
||||
void setCheckBoxText(const QString &);
|
||||
|
||||
bool isCheckBoxVisible() const;
|
||||
void setCheckBoxVisible(bool);
|
||||
|
||||
QDialogButtonBox::StandardButtons standardButtons() const;
|
||||
void setStandardButtons(QDialogButtonBox::StandardButtons s);
|
||||
QPushButton *button(QDialogButtonBox::StandardButton b) const;
|
||||
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
|
||||
|
||||
QDialogButtonBox::StandardButton defaultButton() const;
|
||||
void setDefaultButton(QDialogButtonBox::StandardButton s);
|
||||
|
||||
// See static QMessageBox::standardPixmap()
|
||||
QPixmap iconPixmap() const;
|
||||
void setIconPixmap (const QPixmap &p);
|
||||
|
||||
// Query the result
|
||||
QAbstractButton *clickedButton() const;
|
||||
QDialogButtonBox::StandardButton clickedStandardButton() const;
|
||||
|
||||
// Conversion convenience
|
||||
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton);
|
||||
|
||||
private slots:
|
||||
void slotClicked(QAbstractButton *b);
|
||||
|
||||
private:
|
||||
CheckableMessageBoxPrivate *d;
|
||||
};
|
||||
|
||||
#endif // CHECKABLEMESSAGEBOX_HPP
|
@ -1,52 +0,0 @@
|
||||
#include <QRegExpValidator>
|
||||
#include <QLineEdit>
|
||||
#include <QString>
|
||||
|
||||
#include "profilescombobox.hpp"
|
||||
|
||||
ProfilesComboBox::ProfilesComboBox(QWidget *parent) :
|
||||
QComboBox(parent)
|
||||
{
|
||||
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
|
||||
|
||||
setEditable(true);
|
||||
setValidator(mValidator);
|
||||
setCompleter(0);
|
||||
|
||||
connect(this, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(slotIndexChanged(int)));
|
||||
connect(lineEdit(), SIGNAL(returnPressed()), this,
|
||||
SLOT(slotReturnPressed()));
|
||||
}
|
||||
|
||||
void ProfilesComboBox::setEditEnabled(bool editable)
|
||||
{
|
||||
if (!editable)
|
||||
return setEditable(false);
|
||||
|
||||
// Reset the completer and validator
|
||||
setEditable(true);
|
||||
setValidator(mValidator);
|
||||
setCompleter(0);
|
||||
}
|
||||
|
||||
void ProfilesComboBox::slotReturnPressed()
|
||||
{
|
||||
QString current = currentText();
|
||||
QString previous = itemText(currentIndex());
|
||||
|
||||
if (findText(current) != -1)
|
||||
return;
|
||||
|
||||
setItemText(currentIndex(), current);
|
||||
emit(profileRenamed(previous, current));
|
||||
}
|
||||
|
||||
void ProfilesComboBox::slotIndexChanged(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
return;
|
||||
|
||||
emit(profileChanged(mOldProfile, currentText()));
|
||||
mOldProfile = itemText(index);
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
#include "filedialog.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QRegExpValidator>
|
||||
#include <QRegExp>
|
||||
#include <QSpacerItem>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
|
||||
#include <components/fileorderlist/model/datafilesmodel.hpp>
|
||||
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
|
||||
#include <components/fileorderlist/model/esm/esmfile.hpp>
|
||||
|
||||
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||
|
||||
FileDialog::FileDialog(QWidget *parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
// Models
|
||||
mDataFilesModel = new DataFilesModel(this);
|
||||
|
||||
mMastersProxyModel = new QSortFilterProxyModel();
|
||||
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
|
||||
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mMastersProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mPluginsProxyModel = new PluginsProxyModel();
|
||||
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
|
||||
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||
mPluginsProxyModel->setSourceModel(mDataFilesModel);
|
||||
|
||||
mFilterProxyModel = new QSortFilterProxyModel();
|
||||
mFilterProxyModel->setDynamicSortFilter(true);
|
||||
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
|
||||
|
||||
QCheckBox checkBox;
|
||||
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||
|
||||
mastersTable->setModel(mMastersProxyModel);
|
||||
mastersTable->setObjectName("MastersTable");
|
||||
mastersTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
mastersTable->setSortingEnabled(false);
|
||||
mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
mastersTable->setAlternatingRowColors(true);
|
||||
mastersTable->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
// Set the row height to the size of the checkboxes
|
||||
mastersTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
mastersTable->verticalHeader()->hide();
|
||||
|
||||
pluginsTable->setModel(mFilterProxyModel);
|
||||
pluginsTable->setObjectName("PluginsTable");
|
||||
pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
pluginsTable->setSortingEnabled(false);
|
||||
pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
pluginsTable->setAlternatingRowColors(true);
|
||||
pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||
pluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||
|
||||
pluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||
|
||||
// Hide the profile elements
|
||||
profileLabel->hide();
|
||||
profilesComboBox->hide();
|
||||
newProfileButton->hide();
|
||||
deleteProfileButton->hide();
|
||||
|
||||
// Add some extra widgets
|
||||
QHBoxLayout *nameLayout = new QHBoxLayout();
|
||||
QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
|
||||
mNameLabel = new QLabel(tr("File Name:"), this);
|
||||
|
||||
QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$"));
|
||||
mNameLineEdit = new LineEdit(this);
|
||||
mNameLineEdit->setValidator(validator);
|
||||
|
||||
nameLayout->addSpacerItem(spacer);
|
||||
nameLayout->addWidget(mNameLabel);
|
||||
nameLayout->addWidget(mNameLineEdit);
|
||||
|
||||
mButtonBox = new QDialogButtonBox(this);
|
||||
|
||||
mCreateButton = new QPushButton(tr("Create"), this);
|
||||
mCreateButton->setEnabled(false);
|
||||
|
||||
verticalLayout->addLayout(nameLayout);
|
||||
verticalLayout->addWidget(mButtonBox);
|
||||
|
||||
// Set sizes
|
||||
QList<int> sizeList;
|
||||
sizeList << 175;
|
||||
sizeList << 200;
|
||||
|
||||
splitter->setSizes(sizeList);
|
||||
|
||||
resize(600, 400);
|
||||
|
||||
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
|
||||
connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList)));
|
||||
connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString)));
|
||||
|
||||
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||
|
||||
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||
|
||||
connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked()));
|
||||
|
||||
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||
}
|
||||
|
||||
void FileDialog::updateViews()
|
||||
{
|
||||
// Ensure the columns are hidden because sort() re-enables them
|
||||
mastersTable->setColumnHidden(1, true);
|
||||
mastersTable->setColumnHidden(3, true);
|
||||
mastersTable->setColumnHidden(4, true);
|
||||
mastersTable->setColumnHidden(5, true);
|
||||
mastersTable->setColumnHidden(6, true);
|
||||
mastersTable->setColumnHidden(7, true);
|
||||
mastersTable->setColumnHidden(8, true);
|
||||
mastersTable->resizeColumnsToContents();
|
||||
|
||||
pluginsTable->setColumnHidden(1, true);
|
||||
pluginsTable->setColumnHidden(3, true);
|
||||
pluginsTable->setColumnHidden(4, true);
|
||||
pluginsTable->setColumnHidden(5, true);
|
||||
pluginsTable->setColumnHidden(6, true);
|
||||
pluginsTable->setColumnHidden(7, true);
|
||||
pluginsTable->setColumnHidden(8, true);
|
||||
pluginsTable->resizeColumnsToContents();
|
||||
|
||||
}
|
||||
|
||||
void FileDialog::updateOpenButton(const QStringList &items)
|
||||
{
|
||||
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open);
|
||||
|
||||
if (!openButton)
|
||||
return;
|
||||
|
||||
openButton->setEnabled(!items.isEmpty());
|
||||
}
|
||||
|
||||
void FileDialog::updateCreateButton(const QString &name)
|
||||
{
|
||||
if (!mCreateButton->isVisible())
|
||||
return;
|
||||
|
||||
mCreateButton->setEnabled(!name.isEmpty());
|
||||
}
|
||||
|
||||
void FileDialog::filterChanged(const QString &filter)
|
||||
{
|
||||
QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||
mFilterProxyModel->setFilterRegExp(filterRe);
|
||||
}
|
||||
|
||||
void FileDialog::addFiles(const QString &path)
|
||||
{
|
||||
mDataFilesModel->addFiles(path);
|
||||
mDataFilesModel->sort(3); // Sort by date accessed
|
||||
}
|
||||
|
||||
void FileDialog::setEncoding(const QString &encoding)
|
||||
{
|
||||
mDataFilesModel->setEncoding(encoding);
|
||||
}
|
||||
|
||||
void FileDialog::setCheckState(QModelIndex index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
QObject *object = QObject::sender();
|
||||
|
||||
// Not a signal-slot call
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
|
||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
|
||||
mFilterProxyModel->mapToSource(index));
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
|
||||
|
||||
if (sourceIndex.isValid()) {
|
||||
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
|
||||
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList FileDialog::checkedItemsPaths()
|
||||
{
|
||||
return mDataFilesModel->checkedItemsPaths();
|
||||
}
|
||||
|
||||
QString FileDialog::fileName()
|
||||
{
|
||||
return mNameLineEdit->text();
|
||||
}
|
||||
|
||||
void FileDialog::openFile()
|
||||
{
|
||||
setWindowTitle(tr("Open"));
|
||||
|
||||
mNameLabel->hide();
|
||||
mNameLineEdit->hide();
|
||||
mCreateButton->hide();
|
||||
|
||||
mButtonBox->removeButton(mCreateButton);
|
||||
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open);
|
||||
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open);
|
||||
openButton->setEnabled(false);
|
||||
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
void FileDialog::newFile()
|
||||
{
|
||||
setWindowTitle(tr("New"));
|
||||
|
||||
mNameLabel->show();
|
||||
mNameLineEdit->clear();
|
||||
mNameLineEdit->show();
|
||||
mCreateButton->show();
|
||||
|
||||
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel);
|
||||
mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole);
|
||||
|
||||
show();
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
|
||||
void FileDialog::accept()
|
||||
{
|
||||
emit openFiles();
|
||||
}
|
||||
|
||||
void FileDialog::createButtonClicked()
|
||||
{
|
||||
emit createNewFile();
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
#ifndef FILEDIALOG_HPP
|
||||
#define FILEDIALOG_HPP
|
||||
|
||||
#include <QDialog>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include "ui_datafilespage.h"
|
||||
|
||||
class QDialogButtonBox;
|
||||
class QSortFilterProxyModel;
|
||||
class QAbstractItemModel;
|
||||
class QPushButton;
|
||||
class QStringList;
|
||||
class QString;
|
||||
class QMenu;
|
||||
|
||||
class DataFilesModel;
|
||||
class PluginsProxyModel;
|
||||
|
||||
class FileDialog : public QDialog, private Ui::DataFilesPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileDialog(QWidget *parent = 0);
|
||||
void addFiles(const QString &path);
|
||||
void setEncoding(const QString &encoding);
|
||||
|
||||
void openFile();
|
||||
void newFile();
|
||||
void accepted();
|
||||
|
||||
QStringList checkedItemsPaths();
|
||||
QString fileName();
|
||||
|
||||
signals:
|
||||
void openFiles();
|
||||
void createNewFile();
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
private slots:
|
||||
void updateViews();
|
||||
void updateOpenButton(const QStringList &items);
|
||||
void updateCreateButton(const QString &name);
|
||||
void setCheckState(QModelIndex index);
|
||||
|
||||
void filterChanged(const QString &filter);
|
||||
|
||||
void createButtonClicked();
|
||||
|
||||
private:
|
||||
QLabel *mNameLabel;
|
||||
LineEdit *mNameLineEdit;
|
||||
|
||||
QPushButton *mCreateButton;
|
||||
QDialogButtonBox *mButtonBox;
|
||||
|
||||
DataFilesModel *mDataFilesModel;
|
||||
|
||||
PluginsProxyModel *mPluginsProxyModel;
|
||||
QSortFilterProxyModel *mMastersProxyModel;
|
||||
QSortFilterProxyModel *mFilterProxyModel;
|
||||
};
|
||||
|
||||
#endif // FILEDIALOG_HPP
|
@ -0,0 +1,293 @@
|
||||
#include "spellicons.hpp"
|
||||
|
||||
#include <MyGUI_Widget.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/inventorystore.hpp"
|
||||
|
||||
#include "../mwmechanics/activespells.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "tooltips.hpp"
|
||||
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
void SpellIcons::updateWidgets(MyGUI::Widget *parent, bool adjustSize)
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
const MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
|
||||
std::map <int, std::vector<MagicEffectInfo> > effects;
|
||||
|
||||
// add permanent item enchantments
|
||||
MWWorld::InventoryStore& store = MWWorld::Class::get(player).getInventoryStore(player);
|
||||
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator it = store.getSlot(slot);
|
||||
if (it == store.end())
|
||||
continue;
|
||||
std::string enchantment = MWWorld::Class::get(*it).getEnchantment(*it);
|
||||
if (enchantment.empty())
|
||||
continue;
|
||||
const ESM::Enchantment* enchant = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantment);
|
||||
if (enchant->mData.mType != ESM::Enchantment::ConstantEffect)
|
||||
continue;
|
||||
|
||||
const ESM::EffectList& list = enchant->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = MWWorld::Class::get(*it).getName(*it);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent enchantments with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add permanent spells
|
||||
const MWMechanics::Spells& spells = stats.getSpells();
|
||||
for (MWMechanics::Spells::TIterator it = spells.begin(); it != spells.end(); ++it)
|
||||
{
|
||||
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(it->first);
|
||||
|
||||
// these are the spell types that are permanently in effect
|
||||
if (!(spell->mData.mType == ESM::Spell::ST_Ability)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Disease)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Curse)
|
||||
&& !(spell->mData.mType == ESM::Spell::ST_Blight))
|
||||
continue;
|
||||
const ESM::EffectList& list = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
// just using the min magnitude here, permanent spells with a random magnitude just wouldn't make any sense
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin;
|
||||
effectInfo.mPermanent = true;
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// add lasting effect spells/potions etc
|
||||
const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells();
|
||||
for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin();
|
||||
it != activeSpells.end(); ++it)
|
||||
{
|
||||
const ESM::EffectList& list = getSpellEffectList(it->first);
|
||||
|
||||
float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor();
|
||||
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = list.mList.begin();
|
||||
effectIt != list.mList.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
|
||||
MagicEffectInfo effectInfo;
|
||||
effectInfo.mSource = getSpellDisplayName (it->first);
|
||||
effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID);
|
||||
if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
effectInfo.mKey.mArg = effectIt->mSkill;
|
||||
else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
effectInfo.mKey.mArg = effectIt->mAttribute;
|
||||
effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * it->second.second;
|
||||
effectInfo.mRemainingTime = effectIt->mDuration +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
// ingredients need special casing for their magnitude / duration
|
||||
/// \todo duplicated from ActiveSpells, helper function?
|
||||
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (it->first))
|
||||
{
|
||||
effectInfo.mRemainingTime = effectIt->mDuration * it->second.second +
|
||||
(it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale;
|
||||
|
||||
effectInfo.mMagnitude = static_cast<int> (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost));
|
||||
}
|
||||
|
||||
|
||||
effects[effectIt->mEffectID].push_back (effectInfo);
|
||||
}
|
||||
}
|
||||
|
||||
parent->setVisible(effects.size() != 0);
|
||||
|
||||
int w=2;
|
||||
if (adjustSize)
|
||||
{
|
||||
int s = effects.size() * 16+4;
|
||||
int diff = parent->getWidth() - s;
|
||||
parent->setSize(s, parent->getHeight());
|
||||
parent->setPosition(parent->getLeft()+diff, parent->getTop());
|
||||
}
|
||||
|
||||
|
||||
for (std::map <int, std::vector<MagicEffectInfo> >::const_iterator it = effects.begin(); it != effects.end(); ++it)
|
||||
{
|
||||
MyGUI::ImageBox* image;
|
||||
if (mWidgetMap.find(it->first) == mWidgetMap.end())
|
||||
image = parent->createWidget<MyGUI::ImageBox>
|
||||
("ImageBox", MyGUI::IntCoord(w,2,16,16), MyGUI::Align::Default);
|
||||
else
|
||||
image = mWidgetMap[it->first];
|
||||
mWidgetMap[it->first] = image;
|
||||
image->setPosition(w,2);
|
||||
image->setVisible(true);
|
||||
|
||||
const ESM::MagicEffect* effect =
|
||||
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(it->first);
|
||||
|
||||
std::string icon = effect->mIcon;
|
||||
icon[icon.size()-3] = 'd';
|
||||
icon[icon.size()-2] = 'd';
|
||||
icon[icon.size()-1] = 's';
|
||||
icon = "icons\\" + icon;
|
||||
|
||||
image->setImageTexture(icon);
|
||||
w += 16;
|
||||
|
||||
float remainingDuration = 0;
|
||||
|
||||
std::string sourcesDescription;
|
||||
|
||||
const float fadeTime = 5.f;
|
||||
|
||||
for (std::vector<MagicEffectInfo>::const_iterator effectIt = it->second.begin();
|
||||
effectIt != it->second.end(); ++effectIt)
|
||||
{
|
||||
if (effectIt != it->second.begin())
|
||||
sourcesDescription += "\n";
|
||||
|
||||
// if at least one of the effect sources is permanent, the effect will never wear off
|
||||
if (effectIt->mPermanent)
|
||||
remainingDuration = fadeTime;
|
||||
else
|
||||
remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime);
|
||||
|
||||
sourcesDescription += effectIt->mSource;
|
||||
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetSkill)
|
||||
sourcesDescription += " (" +
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(
|
||||
ESM::Skill::sSkillNameIds[effectIt->mKey.mArg], "") + ")";
|
||||
if (effect->mData.mFlags & ESM::MagicEffect::TargetAttribute)
|
||||
sourcesDescription += " (" +
|
||||
MWBase::Environment::get().getWindowManager()->getGameSettingString(
|
||||
ESM::Attribute::sGmstAttributeIds[effectIt->mKey.mArg], "") + ")";
|
||||
|
||||
if (!(effect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
|
||||
{
|
||||
std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "");
|
||||
std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", "");
|
||||
|
||||
sourcesDescription += ": " + boost::lexical_cast<std::string>(effectIt->mMagnitude);
|
||||
sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? pts : pt);
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = ESM::MagicEffect::effectIdToString (it->first);
|
||||
|
||||
ToolTipInfo tooltipInfo;
|
||||
tooltipInfo.caption = "#{" + name + "}";
|
||||
tooltipInfo.icon = effect->mIcon;
|
||||
tooltipInfo.text = sourcesDescription;
|
||||
tooltipInfo.imageSize = 16;
|
||||
tooltipInfo.wordWrap = false;
|
||||
|
||||
image->setUserData(tooltipInfo);
|
||||
image->setUserString("ToolTipType", "ToolTipInfo");
|
||||
|
||||
// Fade out during the last 5 seconds
|
||||
image->setAlpha(std::min(remainingDuration/fadeTime, 1.f));
|
||||
}
|
||||
|
||||
// hide inactive effects
|
||||
for (std::map<int, MyGUI::ImageBox*>::iterator it = mWidgetMap.begin(); it != mWidgetMap.end(); ++it)
|
||||
{
|
||||
if (effects.find(it->first) == effects.end())
|
||||
it->second->setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::string SpellIcons::getSpellDisplayName (const std::string& id)
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mName;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mName;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
return ingredient->mName;
|
||||
|
||||
throw std::runtime_error ("ID " + id + " has no display name");
|
||||
}
|
||||
|
||||
ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id)
|
||||
{
|
||||
if (const ESM::Spell *spell =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
|
||||
return spell->mEffects;
|
||||
|
||||
if (const ESM::Potion *potion =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
|
||||
return potion->mEffects;
|
||||
|
||||
if (const ESM::Ingredient *ingredient =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
|
||||
{
|
||||
const ESM::MagicEffect *magicEffect =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
ingredient->mData.mEffectID[0]);
|
||||
|
||||
ESM::ENAMstruct effect;
|
||||
effect.mEffectID = ingredient->mData.mEffectID[0];
|
||||
effect.mSkill = ingredient->mData.mSkills[0];
|
||||
effect.mAttribute = ingredient->mData.mAttributes[0];
|
||||
effect.mRange = 0;
|
||||
effect.mArea = 0;
|
||||
effect.mDuration = magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration ? 0 : 1;
|
||||
effect.mMagnMin = 1;
|
||||
effect.mMagnMax = 1;
|
||||
ESM::EffectList result;
|
||||
result.mList.push_back (effect);
|
||||
return result;
|
||||
}
|
||||
throw std::runtime_error("ID " + id + " does not have effects");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
#ifndef MWGUI_SPELLICONS_H
|
||||
#define MWGUI_SPELLICONS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../mwmechanics/magiceffects.hpp"
|
||||
|
||||
namespace MyGUI
|
||||
{
|
||||
class Widget;
|
||||
class ImageBox;
|
||||
}
|
||||
namespace ESM
|
||||
{
|
||||
struct ENAMstruct;
|
||||
struct EffectList;
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
{
|
||||
|
||||
// information about a single magic effect source as required for display in the tooltip
|
||||
struct MagicEffectInfo
|
||||
{
|
||||
MagicEffectInfo() : mPermanent(false) {}
|
||||
std::string mSource; // display name for effect source (e.g. potion name)
|
||||
MWMechanics::EffectKey mKey;
|
||||
int mMagnitude;
|
||||
float mRemainingTime;
|
||||
bool mPermanent; // the effect is permanent
|
||||
};
|
||||
|
||||
class SpellIcons
|
||||
{
|
||||
public:
|
||||
void updateWidgets(MyGUI::Widget* parent, bool adjustSize);
|
||||
|
||||
private:
|
||||
std::string getSpellDisplayName (const std::string& id);
|
||||
ESM::EffectList getSpellEffectList (const std::string& id);
|
||||
|
||||
std::map<int, MyGUI::ImageBox*> mWidgetMap;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue