/* Copyright (C) 2012 Sebastian Herbord. All rights reserved. This file is part of Mod Organizer. Mod Organizer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Mod Organizer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Mod Organizer. If not, see . */ #include "categoriesdialog.h" #include "categories.h" #include "categoryimportdialog.h" #include "messagedialog.h" #include "nexusinterface.h" #include "settings.h" #include "ui_categoriesdialog.h" #include "utility.h" #include #include #include #include class NewIDValidator : public QIntValidator { public: NewIDValidator(const std::set& ids) : m_UsedIDs(ids) {} virtual State validate(QString& input, int& pos) const { State intRes = QIntValidator::validate(input, pos); if (intRes == Acceptable) { bool ok = false; int id = input.toInt(&ok); if (m_UsedIDs.find(id) != m_UsedIDs.end()) { return QValidator::Intermediate; } } return intRes; } private: const std::set& m_UsedIDs; }; class ExistingIDValidator : public QIntValidator { public: ExistingIDValidator(const std::set& ids) : m_UsedIDs(ids) {} virtual State validate(QString& input, int& pos) const { State intRes = QIntValidator::validate(input, pos); if (intRes == Acceptable) { bool ok = false; int id = input.toInt(&ok); if ((id == 0) || (m_UsedIDs.find(id) != m_UsedIDs.end())) { return QValidator::Acceptable; } else { return QValidator::Intermediate; } } else { return intRes; } } private: const std::set& m_UsedIDs; }; class ValidatingDelegate : public QItemDelegate { public: ValidatingDelegate(QObject* parent, QValidator* validator) : QItemDelegate(parent), m_Validator(validator) {} QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { QLineEdit* edit = new QLineEdit(parent); edit->setValidator(m_Validator); return edit; } virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { QLineEdit* edit = qobject_cast(editor); int pos = 0; QString editText = edit->text(); if (m_Validator->validate(editText, pos) == QValidator::Acceptable) { // Set ID and Parent ID as ints so sorting works as expected if (index.column() == 0 || index.column() == 2) { model->setData(index, editText.toInt()); return; } QItemDelegate::setModelData(editor, model, index); } } private: QValidator* m_Validator; }; CategoriesDialog::CategoriesDialog(QWidget* parent) : TutorableDialog("Categories", parent), ui(new Ui::CategoriesDialog) { ui->setupUi(this); fillTable(); connect(ui->categoriesTable, SIGNAL(cellChanged(int, int)), this, SLOT(cellChanged(int, int))); if (Settings::instance().nexus().categoryMappings()) { connect(ui->nexusRefresh, SIGNAL(clicked()), this, SLOT(nexusRefresh_clicked())); connect(ui->nexusImportButton, SIGNAL(clicked()), this, SLOT(nexusImport_clicked())); ui->nexusCategoryList->setDisabled(false); } else { ui->nexusCategoryList->setDisabled(true); } } CategoriesDialog::~CategoriesDialog() { delete ui; } int CategoriesDialog::exec() { GeometrySaver gs(Settings::instance(), this); return QDialog::exec(); } void CategoriesDialog::cellChanged(int row, int) { int currentID = ui->categoriesTable->item(row, 0)->text().toInt(); if (currentID > m_HighestID) { m_HighestID = currentID; } } void CategoriesDialog::commitChanges() { CategoryFactory& categories = CategoryFactory::instance(); categories.reset(); for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) { int index = ui->categoriesTable->verticalHeader()->logicalIndex(i); QVariantList nexusData; if (ui->categoriesTable->item(index, 3) != nullptr) nexusData = ui->categoriesTable->item(index, 3)->data(Qt::UserRole).toList(); std::vector nexusCats; for (auto nexusCat : nexusData) { nexusCats.push_back(CategoryFactory::NexusCategory( nexusCat.toList()[0].toString(), nexusCat.toList()[1].toInt())); } categories.addCategory(ui->categoriesTable->item(index, 0)->text().toInt(), ui->categoriesTable->item(index, 1)->text(), nexusCats, ui->categoriesTable->item(index, 2)->text().toInt()); } categories.setParents(); std::vector nexusCats; for (int i = 0; i < ui->nexusCategoryList->count(); ++i) { nexusCats.push_back(CategoryFactory::NexusCategory( ui->nexusCategoryList->item(i)->data(Qt::DisplayRole).toString(), ui->nexusCategoryList->item(i)->data(Qt::UserRole).toInt())); } categories.setNexusCategories(nexusCats); categories.saveCategories(); } void CategoriesDialog::refreshIDs() { m_HighestID = 0; for (int i = 0; i < ui->categoriesTable->rowCount(); ++i) { int id = ui->categoriesTable->item(i, 0)->text().toInt(); if (id > m_HighestID) { m_HighestID = id; } m_IDs.insert(id); } } void CategoriesDialog::fillTable() { CategoryFactory& categories = CategoryFactory::instance(); QTableWidget* table = ui->categoriesTable; QListWidget* list = ui->nexusCategoryList; table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed); table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch); table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed); table->horizontalHeader()->setSectionResizeMode(3, QHeaderView::Stretch); table->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); table->verticalHeader()->setVisible(false); table->setItemDelegateForColumn( 0, new ValidatingDelegate(this, new NewIDValidator(m_IDs))); table->setItemDelegateForColumn( 2, new ValidatingDelegate(this, new ExistingIDValidator(m_IDs))); table->setItemDelegateForColumn( 3, new ValidatingDelegate(this, new QRegularExpressionValidator( QRegularExpression("([0-9]+)?(,[0-9]+)*"), this))); int row = 0; for (const auto& category : categories.m_Categories) { if (category.ID() == 0) { --row; continue; } ++row; table->insertRow(row); QScopedPointer idItem(new QTableWidgetItem()); idItem->setData(Qt::DisplayRole, category.ID()); QScopedPointer nameItem(new QTableWidgetItem(category.name())); QScopedPointer parentIDItem(new QTableWidgetItem()); parentIDItem->setData(Qt::DisplayRole, category.parentID()); QScopedPointer nexusCatItem(new QTableWidgetItem()); table->setItem(row, 0, idItem.take()); table->setItem(row, 1, nameItem.take()); table->setItem(row, 2, parentIDItem.take()); table->setItem(row, 3, nexusCatItem.take()); } for (const auto& nexusCat : categories.m_NexusMap) { QScopedPointer nexusItem(new QListWidgetItem()); nexusItem->setData(Qt::DisplayRole, nexusCat.second.name()); nexusItem->setData(Qt::UserRole, nexusCat.second.ID()); list->addItem(nexusItem.take()); auto item = table->item(categories.resolveNexusID(nexusCat.first) - 1, 3); if (item != nullptr) { auto itemData = item->data(Qt::UserRole).toList(); QVariantList newData; newData.append(nexusCat.second.name()); newData.append(nexusCat.second.ID()); itemData.insert(itemData.length(), newData); QStringList names; for (auto cat : itemData) { names.append(cat.toList()[0].toString()); } item->setData(Qt::UserRole, itemData); item->setData(Qt::DisplayRole, names.join(", ")); } } table->setSortingEnabled(true); refreshIDs(); } void CategoriesDialog::addCategory_clicked() { int row = m_ContextRow >= 0 ? m_ContextRow : 0; ui->categoriesTable->setSortingEnabled(false); ui->categoriesTable->insertRow(row); QScopedPointer idItem(new QTableWidgetItem()); idItem->setData(Qt::DisplayRole, ++m_HighestID); QScopedPointer parentIDItem(new QTableWidgetItem()); parentIDItem->setData(Qt::DisplayRole, 0); ui->categoriesTable->setItem(row, 0, idItem.take()); ui->categoriesTable->setItem(row, 1, new QTableWidgetItem("new")); ui->categoriesTable->setItem(row, 2, parentIDItem.take()); ui->categoriesTable->setItem(row, 3, new QTableWidgetItem("")); ui->categoriesTable->setSortingEnabled(true); } void CategoriesDialog::removeCategory_clicked() { if (m_ContextRow >= 0) ui->categoriesTable->removeRow(m_ContextRow); } void CategoriesDialog::removeNexusMap_clicked() { if (m_ContextRow >= 0) { ui->categoriesTable->item(m_ContextRow, 3)->setData(Qt::UserRole, QVariantList()); ui->categoriesTable->item(m_ContextRow, 3)->setData(Qt::DisplayRole, QString()); } } void CategoriesDialog::nexusRefresh_clicked() { CategoryFactory::instance().refreshNexusCategories(this); } void CategoriesDialog::nexusImport_clicked() { auto importDialog = CategoryImportDialog(this); if (importDialog.exec() && importDialog.strategy()) { refreshIDs(); QTableWidget* table = ui->categoriesTable; QListWidget* list = ui->nexusCategoryList; if (importDialog.strategy() == CategoryImportDialog::Overwrite) { table->setRowCount(0); m_HighestID = 0; } int row = 0; table->setSortingEnabled(false); for (int i = 0; i < list->count(); ++i) { QString name = list->item(i)->data(Qt::DisplayRole).toString(); int nexusID = list->item(i)->data(Qt::UserRole).toInt(); QStringList nexusLabel; QVariantList nexusData; nexusLabel.append(name); QVariantList data; data.append(QVariant(name)); data.append(QVariant(nexusID)); nexusData.insert(nexusData.size(), data); QScopedPointer nexusCatItem( new QTableWidgetItem(nexusLabel.join(", "))); nexusCatItem->setData(Qt::UserRole, nexusData); if (!table->findItems(name, Qt::MatchExactly).size()) { row = table->rowCount(); table->insertRow(table->rowCount()); QScopedPointer idItem(new QTableWidgetItem()); idItem->setData(Qt::DisplayRole, ++m_HighestID); QScopedPointer nameItem(new QTableWidgetItem(name)); QScopedPointer parentIDItem(new QTableWidgetItem()); parentIDItem->setData(Qt::DisplayRole, 0); // No parent table->setItem(row, 0, idItem.take()); table->setItem(row, 1, nameItem.take()); table->setItem(row, 2, parentIDItem.take()); if (importDialog.assign()) { table->setItem(row, 3, nexusCatItem.take()); } } else { for (auto item : table->findItems(name, Qt::MatchContains | Qt::MatchWrap)) { if (item->column() == 1 && item->text() == name && importDialog.remap()) { table->setItem(item->row(), 3, nexusCatItem.take()); } else if (importDialog.remap()) { QScopedPointer blankItem(new QTableWidgetItem()); blankItem->setData(Qt::UserRole, QVariantList()); table->setItem(item->row(), 3, blankItem.get()); } } } } table->setSortingEnabled(true); refreshIDs(); } } void CategoriesDialog::nxmGameInfoAvailable(QString gameName, QVariant, QVariant resultData, int) { QVariantMap result = resultData.toMap(); QVariantList categories = result["categories"].toList(); CategoryFactory& catFactory = CategoryFactory::instance(); QListWidget* list = ui->nexusCategoryList; list->clear(); for (const auto& category : categories) { auto catMap = category.toMap(); QScopedPointer nexusItem(new QListWidgetItem()); nexusItem->setData(Qt::DisplayRole, catMap["name"].toString()); nexusItem->setData(Qt::UserRole, catMap["category_id"].toInt()); list->addItem(nexusItem.take()); } } void CategoriesDialog::nxmRequestFailed(QString, int, int, QVariant, int, int errorCode, const QString& errorMessage) { MessageDialog::showMessage( tr("Error %1: Request to Nexus failed: %2").arg(errorCode).arg(errorMessage), this); } void CategoriesDialog::on_categoriesTable_customContextMenuRequested(const QPoint& pos) { m_ContextRow = ui->categoriesTable->rowAt(pos.y()); QMenu menu; menu.addAction(tr("Add"), this, SLOT(addCategory_clicked())); menu.addAction(tr("Remove"), this, SLOT(removeCategory_clicked())); if (Settings::instance().nexus().categoryMappings()) { menu.addAction(tr("Remove Nexus Mapping(s)"), this, SLOT(removeNexusMap_clicked())); } menu.exec(ui->categoriesTable->mapToGlobal(pos)); }