Merge branch 'newlauncher'
@ -0,0 +1,79 @@
|
|||||||
|
set(LAUNCHER
|
||||||
|
datafilespage.cpp
|
||||||
|
graphicspage.cpp
|
||||||
|
lineedit.cpp
|
||||||
|
main.cpp
|
||||||
|
maindialog.cpp
|
||||||
|
naturalsort.cpp
|
||||||
|
playpage.cpp
|
||||||
|
pluginsmodel.cpp
|
||||||
|
pluginsview.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LAUNCHER_HEADER
|
||||||
|
combobox.hpp
|
||||||
|
datafilespage.hpp
|
||||||
|
graphicspage.hpp
|
||||||
|
lineedit.hpp
|
||||||
|
maindialog.hpp
|
||||||
|
naturalsort.hpp
|
||||||
|
playpage.hpp
|
||||||
|
pluginsmodel.hpp
|
||||||
|
pluginsview.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Headers that must be pre-processed
|
||||||
|
set(LAUNCHER_HEADER_MOC
|
||||||
|
combobox.hpp
|
||||||
|
datafilespage.hpp
|
||||||
|
graphicspage.hpp
|
||||||
|
lineedit.hpp
|
||||||
|
maindialog.hpp
|
||||||
|
playpage.hpp
|
||||||
|
pluginsmodel.hpp
|
||||||
|
pluginsview.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
|
||||||
|
|
||||||
|
find_package(Qt4 REQUIRED)
|
||||||
|
set(QT_USE_QTGUI 1)
|
||||||
|
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
include_directories(${PNG_INCLUDE_DIR})
|
||||||
|
|
||||||
|
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
|
||||||
|
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
||||||
|
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
|
||||||
|
# Main executable
|
||||||
|
add_executable(omwlauncher
|
||||||
|
${LAUNCHER}
|
||||||
|
${MISC} ${MISC_HEADER}
|
||||||
|
${FILES} ${FILES_HEADER}
|
||||||
|
${TO_UTF8}
|
||||||
|
${ESM}
|
||||||
|
${RCC_SRCS}
|
||||||
|
${MOC_SRCS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(omwlauncher
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
${OGRE_LIBRARIES}
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
${PNG_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${APP_BUNDLE_DIR}/../launcher.qss")
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${APP_BUNDLE_DIR}/../launcher.cfg")
|
||||||
|
else()
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
||||||
|
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
|
||||||
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
|
||||||
|
endif()
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef COMBOBOX_H
|
||||||
|
#define COMBOBOX_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
class ComboBox : public QComboBox
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
QString oldText;
|
||||||
|
public:
|
||||||
|
ComboBox(QWidget *parent=0) : QComboBox(parent), oldText()
|
||||||
|
{
|
||||||
|
connect(this,SIGNAL(editTextChanged(const QString&)), this,
|
||||||
|
SLOT(textChangedSlot(const QString&)));
|
||||||
|
connect(this,SIGNAL(currentIndexChanged(const QString&)), this,
|
||||||
|
SLOT(textChangedSlot(const QString&)));
|
||||||
|
}
|
||||||
|
private slots:
|
||||||
|
void textChangedSlot(const QString &newText)
|
||||||
|
{
|
||||||
|
emit textChanged(oldText, newText);
|
||||||
|
oldText = newText;
|
||||||
|
}
|
||||||
|
signals:
|
||||||
|
void textChanged(const QString &oldText, const QString &newText);
|
||||||
|
};
|
||||||
|
#endif
|
@ -0,0 +1,978 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/esm/esm_reader.hpp>
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
#include <components/files/collections.hpp>
|
||||||
|
#include <components/files/multidircollection.hpp>
|
||||||
|
|
||||||
|
#include "datafilespage.hpp"
|
||||||
|
#include "lineedit.hpp"
|
||||||
|
#include "naturalsort.hpp"
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
#include "pluginsview.hpp"
|
||||||
|
|
||||||
|
using namespace ESM;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//sort QModelIndexList ascending
|
||||||
|
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() >= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort QModelIndexList descending
|
||||||
|
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() <= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters
|
||||||
|
mPluginsModel = new PluginsModel(); // Contains selectable plugins
|
||||||
|
|
||||||
|
mPluginsProxyModel = new QSortFilterProxyModel();
|
||||||
|
mPluginsProxyModel->setDynamicSortFilter(true);
|
||||||
|
mPluginsProxyModel->setSourceModel(mPluginsModel);
|
||||||
|
|
||||||
|
QLabel *filterLabel = new QLabel(tr("&Filter:"), this);
|
||||||
|
LineEdit *filterLineEdit = new LineEdit(this);
|
||||||
|
filterLabel->setBuddy(filterLineEdit);
|
||||||
|
|
||||||
|
QToolBar *filterToolBar = new QToolBar(this);
|
||||||
|
filterToolBar->setMovable(false);
|
||||||
|
|
||||||
|
// Create a container widget and a layout to get the spacer to work
|
||||||
|
QWidget *filterWidget = new QWidget(this);
|
||||||
|
QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget);
|
||||||
|
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
filterLayout->addItem(hSpacer1);
|
||||||
|
filterLayout->addWidget(filterLabel);
|
||||||
|
filterLayout->addWidget(filterLineEdit);
|
||||||
|
|
||||||
|
filterToolBar->addWidget(filterWidget);
|
||||||
|
|
||||||
|
mMastersWidget = new QTableWidget(this); // Contains the available masters
|
||||||
|
mMastersWidget->setObjectName("MastersWidget");
|
||||||
|
mMastersWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
mMastersWidget->setSelectionMode(QAbstractItemView::MultiSelection);
|
||||||
|
mMastersWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
mMastersWidget->setAlternatingRowColors(true);
|
||||||
|
mMastersWidget->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mMastersWidget->horizontalHeader()->hide();
|
||||||
|
mMastersWidget->verticalHeader()->hide();
|
||||||
|
mMastersWidget->insertColumn(0);
|
||||||
|
|
||||||
|
mPluginsTable = new PluginsView(this);
|
||||||
|
mPluginsTable->setModel(mPluginsProxyModel);
|
||||||
|
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||||
|
|
||||||
|
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mPluginsTable->horizontalHeader()->hide();
|
||||||
|
|
||||||
|
// Set the row height to the size of the checkboxes
|
||||||
|
QCheckBox checkBox;
|
||||||
|
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||||
|
|
||||||
|
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||||
|
|
||||||
|
// Add both tables to a splitter
|
||||||
|
QSplitter *splitter = new QSplitter(this);
|
||||||
|
splitter->setOrientation(Qt::Horizontal);
|
||||||
|
|
||||||
|
splitter->addWidget(mMastersWidget);
|
||||||
|
splitter->addWidget(mPluginsTable);
|
||||||
|
|
||||||
|
// Adjust the default widget widths inside the splitter
|
||||||
|
QList<int> sizeList;
|
||||||
|
sizeList << 100 << 300;
|
||||||
|
splitter->setSizes(sizeList);
|
||||||
|
|
||||||
|
// Bottom part with profile options
|
||||||
|
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
|
||||||
|
|
||||||
|
mProfilesComboBox = new ComboBox(this);
|
||||||
|
mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||||
|
mProfilesComboBox->setInsertPolicy(QComboBox::InsertAtBottom);
|
||||||
|
|
||||||
|
mProfileToolBar = new QToolBar(this);
|
||||||
|
mProfileToolBar->setMovable(false);
|
||||||
|
mProfileToolBar->setIconSize(QSize(16, 16));
|
||||||
|
|
||||||
|
mProfileToolBar->addWidget(profileLabel);
|
||||||
|
mProfileToolBar->addWidget(mProfilesComboBox);
|
||||||
|
|
||||||
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(filterToolBar);
|
||||||
|
pageLayout->addWidget(splitter);
|
||||||
|
pageLayout->addWidget(mProfileToolBar);
|
||||||
|
|
||||||
|
connect(mMastersWidget->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
this, SLOT(masterSelectionChanged(const QItemSelection&, const QItemSelection&)));
|
||||||
|
|
||||||
|
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(const QString)));
|
||||||
|
|
||||||
|
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupConfig();
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict)
|
||||||
|
{
|
||||||
|
// Put the paths in a boost::filesystem vector to use with Files::Collections
|
||||||
|
std::vector<boost::filesystem::path> dataDirs;
|
||||||
|
|
||||||
|
foreach (const QString ¤tPath, paths) {
|
||||||
|
dataDirs.push_back(boost::filesystem::path(currentPath.toStdString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a file collection for the dataDirs
|
||||||
|
Files::Collections mFileCollections(dataDirs, strict);
|
||||||
|
|
||||||
|
// First we add all the master files
|
||||||
|
const Files::MultiDirCollection &esm = mFileCollections.getCollection(".esm");
|
||||||
|
unsigned int i = 0; // Row number
|
||||||
|
|
||||||
|
for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::string filename = boost::filesystem::path (iter->second.filename()).string();
|
||||||
|
QString currentMaster = QString::fromStdString(filename);
|
||||||
|
|
||||||
|
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (itemList.isEmpty()) // Master is not yet in the widget
|
||||||
|
{
|
||||||
|
mMastersWidget->insertRow(i);
|
||||||
|
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
|
||||||
|
mMastersWidget->setItem(i, 0, item);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now on to the plugins
|
||||||
|
const Files::MultiDirCollection &esp = mFileCollections.getCollection(".esp");
|
||||||
|
|
||||||
|
for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter)
|
||||||
|
{
|
||||||
|
ESMReader fileReader;
|
||||||
|
QStringList availableMasters; // Will contain all found masters
|
||||||
|
|
||||||
|
fileReader.open(iter->second.string());
|
||||||
|
|
||||||
|
// First we fill the availableMasters and the mMastersWidget
|
||||||
|
ESMReader::MasterList mlist = fileReader.getMasters();
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mlist.size(); ++i) {
|
||||||
|
const QString currentMaster = QString::fromStdString(mlist[i].name);
|
||||||
|
availableMasters.append(currentMaster);
|
||||||
|
|
||||||
|
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (itemList.isEmpty()) // Master is not yet in the widget
|
||||||
|
{
|
||||||
|
// TODO: Show warning, missing master
|
||||||
|
mMastersWidget->insertRow(i);
|
||||||
|
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
|
||||||
|
mMastersWidget->setItem(i, 0, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableMasters.sort(); // Sort the masters alphabetically
|
||||||
|
|
||||||
|
// Now we put the current plugin in the mDataFilesModel under its masters
|
||||||
|
QStandardItem *parent = new QStandardItem(availableMasters.join(","));
|
||||||
|
|
||||||
|
std::string filename = boost::filesystem::path (iter->second.filename()).string();
|
||||||
|
QStandardItem *child = new QStandardItem(QString::fromStdString(std::string(filename)));
|
||||||
|
|
||||||
|
const QList<QStandardItem*> masterList = mDataFilesModel->findItems(availableMasters.join(","));
|
||||||
|
|
||||||
|
if (masterList.isEmpty()) { // Masters node not yet in the mDataFilesModel
|
||||||
|
parent->appendRow(child);
|
||||||
|
mDataFilesModel->appendRow(parent);
|
||||||
|
} else {
|
||||||
|
// Masters node exists, append current plugin
|
||||||
|
foreach (QStandardItem *currentItem, masterList) {
|
||||||
|
currentItem->appendRow(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setupConfig()
|
||||||
|
{
|
||||||
|
QString config = "./launcher.cfg";
|
||||||
|
QFile file(config);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "launcher.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
file.setFileName(config); // Just for displaying information
|
||||||
|
|
||||||
|
// Open our config file
|
||||||
|
mLauncherConfig = new QSettings(config, QSettings::IniFormat);
|
||||||
|
mLauncherConfig->sync();
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
QStringList profiles = mLauncherConfig->childGroups();
|
||||||
|
|
||||||
|
if (profiles.isEmpty()) {
|
||||||
|
// Add a default profile
|
||||||
|
profiles.append("Default");
|
||||||
|
}
|
||||||
|
|
||||||
|
mProfilesComboBox->addItems(profiles);
|
||||||
|
|
||||||
|
QString currentProfile = mLauncherConfig->value("CurrentProfile").toString();
|
||||||
|
|
||||||
|
if (currentProfile.isEmpty()) {
|
||||||
|
// No current profile selected
|
||||||
|
currentProfile = "Default";
|
||||||
|
}
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile));
|
||||||
|
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
// Now we connect the combobox to do something if the profile changes
|
||||||
|
// This prevents strange behaviour while reading and appending the profiles
|
||||||
|
connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::createActions()
|
||||||
|
{
|
||||||
|
// Refresh the plugins
|
||||||
|
QAction *refreshAction = new QAction(tr("Refresh"), this);
|
||||||
|
refreshAction->setShortcut(QKeySequence(tr("F5")));
|
||||||
|
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
|
||||||
|
|
||||||
|
// Profile actions
|
||||||
|
mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this);
|
||||||
|
mNewProfileAction->setToolTip(tr("New Profile"));
|
||||||
|
mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N")));
|
||||||
|
connect(mNewProfileAction, SIGNAL(triggered()), this, SLOT(newProfile()));
|
||||||
|
|
||||||
|
|
||||||
|
mCopyProfileAction = new QAction(QIcon::fromTheme("edit-copy"), tr("&Copy Profile"), this);
|
||||||
|
mCopyProfileAction->setToolTip(tr("Copy Profile"));
|
||||||
|
mCopyProfileAction->setShortcut(QKeySequence(tr("Ctrl+C")));
|
||||||
|
connect(mCopyProfileAction, SIGNAL(triggered()), this, SLOT(copyProfile()));
|
||||||
|
|
||||||
|
mDeleteProfileAction = new QAction(QIcon::fromTheme("edit-delete"), tr("Delete Profile"), this);
|
||||||
|
mDeleteProfileAction->setToolTip(tr("Delete Profile"));
|
||||||
|
mDeleteProfileAction->setShortcut(QKeySequence(tr("Delete")));
|
||||||
|
connect(mDeleteProfileAction, SIGNAL(triggered()), this, SLOT(deleteProfile()));
|
||||||
|
|
||||||
|
// Add the newly created actions to the toolbar
|
||||||
|
mProfileToolBar->addSeparator();
|
||||||
|
mProfileToolBar->addAction(mNewProfileAction);
|
||||||
|
mProfileToolBar->addAction(mCopyProfileAction);
|
||||||
|
mProfileToolBar->addAction(mDeleteProfileAction);
|
||||||
|
|
||||||
|
// Context menu actions
|
||||||
|
mMoveUpAction = new QAction(QIcon::fromTheme("go-up"), tr("Move &Up"), this);
|
||||||
|
mMoveUpAction->setShortcut(QKeySequence(tr("Ctrl+U")));
|
||||||
|
connect(mMoveUpAction, SIGNAL(triggered()), this, SLOT(moveUp()));
|
||||||
|
|
||||||
|
mMoveDownAction = new QAction(QIcon::fromTheme("go-down"), tr("&Move Down"), this);
|
||||||
|
mMoveDownAction->setShortcut(QKeySequence(tr("Ctrl+M")));
|
||||||
|
connect(mMoveDownAction, SIGNAL(triggered()), this, SLOT(moveDown()));
|
||||||
|
|
||||||
|
mMoveTopAction = new QAction(QIcon::fromTheme("go-top"), tr("Move to Top"), this);
|
||||||
|
mMoveTopAction->setShortcut(QKeySequence(tr("Ctrl+Shift+U")));
|
||||||
|
connect(mMoveTopAction, SIGNAL(triggered()), this, SLOT(moveTop()));
|
||||||
|
|
||||||
|
mMoveBottomAction = new QAction(QIcon::fromTheme("go-bottom"), tr("Move to Bottom"), this);
|
||||||
|
mMoveBottomAction->setShortcut(QKeySequence(tr("Ctrl+Shift+M")));
|
||||||
|
connect(mMoveBottomAction, SIGNAL(triggered()), this, SLOT(moveBottom()));
|
||||||
|
|
||||||
|
mCheckAction = new QAction(tr("Check selected"), this);
|
||||||
|
connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check()));
|
||||||
|
|
||||||
|
mUncheckAction = new QAction(tr("Uncheck selected"), this);
|
||||||
|
connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck()));
|
||||||
|
|
||||||
|
// Makes shortcuts work even if the context menu is hidden
|
||||||
|
this->addAction(refreshAction);
|
||||||
|
this->addAction(mMoveUpAction);
|
||||||
|
this->addAction(mMoveDownAction);
|
||||||
|
this->addAction(mMoveTopAction);
|
||||||
|
this->addAction(mMoveBottomAction);
|
||||||
|
|
||||||
|
// Context menu for the plugins table
|
||||||
|
mContextMenu = new QMenu(this);
|
||||||
|
|
||||||
|
mContextMenu->addAction(mMoveUpAction);
|
||||||
|
mContextMenu->addAction(mMoveDownAction);
|
||||||
|
mContextMenu->addSeparator();
|
||||||
|
mContextMenu->addAction(mMoveTopAction);
|
||||||
|
mContextMenu->addAction(mMoveBottomAction);
|
||||||
|
mContextMenu->addSeparator();
|
||||||
|
mContextMenu->addAction(mCheckAction);
|
||||||
|
mContextMenu->addAction(mUncheckAction);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::newProfile()
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString text = QInputDialog::getText(this, tr("New Profile"),
|
||||||
|
tr("Profile Name:"), QLineEdit::Normal,
|
||||||
|
tr("New Profile"), &ok);
|
||||||
|
if (ok && !text.isEmpty()) {
|
||||||
|
if (mProfilesComboBox->findText(text) != -1) {
|
||||||
|
QMessageBox::warning(this, tr("Profile already exists"),
|
||||||
|
tr("the profile <b>%0</b> already exists.").arg(text),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
} else {
|
||||||
|
// Add the new profile to the combobox
|
||||||
|
mProfilesComboBox->addItem(text);
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::copyProfile()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
QString text = QInputDialog::getText(this, tr("Copy Profile"),
|
||||||
|
tr("Profile Name:"), QLineEdit::Normal,
|
||||||
|
tr("%0 Copy").arg(profile), &ok);
|
||||||
|
if (ok && !text.isEmpty()) {
|
||||||
|
if (mProfilesComboBox->findText(text) != -1) {
|
||||||
|
QMessageBox::warning(this, tr("Profile already exists"),
|
||||||
|
tr("the profile <b>%0</b> already exists.").arg(text),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
} else {
|
||||||
|
// Add the new profile to the combobox
|
||||||
|
mProfilesComboBox->addItem(text);
|
||||||
|
|
||||||
|
// First write the current profile as the new profile
|
||||||
|
writeConfig(text);
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::deleteProfile()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
|
||||||
|
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox deleteMessageBox(this);
|
||||||
|
deleteMessageBox.setWindowTitle(tr("Delete Profile"));
|
||||||
|
deleteMessageBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(profile));
|
||||||
|
deleteMessageBox.setIcon(QMessageBox::Warning);
|
||||||
|
QAbstractButton *deleteButton =
|
||||||
|
deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole);
|
||||||
|
|
||||||
|
deleteMessageBox.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
|
deleteMessageBox.exec();
|
||||||
|
|
||||||
|
if (deleteMessageBox.clickedButton() == deleteButton) {
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
|
||||||
|
// Open the profile-name subgroup
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
mLauncherConfig->remove(""); // Clear the subgroup
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
// Remove the profile from the combobox
|
||||||
|
mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveUp()
|
||||||
|
{
|
||||||
|
// Shift the selected plugins up one row
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if the first selected plugin is the top one
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && currentRow > 0) {
|
||||||
|
mPluginsModel->insertRow((currentRow - 1), mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
const QModelIndex targetIndex = mPluginsModel->index((currentRow - 1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
scrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveDown()
|
||||||
|
{
|
||||||
|
// Shift the selected plugins down one row
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection descending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowGreaterThan);
|
||||||
|
|
||||||
|
const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if last selected plugin is bottom one
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) {
|
||||||
|
mPluginsModel->insertRow((currentRow + 1), mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
const QModelIndex targetIndex = mPluginsModel->index((currentRow + 1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
scrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveTop()
|
||||||
|
{
|
||||||
|
// Move the selection to the top of the table
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if the first selected plugin is the top one
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < selectedIndexes.count(); ++i) {
|
||||||
|
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i));
|
||||||
|
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && currentRow > 0) {
|
||||||
|
|
||||||
|
mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentRow));
|
||||||
|
mPluginsTable->selectionModel()->select(mPluginsModel->index(i, 0, QModelIndex()), QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
mPluginsTable->scrollToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveBottom()
|
||||||
|
{
|
||||||
|
// Move the selection to the bottom of the table
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection descending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last());
|
||||||
|
|
||||||
|
// Check if last selected plugin is bottom one
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < selectedIndexes.count(); ++i) {
|
||||||
|
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i));
|
||||||
|
|
||||||
|
// Subtract iterations because takeRow shifts the rows below the taken row up
|
||||||
|
int currentRow = sourceModelIndex.row() - i;
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) {
|
||||||
|
mPluginsModel->appendRow(mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
// Rowcount starts with 1, row numbers start with 0
|
||||||
|
const QModelIndex lastRow = mPluginsModel->index((mPluginsModel->rowCount() -1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(lastRow, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
mPluginsTable->scrollToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::check()
|
||||||
|
{
|
||||||
|
// Check the current selection
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid()) {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::uncheck()
|
||||||
|
{
|
||||||
|
// Uncheck the current selection
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid()) {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::refresh()
|
||||||
|
{
|
||||||
|
// Refresh the plugins table
|
||||||
|
|
||||||
|
writeConfig();
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::scrollToSelection()
|
||||||
|
{
|
||||||
|
// Scroll to the selected plugins
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the selected indexes visible by determining the middle index
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
// The selected rows including non-selected inside selection
|
||||||
|
unsigned int selectedRows = selectedIndexes.last().row() - selectedIndexes.first().row();
|
||||||
|
|
||||||
|
// Determine the row which is roughly in the middle of the selection
|
||||||
|
unsigned int middleRow = selectedIndexes.first().row() + (int)(selectedRows / 2) + 1;
|
||||||
|
|
||||||
|
|
||||||
|
const QModelIndex middleIndex = mPluginsProxyModel->mapFromSource(mPluginsModel->index(middleRow, 0, QModelIndex()));
|
||||||
|
|
||||||
|
// Make sure the whole selection is visible
|
||||||
|
mPluginsTable->scrollTo(selectedIndexes.first());
|
||||||
|
mPluginsTable->scrollTo(selectedIndexes.last());
|
||||||
|
mPluginsTable->scrollTo(middleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::showContextMenu(const QPoint &point)
|
||||||
|
{
|
||||||
|
|
||||||
|
QPoint globalPos = mPluginsTable->mapToGlobal(point);
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
// Show the check/uncheck actions depending on the state of the selected items
|
||||||
|
mUncheckAction->setEnabled(false);
|
||||||
|
mCheckAction->setEnabled(false);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
|
||||||
|
const QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (!sourceIndex.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStandardItem *currentItem = mPluginsModel->itemFromIndex(sourceIndex);
|
||||||
|
|
||||||
|
if (currentItem->checkState() == Qt::Checked) {
|
||||||
|
mUncheckAction->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
mCheckAction->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure these are enabled because they might still be disabled
|
||||||
|
mMoveUpAction->setEnabled(true);
|
||||||
|
mMoveTopAction->setEnabled(true);
|
||||||
|
mMoveDownAction->setEnabled(true);
|
||||||
|
mMoveBottomAction->setEnabled(true);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last());
|
||||||
|
|
||||||
|
// Check if selected first item is top row in model
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
mMoveUpAction->setEnabled(false);
|
||||||
|
mMoveTopAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if last row is bottom row in model
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
mMoveDownAction->setEnabled(false);
|
||||||
|
mMoveBottomAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show menu
|
||||||
|
mContextMenu->exec(globalPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||||
|
{
|
||||||
|
if (mMastersWidget->selectionModel()->hasSelection()) {
|
||||||
|
|
||||||
|
QStringList masters;
|
||||||
|
QString masterstr;
|
||||||
|
|
||||||
|
// Create a QStringList containing all the masters
|
||||||
|
const QStringList masterList = selectedMasters();
|
||||||
|
|
||||||
|
foreach (const QString ¤tMaster, masterList) {
|
||||||
|
masters.append(currentMaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
masters.sort();
|
||||||
|
masterstr = masters.join(","); // Make a comma-separated QString
|
||||||
|
|
||||||
|
// Iterate over all masters in the datafilesmodel to see if they are selected
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(); ++r) {
|
||||||
|
QModelIndex currentIndex = mDataFilesModel->index(r, 0);
|
||||||
|
QString master = currentIndex.data().toString();
|
||||||
|
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
// See if the current master is in the string with selected masters
|
||||||
|
if (masterstr.contains(master))
|
||||||
|
{
|
||||||
|
// Append the plugins from the current master to pluginsmodel
|
||||||
|
addPlugins(currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See what plugins to remove
|
||||||
|
QModelIndexList deselectedIndexes = deselected.indexes();
|
||||||
|
|
||||||
|
if (!deselectedIndexes.isEmpty()) {
|
||||||
|
foreach (const QModelIndex ¤tIndex, deselectedIndexes) {
|
||||||
|
|
||||||
|
QString master = currentIndex.data().toString();
|
||||||
|
master.prepend("*");
|
||||||
|
master.append("*");
|
||||||
|
const QList<QStandardItem *> itemList = mDataFilesModel->findItems(master, Qt::MatchWildcard);
|
||||||
|
|
||||||
|
foreach (const QStandardItem *currentItem, itemList) {
|
||||||
|
QModelIndex index = currentItem->index();
|
||||||
|
removePlugins(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::addPlugins(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
// Find the plugins in the datafilesmodel and append them to the pluginsmodel
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(index); ++r ) {
|
||||||
|
QModelIndex childIndex = index.child(r, 0);
|
||||||
|
|
||||||
|
if (childIndex.isValid()) {
|
||||||
|
// Now we see if the pluginsmodel already contains this plugin
|
||||||
|
const QString childIndexData = QVariant(mDataFilesModel->data(childIndex)).toString();
|
||||||
|
|
||||||
|
const QList<QStandardItem *> itemList = mPluginsModel->findItems(childIndexData);
|
||||||
|
|
||||||
|
if (itemList.isEmpty())
|
||||||
|
{
|
||||||
|
// Plugin not yet in the pluginsmodel, add it
|
||||||
|
QStandardItem *item = new QStandardItem(childIndexData);
|
||||||
|
item->setFlags(item->flags() & ~(Qt::ItemIsDropEnabled));
|
||||||
|
item->setCheckable(true);
|
||||||
|
|
||||||
|
mPluginsModel->appendRow(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::removePlugins(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(index); ++r) {
|
||||||
|
QModelIndex childIndex = index.child(r, 0);
|
||||||
|
|
||||||
|
const QList<QStandardItem *> itemList = mPluginsModel->findItems(QVariant(childIndex.data()).toString());
|
||||||
|
|
||||||
|
if (!itemList.isEmpty()) {
|
||||||
|
foreach (const QStandardItem *currentItem, itemList) {
|
||||||
|
mPluginsModel->removeRow(currentItem->row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setCheckState(QModelIndex index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(index);
|
||||||
|
|
||||||
|
if (mPluginsModel->data(sourceModelIndex, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
// Selected row is checked, uncheck it
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
} else {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList DataFilesPage::selectedMasters()
|
||||||
|
{
|
||||||
|
QStringList masters;
|
||||||
|
const QList<QTableWidgetItem *> selectedMasters = mMastersWidget->selectedItems();
|
||||||
|
|
||||||
|
foreach (const QTableWidgetItem *item, selectedMasters) {
|
||||||
|
masters.append(item->data(Qt::DisplayRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return masters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList DataFilesPage::checkedPlugins()
|
||||||
|
{
|
||||||
|
QStringList checkedItems;
|
||||||
|
|
||||||
|
for (int r=0; r<mPluginsModel->rowCount(); ++r ) {
|
||||||
|
QModelIndex index = mPluginsModel->index(r, 0);
|
||||||
|
|
||||||
|
if (index.isValid()) {
|
||||||
|
// See if the current item is checked
|
||||||
|
if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
checkedItems.append(index.data().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::uncheckPlugins()
|
||||||
|
{
|
||||||
|
for (int r=0; r<mPluginsModel->rowCount(); ++r ) {
|
||||||
|
QModelIndex index = mPluginsModel->index(r, 0);
|
||||||
|
|
||||||
|
if (index.isValid()) {
|
||||||
|
// See if the current item is checked
|
||||||
|
if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
mPluginsModel->setData(index, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::filterChanged(const QString filter)
|
||||||
|
{
|
||||||
|
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||||
|
mPluginsProxyModel->setFilterRegExp(regExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
||||||
|
{
|
||||||
|
if (!previous.isEmpty()) {
|
||||||
|
writeConfig(previous);
|
||||||
|
mLauncherConfig->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
uncheckPlugins();
|
||||||
|
// Deselect the masters
|
||||||
|
mMastersWidget->selectionModel()->clearSelection();
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::readConfig()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
|
||||||
|
QStringList childKeys = mLauncherConfig->childKeys();
|
||||||
|
QStringList plugins;
|
||||||
|
|
||||||
|
// Sort the child keys numerical instead of alphabetically
|
||||||
|
// i.e. Plugin1, Plugin2 instead of Plugin1, Plugin10
|
||||||
|
qSort(childKeys.begin(), childKeys.end(), naturalSortLessThanCI);
|
||||||
|
|
||||||
|
foreach (const QString &key, childKeys) {
|
||||||
|
const QString keyValue = mLauncherConfig->value(key).toString();
|
||||||
|
|
||||||
|
if (key.startsWith("Plugin")) {
|
||||||
|
plugins.append(keyValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.startsWith("Master")) {
|
||||||
|
const QList<QTableWidgetItem*> masterList = mMastersWidget->findItems(keyValue, Qt::MatchFixedString);
|
||||||
|
|
||||||
|
if (!masterList.isEmpty()) {
|
||||||
|
foreach (QTableWidgetItem *currentMaster, masterList) {
|
||||||
|
mMastersWidget->selectionModel()->select(mMastersWidget->model()->index(currentMaster->row(), 0), QItemSelectionModel::Select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the plugins to set their checkstate and position
|
||||||
|
for (int i = 0; i < plugins.size(); ++i) {
|
||||||
|
const QString plugin = plugins.at(i);
|
||||||
|
|
||||||
|
const QList<QStandardItem *> pluginList = mPluginsModel->findItems(plugin);
|
||||||
|
|
||||||
|
if (!pluginList.isEmpty())
|
||||||
|
{
|
||||||
|
foreach (const QStandardItem *currentPlugin, pluginList) {
|
||||||
|
mPluginsModel->setData(currentPlugin->index(), Qt::Checked, Qt::CheckStateRole);
|
||||||
|
|
||||||
|
// Move the plugin to the position specified in the config file
|
||||||
|
mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentPlugin->row()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::writeConfig(QString profile)
|
||||||
|
{
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
profile = mProfilesComboBox->currentText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
mLauncherConfig->setValue("CurrentProfile", profile);
|
||||||
|
|
||||||
|
// Open the profile-name subgroup
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
mLauncherConfig->remove(""); // Clear the subgroup
|
||||||
|
|
||||||
|
// First write the masters to the config
|
||||||
|
const QStringList masterList = selectedMasters();
|
||||||
|
|
||||||
|
// We don't use foreach because we need i
|
||||||
|
for (int i = 0; i < masterList.size(); ++i) {
|
||||||
|
const QString master = masterList.at(i);
|
||||||
|
mLauncherConfig->setValue(QString("Master%0").arg(i), master);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now write all checked plugins
|
||||||
|
const QStringList plugins = checkedPlugins();
|
||||||
|
|
||||||
|
for (int i = 0; i < plugins.size(); ++i)
|
||||||
|
{
|
||||||
|
mLauncherConfig->setValue(QString("Plugin%1").arg(i), plugins.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
#ifndef DATAFILESPAGE_H
|
||||||
|
#define DATAFILESPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include "combobox.hpp"
|
||||||
|
|
||||||
|
class QTableWidget;
|
||||||
|
class QStandardItemModel;
|
||||||
|
class QItemSelection;
|
||||||
|
class QItemSelectionModel;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
class QStringListModel;
|
||||||
|
class QSettings;
|
||||||
|
class QAction;
|
||||||
|
class QToolBar;
|
||||||
|
class QMenu;
|
||||||
|
class PluginsModel;
|
||||||
|
class PluginsView;
|
||||||
|
class ComboBox;
|
||||||
|
|
||||||
|
class DataFilesPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DataFilesPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
ComboBox *mProfilesComboBox;
|
||||||
|
QSettings *mLauncherConfig;
|
||||||
|
|
||||||
|
const QStringList checkedPlugins();
|
||||||
|
const QStringList selectedMasters();
|
||||||
|
void setupConfig();
|
||||||
|
void readConfig();
|
||||||
|
void writeConfig(QString profile = QString());
|
||||||
|
|
||||||
|
void setupDataFiles(const QStringList &paths, bool strict);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||||
|
void setCheckState(QModelIndex index);
|
||||||
|
|
||||||
|
void filterChanged(const QString filter);
|
||||||
|
void showContextMenu(const QPoint &point);
|
||||||
|
void profileChanged(const QString &previous, const QString ¤t);
|
||||||
|
|
||||||
|
// Action slots
|
||||||
|
void newProfile();
|
||||||
|
void copyProfile();
|
||||||
|
void deleteProfile();
|
||||||
|
void moveUp();
|
||||||
|
void moveDown();
|
||||||
|
void moveTop();
|
||||||
|
void moveBottom();
|
||||||
|
void check();
|
||||||
|
void uncheck();
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTableWidget *mMastersWidget;
|
||||||
|
PluginsView *mPluginsTable;
|
||||||
|
|
||||||
|
QStandardItemModel *mDataFilesModel;
|
||||||
|
PluginsModel *mPluginsModel;
|
||||||
|
|
||||||
|
QSortFilterProxyModel *mPluginsProxyModel;
|
||||||
|
QItemSelectionModel *mPluginsSelectModel;
|
||||||
|
|
||||||
|
QToolBar *mProfileToolBar;
|
||||||
|
QMenu *mContextMenu;
|
||||||
|
|
||||||
|
QAction *mNewProfileAction;
|
||||||
|
QAction *mCopyProfileAction;
|
||||||
|
QAction *mDeleteProfileAction;
|
||||||
|
|
||||||
|
QAction *mMoveUpAction;
|
||||||
|
QAction *mMoveDownAction;
|
||||||
|
QAction *mMoveTopAction;
|
||||||
|
QAction *mMoveBottomAction;
|
||||||
|
QAction *mCheckAction;
|
||||||
|
QAction *mUncheckAction;
|
||||||
|
|
||||||
|
void addPlugins(const QModelIndex &index);
|
||||||
|
void removePlugins(const QModelIndex &index);
|
||||||
|
void uncheckPlugins();
|
||||||
|
void createActions();
|
||||||
|
|
||||||
|
void scrollToSelection();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,473 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
|
||||||
|
#include "graphicspage.hpp"
|
||||||
|
|
||||||
|
GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
|
||||||
|
|
||||||
|
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
|
||||||
|
mRendererComboBox = new QComboBox(rendererGroup);
|
||||||
|
|
||||||
|
// Layout for the combobox and label
|
||||||
|
QGridLayout *renderSystemLayout = new QGridLayout();
|
||||||
|
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
|
||||||
|
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
|
||||||
|
|
||||||
|
mRendererStackedWidget = new QStackedWidget(rendererGroup);
|
||||||
|
|
||||||
|
QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup);
|
||||||
|
|
||||||
|
rendererGroupLayout->addLayout(renderSystemLayout);
|
||||||
|
rendererGroupLayout->addWidget(mRendererStackedWidget);
|
||||||
|
|
||||||
|
// Display
|
||||||
|
QGroupBox *displayGroup = new QGroupBox(tr("Display"), this);
|
||||||
|
|
||||||
|
mDisplayStackedWidget = new QStackedWidget(displayGroup);
|
||||||
|
|
||||||
|
QVBoxLayout *displayGroupLayout = new QVBoxLayout(displayGroup);
|
||||||
|
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
displayGroupLayout->addWidget(mDisplayStackedWidget);
|
||||||
|
displayGroupLayout->addItem(vSpacer3);
|
||||||
|
|
||||||
|
// Layout for the whole page
|
||||||
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(rendererGroup);
|
||||||
|
pageLayout->addWidget(displayGroup);
|
||||||
|
|
||||||
|
connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
|
||||||
|
|
||||||
|
createPages();
|
||||||
|
setupConfig();
|
||||||
|
setupOgre();
|
||||||
|
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::createPages()
|
||||||
|
{
|
||||||
|
// OpenGL rendering settings
|
||||||
|
QWidget *mOGLRendererPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *OGLRTTLabel = new QLabel(tr("Preferred RTT Mode:"), mOGLRendererPage);
|
||||||
|
mOGLRTTComboBox = new QComboBox(mOGLRendererPage);
|
||||||
|
|
||||||
|
QLabel *OGLAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mOGLRendererPage);
|
||||||
|
mOGLAntiAliasingComboBox = new QComboBox(mOGLRendererPage);
|
||||||
|
|
||||||
|
QGridLayout *OGLRendererLayout = new QGridLayout(mOGLRendererPage);
|
||||||
|
QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
OGLRendererLayout->addWidget(OGLRTTLabel, 0, 0, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(mOGLRTTComboBox, 0, 1, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(OGLAntiAliasingLabel, 1, 0, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(mOGLAntiAliasingComboBox, 1, 1, 1, 1);
|
||||||
|
OGLRendererLayout->addItem(vSpacer1, 2, 1, 1, 1);
|
||||||
|
|
||||||
|
// OpenGL display settings
|
||||||
|
QWidget *mOGLDisplayPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *OGLResolutionLabel = new QLabel(tr("Resolution:"), mOGLDisplayPage);
|
||||||
|
mOGLResolutionComboBox = new QComboBox(mOGLDisplayPage);
|
||||||
|
|
||||||
|
QLabel *OGLFrequencyLabel = new QLabel(tr("Display Frequency:"), mOGLDisplayPage);
|
||||||
|
mOGLFrequencyComboBox = new QComboBox(mOGLDisplayPage);
|
||||||
|
|
||||||
|
mOGLVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mOGLDisplayPage);
|
||||||
|
mOGLFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mOGLDisplayPage);
|
||||||
|
|
||||||
|
QGridLayout *OGLDisplayLayout = new QGridLayout(mOGLDisplayPage);
|
||||||
|
QSpacerItem *vSpacer2 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
OGLDisplayLayout->addWidget(OGLResolutionLabel, 0, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLResolutionComboBox, 0, 1, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(OGLFrequencyLabel, 1, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLFrequencyComboBox, 1, 1, 1, 1);
|
||||||
|
|
||||||
|
OGLDisplayLayout->addItem(vSpacer2, 2, 1, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLVSyncCheckBox, 3, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLFullScreenCheckBox, 6, 0, 1, 1);
|
||||||
|
|
||||||
|
// Direct3D rendering settings
|
||||||
|
QWidget *mD3DRendererPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *D3DRenderDeviceLabel = new QLabel(tr("Rendering Device:"), mD3DRendererPage);
|
||||||
|
mD3DRenderDeviceComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
QLabel *D3DAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mD3DRendererPage);
|
||||||
|
mD3DAntiAliasingComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
QLabel *D3DFloatingPointLabel = new QLabel(tr("Floating-point Mode:"), mD3DRendererPage);
|
||||||
|
mD3DFloatingPointComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
mD3DNvPerfCheckBox = new QCheckBox(tr("Allow NVPerfHUD"), mD3DRendererPage);
|
||||||
|
|
||||||
|
QGridLayout *D3DRendererLayout = new QGridLayout(mD3DRendererPage);
|
||||||
|
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
QSpacerItem *vSpacer4 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
D3DRendererLayout->addWidget(D3DRenderDeviceLabel, 0, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DRenderDeviceComboBox, 0, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(D3DAntiAliasingLabel, 1, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DAntiAliasingComboBox, 1, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(D3DFloatingPointLabel, 2, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DFloatingPointComboBox, 2, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addItem(vSpacer3, 3, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DNvPerfCheckBox, 4, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addItem(vSpacer4, 5, 1, 1, 1);
|
||||||
|
|
||||||
|
// Direct3D display settings
|
||||||
|
QWidget *mD3DDisplayPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *D3DResolutionLabel = new QLabel(tr("Resolution:"), mD3DDisplayPage);
|
||||||
|
mD3DResolutionComboBox = new QComboBox(mD3DDisplayPage);
|
||||||
|
|
||||||
|
mD3DVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mD3DDisplayPage);
|
||||||
|
mD3DFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mD3DDisplayPage);
|
||||||
|
|
||||||
|
QGridLayout *mD3DDisplayLayout = new QGridLayout(mD3DDisplayPage);
|
||||||
|
QSpacerItem *vSpacer5 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
mD3DDisplayLayout->addWidget(D3DResolutionLabel, 0, 0, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DResolutionComboBox, 0, 1, 1, 1);
|
||||||
|
mD3DDisplayLayout->addItem(vSpacer5, 1, 1, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DVSyncCheckBox, 2, 0, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DFullScreenCheckBox, 5, 0, 1, 1);
|
||||||
|
|
||||||
|
// Add the created pages
|
||||||
|
mRendererStackedWidget->addWidget(mOGLRendererPage);
|
||||||
|
mRendererStackedWidget->addWidget(mD3DRendererPage);
|
||||||
|
|
||||||
|
mDisplayStackedWidget->addWidget(mOGLDisplayPage);
|
||||||
|
mDisplayStackedWidget->addWidget(mD3DDisplayPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::setupConfig()
|
||||||
|
{
|
||||||
|
QString ogreCfg = "./ogre.cfg";
|
||||||
|
|
||||||
|
QFile file(ogreCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
ogreCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "ogre.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::setupOgre()
|
||||||
|
{
|
||||||
|
QString pluginCfg = "./plugins.cfg";
|
||||||
|
QFile file(pluginCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "plugins.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen the file from user directory
|
||||||
|
file.setFileName(pluginCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
// There's no plugins.cfg in the user directory, use global directory
|
||||||
|
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigGlobal,
|
||||||
|
"openmw", "plugins.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a log manager so we can surpress debug text to stdout/stderr
|
||||||
|
Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
|
||||||
|
logMgr->createLog("launcherOgre.log", true, false, false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mOgre = new Ogre::Root(pluginCfg.toStdString());
|
||||||
|
}
|
||||||
|
catch(Ogre::Exception &ex)
|
||||||
|
{
|
||||||
|
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str());
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error creating Ogre::Root");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Failed to create the Ogre::Root object</b><br><br> \
|
||||||
|
Make sure the plugins.cfg is present and valid.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>"));
|
||||||
|
msgBox.setDetailedText(ogreError);
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError));
|
||||||
|
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the available renderers and put them in the combobox
|
||||||
|
const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
|
||||||
|
|
||||||
|
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
|
||||||
|
mSelectedRenderSystem = *r;
|
||||||
|
mRendererComboBox->addItem((*r)->getName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = mRendererComboBox->findText(mOgreConfig->value("Render System").toString());
|
||||||
|
|
||||||
|
if ( index != -1) {
|
||||||
|
mRendererComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create separate rendersystems
|
||||||
|
QString openGLName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("OpenGL"), Qt::MatchStartsWith));
|
||||||
|
QString direct3DName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("Direct3D"), Qt::MatchStartsWith));
|
||||||
|
|
||||||
|
mOpenGLRenderSystem = mOgre->getRenderSystemByName(openGLName.toStdString());
|
||||||
|
mDirect3DRenderSystem = mOgre->getRenderSystemByName(direct3DName.toStdString());
|
||||||
|
|
||||||
|
if (!mOpenGLRenderSystem && !mDirect3DRenderSystem) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error creating renderer");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
|
||||||
|
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now fill the GUI elements
|
||||||
|
// OpenGL
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem));
|
||||||
|
mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem));
|
||||||
|
|
||||||
|
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem);
|
||||||
|
// Remove extraneous spaces
|
||||||
|
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
|
||||||
|
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
|
||||||
|
|
||||||
|
mOGLResolutionComboBox->addItems(videoModes);
|
||||||
|
mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct3D
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem));
|
||||||
|
mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("Anti aliasing"), mDirect3DRenderSystem));
|
||||||
|
mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem));
|
||||||
|
|
||||||
|
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem);
|
||||||
|
// Remove extraneous spaces
|
||||||
|
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
|
||||||
|
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
|
||||||
|
mD3DResolutionComboBox->addItems(videoModes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::readConfig()
|
||||||
|
{
|
||||||
|
// Read the config file settings
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
|
||||||
|
int index = mOGLRTTComboBox->findText(getConfigValue("RTT Preferred Mode", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLRTTComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLAntiAliasingComboBox->findText(getConfigValue("FSAA", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1){
|
||||||
|
mOGLAntiAliasingComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLResolutionComboBox->findText(getConfigValue("Video Mode", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLResolutionComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLFrequencyComboBox->findText(getConfigValue("Display Frequency", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLFrequencyComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we do the same for the checkboxes
|
||||||
|
if (getConfigValue("VSync", mOpenGLRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mOGLVSyncCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Full Screen", mOpenGLRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mOGLFullScreenCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
|
||||||
|
int index = mD3DRenderDeviceComboBox->findText(getConfigValue("Rendering Device", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DRenderDeviceComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DAntiAliasingComboBox->findText(getConfigValue("Anti aliasing", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DAntiAliasingComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DFloatingPointComboBox->findText(getConfigValue("Floating-point mode", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DFloatingPointComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DResolutionComboBox->findText(getConfigValue("Video Mode", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DResolutionComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Allow NVPerfHUD", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DNvPerfCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("VSync", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DVSyncCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Full Screen", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DFullScreenCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::writeConfig()
|
||||||
|
{
|
||||||
|
// Write the config file settings
|
||||||
|
|
||||||
|
// Custom write method: We cannot use QSettings because it does not accept spaces
|
||||||
|
QFile file(mOgreConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
// File could not be opened,
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening Ogre configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream out(&file);
|
||||||
|
|
||||||
|
out << "Render System=" << mSelectedRenderSystem->getName().c_str() << endl << endl;
|
||||||
|
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
QString openGLName = mOpenGLRenderSystem->getName().c_str();
|
||||||
|
openGLName.prepend("[");
|
||||||
|
openGLName.append("]");
|
||||||
|
out << openGLName << endl;
|
||||||
|
|
||||||
|
out << "RTT Preferred Mode=" << mOGLRTTComboBox->currentText() << endl;
|
||||||
|
out << "FSAA=" << mOGLAntiAliasingComboBox->currentText() << endl;
|
||||||
|
out << "Video Mode=" << mOGLResolutionComboBox->currentText() << endl;
|
||||||
|
out << "Display Frequency=" << mOGLFrequencyComboBox->currentText() << endl;
|
||||||
|
|
||||||
|
if (mOGLVSyncCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "VSync=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "VSync=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mOGLFullScreenCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Full Screen=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Full Screen=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
QString direct3DName = mDirect3DRenderSystem->getName().c_str();
|
||||||
|
direct3DName.prepend("[");
|
||||||
|
direct3DName.append("]");
|
||||||
|
out << direct3DName << endl;
|
||||||
|
|
||||||
|
out << "Rendering Device=" << mD3DRenderDeviceComboBox->currentText() << endl;
|
||||||
|
out << "Anti aliasing=" << mD3DAntiAliasingComboBox->currentText() << endl;
|
||||||
|
out << "Floating-point mode=" << mD3DFloatingPointComboBox->currentText() << endl;
|
||||||
|
out << "Video Mode=" << mD3DResolutionComboBox->currentText() << endl;
|
||||||
|
|
||||||
|
if (mD3DNvPerfCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Allow NVPerfHUD=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Allow NVPerfHUD=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mD3DVSyncCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "VSync=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "VSync=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mD3DFullScreenCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Full Screen=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Full Screen=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GraphicsPage::getConfigValue(const QString &key, Ogre::RenderSystem *renderer)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
mOgreConfig->beginGroup(renderer->getName().c_str());
|
||||||
|
result = mOgreConfig->value(key).toString();
|
||||||
|
mOgreConfig->endGroup();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
|
||||||
|
uint row = 0;
|
||||||
|
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
|
||||||
|
|
||||||
|
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++)
|
||||||
|
{
|
||||||
|
Ogre::StringVector::iterator opt_it;
|
||||||
|
uint idx = 0;
|
||||||
|
for (opt_it = i->second.possibleValues.begin ();
|
||||||
|
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0)
|
||||||
|
result << (*opt_it).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::rendererChanged(const QString &renderer)
|
||||||
|
{
|
||||||
|
if (renderer.contains("Direct3D")) {
|
||||||
|
mRendererStackedWidget->setCurrentIndex(1);
|
||||||
|
mDisplayStackedWidget->setCurrentIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer.contains("OpenGL")) {
|
||||||
|
mRendererStackedWidget->setCurrentIndex(0);
|
||||||
|
mDisplayStackedWidget->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef GRAPHICSPAGE_H
|
||||||
|
#define GRAPHICSPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <OgreRoot.h>
|
||||||
|
#include <OgreRenderSystem.h>
|
||||||
|
#include <OgreConfigFile.h>
|
||||||
|
#include <OgreConfigDialog.h>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QCheckBox;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
class GraphicsPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphicsPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
QSettings *mOgreConfig;
|
||||||
|
|
||||||
|
void writeConfig();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void rendererChanged(const QString &renderer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ogre::Root *mOgre;
|
||||||
|
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||||
|
Ogre::RenderSystem *mOpenGLRenderSystem;
|
||||||
|
Ogre::RenderSystem *mDirect3DRenderSystem;
|
||||||
|
|
||||||
|
QComboBox *mRendererComboBox;
|
||||||
|
|
||||||
|
QStackedWidget *mRendererStackedWidget;
|
||||||
|
QStackedWidget *mDisplayStackedWidget;
|
||||||
|
|
||||||
|
// OpenGL
|
||||||
|
QComboBox *mOGLRTTComboBox;
|
||||||
|
QComboBox *mOGLAntiAliasingComboBox;
|
||||||
|
QComboBox *mOGLResolutionComboBox;
|
||||||
|
QComboBox *mOGLFrequencyComboBox;
|
||||||
|
|
||||||
|
QCheckBox *mOGLVSyncCheckBox;
|
||||||
|
QCheckBox *mOGLFullScreenCheckBox;
|
||||||
|
|
||||||
|
// Direct3D
|
||||||
|
QComboBox *mD3DRenderDeviceComboBox;
|
||||||
|
QComboBox *mD3DAntiAliasingComboBox;
|
||||||
|
QComboBox *mD3DFloatingPointComboBox;
|
||||||
|
QComboBox *mD3DResolutionComboBox;
|
||||||
|
|
||||||
|
QCheckBox *mD3DNvPerfCheckBox;
|
||||||
|
QCheckBox *mD3DVSyncCheckBox;
|
||||||
|
QCheckBox *mD3DFullScreenCheckBox;
|
||||||
|
|
||||||
|
QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer);
|
||||||
|
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||||
|
|
||||||
|
void createPages();
|
||||||
|
void setupConfig();
|
||||||
|
void setupOgre();
|
||||||
|
void readConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,30 @@
|
|||||||
|
######################################################################
|
||||||
|
# Automatically generated by qmake (2.01a) Fri Jun 24 21:14:15 2011
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
TARGET =
|
||||||
|
DEPENDPATH += .
|
||||||
|
INCLUDEPATH += .
|
||||||
|
|
||||||
|
# Input
|
||||||
|
HEADERS += combobox.hpp \
|
||||||
|
datafilespage.hpp \
|
||||||
|
graphicspage.hpp \
|
||||||
|
lineedit.hpp \
|
||||||
|
maindialog.hpp \
|
||||||
|
naturalsort.hpp \
|
||||||
|
playpage.hpp \
|
||||||
|
pluginsmodel.hpp \
|
||||||
|
pluginsview.hpp
|
||||||
|
SOURCES += datafilespage.cpp \
|
||||||
|
graphicspage.cpp \
|
||||||
|
lineedit.cpp \
|
||||||
|
main.cpp \
|
||||||
|
maindialog.cpp \
|
||||||
|
naturalsort.cpp \
|
||||||
|
playpage.cpp \
|
||||||
|
pluginsmodel.cpp \
|
||||||
|
pluginsview.cpp
|
||||||
|
RESOURCES += resources.qrc
|
||||||
|
win32:RC_FILE = launcher.rc
|
@ -0,0 +1 @@
|
|||||||
|
IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico"
|
@ -0,0 +1,46 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||||
|
**
|
||||||
|
** Use, modification and distribution is allowed without limitation,
|
||||||
|
** warranty, liability or support of any kind.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "lineedit.hpp"
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
LineEdit::LineEdit(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
clearButton = new QToolButton(this);
|
||||||
|
QPixmap pixmap(":images/clear.png");
|
||||||
|
clearButton->setIcon(QIcon(pixmap));
|
||||||
|
clearButton->setIconSize(pixmap.size());
|
||||||
|
clearButton->setCursor(Qt::ArrowCursor);
|
||||||
|
clearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
|
||||||
|
clearButton->hide();
|
||||||
|
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||||
|
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
|
||||||
|
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(clearButton->sizeHint().width() + frameWidth + 1));
|
||||||
|
QSize msz = minimumSizeHint();
|
||||||
|
setMinimumSize(qMax(msz.width(), clearButton->sizeHint().height() + frameWidth * 2 + 2),
|
||||||
|
qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineEdit::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
QSize sz = clearButton->sizeHint();
|
||||||
|
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
clearButton->move(rect().right() - frameWidth - sz.width(),
|
||||||
|
(rect().bottom() + 1 - sz.height())/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineEdit::updateCloseButton(const QString& text)
|
||||||
|
{
|
||||||
|
clearButton->setVisible(!text.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||||
|
**
|
||||||
|
** Use, modification and distribution is allowed without limitation,
|
||||||
|
** warranty, liability or support of any kind.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LINEEDIT_H
|
||||||
|
#define LINEEDIT_H
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class QToolButton;
|
||||||
|
|
||||||
|
class LineEdit : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LineEdit(QWidget *parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateCloseButton(const QString &text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QToolButton *clearButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LIENEDIT_H
|
||||||
|
|
@ -0,0 +1,35 @@
|
|||||||
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "maindialog.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
// Now we make sure the current dir is set to application path
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
if (dir.dirName() == "MacOS") {
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
|
||||||
|
// Load the stylesheet
|
||||||
|
QFile file("./launcher.qss");
|
||||||
|
|
||||||
|
file.open(QFile::ReadOnly);
|
||||||
|
QString styleSheet = QLatin1String(file.readAll());
|
||||||
|
app.setStyleSheet(styleSheet);
|
||||||
|
|
||||||
|
MainDialog dialog;
|
||||||
|
return dialog.exec();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,355 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
|
||||||
|
#include "maindialog.hpp"
|
||||||
|
#include "playpage.hpp"
|
||||||
|
#include "graphicspage.hpp"
|
||||||
|
#include "datafilespage.hpp"
|
||||||
|
|
||||||
|
MainDialog::MainDialog()
|
||||||
|
{
|
||||||
|
mIconWidget = new QListWidget;
|
||||||
|
mIconWidget->setObjectName("IconWidget");
|
||||||
|
mIconWidget->setViewMode(QListView::IconMode);
|
||||||
|
mIconWidget->setWrapping(false);
|
||||||
|
mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
|
||||||
|
mIconWidget->setIconSize(QSize(48, 48));
|
||||||
|
mIconWidget->setMovement(QListView::Static);
|
||||||
|
|
||||||
|
mIconWidget->setMinimumWidth(400);
|
||||||
|
mIconWidget->setFixedHeight(80);
|
||||||
|
mIconWidget->setSpacing(4);
|
||||||
|
mIconWidget->setCurrentRow(0);
|
||||||
|
mIconWidget->setFlow(QListView::LeftToRight);
|
||||||
|
|
||||||
|
QGroupBox *groupBox = new QGroupBox(this);
|
||||||
|
QVBoxLayout *groupLayout = new QVBoxLayout(groupBox);
|
||||||
|
|
||||||
|
mPagesWidget = new QStackedWidget(groupBox);
|
||||||
|
groupLayout->addWidget(mPagesWidget);
|
||||||
|
|
||||||
|
QPushButton *playButton = new QPushButton(tr("Play"));
|
||||||
|
|
||||||
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Close);
|
||||||
|
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
||||||
|
|
||||||
|
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
|
||||||
|
dialogLayout->addWidget(mIconWidget);
|
||||||
|
dialogLayout->addWidget(groupBox);
|
||||||
|
dialogLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
|
||||||
|
setWindowTitle(tr("OpenMW Launcher"));
|
||||||
|
setWindowIcon(QIcon(":/images/openmw.png"));
|
||||||
|
// Remove what's this? button
|
||||||
|
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
setMinimumSize(QSize(575, 575));
|
||||||
|
|
||||||
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
||||||
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
|
||||||
|
|
||||||
|
setupConfig();
|
||||||
|
createIcons();
|
||||||
|
createPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::createIcons()
|
||||||
|
{
|
||||||
|
if (!QIcon::hasThemeIcon("document-new")) {
|
||||||
|
QIcon::setThemeName("tango");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We create a fallback icon because the default fallback doesn't work
|
||||||
|
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
|
||||||
|
|
||||||
|
QListWidgetItem *playButton = new QListWidgetItem(mIconWidget);
|
||||||
|
playButton->setIcon(QIcon(":/images/openmw.png"));
|
||||||
|
playButton->setText(tr("Play"));
|
||||||
|
playButton->setTextAlignment(Qt::AlignCenter);
|
||||||
|
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget);
|
||||||
|
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
|
||||||
|
graphicsButton->setText(tr("Graphics"));
|
||||||
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget);
|
||||||
|
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||||
|
dataFilesButton->setText(tr("Data Files"));
|
||||||
|
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
connect(mIconWidget,
|
||||||
|
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||||
|
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::createPages()
|
||||||
|
{
|
||||||
|
// Various pages
|
||||||
|
mPlayPage = new PlayPage(this);
|
||||||
|
mGraphicsPage = new GraphicsPage(this);
|
||||||
|
mDataFilesPage = new DataFilesPage(this);
|
||||||
|
|
||||||
|
// First we retrieve all data= keys from the config
|
||||||
|
// We can't use QSettings directly because it
|
||||||
|
// does not support multiple keys with the same name
|
||||||
|
QFile file(mGameConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QApplication::exit(); // File cannot be opened or created
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
QStringList dataDirs;
|
||||||
|
|
||||||
|
// Add each data= value
|
||||||
|
while (!in.atEnd()) {
|
||||||
|
QString line = in.readLine();
|
||||||
|
|
||||||
|
if (line.startsWith("data=")) {
|
||||||
|
dataDirs.append(line.remove("data="));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the data-local= key
|
||||||
|
QString dataLocal = mGameConfig->value("data-local").toString();
|
||||||
|
if (!dataLocal.isEmpty()) {
|
||||||
|
dataDirs.append(dataLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dataDirs.isEmpty()) {
|
||||||
|
// Now pass the datadirs on to the DataFilesPage
|
||||||
|
mDataFilesPage->setupDataFiles(dataDirs, mGameConfig->value("fs-strict").toBool());
|
||||||
|
} else {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error reading OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not read the location of the data files</b><br><br> \
|
||||||
|
Please make sure OpenMW is correctly configured and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QApplication::exit(); // No data or data-local entries in openmw.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the combobox of the play page to imitate the comobox on the datafilespage
|
||||||
|
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
|
||||||
|
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
|
||||||
|
|
||||||
|
// Add the pages to the stacked widget
|
||||||
|
mPagesWidget->addWidget(mPlayPage);
|
||||||
|
mPagesWidget->addWidget(mGraphicsPage);
|
||||||
|
mPagesWidget->addWidget(mDataFilesPage);
|
||||||
|
|
||||||
|
// Select the first page
|
||||||
|
mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play()));
|
||||||
|
|
||||||
|
connect(mPlayPage->mProfilesComboBox,
|
||||||
|
SIGNAL(currentIndexChanged(int)),
|
||||||
|
this, SLOT(profileChanged(int)));
|
||||||
|
|
||||||
|
connect(mDataFilesPage->mProfilesComboBox,
|
||||||
|
SIGNAL(currentIndexChanged(int)),
|
||||||
|
this, SLOT(profileChanged(int)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::profileChanged(int index)
|
||||||
|
{
|
||||||
|
// Just to be sure, should always have a selection
|
||||||
|
if (!mIconWidget->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString currentPage = mIconWidget->currentItem()->data(Qt::DisplayRole).toString();
|
||||||
|
if (currentPage == QLatin1String("Play")) {
|
||||||
|
mDataFilesPage->mProfilesComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage == QLatin1String("Data Files")) {
|
||||||
|
mPlayPage->mProfilesComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||||
|
{
|
||||||
|
if (!current)
|
||||||
|
current = previous;
|
||||||
|
|
||||||
|
mPagesWidget->setCurrentIndex(mIconWidget->row(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
// Now write all config files
|
||||||
|
writeConfig();
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::play()
|
||||||
|
{
|
||||||
|
// First do a write of all the configs, just to be sure
|
||||||
|
writeConfig();
|
||||||
|
|
||||||
|
#ifdef Q_WS_WIN
|
||||||
|
QString game = "./openmw.exe";
|
||||||
|
QFile file(game);
|
||||||
|
#elif defined(Q_WS_MAC)
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
QString game = dir.absoluteFilePath("openmw");
|
||||||
|
QFile file(game);
|
||||||
|
#else
|
||||||
|
QString game = "./openmw";
|
||||||
|
QFile file(game);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
QFileInfo info(file);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not find OpenMW</b><br><br> \
|
||||||
|
The OpenMW application is not found.<br> \
|
||||||
|
Please make sure OpenMW is installed correctly and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.isExecutable()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
||||||
|
The OpenMW application is not executable.<br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the game
|
||||||
|
if (!process.startDetached(game)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
||||||
|
An error occurred while starting OpenMW.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>"));
|
||||||
|
msgBox.setDetailedText(process.errorString());
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::setupConfig()
|
||||||
|
{
|
||||||
|
// First we read the OpenMW config
|
||||||
|
QString config = "./openmw.cfg";
|
||||||
|
QFile file(config);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "openmw.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Open our config file
|
||||||
|
mGameConfig = new QSettings(config, QSettings::IniFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::writeConfig()
|
||||||
|
{
|
||||||
|
// Write the profiles
|
||||||
|
mDataFilesPage->writeConfig();
|
||||||
|
mDataFilesPage->mLauncherConfig->sync();
|
||||||
|
|
||||||
|
// Write the graphics settings
|
||||||
|
mGraphicsPage->writeConfig();
|
||||||
|
mGraphicsPage->mOgreConfig->sync();
|
||||||
|
|
||||||
|
QStringList dataFiles = mDataFilesPage->selectedMasters();
|
||||||
|
dataFiles.append(mDataFilesPage->checkedPlugins());
|
||||||
|
|
||||||
|
// Open the config as a QFile
|
||||||
|
QFile file(mGameConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
// File cannot be opened or created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
// Remove all previous master/plugin entries from config
|
||||||
|
while (!in.atEnd()) {
|
||||||
|
QString line = in.readLine();
|
||||||
|
if (!line.contains("master") && !line.contains("plugin")) {
|
||||||
|
buffer += line += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Now we write back the other config entries
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error writing OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not write to %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(buffer);
|
||||||
|
QTextStream out(&file);
|
||||||
|
|
||||||
|
// Write the list of game files to the config
|
||||||
|
foreach (const QString ¤tFile, dataFiles) {
|
||||||
|
|
||||||
|
if (currentFile.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
|
||||||
|
out << "master=" << currentFile << endl;
|
||||||
|
} else if (currentFile.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
|
||||||
|
out << "plugin=" << currentFile << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef MAINDIALOG_H
|
||||||
|
#define MAINDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QListWidget;
|
||||||
|
class QListWidgetItem;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QStringListModel;
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
class PlayPage;
|
||||||
|
class GraphicsPage;
|
||||||
|
class DataFilesPage;
|
||||||
|
|
||||||
|
class MainDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainDialog();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||||
|
void play();
|
||||||
|
void profileChanged(int index);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createIcons();
|
||||||
|
void createPages();
|
||||||
|
void setupConfig();
|
||||||
|
void writeConfig();
|
||||||
|
void closeEvent(QCloseEvent *event);
|
||||||
|
|
||||||
|
QListWidget *mIconWidget;
|
||||||
|
QStackedWidget *mPagesWidget;
|
||||||
|
|
||||||
|
PlayPage *mPlayPage;
|
||||||
|
GraphicsPage *mGraphicsPage;
|
||||||
|
DataFilesPage *mDataFilesPage;
|
||||||
|
|
||||||
|
QSettings *mGameConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* This file contains code found in the QtGui module of the Qt Toolkit.
|
||||||
|
* See Qt's qfilesystemmodel source files for more information
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "naturalsort.hpp"
|
||||||
|
|
||||||
|
static inline QChar getNextChar(const QString &s, int location)
|
||||||
|
{
|
||||||
|
return (location < s.length()) ? s.at(location) : QChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Natural number sort, skips spaces.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* 1, 2, 10, 55, 100
|
||||||
|
* 01.jpg, 2.jpg, 10.jpg
|
||||||
|
*
|
||||||
|
* Note on the algorithm:
|
||||||
|
* Only as many characters as necessary are looked at and at most they all
|
||||||
|
* are looked at once.
|
||||||
|
*
|
||||||
|
* Slower then QString::compare() (of course)
|
||||||
|
*/
|
||||||
|
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
|
||||||
|
{
|
||||||
|
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {
|
||||||
|
// skip spaces, tabs and 0's
|
||||||
|
QChar c1 = getNextChar(s1, l1);
|
||||||
|
while (c1.isSpace())
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
QChar c2 = getNextChar(s2, l2);
|
||||||
|
while (c2.isSpace())
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
if (c1.isDigit() && c2.isDigit()) {
|
||||||
|
while (c1.digitValue() == 0)
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
while (c2.digitValue() == 0)
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
int lookAheadLocation1 = l1;
|
||||||
|
int lookAheadLocation2 = l2;
|
||||||
|
int currentReturnValue = 0;
|
||||||
|
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
||||||
|
for (
|
||||||
|
QChar lookAhead1 = c1, lookAhead2 = c2;
|
||||||
|
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||||
|
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
|
||||||
|
lookAhead2 = getNextChar(s2, ++lookAheadLocation2)
|
||||||
|
) {
|
||||||
|
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
||||||
|
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
||||||
|
if (!is1ADigit && !is2ADigit)
|
||||||
|
break;
|
||||||
|
if (!is1ADigit)
|
||||||
|
return -1;
|
||||||
|
if (!is2ADigit)
|
||||||
|
return 1;
|
||||||
|
if (currentReturnValue == 0) {
|
||||||
|
if (lookAhead1 < lookAhead2) {
|
||||||
|
currentReturnValue = -1;
|
||||||
|
} else if (lookAhead1 > lookAhead2) {
|
||||||
|
currentReturnValue = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentReturnValue != 0)
|
||||||
|
return currentReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cs == Qt::CaseInsensitive) {
|
||||||
|
if (!c1.isLower()) c1 = c1.toLower();
|
||||||
|
if (!c2.isLower()) c2 = c2.toLower();
|
||||||
|
}
|
||||||
|
int r = QString::localeAwareCompare(c1, c2);
|
||||||
|
if (r < 0)
|
||||||
|
return -1;
|
||||||
|
if (r > 0)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||||
|
return QString::compare(s1, s2, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool naturalSortLessThanCS( const QString &left, const QString &right )
|
||||||
|
{
|
||||||
|
return (naturalCompare( left, right, Qt::CaseSensitive ) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool naturalSortLessThanCI( const QString &left, const QString &right )
|
||||||
|
{
|
||||||
|
return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0);
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef NATURALSORT_H
|
||||||
|
#define NATURALSORT_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
bool naturalSortLessThanCS( const QString &left, const QString &right );
|
||||||
|
bool naturalSortLessThanCI( const QString &left, const QString &right );
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,43 @@
|
|||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include "playpage.hpp"
|
||||||
|
|
||||||
|
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
QWidget *playWidget = new QWidget(this);
|
||||||
|
playWidget->setObjectName("PlayGroup");
|
||||||
|
playWidget->setFixedSize(QSize(425, 375));
|
||||||
|
|
||||||
|
mPlayButton = new QPushButton(tr("Play"), playWidget);
|
||||||
|
mPlayButton->setObjectName("PlayButton");
|
||||||
|
mPlayButton->setMinimumSize(QSize(200, 50));
|
||||||
|
|
||||||
|
QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget);
|
||||||
|
profileLabel->setObjectName("ProfileLabel");
|
||||||
|
|
||||||
|
QPlastiqueStyle *style = new QPlastiqueStyle;
|
||||||
|
mProfilesComboBox = new QComboBox(playWidget);
|
||||||
|
mProfilesComboBox->setObjectName("ProfilesComboBox");
|
||||||
|
mProfilesComboBox->setStyle(style);
|
||||||
|
|
||||||
|
QGridLayout *playLayout = new QGridLayout(playWidget);
|
||||||
|
|
||||||
|
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
playLayout->addWidget(mPlayButton, 1, 1, 1, 1);
|
||||||
|
playLayout->addWidget(profileLabel, 2, 1, 1, 1);
|
||||||
|
playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1);
|
||||||
|
playLayout->addItem(hSpacer1, 2, 0, 1, 1);
|
||||||
|
playLayout->addItem(hSpacer2, 2, 2, 1, 1);
|
||||||
|
playLayout->addItem(vSpacer1, 0, 1, 1, 1);
|
||||||
|
playLayout->addItem(vSpacer2, 4, 1, 1, 1);
|
||||||
|
|
||||||
|
QHBoxLayout *pageLayout = new QHBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(playWidget);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef PLAYPAGE_H
|
||||||
|
#define PLAYPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class PlayPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
QComboBox *mProfilesComboBox;
|
||||||
|
QPushButton *mPlayButton;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,149 @@
|
|||||||
|
#include <QMimeData>
|
||||||
|
#include <QBitArray>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
|
||||||
|
PluginsModel::PluginsModel(QObject *parent) : QStandardItemModel(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void decodeDataRecursive(QDataStream &stream, QStandardItem *item)
|
||||||
|
{
|
||||||
|
int colCount, childCount;
|
||||||
|
stream >> *item;
|
||||||
|
stream >> colCount >> childCount;
|
||||||
|
item->setColumnCount(colCount);
|
||||||
|
|
||||||
|
int childPos = childCount;
|
||||||
|
|
||||||
|
while(childPos > 0) {
|
||||||
|
childPos--;
|
||||||
|
QStandardItem *child = new QStandardItem();
|
||||||
|
decodeDataRecursive(stream, child);
|
||||||
|
item->setChild( childPos / colCount, childPos % colCount, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||||
|
int row, int column, const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
// Code largely based on QStandardItemModel::dropMimeData
|
||||||
|
|
||||||
|
// check if the action is supported
|
||||||
|
if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if the format is supported
|
||||||
|
QString format = QLatin1String("application/x-qstandarditemmodeldatalist");
|
||||||
|
if (!data->hasFormat(format))
|
||||||
|
return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
|
||||||
|
|
||||||
|
if (row > rowCount(parent))
|
||||||
|
row = rowCount(parent);
|
||||||
|
if (row == -1)
|
||||||
|
row = rowCount(parent);
|
||||||
|
if (column == -1)
|
||||||
|
column = 0;
|
||||||
|
|
||||||
|
// decode and insert
|
||||||
|
QByteArray encoded = data->data(format);
|
||||||
|
QDataStream stream(&encoded, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
|
||||||
|
//code based on QAbstractItemModel::decodeData
|
||||||
|
// adapted to work with QStandardItem
|
||||||
|
int top = std::numeric_limits<int>::max();
|
||||||
|
int left = std::numeric_limits<int>::max();
|
||||||
|
int bottom = 0;
|
||||||
|
int right = 0;
|
||||||
|
QVector<int> rows, columns;
|
||||||
|
QVector<QStandardItem *> items;
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
int r, c;
|
||||||
|
QStandardItem *item = new QStandardItem();
|
||||||
|
stream >> r >> c;
|
||||||
|
decodeDataRecursive(stream, item);
|
||||||
|
|
||||||
|
rows.append(r);
|
||||||
|
columns.append(c);
|
||||||
|
items.append(item);
|
||||||
|
top = qMin(r, top);
|
||||||
|
left = qMin(c, left);
|
||||||
|
bottom = qMax(r, bottom);
|
||||||
|
right = qMax(c, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the dragged items into the table, use a bit array to avoid overwriting items,
|
||||||
|
// since items from different tables can have the same row and column
|
||||||
|
int dragRowCount = 0;
|
||||||
|
int dragColumnCount = right - left + 1;
|
||||||
|
|
||||||
|
// Compute the number of continuous rows upon insertion and modify the rows to match
|
||||||
|
QVector<int> rowsToInsert(bottom + 1);
|
||||||
|
for (int i = 0; i < rows.count(); ++i)
|
||||||
|
rowsToInsert[rows.at(i)] = 1;
|
||||||
|
for (int i = 0; i < rowsToInsert.count(); ++i) {
|
||||||
|
if (rowsToInsert[i] == 1){
|
||||||
|
rowsToInsert[i] = dragRowCount;
|
||||||
|
++dragRowCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rows.count(); ++i)
|
||||||
|
rows[i] = top + rowsToInsert[rows[i]];
|
||||||
|
|
||||||
|
QBitArray isWrittenTo(dragRowCount * dragColumnCount);
|
||||||
|
|
||||||
|
// make space in the table for the dropped data
|
||||||
|
int colCount = columnCount(parent);
|
||||||
|
if (colCount < dragColumnCount + column) {
|
||||||
|
insertColumns(colCount, dragColumnCount + column - colCount, parent);
|
||||||
|
colCount = columnCount(parent);
|
||||||
|
}
|
||||||
|
insertRows(row, dragRowCount, parent);
|
||||||
|
|
||||||
|
row = qMax(0, row);
|
||||||
|
column = qMax(0, column);
|
||||||
|
|
||||||
|
QStandardItem *parentItem = itemFromIndex (parent);
|
||||||
|
if (!parentItem)
|
||||||
|
parentItem = invisibleRootItem();
|
||||||
|
|
||||||
|
QVector<QPersistentModelIndex> newIndexes(items.size());
|
||||||
|
// set the data in the table
|
||||||
|
for (int j = 0; j < items.size(); ++j) {
|
||||||
|
int relativeRow = rows.at(j) - top;
|
||||||
|
int relativeColumn = columns.at(j) - left;
|
||||||
|
int destinationRow = relativeRow + row;
|
||||||
|
int destinationColumn = relativeColumn + column;
|
||||||
|
int flat = (relativeRow * dragColumnCount) + relativeColumn;
|
||||||
|
// if the item was already written to, or we just can't fit it in the table, create a new row
|
||||||
|
if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) {
|
||||||
|
destinationColumn = qBound(column, destinationColumn, colCount - 1);
|
||||||
|
destinationRow = row + dragRowCount;
|
||||||
|
insertRows(row + dragRowCount, 1, parent);
|
||||||
|
flat = (dragRowCount * dragColumnCount) + relativeColumn;
|
||||||
|
isWrittenTo.resize(++dragRowCount * dragColumnCount);
|
||||||
|
}
|
||||||
|
if (!isWrittenTo.testBit(flat)) {
|
||||||
|
newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index());
|
||||||
|
isWrittenTo.setBit(flat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int k = 0; k < newIndexes.size(); k++) {
|
||||||
|
if (newIndexes.at(k).isValid()) {
|
||||||
|
parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k));
|
||||||
|
} else {
|
||||||
|
delete items.at(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The important part, tell the view what is dropped
|
||||||
|
emit indexesDropped(newIndexes);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef PLUGINSMODEL_H
|
||||||
|
#define PLUGINSMODEL_H
|
||||||
|
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
|
class PluginsModel : public QStandardItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PluginsModel(QObject *parent = 0);
|
||||||
|
~PluginsModel() {};
|
||||||
|
|
||||||
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void indexesDropped(QVector<QPersistentModelIndex> indexes);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,42 @@
|
|||||||
|
#include <QDebug>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include "pluginsview.hpp"
|
||||||
|
|
||||||
|
PluginsView::PluginsView(QWidget *parent) : QTableView(parent)
|
||||||
|
{
|
||||||
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
setAlternatingRowColors(true);
|
||||||
|
setDragEnabled(true);
|
||||||
|
setDragDropMode(QAbstractItemView::InternalMove);
|
||||||
|
setDropIndicatorShown(true);
|
||||||
|
setDragDropOverwriteMode(false);
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::startDrag(Qt::DropActions supportedActions)
|
||||||
|
{
|
||||||
|
selectionModel()->select( selectionModel()->selection(),
|
||||||
|
QItemSelectionModel::Select | QItemSelectionModel::Rows );
|
||||||
|
QAbstractItemView::startDrag( supportedActions );
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::setModel(QSortFilterProxyModel *model)
|
||||||
|
{
|
||||||
|
QTableView::setModel(model);
|
||||||
|
|
||||||
|
qRegisterMetaType< QVector<QPersistentModelIndex> >();
|
||||||
|
|
||||||
|
connect(model->sourceModel(), SIGNAL(indexesDropped(QVector<QPersistentModelIndex>)),
|
||||||
|
this, SLOT(selectIndexes(QVector<QPersistentModelIndex>)), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::selectIndexes( QVector<QPersistentModelIndex> aIndexes )
|
||||||
|
{
|
||||||
|
selectionModel()->clearSelection();
|
||||||
|
foreach( QPersistentModelIndex pIndex, aIndexes )
|
||||||
|
selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows );
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef PLUGINSVIEW_H
|
||||||
|
#define PLUGINSVIEW_H
|
||||||
|
|
||||||
|
#include <QTableView>
|
||||||
|
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
|
||||||
|
class PluginsView : public QTableView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
PluginsView(QWidget *parent = 0);
|
||||||
|
|
||||||
|
PluginsModel* model() const
|
||||||
|
{ return qobject_cast<PluginsModel*>(QAbstractItemView::model()); }
|
||||||
|
|
||||||
|
void startDrag(Qt::DropActions supportedActions);
|
||||||
|
void setModel(QSortFilterProxyModel *model);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void selectIndexes(QVector<QPersistentModelIndex> aIndexes);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QVector<QPersistentModelIndex>);
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/images">
|
||||||
|
<file alias="clear.png">resources/images/clear.png</file>
|
||||||
|
<file alias="down.png">resources/images/down.png</file>
|
||||||
|
<file alias="openmw.png">resources/images/openmw.png</file>
|
||||||
|
<file alias="openmw-plugin.png">resources/images/openmw-plugin.png</file>
|
||||||
|
<file alias="openmw-header.png">resources/images/openmw-header.png</file>
|
||||||
|
<file alias="playpage-background.png">resources/images/playpage-background.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="icons/tango">
|
||||||
|
<file alias="index.theme">resources/icons/tango/index.theme</file>
|
||||||
|
<file alias="video-display.png">resources/icons/tango/video-display.png</file>
|
||||||
|
<file alias="16x16/document-new.png">resources/icons/tango/document-new.png</file>
|
||||||
|
<file alias="16x16/edit-copy.png">resources/icons/tango/edit-copy.png</file>
|
||||||
|
<file alias="16x16/edit-delete.png">resources/icons/tango/edit-delete.png</file>
|
||||||
|
<file alias="16x16/go-bottom.png">resources/icons/tango/go-bottom.png</file>
|
||||||
|
<file alias="16x16/go-down.png">resources/icons/tango/go-down.png</file>
|
||||||
|
<file alias="16x16/go-top.png">resources/icons/tango/go-top.png</file>
|
||||||
|
<file alias="16x16/go-up.png">resources/icons/tango/go-up.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
After Width: | Height: | Size: 477 B |
After Width: | Height: | Size: 498 B |
After Width: | Height: | Size: 793 B |
After Width: | Height: | Size: 663 B |
After Width: | Height: | Size: 683 B |
After Width: | Height: | Size: 636 B |
After Width: | Height: | Size: 652 B |
@ -0,0 +1,8 @@
|
|||||||
|
[Icon Theme]
|
||||||
|
Name=Tango
|
||||||
|
Comment=Tango Theme
|
||||||
|
Inherits=default
|
||||||
|
Directories=16x16
|
||||||
|
|
||||||
|
[16x16]
|
||||||
|
Size=16
|
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 644 B |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 59 KiB |
After Width: | Height: | Size: 231 KiB |
@ -0,0 +1,5 @@
|
|||||||
|
[Profiles]
|
||||||
|
CurrentProfile=Default
|
||||||
|
Default\Master0=Morrowind.esm
|
||||||
|
Default\Master1=Tribunal.esm
|
||||||
|
Default\Master2=Bloodmoon.esm
|
@ -0,0 +1,120 @@
|
|||||||
|
#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: 24pt "Trebuchet MS";
|
||||||
|
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: 14pt "Trebuchet MS";
|
||||||
|
}
|
||||||
|
|
||||||
|
#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: 11pt "Trebuchet MS";
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=0.11
|
||||||
|
Type=Application
|
||||||
|
Name=OpenMW Launcher
|
||||||
|
GenericName=Role Playing Game
|
||||||
|
Comment=An engine replacement for The Elder Scrolls III: Morrowind
|
||||||
|
TryExec=omwlauncher
|
||||||
|
Exec=omwlauncher
|
||||||
|
Icon=openmw
|
||||||
|
Categories=Game;RolePlaying;
|
@ -0,0 +1,51 @@
|
|||||||
|
OpenMW: A reimplementation of The Elder Scrolls III: Morrowind
|
||||||
|
|
||||||
|
OpenMW is an attempt at recreating the engine for the popular role-playing game
|
||||||
|
Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work.
|
||||||
|
|
||||||
|
Version: 0.11
|
||||||
|
License: GPL (see GPL3.txt for more information)
|
||||||
|
Website: www.openmw.com
|
||||||
|
|
||||||
|
|
||||||
|
THIS IS A WORK IN PROGRESS
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
|
||||||
|
Windows:
|
||||||
|
TODO add description for Windows
|
||||||
|
|
||||||
|
Linux:
|
||||||
|
Ubuntu
|
||||||
|
TODO add description for Ubuntu
|
||||||
|
|
||||||
|
Arch Linux
|
||||||
|
There's an OpenMW package available in the AUR Repository:
|
||||||
|
http://aur.archlinux.org/packages.php?ID=21419
|
||||||
|
|
||||||
|
OS X:
|
||||||
|
TODO add description for OS X
|
||||||
|
|
||||||
|
BUILD FROM SOURCE
|
||||||
|
|
||||||
|
TODO add description here
|
||||||
|
|
||||||
|
COMMAND LINE OPTIONS
|
||||||
|
TODO add description of command line options
|
||||||
|
|
||||||
|
CREDITS
|
||||||
|
|
||||||
|
Developers:
|
||||||
|
TODO add list of developers
|
||||||
|
|
||||||
|
OpenMW:
|
||||||
|
Thanks to DokterDume for kindly providing us with the Moon and Star logo
|
||||||
|
used as the application icon and project logo.
|
||||||
|
|
||||||
|
Launcher:
|
||||||
|
Thanks to Kevin Ryan for kindly providing us with the icon used for the Data Files tab.
|
||||||
|
|
||||||
|
|
||||||
|
CHANGELOG
|
||||||
|
|
||||||
|
TODO add changelog (take pre 0.11.0 changelog from wiki when it is up again; take 0.11.0 and later changelog from tracker)
|