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