1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-01-27 03:35:27 +00:00
This commit is contained in:
vorenon 2013-03-07 08:40:04 +01:00
commit eb3d379a81
67 changed files with 3508 additions and 1711 deletions

View File

@ -379,7 +379,6 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/OFL.txt"
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt"
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
"${OpenMW_BINARY_DIR}/launcher.qss"
"${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
"${OpenMW_BINARY_DIR}/Release/omwlauncher.exe"
@ -566,8 +565,6 @@ if (APPLE)
install(DIRECTORY "${APP_BUNDLE_DIR}" USE_SOURCE_PERMISSIONS DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/settings-default.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
install(FILES "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime)
@ -681,7 +678,4 @@ if (NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)
# Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" )
IF(BUILD_LAUNCHER)
INSTALL(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${DATADIR}/resources" )
ENDIF(BUILD_LAUNCHER)
endif(NOT WIN32 AND NOT DPKG_PROGRAM AND NOT APPLE)

View File

@ -1,35 +1,55 @@
set(LAUNCHER
datafilespage.cpp
graphicspage.cpp
main.cpp
maindialog.cpp
playpage.cpp
datafilespage.cpp
utils/profilescombobox.cpp
settings/gamesettings.cpp
settings/graphicssettings.cpp
settings/launchersettings.cpp
utils/checkablemessagebox.cpp
utils/textinputdialog.cpp
launcher.rc
)
set(LAUNCHER_HEADER
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
datafilespage.hpp
utils/profilescombobox.hpp
settings/gamesettings.hpp
settings/graphicssettings.hpp
settings/launchersettings.hpp
settings/settingsbase.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp
)
# Headers that must be pre-processed
set(LAUNCHER_HEADER_MOC
datafilespage.hpp
graphicspage.hpp
maindialog.hpp
playpage.hpp
datafilespage.hpp
utils/profilescombobox.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
set(LAUNCHER_UI
../../files/ui/datafilespage.ui
../../files/ui/graphicspage.ui
../../files/ui/mainwindow.ui
../../files/ui/playpage.ui
)
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})
find_package(Qt4 REQUIRED)
set(QT_USE_QTGUI 1)
@ -40,10 +60,12 @@ if(WIN32)
set(QT_USE_QTMAIN TRUE)
endif(WIN32)
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
QT4_ADD_RESOURCES(RCC_SRCS ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc)
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
QT4_WRAP_UI(UI_HDRS ${LAUNCHER_UI})
include(${QT_USE_FILE})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Main executable
IF(OGRE_STATIC)
@ -58,8 +80,10 @@ ENDIF(OGRE_STATIC)
add_executable(omwlauncher
${GUI_TYPE}
${LAUNCHER}
${LAUNCHER_HEADER}
${RCC_SRCS}
${MOC_SRCS}
${UI_HDRS}
)
target_link_libraries(omwlauncher
@ -74,18 +98,6 @@ if(DPKG_PROGRAM)
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
endif()
if (APPLE)
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${APP_BUNDLE_DIR}/../launcher.qss")
else()
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss")
# Fallback in case getGlobalDataPath does not point to resources
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
endif()
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(omwlauncher gcov)

View File

@ -1,79 +1,122 @@
#include <QtGui>
#include <components/esm/esmreader.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/fileorderlist/datafileslist.hpp>
#include <components/fileorderlist/utils/lineedit.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include <components/fileorderlist/utils/filedialog.hpp>
////#include "model/datafilesmodel.hpp"
////#include "model/esm/esmfile.hpp"
#include "utils/profilescombobox.hpp"
////#include "utils/filedialog.hpp"
////#include "utils/naturalsort.hpp"
#include "utils/textinputdialog.hpp"
#include "datafilespage.hpp"
#include <boost/version.hpp>
/**
* Workaround for problems with whitespaces in paths in older versions of Boost library
*/
#if (BOOST_VERSION <= 104600)
namespace boost
#include <QtGui>
#include <components/files/configurationmanager.hpp>
#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>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include <components/fileorderlist/utils/profilescombobox.hpp>
#include "settings/gamesettings.hpp"
#include "settings/launchersettings.hpp"
#include "utils/textinputdialog.hpp"
//sort QModelIndexList ascending
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
{
return index1.row() >= index2.row();
}
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) */
using namespace ESM;
using namespace std;
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
//sort QModelIndexList descending
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
{
mDataFilesList = new DataFilesList(mCfgMgr, this);
return index1.row() <= index2.row();
}
// Bottom part with profile options
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent)
: mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings)
, QWidget(parent)
{
setupUi(this);
mProfilesComboBox = new ProfilesComboBox(this);
mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
mProfilesComboBox->setInsertPolicy(QComboBox::NoInsert);
mProfilesComboBox->setDuplicatesEnabled(false);
mProfilesComboBox->setEditEnabled(false);
// Models
mDataFilesModel = new DataFilesModel(this);
mProfileToolBar = new QToolBar(this);
mProfileToolBar->setMovable(false);
mProfileToolBar->setIconSize(QSize(16, 16));
mMastersProxyModel = new QSortFilterProxyModel();
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mMastersProxyModel->setSourceModel(mDataFilesModel);
mProfileToolBar->addWidget(profileLabel);
mProfileToolBar->addWidget(mProfilesComboBox);
mPluginsProxyModel = new PluginsProxyModel();
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mPluginsProxyModel->setSourceModel(mDataFilesModel);
QVBoxLayout *pageLayout = new QVBoxLayout(this);
mFilterProxyModel = new QSortFilterProxyModel();
mFilterProxyModel->setDynamicSortFilter(true);
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
pageLayout->addWidget(mDataFilesList);
pageLayout->addWidget(mProfileToolBar);
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);
mastersTable->horizontalHeader()->hide();
// 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->horizontalHeader()->hide();
pluginsTable->verticalHeader()->setDefaultSectionSize(height);
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
// Adjust the tableview widths inside the splitter
QList<int> sizeList;
sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt();
sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt();
splitter->setSizes(sizeList);
// Create a dialog for the new profile name input
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
connect(mProfilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
connect(mProfilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter()));
createActions();
setupConfig();
setupDataFiles();
}
void DataFilesPage::createActions()
@ -83,7 +126,7 @@ void DataFilesPage::createActions()
refreshAction->setShortcut(QKeySequence(tr("F5")));
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
// Profile actions
// We can't create actions inside the .ui file
mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this);
mNewProfileAction->setToolTip(tr("New Profile"));
mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N")));
@ -91,355 +134,206 @@ void DataFilesPage::createActions()
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(mDeleteProfileAction);
// Add the newly created actions to the toolbuttons
newProfileButton->setDefaultAction(mNewProfileAction);
deleteProfileButton->setDefaultAction(mDeleteProfileAction);
// Context menu actions
mCheckAction = new QAction(tr("Check Selection"), this);
connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check()));
mUncheckAction = new QAction(tr("Uncheck Selection"), this);
connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck()));
mContextMenu = new QMenu(this);
mContextMenu->addAction(mCheckAction);
mContextMenu->addAction(mUncheckAction);
}
void DataFilesPage::setupConfig()
void DataFilesPage::setupDataFiles()
{
// Open our config file
QString config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string());
mLauncherConfig = new QSettings(config, QSettings::IniFormat);
// Set the encoding to the one found in openmw.cfg or the default
mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252")));
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
QStringList paths = mGameSettings.getDataDirs();
foreach (const QString &path, paths) {
mDataFilesModel->addFiles(path);
}
mLauncherConfig->beginGroup("Profiles");
QStringList profiles = mLauncherConfig->childGroups();
QString dataLocal = mGameSettings.getDataLocal();
if (!dataLocal.isEmpty())
mDataFilesModel->addFiles(dataLocal);
// Add the profiles to the combobox
foreach (const QString &profile, profiles) {
// Sort by date accessed for now
mDataFilesModel->sort(3);
if (profile.contains(QRegExp("[^a-zA-Z0-9_]")))
continue; // Profile name contains garbage
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/"));
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (!profiles.isEmpty())
profilesComboBox->addItems(profiles);
qDebug() << "adding " << profile;
mProfilesComboBox->addItem(profile);
}
// Add the current profile if empty
if (profilesComboBox->findText(profile) == -1)
profilesComboBox->addItem(profile);
// Add a default profile
if (mProfilesComboBox->findText(QString("Default")) == -1) {
mProfilesComboBox->addItem(QString("Default"));
}
QString currentProfile = mLauncherConfig->value("CurrentProfile").toString();
if (currentProfile.isEmpty()) {
// No current profile selected
currentProfile = "Default";
}
const int currentIndex = mProfilesComboBox->findText(currentProfile);
if (currentIndex != -1) {
// Profile is found
mProfilesComboBox->setCurrentIndex(currentIndex);
}
mLauncherConfig->endGroup();
}
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();
mDataFilesList->setCheckState(keyValue, Qt::Checked);
}
qDebug() << plugins;
}
bool DataFilesPage::showDataFilesWarning()
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error detecting Morrowind installation");
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the data files was not found.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == dirSelectButton) {
// Show a custom dir selection dialog which only accepts valid dirs
QString selectedDir = FileDialog::getExistingDirectory(
this, tr("Select Data Files Directory"),
QDir::currentPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
// Add the user selected data directory
if (!selectedDir.isEmpty()) {
mDataDirs.push_back(Files::PathContainer::value_type(selectedDir.toStdString()));
mCfgMgr.processPaths(mDataDirs);
} else {
// Cancel from within the dir selection dialog
return false;
}
if (profilesComboBox->findText(QString("Default")) == -1)
profilesComboBox->addItem(QString("Default"));
if (profile.isEmpty() || profile == QLatin1String("Default")) {
profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default")));
} else {
// Cancel
return false;
profilesComboBox->setEditEnabled(true);
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
}
return true;
// We do this here to prevent deletion of profiles when initializing the combobox
connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
loadSettings();
}
bool DataFilesPage::setupDataFiles()
void DataFilesPage::loadSettings()
{
// We use the Configuration Manager to retrieve the configuration values
boost::program_options::variables_map variables;
boost::program_options::options_description desc;
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
if (variables["data"].empty()) {
if (!showDataFilesWarning())
return false;
} else {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
}
std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local));
}
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
// Second chance to display the warning, the data= entries are invalid
while (mDataDirs.empty()) {
if (!showDataFilesWarning())
return false;
}
// Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
Files::PathContainer paths;
paths.insert(paths.end(), mDataDirs.begin(), mDataDirs.end());
paths.insert(paths.end(), mDataLocal.begin(), mDataLocal.end());
mDataFilesList->setupDataFiles(paths, encoding);
readConfig();
return true;
}
void DataFilesPage::writeConfig(QString profile)
{
QString pathStr = QString::fromStdString(mCfgMgr.getUserPath().string());
QDir userPath(pathStr);
if (!userPath.exists()) {
if (!userPath.mkpath(pathStr)) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error creating OpenMW configuration directory");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(pathStr));
msgBox.exec();
qApp->quit();
return;
}
}
// Open the OpenMW config as a QFile
QFile file(pathStr.append("openmw.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle("Error writing OpenMW configuration file");
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0</b><br><br> \
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
msgBox.exec();
qApp->quit();
return;
}
QTextStream in(&file);
QByteArray buffer;
// Remove all previous entries from config
while (!in.atEnd()) {
QString line = in.readLine();
if (!line.startsWith("master") &&
!line.startsWith("plugin") &&
!line.startsWith("data") &&
!line.startsWith("data-local"))
{
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();
qApp->quit();
return;
}
if (!buffer.isEmpty()) {
file.write(buffer);
}
QTextStream gameConfig(&file);
// First write the list of data dirs
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
QString path;
// data= directories
for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) {
path = QString::fromStdString(it->string());
path.remove(QChar('\"'));
// Make sure the string is quoted when it contains spaces
if (path.contains(" ")) {
gameConfig << "data=\"" << path << "\"" << endl;
} else {
gameConfig << "data=" << path << endl;
}
}
// data-local directory
if (!mDataLocal.empty()) {
path = QString::fromStdString(mDataLocal.front().string());
path.remove(QChar('\"'));
if (path.contains(" ")) {
gameConfig << "data-local=\"" << path << "\"" << endl;
} else {
gameConfig << "data-local=" << path << endl;
}
}
if (profile.isEmpty())
profile = mProfilesComboBox->currentText();
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (profile.isEmpty())
return;
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
mDataFilesModel->uncheckAll();
QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly);
QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly);
foreach (const QString &master, masters) {
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master));
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
}
mLauncherConfig->beginGroup("Profiles");
mLauncherConfig->setValue("CurrentProfile", profile);
// Open the profile-name subgroup
mLauncherConfig->beginGroup(profile);
mLauncherConfig->remove(""); // Clear the subgroup
// Now write the masters to the configs
const QStringList checkedFiles = mDataFilesList->checkedFiles();
for(int i=0; i < checkedFiles.size(); i++)
{
if (checkedFiles.at(i).lastIndexOf("esm") != -1)
{
mLauncherConfig->setValue(QString("Master%0").arg(i), checkedFiles.at(i));
gameConfig << "master=" << checkedFiles.at(i) << endl;
}
else
{
mLauncherConfig->setValue(QString("Plugin%1").arg(i), checkedFiles.at(i));
gameConfig << "plugin=" << checkedFiles.at(i) << endl;
}
foreach (const QString &plugin, plugins) {
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin));
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
}
file.close();
mLauncherConfig->endGroup();
mLauncherConfig->endGroup();
mLauncherConfig->sync();
}
void DataFilesPage::saveSettings()
{
if (mDataFilesModel->rowCount() < 1)
return;
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (profile.isEmpty()) {
profile = profilesComboBox->currentText();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
}
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
mGameSettings.remove(QString("master"));
mGameSettings.remove(QString("plugin"));
QStringList items = mDataFilesModel->checkedItems();
foreach(const QString &item, items) {
if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item);
mGameSettings.setMultiValue(QString("master"), item);
} else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item);
mGameSettings.setMultiValue(QString("plugin"), item);
}
}
}
void DataFilesPage::newProfile()
{
if (mNewProfileDialog->exec() == QDialog::Accepted) {
const QString text = mNewProfileDialog->lineEdit()->text();
mProfilesComboBox->addItem(text);
// Copy the currently checked items to cfg
writeConfig(text);
mLauncherConfig->sync();
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
QString profile = mNewProfileDialog->lineEdit()->text();
profilesComboBox->addItem(profile);
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile));
}
}
void DataFilesPage::updateOkButton(const QString &text)
{
// We do this here because we need the profiles combobox text
if (text.isEmpty()) {
mNewProfileDialog->setOkButtonEnabled(false);
return;
}
(mProfilesComboBox->findText(text) == -1)
(profilesComboBox->findText(text) == -1)
? mNewProfileDialog->setOkButtonEnabled(true)
: mNewProfileDialog->setOkButtonEnabled(false);
}
void DataFilesPage::updateSplitter()
{
// Sigh, update the saved splitter size in settings only when moved
// Since getting mSplitter->sizes() if page is hidden returns invalid values
QList<int> sizes = splitter->sizes();
mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0)));
mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1)));
}
void DataFilesPage::updateViews()
{
// Ensure the columns are hidden because sort() re-enables them
mastersTable->setColumnHidden(1, true);
mastersTable->setColumnHidden(2, 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);
pluginsTable->setColumnHidden(1, true);
pluginsTable->setColumnHidden(2, 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);
}
void DataFilesPage::setProfilesComboBoxIndex(int index)
{
profilesComboBox->setCurrentIndex(index);
}
void DataFilesPage::slotCurrentIndexChanged(int index)
{
emit profileChanged(index);
}
QAbstractItemModel* DataFilesPage::profilesComboBoxModel()
{
return profilesComboBox->model();
}
int DataFilesPage::profilesComboBoxIndex()
{
return profilesComboBox->currentIndex();
}
void DataFilesPage::deleteProfile()
{
QString profile = mProfilesComboBox->currentText();
QString profile = profilesComboBox->currentText();
if (profile.isEmpty())
return;
@ -456,49 +350,151 @@ void DataFilesPage::deleteProfile()
msgBox.exec();
if (msgBox.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();
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
// Remove the profile from the combobox
mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile));
profilesComboBox->removeItem(profilesComboBox->findText(profile));
}
}
void DataFilesPage::check()
{
if (pluginsTable->hasFocus())
setPluginsCheckstates(Qt::Checked);
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Checked);
}
void DataFilesPage::uncheck()
{
if (pluginsTable->hasFocus())
setPluginsCheckstates(Qt::Unchecked);
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Unchecked);
}
void DataFilesPage::refresh()
{
// mDataFilesModel->sort(0);
// Refresh the plugins table
pluginsTable->scrollToTop();
}
void DataFilesPage::setMastersCheckstates(Qt::CheckState state)
{
if (!mastersTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
if (!sourceIndex.isValid())
return;
mDataFilesModel->setCheckState(sourceIndex, state);
}
}
void DataFilesPage::setPluginsCheckstates(Qt::CheckState state)
{
if (!pluginsTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (!sourceIndex.isValid())
return;
mDataFilesModel->setCheckState(sourceIndex, state);
}
}
void DataFilesPage::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;
}
void DataFilesPage::filterChanged(const QString filter)
{
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
mFilterProxyModel->setFilterRegExp(regExp);
}
void DataFilesPage::profileChanged(const QString &previous, const QString &current)
{
qDebug() << "Profile is changed from: " << previous << " to " << current;
// Prevent the deletion of the default profile
if (current == QLatin1String("Default")) {
mDeleteProfileAction->setEnabled(false);
mProfilesComboBox->setEditEnabled(false);
profilesComboBox->setEditEnabled(false);
} else {
mDeleteProfileAction->setEnabled(true);
mProfilesComboBox->setEditEnabled(true);
profilesComboBox->setEditEnabled(true);
}
if (!previous.isEmpty()) {
writeConfig(previous);
mLauncherConfig->sync();
if (mProfilesComboBox->currentIndex() == -1)
return;
} else {
if (previous.isEmpty())
return;
}
mDataFilesList->uncheckAll();
readConfig();
if (profilesComboBox->findText(previous) == -1)
return; // Profile was deleted
// Store the previous profile
mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous);
saveSettings();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
loadSettings();
}
void DataFilesPage::profileRenamed(const QString &previous, const QString &current)
@ -507,27 +503,85 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre
return;
// Save the new profile name
writeConfig(current);
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
saveSettings();
// Make sure we have no groups open
while (!mLauncherConfig->group().isEmpty()) {
mLauncherConfig->endGroup();
}
// Remove the old one
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin"));
mLauncherConfig->beginGroup("Profiles");
// Remove the profile from the combobox
profilesComboBox->removeItem(profilesComboBox->findText(previous));
// Open the profile-name subgroup
mLauncherConfig->beginGroup(previous);
mLauncherConfig->remove(""); // Clear the subgroup
mLauncherConfig->endGroup();
mLauncherConfig->endGroup();
mLauncherConfig->sync();
loadSettings();
// Remove the profile from the combobox
mProfilesComboBox->removeItem(mProfilesComboBox->findText(previous));
mDataFilesList->uncheckAll();
////mMastersModel->uncheckAll();
////mPluginsModel->uncheckAll();
readConfig();
}
void DataFilesPage::showContextMenu(const QPoint &point)
{
QObject *object = QObject::sender();
// Not a signal-slot call
if (!object)
return;
if (object->objectName() == QLatin1String("PluginsTable")) {
if (!pluginsTable->selectionModel()->hasSelection())
return;
QPoint globalPos = pluginsTable->mapToGlobal(point);
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
// Show the check/uncheck actions depending on the state of the selected items
mUncheckAction->setEnabled(false);
mCheckAction->setEnabled(false);
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (!sourceIndex.isValid())
return;
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? mUncheckAction->setEnabled(true)
: mCheckAction->setEnabled(true);
}
// Show menu
mContextMenu->exec(globalPos);
}
if (object->objectName() == QLatin1String("MastersTable")) {
if (!mastersTable->selectionModel()->hasSelection())
return;
QPoint globalPos = mastersTable->mapToGlobal(point);
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
// Show the check/uncheck actions depending on the state of the selected items
mUncheckAction->setEnabled(false);
mCheckAction->setEnabled(false);
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index);
if (!sourceIndex.isValid())
return;
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? mUncheckAction->setEnabled(true)
: mCheckAction->setEnabled(true);
}
mContextMenu->exec(globalPos);
}
}

View File

@ -3,57 +3,80 @@
#include <QWidget>
#include <QModelIndex>
#include "utils/profilescombobox.hpp"
#include <components/files/collections.hpp>
#include "ui_datafilespage.h"
class QTableView;
class QSortFilterProxyModel;
class QSettings;
class QAbstractItemModel;
class QAction;
class QToolBar;
class QMenu;
class ProfilesComboBox;
class DataFilesModel;
class DataFilesModel;
class TextInputDialog;
class DataFilesList;
class GameSettings;
class LauncherSettings;
class PluginsProxyModel;
namespace Files { struct ConfigurationManager; }
class DataFilesPage : public QWidget
class DataFilesPage : public QWidget, private Ui::DataFilesPage
{
Q_OBJECT
public:
DataFilesPage(Files::ConfigurationManager& cfg, QWidget *parent = 0);
DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0);
ProfilesComboBox *mProfilesComboBox;
QAbstractItemModel* profilesComboBoxModel();
int profilesComboBoxIndex();
void writeConfig(QString profile = QString());
bool showDataFilesWarning();
bool setupDataFiles();
void saveSettings();
signals:
void profileChanged(int index);
public slots:
void setCheckState(QModelIndex index);
void setProfilesComboBoxIndex(int index);
void filterChanged(const QString filter);
void showContextMenu(const QPoint &point);
void profileChanged(const QString &previous, const QString &current);
void profileRenamed(const QString &previous, const QString &current);
void updateOkButton(const QString &text);
void updateSplitter();
void updateViews();
// Action slots
void newProfile();
void deleteProfile();
// void moveUp();
// void moveDown();
// void moveTop();
// void moveBottom();
void check();
void uncheck();
void refresh();
private slots:
void slotCurrentIndexChanged(int index);
private:
DataFilesList *mDataFilesList;
DataFilesModel *mDataFilesModel;
QToolBar *mProfileToolBar;
PluginsProxyModel *mPluginsProxyModel;
QSortFilterProxyModel *mMastersProxyModel;
QSortFilterProxyModel *mFilterProxyModel;
// QTableView *mMastersTable;
// QTableView *mPluginsTable;
// QToolBar *mProfileToolBar;
QMenu *mContextMenu;
// QSplitter *mSplitter;
QAction *mNewProfileAction;
QAction *mDeleteProfileAction;
QAction *mCheckAction;
QAction *mUncheckAction;
// QAction *mMoveUpAction;
// QAction *mMoveDownAction;
@ -61,20 +84,22 @@ private:
// QAction *mMoveBottomAction;
Files::ConfigurationManager &mCfgMgr;
Files::PathContainer mDataDirs;
Files::PathContainer mDataLocal;
QSettings *mLauncherConfig;
GameSettings &mGameSettings;
LauncherSettings &mLauncherSettings;
TextInputDialog *mNewProfileDialog;
// const QStringList checkedPlugins();
// const QStringList selectedMasters();
void setMastersCheckstates(Qt::CheckState state);
void setPluginsCheckstates(Qt::CheckState state);
void createActions();
void setupDataFiles();
void setupConfig();
void readConfig();
void loadSettings();
};
#endif

View File

@ -1,16 +1,17 @@
#include "graphicspage.hpp"
#include <QtGui>
#include <cstdlib>
#include <boost/math/common_factor.hpp>
#include <boost/filesystem.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/files/ogreplugin.hpp>
#include <components/settings/settings.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include "graphicspage.hpp"
#include "settings/graphicssettings.hpp"
QString getAspect(int x, int y)
{
@ -24,52 +25,22 @@ QString getAspect(int x, int y)
return QString(QString::number(xaspect) + ":" + QString::number(yaspect));
}
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent)
: mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting)
, QWidget(parent)
{
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
setupUi(this);
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
mRendererComboBox = new QComboBox(rendererGroup);
// Set the maximum res we can set in windowed mode
QRect res = QApplication::desktop()->screenGeometry();
customWidthSpinBox->setMaximum(res.width());
customHeightSpinBox->setMaximum(res.height());
// Layout for the combobox and label
QGridLayout *renderSystemLayout = new QGridLayout();
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
connect(rendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
connect(fullScreenCheckBox, SIGNAL(stateChanged(int)), this, SLOT(slotFullScreenChanged(int)));
connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool)));
// Display
QGroupBox *displayGroup = new QGroupBox(tr("Display"), this);
mVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), displayGroup);
mFullScreenCheckBox = new QCheckBox(tr("Full Screen"), displayGroup);
QLabel *antiAliasingLabel = new QLabel(tr("Antialiasing:"), displayGroup);
QLabel *resolutionLabel = new QLabel(tr("Resolution:"), displayGroup);
mResolutionComboBox = new QComboBox(displayGroup);
mAntiAliasingComboBox = new QComboBox(displayGroup);
QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup);
rendererGroupLayout->addLayout(renderSystemLayout);
QGridLayout *displayGroupLayout = new QGridLayout(displayGroup);
displayGroupLayout->addWidget(mVSyncCheckBox, 0, 0, 1, 1);
displayGroupLayout->addWidget(mFullScreenCheckBox, 1, 0, 1, 1);
displayGroupLayout->addWidget(antiAliasingLabel, 2, 0, 1, 1);
displayGroupLayout->addWidget(mAntiAliasingComboBox, 2, 1, 1, 1);
displayGroupLayout->addWidget(resolutionLabel, 3, 0, 1, 1);
displayGroupLayout->addWidget(mResolutionComboBox, 3, 1, 1, 1);
// Layout for the whole page
QVBoxLayout *pageLayout = new QVBoxLayout(this);
QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
pageLayout->addWidget(rendererGroup);
pageLayout->addWidget(displayGroup);
pageLayout->addItem(vSpacer1);
connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
}
bool GraphicsPage::setupOgre()
@ -116,9 +87,8 @@ bool GraphicsPage::setupOgre()
#endif
}
boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir));
pluginDir = absPluginPath.string();
QDir dir(QString::fromStdString(pluginDir));
pluginDir = dir.absolutePath().toStdString();
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre);
Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre);
@ -138,7 +108,7 @@ bool GraphicsPage::setupOgre()
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
mSelectedRenderSystem = *r;
mRendererComboBox->addItem((*r)->getName().c_str());
rendererComboBox->addItem((*r)->getName().c_str());
}
QString openGLName = QString("OpenGL Rendering Subsystem");
@ -154,71 +124,85 @@ bool GraphicsPage::setupOgre()
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>"));
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
msgBox.exec();
return false;
}
// Now fill the GUI elements
int index = mRendererComboBox->findText(QString::fromStdString(Settings::Manager::getString("render system", "Video")));
int index = rendererComboBox->findText(mGraphicsSettings.value(QString("Video/render system")));
if ( index != -1) {
mRendererComboBox->setCurrentIndex(index);
}
else
{
rendererComboBox->setCurrentIndex(index);
} else {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(direct3DName));
rendererComboBox->setCurrentIndex(rendererComboBox->findText(direct3DName));
#else
mRendererComboBox->setCurrentIndex(mRendererComboBox->findText(openGLName));
rendererComboBox->setCurrentIndex(rendererComboBox->findText(openGLName));
#endif
}
mAntiAliasingComboBox->clear();
mResolutionComboBox->clear();
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
antiAliasingComboBox->clear();
resolutionComboBox->clear();
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
readConfig();
// Load the rest of the values
loadSettings();
return true;
}
void GraphicsPage::readConfig()
void GraphicsPage::loadSettings()
{
if (Settings::Manager::getBool("vsync", "Video"))
mVSyncCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/vsync")) == QLatin1String("true"))
vSyncCheckBox->setCheckState(Qt::Checked);
if (Settings::Manager::getBool("fullscreen", "Video"))
mFullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
fullScreenCheckBox->setCheckState(Qt::Checked);
int aaIndex = mAntiAliasingComboBox->findText(QString::fromStdString(Settings::Manager::getString("antialiasing", "Video")));
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
if (aaIndex != -1)
mAntiAliasingComboBox->setCurrentIndex(aaIndex);
antiAliasingComboBox->setCurrentIndex(aaIndex);
QString resolution = QString::number(Settings::Manager::getInt("resolution x", "Video"));
resolution.append(" x " + QString::number(Settings::Manager::getInt("resolution y", "Video")));
QString width = mGraphicsSettings.value(QString("Video/resolution x"));
QString height = mGraphicsSettings.value(QString("Video/resolution y"));
QString resolution = width + QString(" x ") + height;
int resIndex = mResolutionComboBox->findText(resolution, Qt::MatchStartsWith);
if (resIndex != -1)
mResolutionComboBox->setCurrentIndex(resIndex);
int resIndex = resolutionComboBox->findText(resolution, Qt::MatchStartsWith);
if (resIndex != -1) {
standardRadioButton->toggle();
resolutionComboBox->setCurrentIndex(resIndex);
} else {
customRadioButton->toggle();
customWidthSpinBox->setValue(width.toInt());
customHeightSpinBox->setValue(height.toInt());
}
}
void GraphicsPage::writeConfig()
void GraphicsPage::saveSettings()
{
Settings::Manager::setBool("vsync", "Video", mVSyncCheckBox->checkState());
Settings::Manager::setBool("fullscreen", "Video", mFullScreenCheckBox->checkState());
Settings::Manager::setString("antialiasing", "Video", mAntiAliasingComboBox->currentText().toStdString());
Settings::Manager::setString("render system", "Video", mRendererComboBox->currentText().toStdString());
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
// Get the current resolution, but with the tabs replaced with a single space
QString resolution = mResolutionComboBox->currentText().simplified();
QStringList tokens = resolution.split(" ", QString::SkipEmptyParts);
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
int resX = tokens.at(0).toInt();
int resY = tokens.at(2).toInt();
Settings::Manager::setInt("resolution x", "Video", resX);
Settings::Manager::setInt("resolution y", "Video", resY);
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
if (standardRadioButton->isChecked()) {
QRegExp resolutionRe(QString("(\\d+) x (\\d+).*"));
if (resolutionRe.exactMatch(resolutionComboBox->currentText().simplified())) {
mGraphicsSettings.setValue(QString("Video/resolution x"), resolutionRe.cap(1));
mGraphicsSettings.setValue(QString("Video/resolution y"), resolutionRe.cap(2));
}
} else {
mGraphicsSettings.setValue(QString("Video/resolution x"), QString::number(customWidthSpinBox->value()));
mGraphicsSettings.setValue(QString("Video/resolution y"), QString::number(customHeightSpinBox->value()));
}
}
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
@ -232,16 +216,14 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
{
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)
{
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 << ((key == "FSAA") ? QString("MSAA ") : QString("")) + QString::fromStdString((*opt_it).c_str()).simplified();
}
}
}
// Sort ascending
@ -258,7 +240,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer)
{
QString key ("Video Mode");
QString key("Video Mode");
QStringList result;
uint row = 0;
@ -275,24 +257,26 @@ QStringList GraphicsPage::getAvailableResolutions(Ogre::RenderSystem *renderer)
for (opt_it = i->second.possibleValues.begin ();
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
{
QString qval = QString::fromStdString(*opt_it).simplified();
// remove extra tokens after the resolution (for example bpp, can be there or not depending on rendersystem)
QStringList tokens = qval.split(" ", QString::SkipEmptyParts);
assert (tokens.size() >= 3);
QString resolutionStr = tokens.at(0) + QString(" x ") + tokens.at(2);
QRegExp resolutionRe(QString("(\\d+) x (\\d+)"));
QString resolution = QString::fromStdString(*opt_it).simplified();
QString aspect = getAspect(tokens.at(0).toInt(),tokens.at(2).toInt());
if (resolutionRe.exactMatch(resolution)) {
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
resolutionStr.append(tr("\t(Widescreen ") + aspect + ")");
int width = resolutionRe.cap(1).toInt();
int height = resolutionRe.cap(2).toInt();
} else if (aspect == QLatin1String("4:3")) {
resolutionStr.append(tr("\t(Standard 4:3)"));
QString aspect = getAspect(width, height);
if (aspect == QLatin1String("16:9") || aspect == QLatin1String("16:10")) {
resolution.append(tr("\t(Wide ") + aspect + ")");
} else if (aspect == QLatin1String("4:3")) {
resolution.append(tr("\t(Standard 4:3)"));
}
// do not add duplicate resolutions
if (!result.contains(resolution))
result.append(resolution);
}
// do not add duplicate resolutions
if (!result.contains(resolutionStr))
result << resolutionStr;
}
}
@ -306,9 +290,36 @@ void GraphicsPage::rendererChanged(const QString &renderer)
{
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
mAntiAliasingComboBox->clear();
mResolutionComboBox->clear();
antiAliasingComboBox->clear();
resolutionComboBox->clear();
mAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
mResolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
resolutionComboBox->addItems(getAvailableResolutions(mSelectedRenderSystem));
}
void GraphicsPage::slotFullScreenChanged(int state)
{
if (state == Qt::Checked) {
standardRadioButton->toggle();
customRadioButton->setEnabled(false);
customWidthSpinBox->setEnabled(false);
customHeightSpinBox->setEnabled(false);
} else {
customRadioButton->setEnabled(true);
customWidthSpinBox->setEnabled(true);
customHeightSpinBox->setEnabled(true);
}
}
void GraphicsPage::slotStandardToggled(bool checked)
{
if (checked) {
resolutionComboBox->setEnabled(true);
customWidthSpinBox->setEnabled(false);
customHeightSpinBox->setEnabled(false);
} else {
resolutionComboBox->setEnabled(false);
customWidthSpinBox->setEnabled(true);
customHeightSpinBox->setEnabled(true);
}
}

View File

@ -5,8 +5,8 @@
#include <OgreRoot.h>
#include <OgreRenderSystem.h>
#include <OgreConfigFile.h>
#include <OgreConfigDialog.h>
//#include <OgreConfigFile.h>
//#include <OgreConfigDialog.h>
// Static plugin headers
#ifdef ENABLE_PLUGIN_GL
@ -16,26 +16,29 @@
# include "OgreD3D9Plugin.h"
#endif
class QComboBox;
class QCheckBox;
class QStackedWidget;
class QSettings;
#include "ui_graphicspage.h"
class GraphicsSettings;
namespace Files { struct ConfigurationManager; }
class GraphicsPage : public QWidget
class GraphicsPage : public QWidget, private Ui::GraphicsPage
{
Q_OBJECT
public:
GraphicsPage(Files::ConfigurationManager &cfg, QWidget *parent = 0);
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
void saveSettings();
bool setupOgre();
void writeConfig();
public slots:
void rendererChanged(const QString &renderer);
private slots:
void slotFullScreenChanged(int state);
void slotStandardToggled(bool checked);
private:
Ogre::Root *mOgre;
Ogre::RenderSystem *mSelectedRenderSystem;
@ -48,22 +51,14 @@ private:
Ogre::D3D9Plugin* mD3D9Plugin;
#endif
QComboBox *mRendererComboBox;
QStackedWidget *mDisplayStackedWidget;
QComboBox *mAntiAliasingComboBox;
QComboBox *mResolutionComboBox;
QCheckBox *mVSyncCheckBox;
QCheckBox *mFullScreenCheckBox;
Files::ConfigurationManager &mCfgMgr;
GraphicsSettings &mGraphicsSettings;
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableResolutions(Ogre::RenderSystem *renderer);
void createPages();
void readConfig();
void loadSettings();
};
#endif

View File

@ -1,6 +1,6 @@
#include <QApplication>
#include <QTextCodec>
#include <QDir>
#include <QFile>
#include "maindialog.hpp"
@ -30,14 +30,17 @@ int main(int argc, char *argv[])
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
MainDialog mainWin;
if (mainWin.setup()) {
mainWin.show();
return app.exec();
} else {
return 0;
}
return 0;
return app.exec();
}

View File

@ -1,52 +1,16 @@
#include "maindialog.hpp"
#include <QtGui>
#include "maindialog.hpp"
#include "utils/checkablemessagebox.hpp"
#include "playpage.hpp"
#include "graphicspage.hpp"
#include "datafilespage.hpp"
MainDialog::MainDialog()
: mGameSettings(mCfgMgr)
{
QWidget *centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
mIconWidget = new QListWidget(centralWidget);
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(centralWidget);
QVBoxLayout *groupLayout = new QVBoxLayout(groupBox);
mPagesWidget = new QStackedWidget(groupBox);
groupLayout->addWidget(mPagesWidget);
QPushButton *playButton = new QPushButton(tr("Play"));
QDialogButtonBox *buttonBox = new QDialogButtonBox(centralWidget);
buttonBox->setStandardButtons(QDialogButtonBox::Close);
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
QVBoxLayout *dialogLayout = new QVBoxLayout(centralWidget);
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));
// Install the stylesheet font
QFile file;
QFontDatabase fontDatabase;
@ -56,64 +20,67 @@ MainDialog::MainDialog()
// Check if the font is installed
if (!fonts.contains("EB Garamond")) {
QString font = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
file.setFileName(font);
QString font = QString::fromStdString(mCfgMgr.getGlobalDataPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
file.setFileName(font);
if (!file.exists()) {
font = QString::fromStdString((mCfgMgr.getLocalPath() / "resources/mygui/EBGaramond-Regular.ttf").string());
}
if (!file.exists()) {
font = QString::fromStdString(mCfgMgr.getLocalPath().string()) + QString("resources/mygui/EBGaramond-Regular.ttf");
}
fontDatabase.addApplicationFont(font);
fontDatabase.addApplicationFont(font);
}
// Load the stylesheet
QString config = QString::fromStdString((mCfgMgr.getGlobalDataPath() / "resources/launcher.qss").string());
file.setFileName(config);
setupUi(this);
if (!file.exists()) {
file.setFileName(QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.qss").string()));
}
iconWidget->setViewMode(QListView::IconMode);
iconWidget->setWrapping(false);
iconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
iconWidget->setIconSize(QSize(48, 48));
iconWidget->setMovement(QListView::Static);
file.open(QFile::ReadOnly);
QString styleSheet = QLatin1String(file.readAll());
qApp->setStyleSheet(styleSheet);
file.close();
iconWidget->setSpacing(4);
iconWidget->setCurrentRow(0);
iconWidget->setFlow(QListView::LeftToRight);
QPushButton *playButton = new QPushButton(tr("Play"));
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
// Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
createIcons();
createPages();
}
void MainDialog::createIcons()
{
if (!QIcon::hasThemeIcon("document-new")) {
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);
QListWidgetItem *playButton = new QListWidgetItem(iconWidget);
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);
QListWidgetItem *graphicsButton = new QListWidgetItem(iconWidget);
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);
QListWidgetItem *dataFilesButton = new QListWidgetItem(iconWidget);
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,
connect(iconWidget,
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
@ -122,77 +89,185 @@ void MainDialog::createIcons()
void MainDialog::createPages()
{
mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(mCfgMgr, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel());
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex());
// Add the pages to the stacked widget
mPagesWidget->addWidget(mPlayPage);
mPagesWidget->addWidget(mGraphicsPage);
mPagesWidget->addWidget(mDataFilesPage);
pagesWidget->addWidget(mPlayPage);
pagesWidget->addWidget(mGraphicsPage);
pagesWidget->addWidget(mDataFilesPage);
// Select the first page
mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select);
iconWidget->setCurrentItem(iconWidget->item(0), QItemSelectionModel::Select);
connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play()));
connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));
connect(mPlayPage->mProfilesComboBox,
SIGNAL(currentIndexChanged(int)),
mDataFilesPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
connect(mDataFilesPage->mProfilesComboBox,
SIGNAL(currentIndexChanged(int)),
mPlayPage->mProfilesComboBox, SLOT(setCurrentIndex(int)));
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int)));
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int)));
}
bool MainDialog::showFirstRunDialog()
{
CheckableMessageBox msgBox(this);
msgBox.setWindowTitle(tr("Morrowind installation detected"));
QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxQuestion);
int size = QApplication::style()->pixelMetric(QStyle::PM_MessageBoxIconSize);
msgBox.setIconPixmap(icon.pixmap(size, size));
QAbstractButton *importerButton =
msgBox.addButton(tr("Import"), QDialogButtonBox::AcceptRole); // ActionRole doesn't work?!
QAbstractButton *skipButton =
msgBox.addButton(tr("Skip"), QDialogButtonBox::RejectRole);
Q_UNUSED(skipButton); // Surpress compiler unused warning
msgBox.setStandardButtons(QDialogButtonBox::NoButton);
msgBox.setText(tr("<br><b>An existing Morrowind installation was detected</b><br><br> \
Would you like to import settings from Morrowind.ini?<br>"));
msgBox.setCheckBoxText(tr("Include selected masters and plugins (creates a new profile)"));
msgBox.exec();
if (msgBox.clickedButton() == importerButton) {
QStringList iniPaths;
foreach (const QString &path, mGameSettings.getDataDirs()) {
QDir dir(path);
dir.setPath(dir.canonicalPath()); // Resolve symlinks
if (!dir.cdUp())
continue; // Cannot move from Data Files
if (dir.exists(QString("Morrowind.ini")))
iniPaths.append(dir.absoluteFilePath(QString("Morrowind.ini")));
}
if (iniPaths.isEmpty()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error reading Morrowind configuration file"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not find Morrowind.ini</b><br><br> \
The problem may be due to an incomplete installation of Morrowind.<br> \
Reinstalling Morrowind may resolve the problem."));
msgBox.exec();
return false;
}
if (iniPaths.count() > 1) {
// Multiple Morrowind.ini files found
bool ok;
QString path = QInputDialog::getItem(this, tr("Multiple configurations found"),
tr("<br><b>There are multiple Morrowind.ini files found.</b><br><br> \
Please select the one you wish to import from:"), iniPaths, 0, false, &ok);
if (ok && !path.isEmpty()) {
iniPaths.clear();
iniPaths.append(path);
} else {
// Cancel was clicked TODO: should we abort here?
return false;
}
}
// Create the file if it doesn't already exist, else the importer will fail
QString path = QString::fromStdString(mCfgMgr.getUserPath().string()) + QString("openmw.cfg");
QFile file(path);
if (!file.exists()) {
if (!file.open(QIODevice::ReadWrite)) {
// File cannot be created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
file.close();
}
// Construct the arguments to run the importer
QStringList arguments;
if (msgBox.isChecked())
arguments.append(QString("-g"));
arguments.append(iniPaths.first());
arguments.append(path);
if (!startProgram(QString("mwiniimport"), arguments, false))
return false;
// Re-read the game settings
mGameSettings.clear();
if (!setupGameSettings())
return false;
// Add a new profile
if (msgBox.isChecked()) {
mLauncherSettings.setValue(QString("Profiles/CurrentProfile"), QString("Imported"));
mLauncherSettings.remove(QString("Profiles/Imported/master"));
mLauncherSettings.remove(QString("Profiles/Imported/plugin"));
QStringList masters = mGameSettings.values(QString("master"));
QStringList plugins = mGameSettings.values(QString("plugin"));
foreach (const QString &master, masters) {
mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master);
}
foreach (const QString &plugin, plugins) {
mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin);
}
}
}
return true;
}
bool MainDialog::setup()
{
// Create the settings manager and load default settings file
const std::string localdefault = (mCfgMgr.getLocalPath() / "settings-default.cfg").string();
const std::string globaldefault = (mCfgMgr.getGlobalPath() / "settings-default.cfg").string();
// prefer local
if (boost::filesystem::exists(localdefault)) {
mSettings.loadDefault(localdefault);
} else if (boost::filesystem::exists(globaldefault)) {
mSettings.loadDefault(globaldefault);
} 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 find %0</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem.").arg(QString::fromStdString(globaldefault)));
msgBox.exec();
if (!setupLauncherSettings())
return false;
if (!setupGameSettings())
return false;
if (!setupGraphicsSettings())
return false;
// Check if we need to show the importer
if (mLauncherSettings.value(QString("General/firstrun"), QString("true")) == QLatin1String("true"))
{
if (!showFirstRunDialog())
return false;
}
// load user settings if they exist, otherwise just load the default settings as user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
// Now create the pages as they need the settings
createPages();
if (boost::filesystem::exists(settingspath))
mSettings.loadUser(settingspath);
else if (boost::filesystem::exists(localdefault))
mSettings.loadUser(localdefault);
else if (boost::filesystem::exists(globaldefault))
mSettings.loadUser(globaldefault);
// Setup the Graphics page
if (!mGraphicsPage->setupOgre()) {
// Call this so we can exit on Ogre errors before mainwindow is shown
if (!mGraphicsPage->setupOgre())
return false;
}
// Setup the Data Files page
if (!mDataFilesPage->setupDataFiles()) {
return false;
}
loadSettings();
return true;
}
@ -201,89 +276,409 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
if (!current)
current = previous;
mPagesWidget->setCurrentIndex(mIconWidget->row(current));
pagesWidget->setCurrentIndex(iconWidget->row(current));
}
bool MainDialog::setupLauncherSettings()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QStringList paths;
paths.append(QString("launcher.cfg"));
paths.append(userPath + QString("launcher.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mLauncherSettings.readFile(stream);
}
file.close();
}
return true;
}
bool MainDialog::setupGameSettings()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
QStringList paths;
paths.append(userPath + QString("openmw.cfg"));
paths.append(QString("openmw.cfg"));
paths.append(globalPath + QString("openmw.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGameSettings.readFile(stream);
}
file.close();
}
QStringList dataDirs;
// Check if the paths actually contain data files
foreach (const QString path, mGameSettings.getDataDirs()) {
QDir dir(path);
QStringList filters;
filters << "*.esp" << "*.esm";
if (!dir.entryList(filters).isEmpty())
dataDirs.append(path);
}
if (dataDirs.isEmpty())
{
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error detecting Morrowind installation"));
msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Cancel);
msgBox.setText(QObject::tr("<br><b>Could not find the Data Files location</b><br><br> \
The directory containing the data files was not found.<br><br> \
Press \"Browse...\" to specify the location manually.<br>"));
QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("B&rowse..."), QMessageBox::ActionRole);
msgBox.exec();
QString selectedFile;
if (msgBox.clickedButton() == dirSelectButton) {
selectedFile = QFileDialog::getOpenFileName(
NULL,
QObject::tr("Select master file"),
QDir::currentPath(),
QString(tr("Morrowind master file (*.esm)")));
}
if (selectedFile.isEmpty())
return false; // Cancel was clicked;
QFileInfo info(selectedFile);
// Add the new dir to the settings file and to the data dir container
mGameSettings.setValue(QString("data"), info.absolutePath());
mGameSettings.addDataDir(info.absolutePath());
}
return true;
}
bool MainDialog::setupGraphicsSettings()
{
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
QFile localDefault(QString("settings-default.cfg"));
QFile globalDefault(globalPath + QString("settings-default.cfg"));
if (!localDefault.exists() && !globalDefault.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error reading OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not find settings-default.cfg</b><br><br> \
The problem may be due to an incomplete installation of OpenMW.<br> \
Reinstalling OpenMW may resolve the problem."));
msgBox.exec();
return false;
}
QStringList paths;
paths.append(globalPath + QString("settings-default.cfg"));
paths.append(QString("settings-default.cfg"));
paths.append(userPath + QString("settings.cfg"));
foreach (const QString &path, paths) {
qDebug() << "Loading config file:" << qPrintable(path);
QFile file(path);
if (file.exists()) {
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error opening OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(QObject::tr("<br><b>Could not open %0 for reading</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return false;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGraphicsSettings.readFile(stream);
}
file.close();
}
return true;
}
void MainDialog::loadSettings()
{
int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt();
int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt();
int posX = mLauncherSettings.value(QString("General/MainWindow/posx")).toInt();
int posY = mLauncherSettings.value(QString("General/MainWindow/posy")).toInt();
resize(width, height);
move(posX, posY);
}
void MainDialog::saveSettings()
{
QString width = QString::number(this->width());
QString height = QString::number(this->height());
mLauncherSettings.setValue(QString("General/MainWindow/width"), width);
mLauncherSettings.setValue(QString("General/MainWindow/height"), height);
QString posX = QString::number(this->pos().x());
QString posY = QString::number(this->pos().y());
mLauncherSettings.setValue(QString("General/MainWindow/posx"), posX);
mLauncherSettings.setValue(QString("General/MainWindow/posy"), posY);
mLauncherSettings.setValue(QString("General/firstrun"), QString("false"));
}
void MainDialog::writeSettings()
{
// Now write all config files
saveSettings();
mGraphicsPage->saveSettings();
mDataFilesPage->saveSettings();
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QDir dir(userPath);
if (!dir.exists()) {
if (!dir.mkpath(userPath)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error creating OpenMW configuration directory"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not create %0</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(userPath));
msgBox.exec();
return;
}
}
// Game settings
QFile file(userPath + QString("openmw.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
QTextStream stream(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGameSettings.writeFile(stream);
file.close();
// Graphics settings
file.setFileName(userPath + QString("settings.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing OpenMW configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mGraphicsSettings.writeFile(stream);
file.close();
// Launcher settings
file.setFileName(userPath + QString("launcher.cfg"));
if (!file.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)) {
// File cannot be opened or created
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error writing Launcher configuration file"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not open or create %0 for writing</b><br><br> \
Please make sure you have the right permissions \
and try again.<br>").arg(file.fileName()));
msgBox.exec();
return;
}
stream.setDevice(&file);
stream.setCodec(QTextCodec::codecForName("UTF-8"));
mLauncherSettings.writeFile(stream);
file.close();
}
void MainDialog::closeEvent(QCloseEvent *event)
{
// Now write all config files
mDataFilesPage->writeConfig();
mGraphicsPage->writeConfig();
// Save user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
mSettings.saveUser(settingspath);
saveSettings();
writeSettings();
event->accept();
}
void MainDialog::play()
{
// First do a write of all the configs, just to be sure
mDataFilesPage->writeConfig();
mGraphicsPage->writeConfig();
saveSettings();
writeSettings();
// Save user settings
const std::string settingspath = (mCfgMgr.getUserPath() / "settings.cfg").string();
mSettings.saveUser(settingspath);
// Launch the game detached
startProgram(QString("openmw"), true);
qApp->quit();
}
bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached)
{
QString path = name;
#ifdef Q_OS_WIN
QString game = "./openmw.exe";
QFile file(game);
path.append(QString(".exe"));
#elif defined(Q_OS_MAC)
QDir dir(QCoreApplication::applicationDirPath());
QString game = dir.absoluteFilePath("openmw");
QFile file(game);
game = "\"" + game + "\"";
path = dir.absoluteFilePath(name);
#else
QString game = "./openmw";
QFile file(game);
path.prepend(QString("./"));
#endif
QFile file(path);
QProcess process;
QFileInfo info(file);
if (!file.exists()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setWindowTitle(tr("Error starting executable"));
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.setText(tr("<br><b>Could not find %1</b><br><br> \
The application is not found.<br> \
Please make sure OpenMW is installed correctly and try again.<br>").arg(info.fileName()));
msgBox.exec();
return;
return false;
}
if (!info.isExecutable()) {
QMessageBox msgBox;
msgBox.setWindowTitle("Error starting OpenMW");
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Warning);
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.setText(tr("<br><b>Could not start %1</b><br><br> \
The application is not executable.<br> \
Please make sure you have the right permissions and try again.<br>").arg(info.fileName()));
msgBox.exec();
return;
return false;
}
// 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();
// Start the executable
if (detached) {
if (!process.startDetached(path, arguments)) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
An error occurred while starting %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
return;
return false;
}
} else {
qApp->quit();
}
}
process.start(path, arguments);
if (!process.waitForFinished()) {
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error starting executable"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Could not start %1</b><br><br> \
An error occurred while starting %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(process.errorString());
msgBox.exec();
return false;
}
if (process.exitCode() != 0) {
QString error(process.readAllStandardError());
error.append(tr("\nArguments:\n"));
error.append(arguments.join(" "));
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Error running executable"));
msgBox.setIcon(QMessageBox::Critical);
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>Executable %1 returned an error</b><br><br> \
An error occurred while running %1.<br><br> \
Press \"Show Details...\" for more information.<br>").arg(info.fileName()));
msgBox.setDetailedText(error);
msgBox.exec();
return false;
}
}
return true;
}

View File

@ -4,7 +4,12 @@
#include <QMainWindow>
#include <components/files/configurationmanager.hpp>
#include <components/settings/settings.hpp>
#include "settings/gamesettings.hpp"
#include "settings/graphicssettings.hpp"
#include "settings/launchersettings.hpp"
#include "ui_mainwindow.h"
class QListWidget;
class QListWidgetItem;
@ -17,32 +22,46 @@ class PlayPage;
class GraphicsPage;
class DataFilesPage;
class MainDialog : public QMainWindow
class MainDialog : public QMainWindow, private Ui::MainWindow
{
Q_OBJECT
public:
MainDialog();
bool setup();
bool showFirstRunDialog();
public slots:
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void play();
bool setup();
private:
void createIcons();
void createPages();
void closeEvent(QCloseEvent *event);
QListWidget *mIconWidget;
QStackedWidget *mPagesWidget;
bool setupLauncherSettings();
bool setupGameSettings();
bool setupGraphicsSettings();
void loadSettings();
void saveSettings();
void writeSettings();
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); }
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false);
void closeEvent(QCloseEvent *event);
PlayPage *mPlayPage;
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
Files::ConfigurationManager mCfgMgr;
Settings::Manager mSettings;
GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
LauncherSettings mLauncherSettings;
};
#endif

View File

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

View File

@ -3,19 +3,33 @@
#include <QWidget>
#include "ui_playpage.h"
class QComboBox;
class QPushButton;
class QAbstractItemModel;
class PlayPage : public QWidget
class PlayPage : public QWidget, private Ui::PlayPage
{
Q_OBJECT
public:
PlayPage(QWidget *parent = 0);
void setProfilesComboBoxModel(QAbstractItemModel *model);
signals:
void profileChanged(int index);
void playButtonClicked();
public slots:
void setProfilesComboBoxIndex(int index);
private slots:
void slotCurrentIndexChanged(int index);
void slotPlayClicked();
QComboBox *mProfilesComboBox;
QPushButton *mPlayButton;
};
#endif
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 &currentKey, 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 &currentKey, 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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
#include <QDialogButtonBox>
#include <QApplication>
#include <QPushButton>
#include <QDebug>
#include <QLabel>
#include <QVBoxLayout>
#include <QValidator>
@ -17,9 +17,19 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
mButtonBox->addButton(QDialogButtonBox::Ok);
mButtonBox->addButton(QDialogButtonBox::Cancel);
setMaximumHeight(height());
setOkButtonEnabled(false);
setModal(true);
// Line edit
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
mLineEdit = new LineEdit(this);
mLineEdit->setValidator(validator);
mLineEdit->setCompleter(0);
QLabel *label = new QLabel(this);
label->setText(text);
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(label);
dialogLayout->addWidget(mLineEdit);
dialogLayout->addWidget(mButtonBox);
// Messageboxes on mac have no title
#ifndef Q_OS_MAC
@ -28,22 +38,13 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
Q_UNUSED(title);
#endif
QLabel *label = new QLabel(this);
label->setText(text);
// Line edit
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
mLineEdit = new LineEdit(this);
mLineEdit->setValidator(validator);
mLineEdit->setCompleter(0);
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
dialogLayout->addWidget(label);
dialogLayout->addWidget(mLineEdit);
dialogLayout->addWidget(mButtonBox);
setMaximumHeight(height());
setOkButtonEnabled(false);
setModal(true);
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
}
int TextInputDialog::exec()
@ -55,7 +56,17 @@ int TextInputDialog::exec()
void TextInputDialog::setOkButtonEnabled(bool enabled)
{
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
okButton->setEnabled(enabled);
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
if (enabled) {
mLineEdit->setPalette(QApplication::palette());
} else {
// Existing profile name, make the text red
mLineEdit->setPalette(*palette);
}
}

View File

@ -41,7 +41,7 @@ opencs_units_noqt (model/tools
opencs_units (view/doc
viewmanager view operations operation subview startup opendialog
viewmanager view operations operation subview startup filedialog
)
@ -76,6 +76,10 @@ set (OPENCS_US
)
set (OPENCS_RES ../../files/opencs/resources.qrc
../../files/launcher/launcher.qrc
)
set (OPENCS_UI ../../files/ui/datafilespage.ui
)
source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})

View File

@ -1,14 +1,12 @@
#include "editor.hpp"
#include <sstream>
#include <QtGui/QApplication>
#include "model/doc/document.hpp"
#include "model/world/data.hpp"
CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
CS::Editor::Editor() : mViewManager (mDocumentManager)
{
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
@ -16,42 +14,99 @@ CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mOpenDialog, SIGNAL(accepted()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles()));
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile()));
setupDataFiles();
}
void CS::Editor::setupDataFiles()
{
boost::program_options::variables_map variables;
boost::program_options::options_description desc;
desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
boost::program_options::notify(variables);
mCfgMgr.readConfiguration(variables, desc);
Files::PathContainer mDataDirs, mDataLocal;
if (!variables["data"].empty()) {
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
}
std::string local = variables["data-local"].as<std::string>();
if (!local.empty()) {
mDataLocal.push_back(Files::PathContainer::value_type(local));
}
mCfgMgr.processPaths(mDataDirs);
mCfgMgr.processPaths(mDataLocal);
// Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
mFileDialog.setEncoding(encoding);
Files::PathContainer dataDirs;
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{
QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path);
}
}
void CS::Editor::createDocument()
{
mStartup.hide();
/// \todo open the ESX picker instead
std::ostringstream stream;
stream << "NewDocument" << (++mNewDocumentIndex);
std::vector<boost::filesystem::path> files;
files.push_back (stream.str());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
mViewManager.addView (document);
mFileDialog.newFile();
}
void CS::Editor::loadDocument()
{
mStartup.hide();
mOpenDialog.show();
mOpenDialog.raise();
mOpenDialog.activateWindow();
mFileDialog.openFile();
}
void CS::Editor::openFiles()
{
std::vector<boost::filesystem::path> paths;
mOpenDialog.getFileList(paths);
CSMDoc::Document *document = mDocumentManager.addDocument(paths, false);
std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) {
files.push_back(path.toStdString());
}
CSMDoc::Document *document = mDocumentManager.addDocument(files, false);
mViewManager.addView (document);
mFileDialog.hide();
}
void CS::Editor::createNewFile()
{
std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) {
files.push_back(path.toStdString());
}
files.push_back(mFileDialog.fileName().toStdString());
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
mViewManager.addView (document);
mFileDialog.hide();
}
int CS::Editor::run()
@ -59,4 +114,4 @@ int CS::Editor::run()
mStartup.show();
return QApplication::exec();
}
}

View File

@ -3,11 +3,13 @@
#include <QObject>
#include <components/files/configurationmanager.hpp>
#include "model/doc/documentmanager.hpp"
#include "view/doc/viewmanager.hpp"
#include "view/doc/startup.hpp"
#include "view/doc/opendialog.hpp"
#include "view/doc/filedialog.hpp"
namespace CS
{
@ -15,12 +17,14 @@ namespace CS
{
Q_OBJECT
int mNewDocumentIndex; ///< \todo remove when the proper new document dialogue is implemented.
CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup;
OpenDialog mOpenDialog;
FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
void setupDataFiles();
// not implemented
Editor (const Editor&);
@ -39,7 +43,8 @@ namespace CS
void loadDocument();
void openFiles();
void createNewFile();
};
}
#endif
#endif

View File

@ -18,9 +18,6 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
if (lastAsModified)
getData().loadFile (*end2, false);
addOptionalGmsts();
addOptionalGlobals();
}
void CSMDoc::Document::addOptionalGmsts()
@ -199,6 +196,8 @@ void CSMDoc::Document::createBase()
getData().getGlobals().add (record);
}
/// \todo add GMSTs
}
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_)
@ -213,7 +212,9 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b
mName = files.back().filename().string();
if (files.size()>1 || !new_)
if (new_ && files.size()==1)
createBase();
else if (files.size()>1)
{
std::vector<boost::filesystem::path>::const_iterator end = files.end();
@ -223,8 +224,8 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b
load (files.begin(), end, !new_);
}
if (new_ && files.size()==1)
createBase();
addOptionalGmsts();
addOptionalGlobals();
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));

View File

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

View File

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

View File

@ -70,8 +70,8 @@ find_package(Qt4 COMPONENTS QtCore QtGui)
if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
add_component_qt_dir (fileorderlist
datafileslist model/modelitem model/datafilesmodel model/esm/esmfile
utils/filedialog utils/lineedit utils/naturalsort
model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile
utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort
)
include(${QT_USE_FILE})

View File

@ -1,351 +0,0 @@
#include <QtGui>
#include <components/esm/esmreader.hpp>
#include <components/files/configurationmanager.hpp>
#include "model/datafilesmodel.hpp"
#include "model/esm/esmfile.hpp"
#include "utils/filedialog.hpp"
#include "utils/lineedit.hpp"
#include "utils/naturalsort.hpp"
#include "datafileslist.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) */
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();
}
DataFilesList::DataFilesList(Files::ConfigurationManager &cfg, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
{
// Models
mMastersModel = new DataFilesModel(this);
mPluginsModel = new DataFilesModel(this);
mPluginsProxyModel = new QSortFilterProxyModel();
mPluginsProxyModel->setDynamicSortFilter(true);
mPluginsProxyModel->setSourceModel(mPluginsModel);
// Filter toolbar
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);
QCheckBox checkBox;
unsigned int height = checkBox.sizeHint().height() + 4;
mMastersTable = new QTableView(this);
mMastersTable->setModel(mMastersModel);
mMastersTable->setObjectName("MastersTable");
mMastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
mMastersTable->setSelectionMode(QAbstractItemView::SingleSelection);
mMastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
mMastersTable->setAlternatingRowColors(true);
mMastersTable->horizontalHeader()->setStretchLastSection(true);
mMastersTable->horizontalHeader()->hide();
// Set the row height to the size of the checkboxes
mMastersTable->verticalHeader()->setDefaultSectionSize(height);
mMastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
mMastersTable->verticalHeader()->hide();
mMastersTable->setColumnHidden(1, true);
mMastersTable->setColumnHidden(2, true);
mMastersTable->setColumnHidden(3, true);
mMastersTable->setColumnHidden(4, true);
mMastersTable->setColumnHidden(5, true);
mMastersTable->setColumnHidden(6, true);
mMastersTable->setColumnHidden(7, true);
mMastersTable->setColumnHidden(8, true);
mPluginsTable = new QTableView(this);
mPluginsTable->setModel(mPluginsProxyModel);
mPluginsTable->setObjectName("PluginsTable");
mPluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
mPluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
mPluginsTable->setSelectionMode(QAbstractItemView::SingleSelection);
mPluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
mPluginsTable->setAlternatingRowColors(true);
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
mPluginsTable->horizontalHeader()->hide();
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
mPluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
mPluginsTable->setColumnHidden(1, true);
mPluginsTable->setColumnHidden(2, true);
mPluginsTable->setColumnHidden(3, true);
mPluginsTable->setColumnHidden(4, true);
mPluginsTable->setColumnHidden(5, true);
mPluginsTable->setColumnHidden(6, true);
mPluginsTable->setColumnHidden(7, true);
mPluginsTable->setColumnHidden(8, true);
// Add both tables to a splitter
QSplitter *splitter = new QSplitter(this);
splitter->setOrientation(Qt::Horizontal);
splitter->addWidget(mMastersTable);
splitter->addWidget(mPluginsTable);
// Adjust the default widget widths inside the splitter
QList<int> sizeList;
sizeList << 175 << 200;
splitter->setSizes(sizeList);
QVBoxLayout *pageLayout = new QVBoxLayout(this);
pageLayout->addWidget(filterToolBar);
pageLayout->addWidget(splitter);
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(mMastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
connect(mMastersModel, SIGNAL(checkedItemsChanged(QStringList,QStringList)), mPluginsModel, SLOT(slotcheckedItemsChanged(QStringList,QStringList)));
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
connect(mPluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
createActions();
}
void DataFilesList::createActions()
{
// Refresh the plugins
QAction *refreshAction = new QAction(tr("Refresh"), this);
refreshAction->setShortcut(QKeySequence(tr("F5")));
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
// Context menu actions
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()));
// Context menu for the plugins table
mContextMenu = new QMenu(this);
mContextMenu->addAction(mCheckAction);
mContextMenu->addAction(mUncheckAction);
}
bool DataFilesList::setupDataFiles(Files::PathContainer dataDirs, const QString encoding)
{
// Set the charset for reading the esm/esp files
if (!encoding.isEmpty() && encoding != QLatin1String("win1252")) {
mMastersModel->setEncoding(encoding);
mPluginsModel->setEncoding(encoding);
}
// Add the paths to the respective models
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) {
QString path = QString::fromStdString(it->string());
path.remove(QChar('\"'));
mMastersModel->addMasters(path);
mPluginsModel->addPlugins(path);
}
mMastersModel->sort(0);
mPluginsModel->sort(0);
// mMastersTable->sortByColumn(3, Qt::AscendingOrder);
// mPluginsTable->sortByColumn(3, Qt::AscendingOrder);
return true;
}
void DataFilesList::selectedFiles(std::vector<boost::filesystem::path>& paths)
{
QStringList masterPaths = mMastersModel->checkedItemsPaths();
foreach (const QString &path, masterPaths)
{
paths.push_back(path.toStdString());
}
QStringList pluginPaths = mPluginsModel->checkedItemsPaths();
foreach (const QString &path, pluginPaths)
{
paths.push_back(path.toStdString());
}
}
void DataFilesList::check()
{
// Check the current selection
if (!mPluginsTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
//sort selection ascending because selectedIndexes returns an unsorted list
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
foreach (const QModelIndex &index, indexes) {
if (!index.isValid())
return;
mPluginsModel->setCheckState(index, Qt::Checked);
}
}
void DataFilesList::uncheck()
{
// uncheck the current selection
if (!mPluginsTable->selectionModel()->hasSelection()) {
return;
}
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
//sort selection ascending because selectedIndexes returns an unsorted list
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
foreach (const QModelIndex &index, indexes) {
if (!index.isValid())
return;
mPluginsModel->setCheckState(index, Qt::Unchecked);
}
}
void DataFilesList::refresh()
{
mPluginsModel->sort(0);
// Refresh the plugins table
mPluginsTable->scrollToTop();
}
void DataFilesList::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(index);
(mPluginsModel->checkState(sourceIndex) == Qt::Checked)
? mPluginsModel->setCheckState(sourceIndex, Qt::Unchecked)
: mPluginsModel->setCheckState(sourceIndex, Qt::Checked);
}
if (object->objectName() == QLatin1String("MastersTable")) {
(mMastersModel->checkState(index) == Qt::Checked)
? mMastersModel->setCheckState(index, Qt::Unchecked)
: mMastersModel->setCheckState(index, Qt::Checked);
}
return;
}
void DataFilesList::uncheckAll()
{
mMastersModel->uncheckAll();
mPluginsModel->uncheckAll();
}
void DataFilesList::filterChanged(const QString filter)
{
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
mPluginsProxyModel->setFilterRegExp(regExp);
}
void DataFilesList::showContextMenu(const QPoint &point)
{
// Make sure there are plugins in the view
if (!mPluginsTable->selectionModel()->hasSelection()) {
return;
}
QPoint globalPos = mPluginsTable->mapToGlobal(point);
QModelIndexList indexes = 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 &index, indexes) {
if (!index.isValid())
return;
(mPluginsModel->checkState(index) == Qt::Checked)
? mUncheckAction->setEnabled(true)
: mCheckAction->setEnabled(true);
}
// Show menu
mContextMenu->exec(globalPos);
}
void DataFilesList::setCheckState(const QString& element, Qt::CheckState state)
{
EsmFile *file = mPluginsModel->findItem(element);
if (file)
{
mPluginsModel->setCheckState(mPluginsModel->indexFromItem(file), Qt::Checked);
}
else
{
file = mMastersModel->findItem(element);
mMastersModel->setCheckState(mMastersModel->indexFromItem(file), Qt::Checked);
}
}
QStringList DataFilesList::checkedFiles()
{
return mMastersModel->checkedItems() + mPluginsModel->checkedItems();
}

View File

@ -1,77 +0,0 @@
#ifndef DATAFILESLIST_H
#define DATAFILESLIST_H
#include <QWidget>
#include <QModelIndex>
#include <components/files/collections.hpp>
class QTableView;
class QSortFilterProxyModel;
class QSettings;
class QAction;
class QToolBar;
class QMenu;
class ProfilesComboBox;
class DataFilesModel;
class TextInputDialog;
namespace Files { struct ConfigurationManager; }
class DataFilesList : public QWidget
{
Q_OBJECT
public:
DataFilesList(Files::ConfigurationManager& cfg, QWidget *parent = 0);
bool setupDataFiles(Files::PathContainer dataDirs, const QString encoding);
void selectedFiles(std::vector<boost::filesystem::path>& paths);
void uncheckAll();
QStringList checkedFiles();
void setCheckState(const QString& element, Qt::CheckState);
public slots:
void setCheckState(QModelIndex index);
void filterChanged(const QString filter);
void showContextMenu(const QPoint &point);
// Action slots
// void moveUp();
// void moveDown();
// void moveTop();
// void moveBottom();
void check();
void uncheck();
void refresh();
private:
DataFilesModel *mMastersModel;
DataFilesModel *mPluginsModel;
QSortFilterProxyModel *mPluginsProxyModel;
QTableView *mMastersTable;
QTableView *mPluginsTable;
QMenu *mContextMenu;
// QAction *mMoveUpAction;
// QAction *mMoveDownAction;
// QAction *mMoveTopAction;
// QAction *mMoveBottomAction;
QAction *mCheckAction;
QAction *mUncheckAction;
Files::ConfigurationManager &mCfgMgr;
// const QStringList checkedPlugins();
// const QStringList selectedMasters();
void createActions();
};
#endif

View File

@ -8,8 +8,6 @@
#include "esm/esmfile.hpp"
#include "../utils/naturalsort.hpp"
#include "datafilesmodel.hpp"
DataFilesModel::DataFilesModel(QObject *parent) :
@ -159,7 +157,7 @@ Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const
if (!file)
return Qt::NoItemFlags;
if (mAvailableFiles.contains(file->fileName())) {
if (canBeChecked(file)) {
if (index.column() == 0) {
return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
} else {
@ -206,53 +204,55 @@ bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, in
return false;
if (role == Qt::CheckStateRole) {
emit layoutAboutToBeChanged();
QString name = item(index.row())->fileName();
mCheckStates[name] = static_cast<Qt::CheckState>(value.toInt());
emit checkedItemsChanged(checkedItems(), uncheckedItems());
emit layoutChanged();
// Force a redraw of the view since unchecking one item can affect another
QModelIndex firstIndex = indexFromItem(mFiles.first());
QModelIndex lastIndex = indexFromItem(mFiles.last());
emit dataChanged(firstIndex, lastIndex);
emit checkedItemsChanged(checkedItems());
return true;
}
return false;
}
bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2)
{
//Masters first then alphabetically
if (e1->fileName().endsWith(".esm") && !e2->fileName().endsWith(".esm"))
return true;
if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm"))
return false;
return e1->fileName().toLower() < e2->fileName().toLower();
}
bool lessThanDate(const EsmFile *e1, const EsmFile *e2)
{
if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) {
return true;
} else {
return false;
}
// if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm"))
// return false;
// return e1->fileName().toLower() < e2->fileName().toLower();
}
void DataFilesModel::sort(int column, Qt::SortOrder order)
{
// TODO: Make this more efficient
emit layoutAboutToBeChanged();
QList<EsmFile *> sortedFiles;
QMultiMap<QString, QString> timestamps;
foreach (EsmFile *file, mFiles)
timestamps.insert(file->modified().toString(Qt::ISODate), file->fileName());
QMapIterator<QString, QString> ti(timestamps);
while (ti.hasNext()) {
ti.next();
QModelIndex index = indexFromItem(findItem(ti.value()));
if (!index.isValid())
continue;
EsmFile *file = item(index.row());
if (!file)
continue;
sortedFiles.append(file);
if (column == 3) {
qSort(mFiles.begin(), mFiles.end(), lessThanDate);
} else {
qSort(mFiles.begin(), mFiles.end(), lessThanEsmFile);
}
mFiles.clear();
mFiles = sortedFiles;
emit layoutChanged();
}
@ -263,78 +263,18 @@ void DataFilesModel::addFile(EsmFile *file)
emit endInsertRows();
}
void DataFilesModel::addMasters(const QString &path)
void DataFilesModel::addFiles(const QString &path)
{
QDir dir(path);
dir.setNameFilters(QStringList(QLatin1String("*.esp")));
// Read the dependencies from the plugins
foreach (const QString &path, dir.entryList()) {
try {
ESM::ESMReader fileReader;
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
fileReader.setEncoder(&encoder);
fileReader.open(dir.absoluteFilePath(path).toStdString());
ESM::ESMReader::MasterList mlist = fileReader.getMasters();
for (unsigned int i = 0; i < mlist.size(); ++i) {
QString master = QString::fromStdString(mlist[i].name);
// Add the plugin to the internal dependency map
mDependencies[master].append(path);
// Don't add esps
if (master.endsWith(".esp", Qt::CaseInsensitive))
continue;
QFileInfo info(dir.absoluteFilePath(master));
EsmFile *file = new EsmFile(master);
file->setDates(info.lastModified(), info.lastRead());
file->setPath(info.absoluteFilePath());
// Add the master to the table
if (findItem(master) == 0)
addFile(file);
}
} catch(std::runtime_error &e) {
// An error occurred while reading the .esp
qWarning() << "Error reading esp: " << e.what();
continue;
}
}
// See if the masters actually exist in the filesystem
dir.setNameFilters(QStringList(QLatin1String("*.esm")));
foreach (const QString &path, dir.entryList()) {
QFileInfo info(dir.absoluteFilePath(path));
if (findItem(path) == 0) {
EsmFile *file = new EsmFile(path);
file->setDates(info.lastModified(), info.lastRead());
addFile(file);
}
// Make the master selectable
mAvailableFiles.append(path);
}
}
void DataFilesModel::addPlugins(const QString &path)
{
QDir dir(path);
dir.setNameFilters(QStringList(QLatin1String("*.esp")));
QStringList filters;
filters << "*.esp" << "*.esm";
dir.setNameFilters(filters);
foreach (const QString &path, dir.entryList()) {
QFileInfo info(dir.absoluteFilePath(path));
EsmFile *file = new EsmFile(path);
try {
ESM::ESMReader fileReader;
ToUTF8::Utf8Encoder encoder (ToUTF8::calculateEncoding(mEncoding.toStdString()));
@ -347,9 +287,6 @@ void DataFilesModel::addPlugins(const QString &path)
for (unsigned int i = 0; i < mlist.size(); ++i) {
QString master = QString::fromStdString(mlist[i].name);
masters.append(master);
// Add the plugin to the internal dependency map
mDependencies[master].append(path);
}
file->setAuthor(QString::fromStdString(fileReader.getAuthor()));
@ -362,7 +299,8 @@ void DataFilesModel::addPlugins(const QString &path)
// Put the file in the table
addFile(file);
if (findItem(path) == 0)
addFile(file);
} catch(std::runtime_error &e) {
// An error occurred while reading the .esp
qWarning() << "Error reading esp: " << e.what();
@ -421,7 +359,7 @@ QStringList DataFilesModel::checkedItems()
QString name = file->fileName();
// Only add the items that are in the checked list and available
if (mCheckStates[name] == Qt::Checked && mAvailableFiles.contains(name))
if (mCheckStates[name] == Qt::Checked && canBeChecked(file))
list << name;
}
@ -439,8 +377,8 @@ QStringList DataFilesModel::checkedItemsPaths()
for (it = mFiles.constBegin(); it != itEnd; ++it) {
EsmFile *file = item(i);
++i;
if (mCheckStates[file->fileName()] == Qt::Checked && mAvailableFiles.contains(file->fileName()))
if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file))
list << file->path();
}
@ -475,24 +413,17 @@ QStringList DataFilesModel::uncheckedItems()
return list;
}
void DataFilesModel::slotcheckedItemsChanged(const QStringList &checkedItems, const QStringList &unCheckedItems)
bool DataFilesModel::canBeChecked(EsmFile *file) const
{
emit layoutAboutToBeChanged();
QStringList list;
foreach (const QString &file, checkedItems) {
list << mDependencies[file];
}
foreach (const QString &file, unCheckedItems) {
foreach (const QString &remove, mDependencies[file]) {
list.removeAll(remove);
//element can be checked if all its dependencies are
bool canBeChecked = true;
foreach (const QString &master, file->masters())
{
if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked)
{
canBeChecked = false;
break;
}
}
mAvailableFiles.clear();
mAvailableFiles.append(list);
emit layoutChanged();
return canBeChecked;
}

View File

@ -34,10 +34,7 @@ public:
void setEncoding(const QString &encoding);
void addFile(EsmFile *file);
void addMasters(const QString &path);
void addPlugins(const QString &path);
void addFiles(const QString &path);
void uncheckAll();
@ -53,16 +50,13 @@ public:
EsmFile* item(int row) const;
signals:
void checkedItemsChanged(const QStringList checkedItems, const QStringList unCheckedItems);
public slots:
void slotcheckedItemsChanged(const QStringList &checkedItems, const QStringList &unCheckedItems);
void checkedItemsChanged(const QStringList &items);
private:
bool canBeChecked(EsmFile *file) const;
void addFile(EsmFile *file);
QList<EsmFile *> mFiles;
QStringList mAvailableFiles;
QHash<QString, QStringList> mDependencies;
QHash<QString, Qt::CheckState> mCheckStates;
QString mEncoding;

View File

@ -26,15 +26,15 @@ public:
void setMasters(const QStringList &masters);
void setDescription(const QString &description);
inline QString fileName() { return mFileName; }
inline QString author() { return mAuthor; }
inline int size() { return mSize; }
inline QDateTime modified() { return mModified; }
inline QDateTime accessed() { return mAccessed; }
inline float version() { return mVersion; }
inline QString path() { return mPath; }
inline QStringList masters() { return mMasters; }
inline QString description() { return mDescription; }
inline QString fileName() const { return mFileName; }
inline QString author() const { return mAuthor; }
inline int size() const { return mSize; }
inline QDateTime modified() const { return mModified; }
inline QDateTime accessed() const { return mAccessed; }
inline float version() const { return mVersion; }
inline QString path() const { return mPath; }
inline QStringList masters() const { return mMasters; }
inline QString description() const { return mDescription; }
private:

View File

@ -0,0 +1,17 @@
#include "pluginsproxymodel.hpp"
PluginsProxyModel::PluginsProxyModel(QObject *parent) :
QSortFilterProxyModel(parent)
{
}
PluginsProxyModel::~PluginsProxyModel()
{
}
QVariant PluginsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation != Qt::Vertical || role != Qt::DisplayRole)
return QSortFilterProxyModel::headerData(section, orientation, role);
return section + 1;
}

View File

@ -0,0 +1,18 @@
#ifndef PLUGINSPROXYMODEL_HPP
#define PLUGINSPROXYMODEL_HPP
#include <QSortFilterProxyModel>
class QVariant;
class PluginsProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit PluginsProxyModel(QObject *parent = 0);
~PluginsProxyModel();
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
};
#endif // PLUGINSPROXYMODEL_HPP

View File

@ -0,0 +1,35 @@
#include <QToolButton>
#include <QStyle>
#include "comboboxlineedit.hpp"
ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent)
: QLineEdit(parent)
{
mClearButton = new QToolButton(this);
QPixmap pixmap(":images/clear.png");
mClearButton->setIcon(QIcon(pixmap));
mClearButton->setIconSize(pixmap.size());
mClearButton->setCursor(Qt::ArrowCursor);
mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
mClearButton->hide();
connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&)));
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setObjectName(QString("ComboBoxLineEdit"));
setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
}
void ComboBoxLineEdit::resizeEvent(QResizeEvent *)
{
QSize sz = mClearButton->sizeHint();
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
mClearButton->move(rect().right() - frameWidth - sz.width(),
(rect().bottom() + 1 - sz.height())/2);
}
void ComboBoxLineEdit::updateClearButton(const QString& text)
{
mClearButton->setVisible(!text.isEmpty());
}

View File

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

View File

@ -1,57 +0,0 @@
#include "filedialog.hpp"
#include <QDialogButtonBox>
#include <QPushButton>
FileDialog::FileDialog(QWidget *parent)
: QFileDialog(parent)
{
// Remove the default Choose button to prevent it being updated elsewhere
QDialogButtonBox *box = qFindChild<QDialogButtonBox*>(this);
Q_ASSERT(box);
box->removeButton(box->button(QDialogButtonBox::Open));
// Add our own button so we can disable/enable it
mChooseButton = new QPushButton(tr("&Choose"));
mChooseButton->setIcon(QIcon::fromTheme("document-open"));
mChooseButton->setEnabled(false);
box->addButton(mChooseButton, QDialogButtonBox::AcceptRole);
connect(this, SIGNAL(directoryEntered(const QString&)), this, SLOT(updateChooseButton(const QString&)));
emit directoryEntered(QDir::currentPath());
}
QString FileDialog::getExistingDirectory(QWidget *parent,
const QString &caption,
const QString &dir,
Options options)
{
// create a non-native file dialog
FileDialog dialog;
dialog.setFileMode(DirectoryOnly);
dialog.setOptions(options |= QFileDialog::DontUseNativeDialog | QFileDialog::ShowDirsOnly | QFileDialog::ReadOnly);
if (!caption.isEmpty())
dialog.setWindowTitle(caption);
if (!dir.isEmpty())
dialog.setDirectory(dir);
if (dialog.exec() == QDialog::Accepted) {
return dialog.selectedFiles().value(0);
}
return QString();
}
void FileDialog::updateChooseButton(const QString &directory)
{
QDir currentDir = QDir(directory);
currentDir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
currentDir.setNameFilters(QStringList() << "*.esm" << "*.esp");
if (!currentDir.entryList().isEmpty()) {
// There are data files in the current dir
mChooseButton->setEnabled(true);
} else {
mChooseButton->setEnabled(false);
}
}

View File

@ -1,28 +0,0 @@
#ifndef FILEDIALOG_HPP
#define FILEDIALOG_HPP
#include <QFileDialog>
class QPushButton;
class FileDialog : public QFileDialog
{
Q_OBJECT
public:
FileDialog(QWidget *parent = 0);
static QString getExistingDirectory(QWidget *parent = 0,
const QString &caption = QString(),
const QString &dir = QString(),
Options options = ShowDirsOnly);
private slots:
void updateChooseButton(const QString &directory);
private:
QPushButton *mChooseButton;
};
#endif // FILEDIALOG_HPP

View File

@ -1,7 +1,8 @@
#include "lineedit.hpp"
#include <QToolButton>
#include <QStyle>
#include "lineedit.hpp"
LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent)
{
@ -13,9 +14,11 @@ LineEdit::LineEdit(QWidget *parent)
mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
mClearButton->hide();
connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&)));
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
setObjectName(QString("LineEdit"));
setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1));
QSize msz = minimumSizeHint();
setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2),
qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2));
@ -29,9 +32,7 @@ void LineEdit::resizeEvent(QResizeEvent *)
(rect().bottom() + 1 - sz.height())/2);
}
void LineEdit::updateCloseButton(const QString& text)
void LineEdit::updateClearButton(const QString& text)
{
mClearButton->setVisible(!text.isEmpty());
}

View File

@ -25,7 +25,7 @@ protected:
void resizeEvent(QResizeEvent *);
private slots:
void updateCloseButton(const QString &text);
void updateClearButton(const QString &text);
private:
QToolButton *mClearButton;

View File

@ -0,0 +1,92 @@
#include <QRegExpValidator>
#include <QLineEdit>
#include <QString>
#include <QApplication>
#include <QKeyEvent>
#include "profilescombobox.hpp"
#include "comboboxlineedit.hpp"
ProfilesComboBox::ProfilesComboBox(QWidget *parent) :
QComboBox(parent)
{
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
setEditEnabled(true);
setValidator(mValidator);
setCompleter(0);
connect(this, SIGNAL(currentIndexChanged(int)), this,
SLOT(slotIndexChanged(int)));
setInsertPolicy(QComboBox::NoInsert);
}
void ProfilesComboBox::setEditEnabled(bool editable)
{
if (isEditable() == editable)
return;
if (!editable) {
disconnect(lineEdit(), SIGNAL(editingFinished()), this, SLOT(slotEditingFinished()));
disconnect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString)));
return setEditable(false);
}
// Reset the completer and validator
setEditable(true);
setValidator(mValidator);
ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);
setLineEdit(edit);
setCompleter(0);
connect(lineEdit(), SIGNAL(editingFinished()), this,
SLOT(slotEditingFinished()));
connect(lineEdit(), SIGNAL(textChanged(QString)), this,
SLOT(slotTextChanged(QString)));
}
void ProfilesComboBox::slotTextChanged(const QString &text)
{
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
int index = findText(text);
if (text.isEmpty() || (index != -1 && index != currentIndex())) {
lineEdit()->setPalette(*palette);
} else {
lineEdit()->setPalette(QApplication::palette());
}
}
void ProfilesComboBox::slotEditingFinished()
{
QString current = currentText();
QString previous = itemText(currentIndex());
if (currentIndex() == -1)
return;
if (current.isEmpty())
return;
if (current == previous)
return;
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);
}

View File

@ -4,7 +4,6 @@
#include <QComboBox>
class QString;
class QRegExpValidator;
class ProfilesComboBox : public QComboBox
@ -19,8 +18,9 @@ signals:
void profileRenamed(const QString &oldName, const QString &newName);
private slots:
void slotReturnPressed();
void slotEditingFinished();
void slotIndexChanged(int index);
void slotTextChanged(const QString &text);
private:
QString mOldProfile;

View File

@ -1,123 +0,0 @@
#PlayGroup {
background-image: url(":/images/playpage-background.png");
background-repeat: no-repeat;
background-position: top;
padding-left: 30px;
padding-right: 30px;
}
#MastersWidget {
selection-background-color: palette(highlight);
}
#PlayButton {
height: 50px;
margin-bottom: 30px;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 255, 255, 200),
stop:0.1 rgba(255, 255, 255, 15),
stop:0.49 rgba(255, 255, 255, 75),
stop:0.5 rgba(0, 0, 0, 0),
stop:0.9 rgba(0, 0, 0, 55),
stop:1 rgba(0, 0, 0, 100));
font-size: 26pt;
font-family: "EB Garamond", "EB Garamond 08";
color: black;
border-right: 1px solid rgba(0, 0, 0, 155);
border-left: 1px solid rgba(0, 0, 0, 55);
border-top: 1px solid rgba(0, 0, 0, 55);
border-bottom: 1px solid rgba(0, 0, 0, 155);
border-radius: 5px;
}
#PlayButton:hover {
border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-width: 2px;
border-style: solid;
}
#PlayButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55)
stop:0.95 rgba(255, 255, 255, 55),
stop:1 rgba(255, 255, 255, 155));
border: 1px solid rgba(0, 0, 0, 55);
}
#ProfileLabel {
font-size: 18pt;
font-family: "EB Garamond", "EB Garamond 08";
}
#ProfilesComboBox {
padding: 1px 18px 1px 3px;
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white);
border-width: 1px;
border-color: rgba(0, 0, 0, 125);
border-style: solid;
border-radius: 2px;
}
/*QComboBox gets the "on" state when the popup is open */
#ProfilesComboBox:!editable:on, #ProfilesComboBox::drop-down:editable:on {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55));
border: 1px solid rgba(0, 0, 0, 55);
}
#ProfilesComboBox { /* shift the text when the popup opens */
padding-top: 3px;
padding-left: 4px;
font-size: 12pt;
font-family: "EB Garamond", "EB Garamond 08";
}
#ProfilesComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
border-width: 1px;
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid; /* just a single line */
border-top-right-radius: 3px; /* same radius as the QComboBox */
border-bottom-right-radius: 3px;
}
#ProfilesComboBox::down-arrow {
image: url(":/images/down.png");
}
#ProfilesComboBox::down-arrow:on { /* shift the arrow when popup is open */
top: 1px;
left: 1px;
}
#ProfilesComboBox QAbstractItemView {
border: 2px solid lightgray;
border-radius: 5px;
}
#IconWidget {
background-image: url(":/images/openmw-header.png");
background-color: white;
background-repeat: no-repeat;
background-attachment: scroll;
background-position: right;
}

View File

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 477 B

View File

Before

Width:  |  Height:  |  Size: 498 B

After

Width:  |  Height:  |  Size: 498 B

View File

Before

Width:  |  Height:  |  Size: 793 B

After

Width:  |  Height:  |  Size: 793 B

View File

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 663 B

View File

Before

Width:  |  Height:  |  Size: 683 B

After

Width:  |  Height:  |  Size: 683 B

View File

Before

Width:  |  Height:  |  Size: 636 B

After

Width:  |  Height:  |  Size: 636 B

View File

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 644 B

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 59 KiB

View File

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

View File

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

125
files/ui/datafilespage.ui Normal file
View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DataFilesPage</class>
<widget class="QWidget" name="DataFilesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>256</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="filterLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="filterLabel">
<property name="text">
<string>Filter:</string>
</property>
</widget>
</item>
<item>
<widget class="LineEdit" name="filterLineEdit"/>
</item>
</layout>
</item>
<item>
<widget class="QSplitter" name="splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="childrenCollapsible">
<bool>false</bool>
</property>
<widget class="QTableView" name="mastersTable"/>
<widget class="QTableView" name="pluginsTable"/>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="profileLabel">
<property name="text">
<string>Current Profile:</string>
</property>
</widget>
</item>
<item>
<widget class="ProfilesComboBox" name="profilesComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="newProfileButton">
<property name="toolTip">
<string>New Profile</string>
</property>
<property name="text">
<string>&amp;New Profile</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteProfileButton">
<property name="toolTip">
<string>Delete Profile</string>
</property>
<property name="text">
<string>Delete Profile</string>
</property>
<property name="shortcut">
<string>Ctrl+D</string>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LineEdit</class>
<extends>QLineEdit</extends>
<header location="global">components/fileorderlist/utils/lineedit.hpp</header>
</customwidget>
<customwidget>
<class>ProfilesComboBox</class>
<extends>QComboBox</extends>
<header location="global">components/fileorderlist/utils/profilescombobox.hpp</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

142
files/ui/graphicspage.ui Normal file
View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GraphicsPage</class>
<widget class="QWidget" name="GraphicsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>332</width>
<height>297</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="rendererGroup">
<property name="title">
<string>Render System</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="rendererLabel">
<property name="text">
<string>Rendering Subsystem:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="rendererComboBox"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="displayGroup">
<property name="title">
<string>Display</string>
</property>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1,1">
<item row="0" column="0">
<widget class="QCheckBox" name="vSyncCheckBox">
<property name="text">
<string>Vertical Sync</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="fullScreenCheckBox">
<property name="text">
<string>Full Screen</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="antiAliasingLabel">
<property name="text">
<string>Anti-aliasing:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="resolutionLabel">
<property name="text">
<string>Resolution:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="antiAliasingComboBox"/>
</item>
<item row="3" column="1">
<layout class="QGridLayout" name="resolutionLayout">
<item row="1" column="2">
<layout class="QHBoxLayout" name="customResolutionLayout" stretch="1,0,1">
<item>
<widget class="QSpinBox" name="customWidthSpinBox">
<property name="minimum">
<number>800</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="multiplyLabel">
<property name="text">
<string> x </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="customHeightSpinBox">
<property name="minimum">
<number>600</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QRadioButton" name="customRadioButton">
<property name="text">
<string>Custom:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="standardRadioButton">
<property name="text">
<string>Standard:</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QComboBox" name="resolutionComboBox"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>61</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

80
files/ui/mainwindow.ui Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>575</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>575</width>
<height>575</height>
</size>
</property>
<property name="windowTitle">
<string>OpenMW Launcher</string>
</property>
<property name="windowIcon">
<iconset resource="../resources.qrc">
<normaloff>:/images/openmw.png</normaloff>:/images/openmw.png</iconset>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QListWidget" name="iconWidget">
<property name="minimumSize">
<size>
<width>400</width>
<height>80</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#iconWidget {
background-image: url(&quot;:/images/openmw-header.png&quot;);
background-color: white;
background-repeat: no-repeat;
background-attachment: scroll;
background-position: right;
}
</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QStackedWidget" name="pagesWidget"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>

192
files/ui/playpage.ui Normal file
View File

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PlayPage</class>
<widget class="QWidget" name="PlayPage">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QFrame" name="Scroll">
<property name="styleSheet">
<string notr="true">#Scroll {
background-image: url(&quot;:/images/playpage-background.png&quot;);
background-repeat: no-repeat;
background-position: top;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>30</number>
</property>
<property name="topMargin">
<number>100</number>
</property>
<property name="rightMargin">
<number>30</number>
</property>
<item row="4" column="1">
<widget class="QComboBox" name="profilesComboBox">
<property name="styleSheet">
<string notr="true">#profilesComboBox {
padding: 1px 18px 1px 3px;
background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 white, stop:0.2 rgba(0, 0, 0, 25), stop:1 white);
border-width: 1px;
border-color: rgba(0, 0, 0, 125);
border-style: solid;
border-radius: 2px;
}
/*QComboBox gets the &quot;on&quot; state when the popup is open */
#profilesComboBox:!editable:on, #ProfilesComboBox::drop-down:editable:on {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55));
border: 1px solid rgba(0, 0, 0, 55);
}
#profilesComboBox { /* shift the text when the popup opens */
padding-top: 3px;
padding-left: 4px;
font-size: 12pt;
font-family: &quot;EB Garamond&quot;, &quot;EB Garamond 08&quot;;
}
#profilesComboBox::drop-down {
subcontrol-origin: padding;
subcontrol-position: top right;
border-width: 1px;
border-left-width: 1px;
border-left-color: darkgray;
border-left-style: solid; /* just a single line */
border-top-right-radius: 3px; /* same radius as the QComboBox */
border-bottom-right-radius: 3px;
}
#profilesComboBox::down-arrow {
image: url(&quot;:/images/down.png&quot;);
}
#profilesComboBox::down-arrow:on { /* shift the arrow when popup is open */
top: 1px;
left: 1px;
}
#profilesComboBox QAbstractItemView {
border: 2px solid lightgray;
border-radius: 5px;
}
</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="profileLabel">
<property name="styleSheet">
<string notr="true">#profileLabel {
font-size: 18pt;
font-family: &quot;EB Garamond&quot;, &quot;EB Garamond 08&quot;;
}
</string>
</property>
<property name="text">
<string>Current Profile:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="playButton">
<property name="minimumSize">
<size>
<width>200</width>
<height>85</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>85</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">#playButton {
height: 50px;
margin-bottom: 30px;
background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(255, 255, 255, 200),
stop:0.1 rgba(255, 255, 255, 15),
stop:0.49 rgba(255, 255, 255, 75),
stop:0.5 rgba(0, 0, 0, 0),
stop:0.9 rgba(0, 0, 0, 55),
stop:1 rgba(0, 0, 0, 100));
font-size: 26pt;
font-family: &quot;EB Garamond&quot;, &quot;EB Garamond 08&quot;;
color: black;
border-right: 1px solid rgba(0, 0, 0, 155);
border-left: 1px solid rgba(0, 0, 0, 55);
border-top: 1px solid rgba(0, 0, 0, 55);
border-bottom: 1px solid rgba(0, 0, 0, 155);
border-radius: 5px;
}
#playButton:hover {
border-bottom: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-top: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-right: qlineargradient(spread:pad, x1:1, y1:0, x2:0, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-left: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(164, 192, 228, 255), stop:1 rgba(255, 255, 255, 0));
border-width: 2px;
border-style: solid;
}
#playButton:pressed {
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 rgba(0, 0, 0, 75),
stop:0.1 rgba(0, 0, 0, 15),
stop:0.2 rgba(255, 255, 255, 55)
stop:0.95 rgba(255, 255, 255, 55),
stop:1 rgba(255, 255, 255, 155));
border: 1px solid rgba(0, 0, 0, 55);
}</string>
</property>
<property name="text">
<string>Play</string>
</property>
</widget>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../resources.qrc"/>
</resources>
<connections/>
</ui>