diff --git a/CMakeLists.txt b/CMakeLists.txt index e62391c741..666522e366 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -428,7 +428,7 @@ if (APPLE) install(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" RENAME "openmw.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) install(FILES "${OpenMW_BINARY_DIR}/plugins.cfg" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) - install(FILES "${OpenMW_BINARY_DIR}/launcher.qss" DESTINATION "${INSTALL_SUBDIR}" COMPONENT Runtime) + set(CPACK_GENERATOR "DragNDrop") set(CPACK_PACKAGE_VERSION ${OPENMW_VERSION}) set(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index d648f4674e..80e8f61c7b 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -72,13 +72,13 @@ endif() if (APPLE) configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss - "${APP_BUNDLE_DIR}/../launcher.qss") + "${APP_BUNDLE_DIR}/resources/../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") + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/resources/launcher.qss") configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg - "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg") + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}launcher.cfg") endif() diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 8100631d7a..8c203a771e 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -1,8 +1,6 @@ #include #include -#include -#include #include #include "datafilespage.hpp" @@ -120,11 +118,12 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent) connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); + connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&))); - + createActions(); setupConfig(); setupDataFiles(); - createActions(); + } void DataFilesPage::setupConfig() @@ -138,8 +137,7 @@ void DataFilesPage::setupConfig() // Open our config file mLauncherConfig = new QSettings(config, QSettings::IniFormat); - mLauncherConfig->sync(); - + file.close(); // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { @@ -162,13 +160,14 @@ void DataFilesPage::setupConfig() // No current profile selected currentProfile = "Default"; } - mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile)); - mLauncherConfig->endGroup(); + const int currentIndex = mProfilesComboBox->findText(currentProfile); + if (currentIndex != -1) { + // Profile is found + mProfilesComboBox->setCurrentIndex(currentIndex); + } - // 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&))); + mLauncherConfig->endGroup(); } @@ -188,19 +187,51 @@ void DataFilesPage::setupDataFiles() // Put the paths in a boost::filesystem vector to use with Files::Collections Files::PathContainer dataDirs(variables["data"].as()); - - std::string local(variables["data-local"].as()); - if (!local.empty()) - { + mDataDirs = dataDirs; + + std::string local = variables["data-local"].as(); + if (!local.empty()) { + mDataLocal.push_back(Files::PathContainer::value_type(local)); dataDirs.push_back(Files::PathContainer::value_type(local)); } - if (dataDirs.empty()) - { - dataDirs.push_back(mCfgMgr.getLocalPath()); + mCfgMgr.processPaths(dataDirs); + + while (dataDirs.empty()) { + // No valid data files directory found + + QMessageBox msgBox; + msgBox.setWindowTitle("Error detecting Morrowind installation"); + msgBox.setIcon(QMessageBox::Critical); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("
Could not find the Data Files location

\ + The directory containing the Data Files was not found.

\ + Press \"Browse...\" to specify the location manually.
")); + + QAbstractButton *dirSelectButton = + msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() == dirSelectButton) { + + QString dataDir = QFileDialog::getExistingDirectory( + this, tr("Select Data Files Directory"), + "/home", + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + dataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + mDataDirs.push_back(Files::PathContainer::value_type(dataDir.toStdString())); + } else { + // Cancel + break; + } } - mCfgMgr.processPaths(dataDirs); + // Check if cancel was clicked because we can't exit from while loop + if (dataDirs.empty()) { + QApplication::exit(1); + } // Create a file collection for the dataDirs Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as()); @@ -209,15 +240,13 @@ void DataFilesPage::setupDataFiles() const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm"); unsigned int i = 0; // Row number - for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) { QString currentMaster = QString::fromStdString( boost::filesystem::path (iter->second.filename()).string()); const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); mMastersWidget->setItem(i, 0, item); @@ -228,8 +257,7 @@ void DataFilesPage::setupDataFiles() // Now on to the plugins const Files::MultiDirCollection &esp = fileCollections.getCollection(".esp"); - for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) - { + for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) { ESMReader fileReader; QStringList availableMasters; // Will contain all found masters @@ -245,8 +273,7 @@ void DataFilesPage::setupDataFiles() const QList itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly); - if (itemList.isEmpty()) // Master is not yet in the widget - { + if (itemList.isEmpty()) { // Master is not yet in the widget mMastersWidget->insertRow(i); QTableWidgetItem *item = new QTableWidgetItem(currentMaster); @@ -435,18 +462,18 @@ void DataFilesPage::deleteProfile() return; } - QMessageBox deleteMessageBox(this); - deleteMessageBox.setWindowTitle(tr("Delete Profile")); - deleteMessageBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - deleteMessageBox.setIcon(QMessageBox::Warning); - QAbstractButton *deleteButton = - deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole); + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - deleteMessageBox.addButton(QMessageBox::Cancel); + QAbstractButton *deleteButton = + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - deleteMessageBox.exec(); + msgBox.exec(); - if (deleteMessageBox.clickedButton() == deleteButton) { + if (msgBox.clickedButton() == deleteButton) { // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -503,7 +530,6 @@ void DataFilesPage::moveUp() void DataFilesPage::moveDown() { // Shift the selected plugins down one row - if (!mPluginsTable->selectionModel()->hasSelection()) { return; } @@ -919,9 +945,18 @@ void DataFilesPage::filterChanged(const QString filter) void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) { + // Prevent the deletion of the default profile + if (current == "Default") { + mDeleteProfileAction->setEnabled(false); + } else { + mDeleteProfileAction->setEnabled(true); + } + if (!previous.isEmpty()) { writeConfig(previous); mLauncherConfig->sync(); + } else { + return; } uncheckPlugins(); @@ -989,22 +1024,12 @@ void DataFilesPage::readConfig() void DataFilesPage::writeConfig(QString profile) { - // Don't overwrite the config if no plugins are found - if (mPluginsModel->rowCount() < 1) { - return; - } - - if (profile.isEmpty()) { - profile = mProfilesComboBox->currentText(); - } - - if (profile.isEmpty()) { + // Don't overwrite the config if no masters are found + if (mMastersWidget->rowCount() < 1) { return; } - // Prepare the OpenMW config - - // Open the config as a QFile + // Open the OpenMW config as a QFile QFile file(QString::fromStdString((mCfgMgr.getUserPath()/"openmw.cfg").string())); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { @@ -1023,10 +1048,14 @@ void DataFilesPage::writeConfig(QString profile) QTextStream in(&file); QByteArray buffer; - // Remove all previous master/plugin entries from config + // Remove all previous entries from config while (!in.atEnd()) { QString line = in.readLine(); - if (!line.contains("master") && !line.contains("plugin")) { + if (!line.startsWith("master") && + !line.startsWith("plugin") && + !line.startsWith("data") && + !line.startsWith("data-local")) + { buffer += line += "\n"; } } @@ -1046,9 +1075,52 @@ void DataFilesPage::writeConfig(QString profile) QApplication::exit(1); } - file.write(buffer); + if (!buffer.isEmpty()) { + file.write(buffer); + } + QTextStream gameConfig(&file); + // First write the list of data dirs + mCfgMgr.processPaths(mDataDirs); + mCfgMgr.processPaths(mDataLocal); + + QString path; + + // data= directories + for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) { + path = QString::fromStdString(it->string()); + path.remove(QChar('\"')); + + // Make sure the string is quoted when it contains spaces + if (path.contains(" ")) { + gameConfig << "data=\"" << path << "\"" << endl; + } else { + gameConfig << "data=" << path << endl; + } + } + + // data-local directory + if (!mDataLocal.empty()) { + path = QString::fromStdString(mDataLocal.front().string()); + path.remove(QChar('\"')); + + if (path.contains(" ")) { + gameConfig << "data-local=\"" << path << "\"" << endl; + } else { + gameConfig << "data-local=" << path << endl; + } + } + + + if (profile.isEmpty()) { + profile = mProfilesComboBox->currentText(); + } + + if (profile.isEmpty()) { + return; + } + // Make sure we have no groups open while (!mLauncherConfig->group().isEmpty()) { mLauncherConfig->endGroup(); @@ -1061,7 +1133,7 @@ void DataFilesPage::writeConfig(QString profile) mLauncherConfig->beginGroup(profile); mLauncherConfig->remove(""); // Clear the subgroup - // First write the masters to the configs + // Now write the masters to the configs const QStringList masters = selectedMasters(); // We don't use foreach because we need i @@ -1073,11 +1145,10 @@ void DataFilesPage::writeConfig(QString profile) } - // Now write all checked plugins + // And finally write all checked plugins const QStringList plugins = checkedPlugins(); - for (int i = 0; i < plugins.size(); ++i) - { + for (int i = 0; i < plugins.size(); ++i) { const QString currentPlugin = plugins.at(i); mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin); gameConfig << "plugin=" << currentPlugin << endl; @@ -1086,5 +1157,6 @@ void DataFilesPage::writeConfig(QString profile) file.close(); mLauncherConfig->endGroup(); mLauncherConfig->endGroup(); + mLauncherConfig->sync(); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index a454fa871b..ad5e90511c 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -3,6 +3,9 @@ #include #include + +#include + #include "combobox.hpp" class QTableWidget; @@ -77,6 +80,8 @@ private: QAction *mUncheckAction; Files::ConfigurationManager &mCfgMgr; + Files::PathContainer mDataDirs; + Files::PathContainer mDataLocal; QSettings *mLauncherConfig;