diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt new file mode 100644 index 0000000000..291103b28c --- /dev/null +++ b/apps/launcher/CMakeLists.txt @@ -0,0 +1,53 @@ +set(LAUNCHER + datafilesdialog.cpp + datafilesmodel.cpp + lineedit.cpp + main.cpp + maindialog.cpp + settingsdialog.cpp + datafilesdialog.h + datafilesmodel.h + lineedit.h + maindialog.h + settingsdialog.h +) + +SET(MOC_HDRS + datafilesdialog.h + lineedit.h + maindialog.h + settingsdialog.h +) + +source_group(apps\\launcher FILES ${ESMTOOL}) + +find_package(Qt4 REQUIRED) +set(QT_USE_QTGUI 1) + +QT4_ADD_RESOURCES(RCC_SRCS resources.qrc) +QT4_WRAP_CPP(MOC_SRCS ${MOC_HDRS}) + +include(${QT_USE_FILE}) + +# Main executable +add_executable(launcher + ${LAUNCHER} + ${MISC} ${MISC_HEADER} + ${TO_UTF8} + ${ESM} + ${RCC_SRCS} + ${MOC_SRCS} +) + +target_link_libraries(launcher + ${Boost_LIBRARIES} + ${OGRE_LIBRARIES} + ${QT_LIBRARIES} +) + + + +#if (APPLE) +# find_library(CARBON_FRAMEWORK Carbon) +# target_link_libraries(openmw ${CARBON_FRAMEWORK}) +#endif (APPLE) diff --git a/apps/launcher/datafilesdialog.cpp b/apps/launcher/datafilesdialog.cpp new file mode 100644 index 0000000000..201e2939c9 --- /dev/null +++ b/apps/launcher/datafilesdialog.cpp @@ -0,0 +1,371 @@ +#include +#include + +#include "datafilesdialog.h" +#include "datafilesmodel.h" + +using namespace ESM; + +//DataFilesDialog::DataFilesDialog(QWidget *parent) +// : QDialog(parent) + +DataFilesDialog::DataFilesDialog() +{ + //QWidget *centralWidget = new QWidget; + + dataFilesModel = new DataFilesModel(); + dataFilesModel->setReadOnly(true); // Prevent changes to files + dataFilesModel->setRootPath("/opt/openmw/data"); + +// sortModel = new QSortFilterProxyModel(); +// sortModel->setSourceModel(dataFilesModel); + + selectionModel = new QItemSelectionModel(dataFilesModel); +// selectionModel = new QItemSelectionModel(sortModel); + + // First, show only plugin files and sort them + QStringList acceptedfiles = (QStringList() << "*.esp"); + dataFilesModel->setNameFilters(acceptedfiles); + dataFilesModel->setNameFilterDisables(false); // Hide all other files + + dataFilesModel->sort(3, Qt::AscendingOrder); // Sort the plugins by date + dataFilesModel->submit(); // Force refresh of the data + + // Now show master files too, to make them appear at the top of the list + acceptedfiles = (QStringList() << "*.esm" << "*.esp"); + dataFilesModel->setNameFilters(acceptedfiles); + dataFilesModel->setFilter(QDir::Files); + + // Column 1 + QVBoxLayout *dialogLayout = new QVBoxLayout(this); + QHBoxLayout *groupsLayout = new QHBoxLayout(); + + QGroupBox *groupFiles = new QGroupBox(tr("Morrowind Files"), this); + groupFiles->setMinimumWidth(450); + QVBoxLayout *groupFilesLayout = new QVBoxLayout(groupFiles); + + QSpacerItem *vSpacer1 = new QSpacerItem(20, 2, QSizePolicy::Minimum, QSizePolicy::Fixed); + + QHBoxLayout *filterLayout = new QHBoxLayout(); + QLabel *labelFilter = new QLabel(tr("Filter:"), groupFiles); + lineFilter = new LineEdit(groupFiles); + + filterLayout->addWidget(labelFilter); + filterLayout->addWidget(lineFilter); + + // View for the game files + dataFilesView = new QTableView(groupFiles); + + // Put everything in the correct layouts + groupFilesLayout->addLayout(filterLayout); + groupFilesLayout->addItem(vSpacer1); + groupFilesLayout->addWidget(dataFilesView); + groupsLayout->addWidget(groupFiles); + + // Column 2 + QGroupBox *groupInfo = new QGroupBox(tr("File Information"), this); + groupInfo->setFixedWidth(250); + QVBoxLayout *groupInfoLayout = new QVBoxLayout(groupInfo); + + QSpacerItem *vSpacer2 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed); + + QLabel *labelAuthor = new QLabel(tr("Author:"), groupInfo); + lineAuthor = new QLineEdit(groupInfo); + lineAuthor->setReadOnly(true); + + QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed); + + QLabel *labelDesc = new QLabel(tr("Description:"), groupInfo); + textDesc = new QPlainTextEdit(groupInfo); + textDesc->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + textDesc->setMinimumHeight(180); + textDesc->setReadOnly(true); + + QSpacerItem *vSpacer4 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Fixed); + + QLabel *labelDepends = new QLabel(tr("Dependencies:"), groupInfo); + textDepends = new QPlainTextEdit(groupInfo); + textDepends->setFixedHeight(80); + textDepends->setReadOnly(true); + + QHBoxLayout *buttonsLayout = new QHBoxLayout(); + QSpacerItem *horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults); + buttonBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + buttonsLayout->addItem(horizontalSpacer); + buttonsLayout->addWidget(buttonBox); + + // Put everything in the correct layouts + groupInfoLayout->addItem(vSpacer2); + groupInfoLayout->addWidget(labelAuthor); + groupInfoLayout->addWidget(lineAuthor); + groupInfoLayout->addItem(vSpacer3); + groupInfoLayout->addWidget(labelDesc); + groupInfoLayout->addWidget(textDesc); + groupInfoLayout->addItem(vSpacer4); + groupInfoLayout->addWidget(labelDepends); + groupInfoLayout->addWidget(textDepends); + + groupsLayout->addWidget(groupInfo); + + dialogLayout->addLayout(groupsLayout); + dialogLayout->addLayout(buttonsLayout); + + setWindowTitle(tr("Data Files")); + + + // Signals and slots + connect(dataFilesModel, SIGNAL(directoryLoaded(const QString &)), this, SLOT(setupView())); +// connect(dataFilesModel, SIGNAL(dataChanged(const QModelIndex, const QModelIndex)), this, SLOT(readConfig())); + + connect(dataFilesView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckstate(QModelIndex))); + connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(changeData(QModelIndex, QModelIndex))); + + connect(lineFilter, SIGNAL(textChanged(const QString &)), this, SLOT(setFilter())); + + connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), this, SLOT(restoreDefaults())); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(writeConfig())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); + + readConfig(); +} + +void DataFilesDialog::changeData(QModelIndex index, QModelIndex bottom) +{ + if (!index.isValid()) { + return; + } + + // Added for testing + + textDepends->clear(); // Clear the dependencies of previous file + + ESMReader datafile; + QString path(dataFilesModel->filePath(index)); +// QString path(dataFilesModel->filePath(sortModel->mapToSource(index))); + + datafile.open(path.toStdString()); // Open the selected file + + // Get the author of the selected file and display it + QString author(QString::fromStdString(datafile.getAuthor())); + lineAuthor->setText(author); + + // Get the file desciption + QString desc(QString::fromStdString(datafile.getDesc())); + textDesc->setPlainText(desc); + + // Get a list of master files on which the file depends + ESMReader::MasterList mlist = datafile.getMasters(); + + for (unsigned int i = 0; i < mlist.size(); ++i) // Add each master file + textDepends->appendPlainText(QString::fromStdString(mlist[i].name)); + + /* Get the date of creation + QDateTime dateCreated = dataFilesModel->fileInfo(index).created(); + QString created = dateCreated.toString(QString("dd.MM.yyyy")); + labelDateCreated->setText(created); + + // Get the date last modified + QDateTime dateModified = dataFilesModel->fileInfo(index).lastModified(); + QString modified = dateModified.toString(QString("dd.MM.yyyy")); + labelDateModified->setText(modified);*/ +} + +void DataFilesDialog::setupView() +{ + // The signal directoryLoaded is emitted after all files are in the model + dataFilesView->setModel(dataFilesModel); +// dataFilesView->setModel(sortModel); + + // Set the view to the data directory + dataFilesView->setRootIndex(QModelIndex(dataFilesModel->index("/opt/openmw/data"))); +// dataFilesView->setRootIndex(sortModel->mapFromSource(QModelIndex(dataFilesModel->index("/opt/openmw/data")))); + + dataFilesView->verticalHeader()->hide(); + + //dataFilesView->hideColumn(1); + dataFilesView->hideColumn(3); // Hide Date Modified column + dataFilesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + dataFilesView->verticalHeader()->setDefaultSectionSize(25); //setHeight + + dataFilesView->setSortingEnabled(true); + + dataFilesView->setSelectionBehavior(QAbstractItemView::SelectRows); + dataFilesView->setSelectionModel(selectionModel); + + + dataFilesView->setAlternatingRowColors(true); // Fancy colors! + + dataFilesView->resizeColumnsToContents(); + dataFilesView->horizontalHeader()->setStretchLastSection(true); + + + //sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); + +} + +void DataFilesDialog::readConfig() +{ + qDebug() << "datachanged"; + + // Morrowind.ini settings + QSettings settings("Morrowind.ini", + QSettings::IniFormat); + settings.beginGroup("Game Files"); + + const QStringList childKeys = settings.childKeys(); + + // See if the files from the config file actually exist + foreach (const QString &childKey, childKeys) { + // Create full path to current file found in config + QString path = "/opt/openmw/data/"; // Note: get path from config + path.append(settings.value(childKey).toString()); + + QModelIndex index = dataFilesModel->index(path, 0); +// QModelIndex index = sortModel->mapFromSource(dataFilesModel->index(path, 0)); +// QModelIndex index = sortModel->mapFromSource(dataFilesModel->index(path)); + + if (index.isValid()) { + // File is found in model, set it to checked + qDebug() << "File is found in model, set it to checked"; + // dataFilesModel->setData(sortModel->mapToSource(index), Qt::Checked, Qt::CheckStateRole); + dataFilesModel->setData(index, Qt::Checked, Qt::CheckStateRole); +// dataFilesModel->checkedItems.insert(QPersistentModelIndex(sortModel->mapToSource(index))); + // dataFilesModel->checkedItems.insert(index); + + //qDebug() << index; + } else { + // File is not found in the model + qDebug() << "file not found!"; + } + } + settings.endGroup(); +} + +void DataFilesDialog::writeConfig() +{ +/* // Custom write method: We cannot use QSettings because it does not accept spaces + +// QList checkedItems = dataFilesModel->getCheckedItems().toList(); + QList checkedItems = dataFilesModel->getCheckedItems(); + + qSort(checkedItems); // Sort the indexes so that master files end up on top + + QString keyName; + QString fileName; + + QFile file("Morrowind.ini"); // Specify filepath later + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + close(); // File cannot be opened or created + + //QTextStream in(&file); + QTextStream in(&file); + + //QString buffer; + QByteArray buffer; + + while (!in.atEnd()) { + QString line = in.readLine(); + if (!line.contains("GameFile") && line != "[Game Files]") { + buffer += line + "\n"; + } + } + + file.close(); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + close(); + + QTextStream out(&file); + file.write(buffer); + + out << "[Game Files]\n"; + + // Write the list of game files to the config + for (int i = 0; i < checkedItems.size(); ++i) { + fileName = dataFilesModel->fileName(checkedItems.at(i)); + + keyName.setNum(i); + keyName.prepend("GameFile"); + + out << keyName << "=" << fileName << "\n"; + } + + file.close();*/ + close(); // Exit dialog +} + +void DataFilesDialog::restoreDefaults() +{ + // Uncheck all checked items + dataFilesModel->checkedItems.clear(); + + QModelIndexList indexlist; // Make a list of default master files + indexlist.append(dataFilesModel->index("/opt/openmw/data/Morrowind.esm", 0)); + indexlist.append(dataFilesModel->index("/opt/openmw/data/Tribunal.esm", 0)); + indexlist.append(dataFilesModel->index("/opt/openmw/data/Bloodmoon.esm", 0)); + + foreach (const QModelIndex &index, indexlist) { + if (index.isValid()) { + // Master file found, check it + dataFilesModel->setData(index, Qt::Checked, Qt::CheckStateRole); + } + } + dataFilesModel->submit(); // Force refresh of view +} + +void DataFilesDialog::setCheckstate(QModelIndex index) +{ + // No check if index is valid: doubleclicked() always returns + // a valid index when emitted + + //index = QModelIndex(sortModel->mapToSource(index)); // Get a valid index + index = index.sibling(index.row(), 0); // reset index to first column + // because that's where te checkbox is; makes it possible to doubleclick whole row + + if (!index.isValid()) + return; + + if (dataFilesModel->data(index, Qt::CheckStateRole) == Qt::Checked) { + // Selected row is checked, uncheck it + dataFilesModel->setData(index, Qt::Unchecked, Qt::CheckStateRole); + } else { + dataFilesModel->setData(index, Qt::Checked, Qt::CheckStateRole); + } +} + +void DataFilesDialog::setFilter() +{ + QStringList filefilter = (QStringList() << "*.esm" << "*.esp"); + QStringList currentfilefilter; + + QString esmfilter = lineFilter->text(); + QString espfilter = lineFilter->text(); + + if (lineFilter->text().isEmpty()) { + dataFilesModel->setNameFilters(filefilter); + // sortModel->setFilterRegExp(QRegExp("*.esp", Qt::CaseInsensitive, + // QRegExp::FixedString)); + // sortModel->setFilterKeyColumn(0); + return; + } + + esmfilter.prepend("*"); + esmfilter.append("*.esm"); + espfilter.prepend("*"); + espfilter.append("*.esp"); + + currentfilefilter << esmfilter << espfilter; +// sortModel->setFilterRegExp(QRegExp(espfilter, Qt::CaseInsensitive, + // QRegExp::FixedString)); + // sortModel->setFilterKeyColumn(0); + dataFilesModel->setNameFilters(currentfilefilter); + +// readConfig(); +// dataFilesModel->submit(); +// dataFilesModel->setData(); + +} diff --git a/apps/launcher/datafilesdialog.h b/apps/launcher/datafilesdialog.h new file mode 100644 index 0000000000..884de52191 --- /dev/null +++ b/apps/launcher/datafilesdialog.h @@ -0,0 +1,54 @@ +#ifndef DATAFILESDIALOG_H +#define DATAFILESDIALOG_H + +#include +#include +#include +#include + +#include "lineedit.h" + +class DataFilesModel; +class QStringList; +class QTableView; +class QLineEdit; +class QPlainTextEdit; +class QItemSelectionModel; +class QSortFilterProxyModel; + +class DataFilesDialog : public QDialog +{ + Q_OBJECT + +public: + DataFilesDialog(); +// ~DataFilesDialog() { }; + +private: + DataFilesModel *dataFilesModel; + + QTableView *dataFilesView; + QItemSelectionModel *selectionModel; + QSortFilterProxyModel *sortModel; + + QLineEdit *lineAuthor; + LineEdit *lineFilter; + QPlainTextEdit *textDesc; + QPlainTextEdit *textDepends; + + +public slots: + + void changeData(QModelIndex top, QModelIndex bottom); // edit + void restoreDefaults(); + void readConfig(); + void writeConfig(); + void setupView(); + + void setFilter(); + void setCheckstate(QModelIndex index); +// void doubleClicked(QModelIndex index); + +}; + +#endif diff --git a/apps/launcher/datafilesmodel.cpp b/apps/launcher/datafilesmodel.cpp new file mode 100644 index 0000000000..99b924d67a --- /dev/null +++ b/apps/launcher/datafilesmodel.cpp @@ -0,0 +1,145 @@ +#include "datafilesmodel.h" + +DataFilesModel::DataFilesModel(QObject *parent) + : QFileSystemModel(parent) +{ +} + +QVariant DataFilesModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && role == Qt::DecorationRole) { + if (index.column() == 2) { + QFileIconProvider fip; + QIcon fileIcon = fip.icon(fileInfo(index)); + return fileIcon; + } + else { + return QIcon(); + } + + } + + if (index.isValid() && role == Qt::DisplayRole) { + if (index.column() == 2) { + //qDebug() << index.data(Qt::DisplayRole); + if (fileInfo(index).suffix().toLower() == "esp") { + return QString("Plugin File"); + } + else if (fileInfo(index).suffix().toLower() == "esm") { + return QString("Master File"); + + } + } + } + + if (index.isValid() && role == Qt::CheckStateRole && index.column() == 0) { + // Put a checkbox in the first column + if (index.isValid()) + + if (checkedItems.contains(filePath(index))) { +// if (checkedItems.contains(index)) { + return Qt::Checked; + } + else { + return Qt::Unchecked; + } + } + return QFileSystemModel::data(index, role); +} + +bool DataFilesModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + + if (index.isValid() && role == Qt::CheckStateRole && index.column() == 0) { +// QPersistentModelIndex pindex(index); + + // qDebug() << pindex; + + if (value == Qt::Checked) { + //checkedItems.insert(pindex); + checkedItems.append(filePath(index)); + } else { +// checkedItems.remove(pindex); + checkedItems.removeAll(filePath(index)); + } + + emit dataChanged(index, index); + return true; + } + + return QFileSystemModel::setData(index, value, role); +} + + + +Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const +{ + return QFileSystemModel::flags(index) | Qt::ItemIsUserCheckable; +} + + + +/*QVariant DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) { + case Qt::DecorationRole: + if (section == 0) { + // ### TODO oh man this is ugly and doesn't even work all the way! + // it is still 2 pixels off + QImage pixmap(16, 1, QImage::Format_Mono); + pixmap.fill(0); + pixmap.setAlphaChannel(pixmap.createAlphaMask()); + return pixmap; + } + break; + case Qt::TextAlignmentRole: + return Qt::AlignLeft; + } + + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QAbstractItemModel::headerData(section, orientation, role); + + QString returnValue; + switch (section) { + case 0: returnValue = tr("TES3 File"); + break; + case 1: returnValue = tr("Size"); + break; + case 2: returnValue = tr("Status"); + break; + case 3: returnValue = tr("Date Modified"); + break; + default: return QVariant(); + } + return returnValue; +} +*/ + +/*test +void DataFilesModel::setCheckedItems(const QStringList &gameFiles) +{ + qDebug() << "test"; + qDebug() << gameFiles.join("lol"); + + +}*/ + +/*void DataFilesModel::unCheckAll() +{ + checkedItems.clear(); +// data(); + emit dataChanged(QModelIndex(), QModelIndex()); + submit(); +}*/ + +const QStringList DataFilesModel::getCheckedItems() +//const QList DataFilesModel::getCheckedItems() +//const QSet DataFilesModel::getCheckedItems() +{ + return checkedItems; +} + +/*void DataFilesModel::sort(int column, Qt::SortOrder order) +{ + qDebug() << "Sort!"; +}*/ diff --git a/apps/launcher/datafilesmodel.h b/apps/launcher/datafilesmodel.h new file mode 100644 index 0000000000..0e376865b7 --- /dev/null +++ b/apps/launcher/datafilesmodel.h @@ -0,0 +1,38 @@ +#ifndef DATAFILESMODEL_H +#define DATAFILESMODEL_H + +#include +#include + +#include + +class DataFilesModel : public QFileSystemModel +{ +public: + DataFilesModel(QObject *parent = 0); + ~DataFilesModel() {}; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + Qt::ItemFlags flags(const QModelIndex &index) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + +// void sort(int column, Qt::SortOrder order); + //test +// void setCheckedItems(const QStringList& files); +// void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); +// void unCheckAll(); + +// const QSet getCheckedItems(); +// const QList getCheckedItems(); + const QStringList getCheckedItems(); + +// QVariant headerData(int section, Qt::Orientation orientation, int role) const; + +// QSet checkedItems; +// QList checkedItems; + QStringList checkedItems; +}; + +#endif diff --git a/apps/launcher/launcher.pro b/apps/launcher/launcher.pro new file mode 100644 index 0000000000..ac17e019ef --- /dev/null +++ b/apps/launcher/launcher.pro @@ -0,0 +1,23 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Sun Mar 27 19:33:07 2011 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += datafilesdialog.h \ + datafilesmodel.h \ + lineedit.h \ + maindialog.h \ + settingsdialog.h +SOURCES += datafilesdialog.cpp \ + datafilesmodel.cpp \ + lineedit.cpp \ + main.cpp \ + maindialog.cpp \ + settingsdialog.cpp +RESOURCES += resources.qrc +RC_FILE = launcher.rc \ No newline at end of file diff --git a/apps/launcher/launcher.rc b/apps/launcher/launcher.rc new file mode 100644 index 0000000000..3d22c74fc9 --- /dev/null +++ b/apps/launcher/launcher.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "resources/openmw_icon.ico" diff --git a/apps/launcher/lineedit.cpp b/apps/launcher/lineedit.cpp new file mode 100644 index 0000000000..7cd7f4d1da --- /dev/null +++ b/apps/launcher/lineedit.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (c) 2007 Trolltech ASA +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#include "lineedit.h" +#include +#include + +LineEdit::LineEdit(QWidget *parent) + : QLineEdit(parent) +{ + clearButton = new QToolButton(this); + QPixmap pixmap(":resources/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()); +} + + diff --git a/apps/launcher/lineedit.h b/apps/launcher/lineedit.h new file mode 100644 index 0000000000..7a96a523c8 --- /dev/null +++ b/apps/launcher/lineedit.h @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (c) 2007 Trolltech ASA +** +** Use, modification and distribution is allowed without limitation, +** warranty, liability or support of any kind. +** +****************************************************************************/ + +#ifndef LINEEDIT_H +#define LINEEDIT_H + +#include + +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 + diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp new file mode 100644 index 0000000000..77c01148a8 --- /dev/null +++ b/apps/launcher/main.cpp @@ -0,0 +1,13 @@ +#include + +#include "maindialog.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + MainDialog dialog; + return dialog.exec(); + +} + diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp new file mode 100644 index 0000000000..8ff37dda45 --- /dev/null +++ b/apps/launcher/maindialog.cpp @@ -0,0 +1,121 @@ +#include + +#include "maindialog.h" +#include "datafilesdialog.h" +#include "settingsdialog.h" + +MainDialog::MainDialog() +{ + + QLabel *header = new QLabel(this); + header->setMinimumSize(QSize(400, 150)); + header->setPixmap(QPixmap(":resources/openmw_header.png")); + + // Buttons + QPushButton *buttonStart = new QPushButton(this); + buttonStart->setMinimumSize(QSize(200, 40)); + buttonStart->setText(tr("Start OpenMW")); + + QPushButton *buttonDataFiles = new QPushButton(this); + buttonDataFiles->setMinimumSize(QSize(200, 40)); + buttonDataFiles->setText(tr("Data Files")); + + QPushButton *buttonSettings = new QPushButton(this); + buttonSettings->setText(tr("Settings")); + buttonSettings->setIcon(QIcon::fromTheme("preferences-other")); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(this); + buttonBox->setStandardButtons(QDialogButtonBox::Close); + buttonBox->addButton(buttonSettings, QDialogButtonBox::ActionRole); + + QVBoxLayout *dialogLayout = new QVBoxLayout(this); + QVBoxLayout *buttonsLayout = new QVBoxLayout(); + QHBoxLayout *bodyLayout = new QHBoxLayout(); + QHBoxLayout *dialogButtonsLayout = new QHBoxLayout(); + + QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *hSpacer3 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + QSpacerItem *vSpacer1 = new QSpacerItem(40, 150, QSizePolicy::Minimum, QSizePolicy::Fixed); + QSpacerItem *vSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + buttonsLayout->addItem(vSpacer1); + buttonsLayout->addWidget(buttonStart); + buttonsLayout->addWidget(buttonDataFiles); + buttonsLayout->addItem(vSpacer2); + + bodyLayout->addItem(hSpacer1); + bodyLayout->addLayout(buttonsLayout); + bodyLayout->addItem(hSpacer2); + + dialogButtonsLayout->addItem(hSpacer3); + dialogButtonsLayout->addWidget(buttonBox); + + dialogLayout->addLayout(bodyLayout); + dialogLayout->addLayout(dialogButtonsLayout); + + setMinimumSize(QSize(400, 310)); + setMaximumSize(QSize(400, 310)); + + setWindowTitle(tr("OpenMW Launcher")); + QPixmap pixmap(":resources/openmw_icon.png"); + setWindowIcon(QIcon(pixmap)); + + // Signals and slots + connect(buttonStart, SIGNAL(clicked()), this, SLOT(start())); + connect(buttonDataFiles, SIGNAL(clicked()), this, SLOT(showDataFiles())); + connect(buttonSettings, SIGNAL(clicked()), this, SLOT(showSettings())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); + + openmw = new QProcess(NULL); + +} + +void MainDialog::start() +{ + // Start the game! + openmw->start("./openmw"); + connect(openmw, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); +} + +void MainDialog::finished(int exitCode, QProcess::ExitStatus exitStatus) +{ + QString debuginfo = openmw->readAllStandardOutput(); + + if (exitCode == 0 && exitStatus == QProcess::NormalExit) { + // Close the launcher if the game is quitted + close(); + } + + if (exitCode != 0) { + // An error occured whilst starting OpenMW + + // First check if readAllStandardOutput is empty + // Finished gets signaled twice sometimes + + if (!debuginfo.isEmpty()) + { + QMessageBox msgBox; + msgBox.setWindowTitle("Error"); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Ok); + msgBox.setText(tr("\nAn error occured while starting OpenMW.")); + msgBox.setDetailedText(debuginfo); + msgBox.exec(); + } + + } +} + +void MainDialog::showDataFiles() +{ + DataFilesDialog dialog; + dialog.exec(); +} + +void MainDialog::showSettings() +{ + SettingsDialog dialog; + dialog.exec(); +} diff --git a/apps/launcher/maindialog.h b/apps/launcher/maindialog.h new file mode 100644 index 0000000000..db69111153 --- /dev/null +++ b/apps/launcher/maindialog.h @@ -0,0 +1,25 @@ +#ifndef MAINDIALOG_H +#define MAINDIALOG_H + +#include +#include + +class MainDialog : public QDialog +{ + Q_OBJECT + +public: + MainDialog(); + + QProcess *openmw; + +public slots: + + void start(); + void showDataFiles(); + void showSettings(); + void finished(int, QProcess::ExitStatus exitStatus); + +}; + +#endif diff --git a/apps/launcher/resources.qrc b/apps/launcher/resources.qrc new file mode 100644 index 0000000000..1116523573 --- /dev/null +++ b/apps/launcher/resources.qrc @@ -0,0 +1,7 @@ + + + resources/clear.png + resources/openmw_header.png + resources/openmw_icon.png + + diff --git a/apps/launcher/resources/clear.png b/apps/launcher/resources/clear.png new file mode 100644 index 0000000000..6c4b83b7ac Binary files /dev/null and b/apps/launcher/resources/clear.png differ diff --git a/apps/launcher/resources/openmw_header.png b/apps/launcher/resources/openmw_header.png new file mode 100644 index 0000000000..a3045ce615 Binary files /dev/null and b/apps/launcher/resources/openmw_header.png differ diff --git a/apps/launcher/resources/openmw_icon.ico b/apps/launcher/resources/openmw_icon.ico new file mode 100644 index 0000000000..c04fc3d9ce Binary files /dev/null and b/apps/launcher/resources/openmw_icon.ico differ diff --git a/apps/launcher/resources/openmw_icon.png b/apps/launcher/resources/openmw_icon.png new file mode 100644 index 0000000000..7a5393821f Binary files /dev/null and b/apps/launcher/resources/openmw_icon.png differ diff --git a/apps/launcher/settingsdialog.cpp b/apps/launcher/settingsdialog.cpp new file mode 100644 index 0000000000..100254ce65 --- /dev/null +++ b/apps/launcher/settingsdialog.cpp @@ -0,0 +1,259 @@ +#include + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog() +{ + QTabWidget *tabWidget = new QTabWidget(this); + QWidget *graphicsTab = new QWidget(); + + // Render group + QGroupBox *groupRender = new QGroupBox(tr("Renderer"), graphicsTab); + groupRender->setMinimumWidth(450); + + QVBoxLayout *groupRenderLayout = new QVBoxLayout(groupRender); + QVBoxLayout *dialogLayout = new QVBoxLayout(this); + QVBoxLayout *pageLayout = new QVBoxLayout(graphicsTab); + + QGridLayout *renderLayout = new QGridLayout(); + + QLabel *labelRender = new QLabel(tr("Rendering Subsystem:"), groupRender); + comboRender = new QComboBox(groupRender); + + QLabel *labelRTT = new QLabel(tr("Preferred RTT Mode:"), groupRender); + comboRTT = new QComboBox(groupRender); + + QLabel *labelAA = new QLabel(tr("Antialiasing:"), groupRender); + comboAA = new QComboBox(groupRender); + + renderLayout->addWidget(labelRender, 0, 0, 1, 1); + renderLayout->addWidget(comboRender, 0, 1, 1, 1); + + QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum); + renderLayout->addItem(vSpacer1, 1, 1, 1, 1); + + renderLayout->addWidget(labelRTT, 2, 0, 1, 1); + renderLayout->addWidget(comboRTT, 2, 1, 1, 1); + renderLayout->addWidget(labelAA, 3, 0, 1, 1); + renderLayout->addWidget(comboAA, 3, 1, 1, 1); + + QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + groupRenderLayout->addLayout(renderLayout); + groupRenderLayout->addItem(vSpacer2); + + pageLayout->addWidget(groupRender); + + // Display group + QGroupBox *groupDisplay = new QGroupBox(tr("Display"), graphicsTab); + QVBoxLayout *groupDisplayLayout = new QVBoxLayout(groupDisplay); + + QGridLayout *displayLayout = new QGridLayout(); + + QLabel *labelResolution = new QLabel(tr("Resolution:"), groupDisplay); + comboResolution = new QComboBox(groupDisplay); + + QLabel *labelFrequency = new QLabel(tr("Display Frequency:"), groupDisplay); + comboFrequency = new QComboBox(groupDisplay); + + checkVSync = new QCheckBox(tr("Vertical Sync"), groupDisplay); + checkGamma = new QCheckBox(tr("sRGB Gamma Conversion"), groupDisplay); + checkFullScreen = new QCheckBox(tr("Full Screen"), groupDisplay); + + displayLayout->addWidget(labelResolution, 0, 0, 1, 1); + displayLayout->addWidget(comboResolution, 0, 1, 1, 1); + displayLayout->addWidget(labelFrequency, 1, 0, 1, 1); + displayLayout->addWidget(comboFrequency, 1, 1, 1, 1); + + QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum); + displayLayout->addItem(vSpacer3, 2, 1, 1, 1); + + displayLayout->addWidget(checkVSync, 3, 0, 1, 1); + displayLayout->addWidget(checkGamma, 3, 1, 1, 1); + displayLayout->addWidget(checkFullScreen, 6, 0, 1, 1); + + QSpacerItem *vSpacer4 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding); + + groupDisplayLayout->addLayout(displayLayout); + groupDisplayLayout->addItem(vSpacer4); + + pageLayout->addWidget(groupDisplay); + + + tabWidget->addTab(graphicsTab, QString(tr("Graphics"))); + tabWidget->setCurrentIndex(0); + + dialogLayout->addWidget(tabWidget); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(this); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + dialogLayout->addWidget(buttonBox); + + setWindowTitle(tr("Settings")); + setWindowIcon(QIcon::fromTheme("preferences-other")); + + // Ogre Settings + // TODO: Find out a way to do this nice and platform-independent + QString filepath = QDir::homePath(); + filepath.append("/.config/openmw/ogre.cfg"); + + ogreConfig = new QSettings(filepath, QSettings::IniFormat); + + // Signals and slots + connect(comboRender, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&))); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(writeConfig())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(close())); + + // Ogre stuff + root = new Ogre::Root("plugins.cfg"); + + // Get the available renderers and put them in the combobox + const Ogre::RenderSystemList &renderers = root->getAvailableRenderers(); + + for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) { + comboRender->addItem((*r)->getName().c_str()); + } + + int index = comboRender->findText(getConfigValue("Render System")); + + if ( index != -1) { + comboRender->setCurrentIndex(index); + } + +} + +QStringList SettingsDialog::getAvailableOptions(const QString& key) +{ + QStringList result; + + uint row = 0; + Ogre::ConfigOptionMap options = mSelectedRenderSystem->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 SettingsDialog::rendererChanged(const QString& renderer) +{ + // Set the render system to the selected one + mSelectedRenderSystem = root->getRenderSystemByName(renderer.toStdString().c_str()); + + comboRTT->addItems(getAvailableOptions("RTT Preferred Mode")); + comboAA->addItems(getAvailableOptions("FSAA")); + comboResolution->addItems(getAvailableOptions("Video Mode")); + comboFrequency->addItems(getAvailableOptions("Display Frequency")); + + // Get the value for the option the config file, find if it's in the combobox. + // If it's found, set the current index to that value, otherwise: ignore. + + int index = comboRTT->findText(getConfigValue("RTT Preferred Mode")); + if ( index != -1) + comboRTT->setCurrentIndex(index); + + index = comboAA->findText(getConfigValue("FSAA")); + if ( index != -1) + comboAA->setCurrentIndex(index); + + index = comboResolution->findText(getConfigValue("Video Mode")); + if ( index != -1) + comboResolution->setCurrentIndex(index); + + index = comboFrequency->findText(getConfigValue("Display Frequency")); + if ( index != -1) + comboFrequency->setCurrentIndex(index); + + // Now we do the same for the checkboxes + if (QString::compare(getConfigValue("VSync"), QString("Yes")) == 0) { + checkVSync->setCheckState(Qt::Checked); + } + + if (QString::compare(getConfigValue("sRGB Gamma Conversion"), QString("Yes")) == 0) { + checkGamma->setCheckState(Qt::Checked); + } + + if (QString::compare(getConfigValue("Full Screen"), QString("Yes")) == 0) { + checkFullScreen->setCheckState(Qt::Checked); + } + +} + +QString SettingsDialog::getConfigValue(const QString& key) +{ + QString result; + + ogreConfig->beginGroup(mSelectedRenderSystem->getName().c_str()); + result = ogreConfig->value(key).toString(); + ogreConfig->endGroup(); + + return result; +} + +void SettingsDialog::writeConfig() +{ + // Get the values from the GUI elements and write them to the config file + + // Custom write method: We cannot use QSettings because it does not accept spaces + + QString keyName; + QString fileName; + + QFile file(ogreConfig->fileName()); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + // File could not be opened, TODO error + close(); + + QTextStream out(&file); + + out << "Render System=" << mSelectedRenderSystem->getName().c_str() << endl << endl; + + // add brackets to the renderer's name + QString renderer = mSelectedRenderSystem->getName().c_str(); + renderer.prepend("["); + renderer.append("]"); + out << renderer << endl; + + out << "Display Frequency=" << comboFrequency->currentText() << endl; + out << "FSAA=" << comboAA->currentText() << endl; + + if (checkFullScreen->checkState() == Qt::Checked) { + out << "Full Screen=Yes" << endl; + } else { + out << "Full Screen=No" << endl; + } + + out << "RTT Preferred Mode=" << comboRTT->currentText() << endl; + + if (checkVSync->checkState() == Qt::Checked) { + out << "VSync=Yes" << endl; + } else { + out << "VSync=No" << endl; + } + + out << "Video Mode=" << comboResolution->currentText() << endl; + + if (checkGamma->checkState() == Qt::Checked) { + out << "sRGB Gamma Conversion=Yes" << endl; + } else { + out << "sRGB Gamma Conversion=No" << endl; + } + + file.close(); + + close(); // Exit dialog +} + diff --git a/apps/launcher/settingsdialog.h b/apps/launcher/settingsdialog.h new file mode 100644 index 0000000000..964f2736b0 --- /dev/null +++ b/apps/launcher/settingsdialog.h @@ -0,0 +1,46 @@ +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +class SettingsDialog : public QDialog +{ + Q_OBJECT + +public: + SettingsDialog(); + QStringList getAvailableOptions(const QString& key); + + Ogre::Root *root; + Ogre::RenderSystem *mSelectedRenderSystem; + + QComboBox *comboRender; + QComboBox *comboRTT; + QComboBox *comboAA; + QComboBox *comboResolution; + QComboBox *comboFrequency; + + QCheckBox *checkVSync; + QCheckBox *checkGamma; + QCheckBox *checkFullScreen; + + QSettings *ogreConfig; + + QString getConfigValue(const QString& key); + +public slots: + void rendererChanged(const QString& renderer); + void writeConfig(); +}; + +#endif