mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 02:26:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1199 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1199 lines
		
	
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include <QtGui>
 | |
| 
 | |
| #include <components/esm/esm_reader.hpp>
 | |
| #include <components/files/configurationmanager.hpp>
 | |
| 
 | |
| #include "datafilespage.hpp"
 | |
| #include "lineedit.hpp"
 | |
| #include "naturalsort.hpp"
 | |
| #include "pluginsmodel.hpp"
 | |
| #include "pluginsview.hpp"
 | |
| 
 | |
| #include <boost/version.hpp>
 | |
| /**
 | |
|  * Workaround for problems with whitespaces in paths in older versions of Boost library
 | |
|  */
 | |
| #if (BOOST_VERSION <= 104600)
 | |
| namespace boost
 | |
| {
 | |
| 
 | |
|     template<>
 | |
|     inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
 | |
|     {
 | |
|         return boost::filesystem::path(arg);
 | |
|     }
 | |
| 
 | |
| } /* namespace boost */
 | |
| #endif /* (BOOST_VERSION <= 104600) */
 | |
| 
 | |
| using namespace ESM;
 | |
| using namespace std;
 | |
| 
 | |
| //sort QModelIndexList ascending
 | |
| bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
 | |
| {
 | |
|     return index1.row() >= index2.row();
 | |
| }
 | |
| 
 | |
| //sort QModelIndexList descending
 | |
| bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
 | |
| {
 | |
|     return index1.row() <= index2.row();
 | |
| }
 | |
| 
 | |
| DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
 | |
|     : QWidget(parent)
 | |
|     , mCfgMgr(cfg)
 | |
| {
 | |
|     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&)));
 | |
| 
 | |
|     connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&)));
 | |
| 
 | |
|     createActions();
 | |
|     setupConfig();
 | |
|     setupDataFiles();
 | |
| 
 | |
| }
 | |
| 
 | |
| void DataFilesPage::setupConfig()
 | |
| {
 | |
|     QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "launcher.cfg").string());
 | |
|     QFile file(config);
 | |
| 
 | |
|     if (!file.exists()) {
 | |
|         config = QString::fromStdString((mCfgMgr.getUserPath() / "launcher.cfg").string());
 | |
|     }
 | |
| 
 | |
|     // Open our config file
 | |
|     mLauncherConfig = new QSettings(config, QSettings::IniFormat);
 | |
|     file.close();
 | |
| 
 | |
|     // 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";
 | |
|     }
 | |
| 
 | |
|     const int currentIndex = mProfilesComboBox->findText(currentProfile);
 | |
|     if (currentIndex != -1) {
 | |
|         // Profile is found
 | |
|         mProfilesComboBox->setCurrentIndex(currentIndex);
 | |
|     }
 | |
| 
 | |
|     mLauncherConfig->endGroup();
 | |
| }
 | |
| 
 | |
| 
 | |
| void DataFilesPage::setupDataFiles()
 | |
| {
 | |
|     // We use the Configuration Manager to retrieve the configuration values
 | |
|     boost::program_options::variables_map variables;
 | |
|     boost::program_options::options_description desc;
 | |
| 
 | |
|     desc.add_options()
 | |
|         ("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
 | |
| //        ("data-local", boost::program_options::value<std::string>()->default_value(""))
 | |
|         ("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
 | |
|         ("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
 | |
| 
 | |
|     mCfgMgr.readConfiguration(variables, desc);
 | |
| 
 | |
|     // Put the paths in a boost::filesystem vector to use with Files::Collections
 | |
|     Files::PathContainer dataDirs(variables["data"].as<Files::PathContainer>());
 | |
|     mDataDirs = dataDirs;
 | |
| 
 | |
| //     std::string local = variables["data-local"].as<std::string>();
 | |
| //     if (!local.empty()) {
 | |
| //         mDataLocal.push_back(Files::PathContainer::value_type(local));
 | |
| //         dataDirs.push_back(Files::PathContainer::value_type(local));
 | |
| //     }
 | |
| 
 | |
|     if (dataDirs.size()>1)
 | |
|         dataDirs.resize (1);
 | |
| 
 | |
|     mCfgMgr.processPaths(dataDirs);
 | |
| 
 | |
|     while (dataDirs.empty()) {
 | |
|         // No valid data files directory found
 | |
| 
 | |
|         QMessageBox msgBox;
 | |
|         msgBox.setWindowTitle("Error detecting Morrowind installation");
 | |
|         msgBox.setIcon(QMessageBox::Warning);
 | |
|         msgBox.setStandardButtons(QMessageBox::Cancel);
 | |
|         msgBox.setText(tr("<br><b>Could not find the Data Files location</b><br><br> \
 | |
|         The directory containing the Data Files was not found.<br><br> \
 | |
|         Press \"Browse...\" to specify the location manually.<br>"));
 | |
| 
 | |
|         QAbstractButton *dirSelectButton =
 | |
|         msgBox.addButton(tr("B&rowse..."), QMessageBox::ActionRole);
 | |
| 
 | |
|         msgBox.exec();
 | |
| 
 | |
|         if (msgBox.clickedButton() == dirSelectButton) {
 | |
| 
 | |
|             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;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Check if cancel was clicked because we can't exit from while loop
 | |
|     if (dataDirs.empty()) {
 | |
|         QApplication::exit(1);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Create a file collection for the dataDirs
 | |
|     Files::Collections fileCollections(dataDirs, !variables["fs-strict"].as<bool>());
 | |
| 
 | |
|      // First we add all the master files
 | |
|     const Files::MultiDirCollection &esm = fileCollections.getCollection(".esm");
 | |
|     unsigned int i = 0; // Row number
 | |
| 
 | |
|     for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter) {
 | |
|         QString currentMaster = QString::fromStdString(
 | |
|                 boost::filesystem::path (iter->second.filename()).string());
 | |
| 
 | |
|         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 = fileCollections.getCollection(".esp");
 | |
| 
 | |
|     for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter) {
 | |
| 
 | |
|         try {
 | |
|             ESMReader fileReader;
 | |
|             QStringList availableMasters; // Will contain all found masters
 | |
| 
 | |
|             fileReader.setEncoding(variables["encoding"].as<std::string>());
 | |
|             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
 | |
|                     mMastersWidget->insertRow(i);
 | |
| 
 | |
|                     QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
 | |
|                     item->setForeground(Qt::red);
 | |
|                     item->setFlags(item->flags() & ~(Qt::ItemIsSelectable));
 | |
| 
 | |
|                     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(","));
 | |
| 
 | |
|             QString fileName = QString::fromStdString(boost::filesystem::path (iter->second.filename()).string());
 | |
|             QStandardItem *child = new QStandardItem(fileName);
 | |
| 
 | |
|             // Tooltip information
 | |
|             QString author = QString::fromStdString(fileReader.getAuthor());
 | |
|             float version = fileReader.getFVer();
 | |
|             QString description = QString::fromStdString(fileReader.getDesc());
 | |
| 
 | |
|             // For the date created/modified
 | |
|             QFileInfo fi(QString::fromStdString(iter->second.string()));
 | |
| 
 | |
|             QString toolTip= QString("<b>Author:</b> %1<br/> \
 | |
|                                     <b>Version:</b> %2<br/><br/> \
 | |
|                                     <b>Description:</b><br/> \
 | |
|                                     %3<br/><br/> \
 | |
|                                     <b>Created on:</b> %4<br/> \
 | |
|                                     <b>Last modified:</b> %5")
 | |
|                                 .arg(author)
 | |
|                                 .arg(version)
 | |
|                                 .arg(description)
 | |
|                                 .arg(fi.created().toString(Qt::TextDate))
 | |
|                                 .arg(fi.lastModified().toString(Qt::TextDate));
 | |
| 
 | |
|             child->setToolTip(toolTip);
 | |
| 
 | |
|             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);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|         } catch(std::runtime_error &e) {
 | |
|             // An error occurred while reading the .esp
 | |
|             continue;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     readConfig();
 | |
| }
 | |
| 
 | |
| 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 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 <b>%0</b>?").arg(profile));
 | |
| 
 | |
|     QAbstractButton *deleteButton =
 | |
|     msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);
 | |
| 
 | |
|     msgBox.exec();
 | |
| 
 | |
|     if (msgBox.clickedButton() == deleteButton) {
 | |
|        // Make sure we have no groups open
 | |
|         while (!mLauncherConfig->group().isEmpty()) {
 | |
|             mLauncherConfig->endGroup();
 | |
|         }
 | |
| 
 | |
|         mLauncherConfig->beginGroup("Profiles");
 | |
| 
 | |
|         // Open the profile-name subgroup
 | |
|         mLauncherConfig->beginGroup(profile);
 | |
|         mLauncherConfig->remove(""); // Clear the subgroup
 | |
|         mLauncherConfig->endGroup();
 | |
|         mLauncherConfig->endGroup();
 | |
| 
 | |
|         // 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)
 | |
| {
 | |
|     // Make sure there are plugins in the view
 | |
|     if (!mPluginsTable->selectionModel()->hasSelection()) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     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 QString childIndexToolTip = QVariant(mDataFilesModel->data(childIndex, Qt::ToolTipRole)).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);
 | |
|                 item->setToolTip(childIndexToolTip);
 | |
| 
 | |
|                 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)
 | |
| {
 | |
|     // 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();
 | |
|     // 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)
 | |
| {
 | |
|     // Don't overwrite the config if no masters are found
 | |
|     if (mMastersWidget->rowCount() < 1) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     // Prepare the OpenMW config
 | |
|     QString config = QString::fromStdString((mCfgMgr.getLocalPath() / "openmw.cfg").string());
 | |
|     QFile file(config);
 | |
| 
 | |
|     if (!file.exists()) {
 | |
|         config = QString::fromStdString((mCfgMgr.getUserPath() / "openmw.cfg").string());
 | |
|     }
 | |
| 
 | |
|     // Open the config as a QFile
 | |
|     file.setFileName(config);
 | |
| 
 | |
|     if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) {
 | |
|         // File cannot be opened or created
 | |
|         QMessageBox msgBox;
 | |
|         msgBox.setWindowTitle("Error writing OpenMW configuration file");
 | |
|         msgBox.setIcon(QMessageBox::Critical);
 | |
|         msgBox.setStandardButtons(QMessageBox::Ok);
 | |
|         msgBox.setText(tr("<br><b>Could not open or create %0</b><br><br> \
 | |
|         Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
 | |
|         msgBox.exec();
 | |
| 
 | |
|         qApp->exit(1);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     QTextStream in(&file);
 | |
|     QByteArray buffer;
 | |
| 
 | |
|     // Remove all previous entries from config
 | |
|     while (!in.atEnd()) {
 | |
|         QString line = in.readLine();
 | |
|         if (!line.startsWith("master") &&
 | |
|             !line.startsWith("plugin") &&
 | |
|             !line.startsWith("data") &&
 | |
|             !line.startsWith("data-local"))
 | |
|         {
 | |
|             buffer += line += "\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     file.close();
 | |
| 
 | |
|     // Now we write back the other config entries
 | |
|     if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
 | |
|         QMessageBox msgBox;
 | |
|         msgBox.setWindowTitle("Error writing OpenMW configuration file");
 | |
|         msgBox.setIcon(QMessageBox::Critical);
 | |
|         msgBox.setStandardButtons(QMessageBox::Ok);
 | |
|         msgBox.setText(tr("<br><b>Could not write to %0</b><br><br> \
 | |
|         Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
 | |
|         msgBox.exec();
 | |
| 
 | |
|         qApp->exit(1);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     if (!buffer.isEmpty()) {
 | |
|         file.write(buffer);
 | |
|     }
 | |
| 
 | |
|     QTextStream gameConfig(&file);
 | |
| 
 | |
|     // First write the list of data dirs
 | |
|     mCfgMgr.processPaths(mDataDirs);
 | |
|     mCfgMgr.processPaths(mDataLocal);
 | |
| 
 | |
|     QString path;
 | |
| 
 | |
|     // data= directories
 | |
|     for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) {
 | |
|         path = QString::fromStdString(it->string());
 | |
|         path.remove(QChar('\"'));
 | |
| 
 | |
|         // Make sure the string is quoted when it contains spaces
 | |
|         if (path.contains(" ")) {
 | |
|             gameConfig << "data=\"" << path << "\"" << endl;
 | |
|         } else {
 | |
|             gameConfig << "data=" << path << endl;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // data-local directory
 | |
|     if (!mDataLocal.empty()) {
 | |
|         path = QString::fromStdString(mDataLocal.front().string());
 | |
|         path.remove(QChar('\"'));
 | |
| 
 | |
|         if (path.contains(" ")) {
 | |
|             gameConfig << "data-local=\"" << path << "\"" << endl;
 | |
|         } else {
 | |
|             gameConfig << "data-local=" << path << endl;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     if (profile.isEmpty()) {
 | |
|         profile = mProfilesComboBox->currentText();
 | |
|     }
 | |
| 
 | |
|     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
 | |
| 
 | |
|     // Now write the masters to the configs
 | |
|     const QStringList masters = selectedMasters();
 | |
| 
 | |
|     // We don't use foreach because we need i
 | |
|     for (int i = 0; i < masters.size(); ++i) {
 | |
|         const QString currentMaster = masters.at(i);
 | |
| 
 | |
|         mLauncherConfig->setValue(QString("Master%0").arg(i), currentMaster);
 | |
|         gameConfig << "master=" << currentMaster << endl;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     // And finally write all checked plugins
 | |
|     const QStringList plugins = checkedPlugins();
 | |
| 
 | |
|     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;
 | |
|     }
 | |
| 
 | |
|     file.close();
 | |
|     mLauncherConfig->endGroup();
 | |
|     mLauncherConfig->endGroup();
 | |
|     mLauncherConfig->sync();
 | |
| }
 |