|
|
|
#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"
|
|
|
|
|
|
|
|
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::Critical);
|
|
|
|
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) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|