summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ModOrganizer.pro2
-rw-r--r--src/baincomplexinstallerdialog.cpp124
-rw-r--r--src/baincomplexinstallerdialog.h95
-rw-r--r--src/categories.cpp23
-rw-r--r--src/categories.h335
-rw-r--r--src/fomodinstallerdialog.cpp959
-rw-r--r--src/fomodinstallerdialog.h214
-rw-r--r--src/icondelegate.cpp120
-rw-r--r--src/icondelegate.h62
-rw-r--r--src/installationmanager.cpp299
-rw-r--r--src/installationmanager.h16
-rw-r--r--src/installdialog.cpp308
-rw-r--r--src/installdialog.h127
-rw-r--r--src/mainwindow.cpp140
-rw-r--r--src/mainwindow.h6
-rw-r--r--src/mainwindow.ui24
-rw-r--r--src/modinfo.cpp37
-rw-r--r--src/modinfo.h9
-rw-r--r--src/modlist.cpp23
-rw-r--r--src/modlistsortproxy.cpp7
-rw-r--r--src/modlistsortproxy.h2
-rw-r--r--src/organizer.pro14
-rw-r--r--src/pluginlist.cpp14
-rw-r--r--src/profile.cpp75
-rw-r--r--src/profile.h15
-rw-r--r--src/profilesdialog.cpp82
-rw-r--r--src/profilesdialog.h2
-rw-r--r--src/profilesdialog.ui10
-rw-r--r--src/qtgroupingproxy.cpp897
-rw-r--r--src/qtgroupingproxy.h144
-rw-r--r--src/simpleinstalldialog.cpp55
-rw-r--r--src/simpleinstalldialog.h69
32 files changed, 1706 insertions, 2603 deletions
diff --git a/src/ModOrganizer.pro b/src/ModOrganizer.pro
index 8b6db27a..01c9c146 100644
--- a/src/ModOrganizer.pro
+++ b/src/ModOrganizer.pro
@@ -13,7 +13,7 @@ SUBDIRS = bsatk \
proxydll
hookdll.depends = shared
-organizer.depends = shared, uibase
+organizer.depends = shared, uibase, plugins
CONFIG(debug, debug|release) {
DESTDIR = outputd
diff --git a/src/baincomplexinstallerdialog.cpp b/src/baincomplexinstallerdialog.cpp
deleted file mode 100644
index f1440ff3..00000000
--- a/src/baincomplexinstallerdialog.cpp
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "baincomplexinstallerdialog.h"
-#include "textviewer.h"
-#include "ui_baincomplexinstallerdialog.h"
-
-#include <QDir>
-
-
-using namespace MOBase;
-
-
-BainComplexInstallerDialog::BainComplexInstallerDialog(DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent)
- : TutorableDialog("BainInstaller", parent), ui(new Ui::BainComplexInstallerDialog), m_Manual(false)
-{
- ui->setupUi(this);
-
- ui->nameEdit->setText(modName);
-
- for (DirectoryTree::const_node_iterator iter = tree->nodesBegin(); iter != tree->nodesEnd(); ++iter) {
- const QString &dirName = (*iter)->getData().name;
- if ((dirName.compare("fomod", Qt::CaseInsensitive) == 0) ||
- (dirName.startsWith("--"))) {
- continue;
- }
-
- QListWidgetItem *item = new QListWidgetItem(ui->optionsList);
- item->setText((*iter)->getData().name);
- item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
- item->setCheckState(item->text().mid(0, 2) == "00" ? Qt::Checked : Qt::Unchecked);
- item->setData(Qt::UserRole, qVariantFromValue((void*)(*iter)));
- ui->optionsList->addItem(item);
- }
-
- ui->packageBtn->setEnabled(hasPackageTXT);
-}
-
-
-BainComplexInstallerDialog::~BainComplexInstallerDialog()
-{
- delete ui;
-}
-
-
-QString BainComplexInstallerDialog::getName() const
-{
- return ui->nameEdit->text();
-}
-
-
-void BainComplexInstallerDialog::moveTreeUp(DirectoryTree *target, DirectoryTree::Node *child)
-{
- for (DirectoryTree::const_node_iterator iter = child->nodesBegin();
- iter != child->nodesEnd();) {
- target->addNode(*iter, true);
- iter = child->detach(iter);
- }
-
- for (DirectoryTree::const_leaf_reverse_iterator iter = child->leafsRBegin();
- iter != child->leafsREnd(); ++iter) {
- target->addLeaf(*iter);
- }
-}
-
-
-DirectoryTree *BainComplexInstallerDialog::updateTree(DirectoryTree *tree)
-{
- DirectoryTree *newTree = new DirectoryTree;
-
- for (DirectoryTree::const_node_reverse_iterator iter = tree->nodesRBegin();
- iter != tree->nodesREnd();) {
- QList<QListWidgetItem*> items = ui->optionsList->findItems((*iter)->getData().name, Qt::MatchFixedString);
- if ((items.count() == 1) && (items.at(0)->checkState() == Qt::Checked)) {
- moveTreeUp(newTree, *iter);
- }
- iter = tree->erase(iter);
- }
-
- return newTree;
-}
-
-
-void BainComplexInstallerDialog::on_okBtn_clicked()
-{
- this->accept();
-}
-
-
-void BainComplexInstallerDialog::on_cancelBtn_clicked()
-{
- this->reject();
-}
-
-
-void BainComplexInstallerDialog::on_manualBtn_clicked()
-{
- m_Manual = true;
- this->reject();
-}
-
-void BainComplexInstallerDialog::on_packageBtn_clicked()
-{
- TextViewer viewer("package.txt", this);
- viewer.setDescription("");
- viewer.addFile(QDir::tempPath().append("/package.txt"), false);
- viewer.exec();
-}
diff --git a/src/baincomplexinstallerdialog.h b/src/baincomplexinstallerdialog.h
deleted file mode 100644
index 136a4e54..00000000
--- a/src/baincomplexinstallerdialog.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef BAINCOMPLEXINSTALLERDIALOG_H
-#define BAINCOMPLEXINSTALLERDIALOG_H
-
-
-#include "mytree.h"
-#include "installdialog.h"
-#include "tutorabledialog.h"
-
-
-namespace Ui {
-class BainComplexInstallerDialog;
-}
-
-
-/**
- * @brief Dialog to choose from options offered by a (complex) bain package
- **/
-class BainComplexInstallerDialog : public MOBase::TutorableDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief Constructor
- *
- * @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer
- * @param modName proposed name for the mod. The dialog allows the user to change this
- * @param hasPackageTXT set to true if a package.txt is available for this archive. The file has to be unpacked to QDir::tempPath before displaying the dialog!
- * @param parent parent widget
- **/
- explicit BainComplexInstallerDialog(MOBase::DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent);
- ~BainComplexInstallerDialog();
-
- /**
- * @return bool true if the user requested the manual dialog
- **/
- bool manualRequested() const { return m_Manual; }
-
- /**
- * @return QString the (user-modified) name to be used for the mod
- **/
- QString getName() const;
-
- /**
- * @brief retrieve the updated archive tree from the dialog. The caller is responsible to delete the returned tree.
- *
- * @note This call is destructive on the input tree!
- *
- * @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
- * @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
- **/
- MOBase::DirectoryTree *updateTree(MOBase::DirectoryTree *tree);
-
-private slots:
-
- void on_manualBtn_clicked();
-
- void on_okBtn_clicked();
-
- void on_cancelBtn_clicked();
-
- void on_packageBtn_clicked();
-
-private:
-
- void moveTreeUp(MOBase::DirectoryTree *target, MOBase::DirectoryTree::Node *child);
-
-private:
-
- Ui::BainComplexInstallerDialog *ui;
-
- bool m_Manual;
-
-};
-
-#endif // BAINCOMPLEXINSTALLERDIALOG_H
diff --git a/src/categories.cpp b/src/categories.cpp
index f8bc3530..62ba3ca5 100644
--- a/src/categories.cpp
+++ b/src/categories.cpp
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QFile>
#include <QDir>
#include <QList>
+#include <QCoreApplication>
using namespace MOBase;
@@ -34,11 +35,17 @@ using namespace MOShared;
CategoryFactory* CategoryFactory::s_Instance = NULL;
+QString CategoryFactory::categoriesFilePath()
+{
+ return QCoreApplication::applicationDirPath() + "/categories.dat";
+}
+
+
CategoryFactory::CategoryFactory()
{
reset();
- QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
+ QFile categoryFile(categoriesFilePath());
if (!categoryFile.open(QIODevice::ReadOnly)) {
loadDefaultCategories();
@@ -120,7 +127,7 @@ void CategoryFactory::setParents()
void CategoryFactory::saveCategories()
{
- QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
+ QFile categoryFile(categoriesFilePath());
if (!categoryFile.open(QIODevice::WriteOnly)) {
reportError(QObject::tr("Failed to save custom categories"));
@@ -144,6 +151,18 @@ void CategoryFactory::saveCategories()
}
+unsigned int CategoryFactory::countCategories(std::tr1::function<bool (const Category &category)> filter)
+{
+ unsigned int result = 0;
+ for (auto iter = m_Categories.begin(); iter != m_Categories.end(); ++iter) {
+ if (filter(*iter)) {
+ ++result;
+ }
+ }
+ return result;
+}
+
+
void CategoryFactory::addCategory(int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
{
int index = m_Categories.size();
diff --git a/src/categories.h b/src/categories.h
index e6978259..8ec573b2 100644
--- a/src/categories.h
+++ b/src/categories.h
@@ -17,164 +17,177 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef CATEGORIES_H
-#define CATEGORIES_H
-
-
-#include <QString>
-#include <vector>
-#include <map>
-
-
-/**
- * @brief Manage the available mod categories
- * @warning member functions of this class currently use a wild mix of ids and indexes to look up categories,
- * optimized to where the request comes from. Therefore be very careful which of the two you have available
- **/
-class CategoryFactory {
-
- friend class CategoriesDialog;
-
-public:
-
- static const int CATEGORY_NONE = 0;
-
- static const int CATEGORY_SPECIAL_FIRST = 10000;
- static const int CATEGORY_SPECIAL_CHECKED = CATEGORY_SPECIAL_FIRST;
- static const int CATEGORY_SPECIAL_UNCHECKED = 10001;
- static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002;
- static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
- static const int CATEGORY_SPECIAL_CONFLICT = 10004;
-
-public:
-
- /**
- * @brief reset the list of categories
- **/
- void reset();
-
- /**
- * @brief save the categories to the categories.dat file
- **/
- void saveCategories();
-
- /**
- * @brief retrieve the number of available categories
- *
- * @return unsigned int number of categories
- **/
- unsigned numCategories() const { return m_Categories.size(); }
-
- /**
- * @brief get the id of the parent category
- *
- * @param index the index to look up
- * @return int id of the parent category
- **/
- int getParentID(unsigned int index) const;
-
- /**
- * @brief determine if a category exists (by id)
- *
- * @param id the id to check for existance
- * @return true if the category exists, false otherwise
- **/
- bool categoryExists(int id) const;
-
- /**
- * @brief test if a category is child of a second one
- * @param id the presumed child id
- * @param parentID the parent id to test for
- * @return true if id is a child of parentID
- **/
- bool isDecendantOf(int id, int parentID) const;
-
- /**
- * @brief test if the specified category has child categories
- *
- * @param index index of the category to look up
- * @return bool true if the category has child categories
- **/
- bool hasChildren(unsigned int index) const;
-
- /**
- * @brief retrieve the name of a category
- *
- * @param index index of the category to look up
- * @return QString name of the category
- **/
- QString getCategoryName(unsigned int index) const;
-
- /**
- * @brief look up the id of a category by its index
- *
- * @param index index of the category to look up
- * @return int id of the category
- **/
- int getCategoryID(unsigned int index) const;
-
- /**
- * @brief look up the index of a category by its id
- *
- * @param id index of the category to look up
- * @return unsigned int index of the category
- **/
- int getCategoryIndex(int ID) const;
-
- /**
- * @brief retrieve the index of a category by its nexus id
- *
- * @param nexusID nexus id of the category to look up
- * @return unsigned int index of the category or 0 if no category matches
- **/
- unsigned int resolveNexusID(int nexusID) const;
-
-public:
-
- /**
- * @brief retrieve a reference to the singleton instance
- *
- * @return the reference to the singleton
- **/
- static CategoryFactory &instance();
-
-private:
-
- struct Category {
- Category(int sortValue, int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
- : m_SortValue(sortValue), m_ID(id), m_Name(name), m_HasChildren(false),
- m_NexusIDs(nexusIDs), m_ParentID(parentID) {}
- int m_SortValue;
- int m_ID;
- int m_ParentID;
- bool m_HasChildren;
- QString m_Name;
- std::vector<int> m_NexusIDs;
-
- friend bool operator<(const Category &LHS, const Category &RHS) {
- return LHS.m_SortValue < RHS.m_SortValue;
- }
- };
-
-private:
-
- CategoryFactory();
-
- void loadDefaultCategories();
-
- void addCategory(int id, const QString &name, const std::vector<int> &nexusID, int parentID);
-
- void setParents();
-
-private:
-
- static CategoryFactory *s_Instance;
-
- std::vector<Category> m_Categories;
- std::map<int, unsigned int> m_IDMap;
- std::map<int, unsigned int> m_NexusMap;
-
-};
-
-
-#endif // CATEGORIES_H
+#ifndef CATEGORIES_H
+#define CATEGORIES_H
+
+
+#include <QString>
+#include <vector>
+#include <map>
+#include <functional>
+
+
+/**
+ * @brief Manage the available mod categories
+ * @warning member functions of this class currently use a wild mix of ids and indexes to look up categories,
+ * optimized to where the request comes from. Therefore be very careful which of the two you have available
+ **/
+class CategoryFactory {
+
+ friend class CategoriesDialog;
+
+public:
+
+ static const int CATEGORY_NONE = 0;
+
+ static const int CATEGORY_SPECIAL_FIRST = 10000;
+ static const int CATEGORY_SPECIAL_CHECKED = CATEGORY_SPECIAL_FIRST;
+ static const int CATEGORY_SPECIAL_UNCHECKED = 10001;
+ static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002;
+ static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
+ static const int CATEGORY_SPECIAL_CONFLICT = 10004;
+
+public:
+
+ struct Category {
+ Category(int sortValue, int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
+ : m_SortValue(sortValue), m_ID(id), m_Name(name), m_HasChildren(false),
+ m_NexusIDs(nexusIDs), m_ParentID(parentID) {}
+ int m_SortValue;
+ int m_ID;
+ int m_ParentID;
+ bool m_HasChildren;
+ QString m_Name;
+ std::vector<int> m_NexusIDs;
+
+ friend bool operator<(const Category &LHS, const Category &RHS) {
+ return LHS.m_SortValue < RHS.m_SortValue;
+ }
+ };
+
+public:
+
+ /**
+ * @brief reset the list of categories
+ **/
+ void reset();
+
+ /**
+ * @brief save the categories to the categories.dat file
+ **/
+ void saveCategories();
+
+ /**
+ * @brief retrieve the number of available categories
+ *
+ * @return unsigned int number of categories
+ **/
+ unsigned numCategories() const { return m_Categories.size(); }
+
+ /**
+ * @brief count all categories that match a specified filter
+ * @param filter the filter to test
+ * @return number of matching categories
+ */
+ unsigned int countCategories(std::tr1::function<bool (const Category &category)> filter);
+
+ /**
+ * @brief get the id of the parent category
+ *
+ * @param index the index to look up
+ * @return int id of the parent category
+ **/
+ int getParentID(unsigned int index) const;
+
+ /**
+ * @brief determine if a category exists (by id)
+ *
+ * @param id the id to check for existance
+ * @return true if the category exists, false otherwise
+ **/
+ bool categoryExists(int id) const;
+
+ /**
+ * @brief test if a category is child of a second one
+ * @param id the presumed child id
+ * @param parentID the parent id to test for
+ * @return true if id is a child of parentID
+ **/
+ bool isDecendantOf(int id, int parentID) const;
+
+ /**
+ * @brief test if the specified category has child categories
+ *
+ * @param index index of the category to look up
+ * @return bool true if the category has child categories
+ **/
+ bool hasChildren(unsigned int index) const;
+
+ /**
+ * @brief retrieve the name of a category
+ *
+ * @param index index of the category to look up
+ * @return QString name of the category
+ **/
+ QString getCategoryName(unsigned int index) const;
+
+ /**
+ * @brief look up the id of a category by its index
+ *
+ * @param index index of the category to look up
+ * @return int id of the category
+ **/
+ int getCategoryID(unsigned int index) const;
+
+ /**
+ * @brief look up the index of a category by its id
+ *
+ * @param id index of the category to look up
+ * @return unsigned int index of the category
+ **/
+ int getCategoryIndex(int ID) const;
+
+ /**
+ * @brief retrieve the index of a category by its nexus id
+ *
+ * @param nexusID nexus id of the category to look up
+ * @return unsigned int index of the category or 0 if no category matches
+ **/
+ unsigned int resolveNexusID(int nexusID) const;
+
+public:
+
+ /**
+ * @brief retrieve a reference to the singleton instance
+ *
+ * @return the reference to the singleton
+ **/
+ static CategoryFactory &instance();
+
+ /**
+ * @return path to the file that contains the categories list
+ */
+ static QString categoriesFilePath();
+
+private:
+
+ CategoryFactory();
+
+ void loadDefaultCategories();
+
+ void addCategory(int id, const QString &name, const std::vector<int> &nexusID, int parentID);
+
+ void setParents();
+
+private:
+
+ static CategoryFactory *s_Instance;
+
+ std::vector<Category> m_Categories;
+ std::map<int, unsigned int> m_IDMap;
+ std::map<int, unsigned int> m_NexusMap;
+
+};
+
+
+#endif // CATEGORIES_H
diff --git a/src/fomodinstallerdialog.cpp b/src/fomodinstallerdialog.cpp
deleted file mode 100644
index 97476092..00000000
--- a/src/fomodinstallerdialog.cpp
+++ /dev/null
@@ -1,959 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "fomodinstallerdialog.h"
-#include "report.h"
-#include "utility.h"
-#include "ui_fomodinstallerdialog.h"
-
-#include <QFile>
-#include <QDir>
-#include <QImage>
-#include <QCheckBox>
-#include <QRadioButton>
-#include <QScrollArea>
-#include <Shellapi.h>
-
-
-using namespace MOBase;
-
-
-bool ControlsAscending(QAbstractButton *LHS, QAbstractButton *RHS)
-{
- return LHS->text() < RHS->text();
-}
-
-
-bool ControlsDescending(QAbstractButton *LHS, QAbstractButton *RHS)
-{
- return LHS->text() > RHS->text();
-}
-
-
-bool PagesAscending(QGroupBox *LHS, QGroupBox *RHS)
-{
- return LHS->title() < RHS->title();
-}
-
-
-bool PagesDescending(QGroupBox *LHS, QGroupBox *RHS)
-{
- return LHS->title() > RHS->title();
-}
-
-
-FomodInstallerDialog::FomodInstallerDialog(const QString &modName, bool nameWasGuessed, const QString &fomodPath, QWidget *parent)
- : QDialog(parent), ui(new Ui::FomodInstallerDialog), m_NameWasGuessed(nameWasGuessed), m_FomodPath(fomodPath), m_Manual(false)
-{
- ui->setupUi(this);
- ui->nameEdit->setText(modName);
-}
-
-FomodInstallerDialog::~FomodInstallerDialog()
-{
- delete ui;
-}
-
-
-int FomodInstallerDialog::bomOffset(const QByteArray &buffer)
-{
- static const unsigned char BOM_UTF8[] = { 0xEF, 0xBB, 0xBF };
- static const unsigned char BOM_UTF16BE[] = { 0xFE, 0xFF };
- static const unsigned char BOM_UTF16LE[] = { 0xFF, 0xFE };
-
- if (buffer.startsWith(reinterpret_cast<const char*>(BOM_UTF8))) return 3;
- if (buffer.startsWith(reinterpret_cast<const char*>(BOM_UTF16BE)) ||
- buffer.startsWith(reinterpret_cast<const char*>(BOM_UTF16LE))) return 2;
-
- return 0;
-}
-
-#pragma message("implement module dependencies->file dependencies")
-
-void FomodInstallerDialog::initData()
-{
- { // parse provided package information
- QFile file(QDir::tempPath().append("/info.xml"));
- if (file.open(QIODevice::ReadOnly)) {
- // nmm allows files with wrong encoding and of course there are now files with broken
- // so, let's do as nmm does and ignore the standard. yay
- QByteArray header = file.readLine();
- if (strncmp(header.constData() + bomOffset(header), "<?", 2) != 0) {
- // not a header, rewind
- file.seek(0);
- }
- parseInfo(file.readAll());
- }
- file.close();
- }
-
- QImage screenshot(QDir::tempPath().append("/screenshot.png"));
- if (!screenshot.isNull()) {
- screenshot = screenshot.scaledToWidth(ui->screenshotLabel->width());
- ui->screenshotLabel->setPixmap(QPixmap::fromImage(screenshot));
- }
-
- { // parse xml installer file
- QFile file(QDir::tempPath().append("/ModuleConfig.xml"));
- if (!file.open(QIODevice::ReadOnly)) {
- throw MyException(tr("ModuleConfig.xml missing"));
- }
- // nmm allows files with wrong encoding and of course there are now files that are broken
- QByteArray header = file.readLine();
-
- if (strncmp(header.constData() + bomOffset(header), "<?", 2) != 0) {
- // not a header, rewind
- if (!file.seek(0)) {
- qCritical("failed to rewind file");
- }
- }
- parseModuleConfig(file.readAll());
- file.close();
- }
-}
-
-
-QString FomodInstallerDialog::getName() const
-{
- return ui->nameEdit->text();
-}
-
-
-void FomodInstallerDialog::moveTree(DirectoryTree::Node *target, DirectoryTree::Node *source)
-{
- for (DirectoryTree::const_node_iterator iter = source->nodesBegin(); iter != source->nodesEnd();) {
- target->addNode(*iter, true);
- iter = source->detach(iter);
- }
-
- for (DirectoryTree::const_leaf_reverse_iterator iter = source->leafsRBegin();
- iter != source->leafsREnd(); ++iter) {
- target->addLeaf(*iter);
- }
-}
-
-
-DirectoryTree::Node *FomodInstallerDialog::findNode(DirectoryTree::Node *node, const QString &path, bool create)
-{
- if (path.length() == 0) {
- return node;
- }
-
-// static QRegExp pathSeparator("[/\\]");
- int pos = path.indexOf('\\');
- if (pos == -1) {
- pos = path.indexOf('/');
- }
- QString subPath = path;
- if (pos > 0) {
- subPath = path.mid(0, pos);
- }
- for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
- if ((*iter)->getData().name.compare(subPath, Qt::CaseInsensitive) == 0) {
- if (pos <= 0) {
- return *iter;
- } else {
- return findNode(*iter, path.mid(pos + 1), create);
- }
- }
- }
- if (create) {
- DirectoryTree::Node *newNode = new DirectoryTree::Node;
- newNode->setData(subPath);
- node->addNode(newNode, false);
- if (pos <= 0) {
- return newNode;
- } else {
- return findNode(newNode, path.mid(pos + 1), create);
- }
- } else {
- throw MyException(QString("%1 not found in archive").arg(path));
- }
-}
-
-void FomodInstallerDialog::copyLeaf(DirectoryTree::Node *sourceTree, const QString &sourcePath,
- DirectoryTree::Node *destinationTree, const QString &destinationPath)
-{
- int sourceFileIndex = sourcePath.lastIndexOf('\\');
- if (sourceFileIndex == -1) {
- sourceFileIndex = sourcePath.lastIndexOf('/');
- if (sourceFileIndex == -1) {
- sourceFileIndex = 0;
- }
- }
- DirectoryTree::Node *sourceNode = sourceFileIndex == 0 ? sourceTree : findNode(sourceTree, sourcePath.mid(0, sourceFileIndex), false);
-
- int destinationFileIndex = destinationPath.lastIndexOf('\\');
- if (destinationFileIndex == -1) {
- destinationFileIndex = destinationPath.lastIndexOf('/');
- if (destinationFileIndex == -1) {
- destinationFileIndex = 0;
- }
- }
-
- DirectoryTree::Node *destinationNode =
- destinationFileIndex == 0 ? destinationTree
- : findNode(destinationTree, destinationPath.mid(0, destinationFileIndex), true);
-
- QString sourceName = sourcePath.mid((sourceFileIndex != 0) ? sourceFileIndex + 1 : 0);
- QString destinationName = (destinationFileIndex != 0) ? destinationPath.mid(destinationFileIndex + 1) : destinationPath;
- if (destinationName.length() == 0) {
- destinationName = sourceName;
- }
-
- bool found = false;
- for (DirectoryTree::const_leaf_reverse_iterator iter = sourceNode->leafsRBegin();
- iter != sourceNode->leafsREnd(); ++iter) {
- if (iter->getName().compare(sourceName, Qt::CaseInsensitive) == 0) {
- destinationNode->addLeaf(*iter);
- found = true;
- }
- }
- if (!found) {
- qCritical("%s not found!", sourceName.toUtf8().constData());
- }
-}
-
-
-void dumpTree(DirectoryTree::Node *node, int indent)
-{
- for (DirectoryTree::const_leaf_reverse_iterator iter = node->leafsRBegin();
- iter != node->leafsREnd(); ++iter) {
- qDebug("%.*s%s", indent, " ", iter->getName().toUtf8().constData());
- }
-
- for (DirectoryTree::const_node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
- qDebug("%.*s-- %s", indent, " ", (*iter)->getData().name.toUtf8().constData());
- dumpTree(*iter, indent + 2);
- }
-}
-
-
-bool FomodInstallerDialog::copyFileIterator(DirectoryTree *sourceTree, DirectoryTree *destinationTree, FileDescriptor *descriptor)
-{
- QString source = (m_FomodPath.length() != 0) ? m_FomodPath.mid(0).append("\\").append(descriptor->m_Source)
- : descriptor->m_Source;
- QString destination = descriptor->m_Destination;
- try {
- if (descriptor->m_IsFolder) {
- DirectoryTree::Node *sourceNode = findNode(sourceTree, source, false);
- DirectoryTree::Node *targetNode = findNode(destinationTree, destination, true);
- moveTree(targetNode, sourceNode);
- } else {
- copyLeaf(sourceTree, source, destinationTree, destination);
- }
- return true;
- } catch (const MyException &e) {
- qCritical("failed to extract %s to %s: %s",
- source.toUtf8().constData(), destination.toUtf8().constData(), e.what());
- return false;
- }
-}
-
-
-DirectoryTree *FomodInstallerDialog::updateTree(DirectoryTree *tree)
-{
- DirectoryTree *newTree = new DirectoryTree;
-
- for (std::vector<FileDescriptor*>::iterator iter = m_RequiredFiles.begin(); iter != m_RequiredFiles.end(); ++iter) {
- copyFileIterator(tree, newTree, *iter);
- }
-
- for (std::vector<ConditionalInstall>::iterator installIter = m_ConditionalInstalls.begin();
- installIter != m_ConditionalInstalls.end(); ++installIter) {
- bool match = installIter->m_Operator == ConditionalInstall::OP_AND;
- for (std::vector<Condition>::iterator conditionIter = installIter->m_Conditions.begin();
- conditionIter != installIter->m_Conditions.end(); ++conditionIter) {
- bool conditionMatches = testCondition(ui->stepsStack->count(), conditionIter->m_Name, conditionIter->m_Value);
- if (conditionMatches && (installIter->m_Operator == ConditionalInstall::OP_OR)) {
- match = true;
- break;
- } else if (!conditionMatches && (installIter->m_Operator == ConditionalInstall::OP_AND)) {
- match = false;
- break;
- }
- }
- if (match) {
- for (std::vector<FileDescriptor*>::iterator fileIter = installIter->m_Files.begin();
- fileIter != installIter->m_Files.end(); ++fileIter) {
- copyFileIterator(tree, newTree, *fileIter);
- }
- }
- }
-
- QList<QAbstractButton*> choices = ui->stepsStack->findChildren<QAbstractButton*>("choice");
- foreach (QAbstractButton* choice, choices) {
- if (choice->isChecked()) {
- QVariantList fileList = choice->property("files").toList();
- foreach (QVariant fileVariant, fileList) {
- copyFileIterator(tree, newTree, fileVariant.value<FileDescriptor*>());
- }
- }
- }
-
-// dumpTree(newTree, 0);
-
- return newTree;
-}
-
-
-void FomodInstallerDialog::highlightControl(QAbstractButton *button)
-{
- QVariant screenshotName = button->property("screenshot");
- if (screenshotName.isValid()) {
- QString screenshotFileName = screenshotName.toString();
- if (!screenshotFileName.isEmpty()) {
- QString temp = QFileInfo(screenshotFileName).fileName();
- QImage screenshot(QDir::tempPath().append("/").append(temp));
- if (screenshot.isNull()) {
- qWarning(">%s< is a null image", screenshotName.toString().toUtf8().constData());
- }
- screenshot = screenshot.scaledToWidth(ui->screenshotLabel->width());
- ui->screenshotLabel->setPixmap(QPixmap::fromImage(screenshot));
- } else {
- ui->screenshotLabel->setPixmap(QPixmap());
- }
- }
- ui->descriptionText->setText(button->property("description").toString());
-}
-
-
-bool FomodInstallerDialog::eventFilter(QObject *object, QEvent *event)
-{
- QAbstractButton *button = qobject_cast<QAbstractButton*>(object);
- if ((button != NULL) && (event->type() == QEvent::HoverEnter)) {
- highlightControl(button);
-
- }
- return QDialog::eventFilter(object, event);
-}
-
-
-QString FomodInstallerDialog::readContent(QXmlStreamReader &reader)
-{
- if (reader.readNext() == QXmlStreamReader::Characters) {
- return reader.text().toString();
- } else {
- return QString();
- }
-}
-
-
-void FomodInstallerDialog::parseInfo(const QByteArray &data)
-{
- QXmlStreamReader reader(data);
-/* while (reader.readNext() != QXmlStreamReader::StartDocument) {}
- QTextDecoder *decoder = QTextCodec::codecForName(reader.documentEncoding().toLocal8Bit())->makeDecoder(QTextCodec::ConvertInvalidToNull);
- QString test(decoder->toUnicode(data));
- qDebug("test: %d, %s", test.isNull(), test.toUtf8().constData());
-
- qDebug(">%s<", reader.documentEncoding().toUtf8().constData());*/
- while (!reader.atEnd() && !reader.hasError()) {
- switch (reader.readNext()) {
- case QXmlStreamReader::StartElement: {
- if (reader.name() == "Name") {
- if (ui->nameEdit->text().isEmpty() || m_NameWasGuessed) {
- ui->nameEdit->setText(readContent(reader));
- }
- } else if (reader.name() == "Author") {
- ui->authorLabel->setText(readContent(reader));
- } else if (reader.name() == "Version") {
- ui->versionLabel->setText(readContent(reader));
- } else if (reader.name() == "Website") {
- QString url = readContent(reader);
- ui->websiteLabel->setText(tr("<a href=\"%1\">Link</a>").arg(url));
- ui->websiteLabel->setToolTip(url);
- }
- } break;
- default: {} break;
- }
- }
- if (reader.hasError()) {
- throw MyException(tr("failed to parse info.xml: %1 (%2) (line %3, column %4)")
- .arg(reader.errorString())
- .arg(reader.error())
- .arg(reader.lineNumber())
- .arg(reader.columnNumber()));
-
- }
-}
-
-
-FomodInstallerDialog::ItemOrder FomodInstallerDialog::getItemOrder(const QString &orderString)
-{
- if (orderString == "Ascending") {
- return ORDER_ASCENDING;
- } else if (orderString == "Descending") {
- return ORDER_DESCENDING;
- } else if (orderString == "Explicit") {
- return ORDER_EXPLICIT;
- } else {
- throw MyException(tr("unsupported order type %1").arg(orderString));
- }
-}
-
-
-FomodInstallerDialog::GroupType FomodInstallerDialog::getGroupType(const QString &typeString)
-{
- if (typeString == "SelectAtLeastOne") {
- return TYPE_SELECTATLEASTONE;
- } else if (typeString == "SelectAtMostOne") {
- return TYPE_SELECTATMOSTONE;
- } else if (typeString == "SelectExactlyOne") {
- return TYPE_SELECTEXACTLYONE;
- } else if (typeString == "SelectAny") {
- return TYPE_SELECTANY;
- } else if (typeString == "SelectAll") {
- return TYPE_SELECTALL;
- } else {
- throw MyException(tr("unsupported group type %1").arg(typeString));
- }
-}
-
-
-FomodInstallerDialog::PluginType FomodInstallerDialog::getPluginType(const QString &typeString)
-{
- if (typeString == "Required") {
- return FomodInstallerDialog::TYPE_REQUIRED;
- } else if (typeString == "Optional") {
- return FomodInstallerDialog::TYPE_OPTIONAL;
- } else if (typeString == "Recommended") {
- return FomodInstallerDialog::TYPE_RECOMMENDED;
- } else if (typeString == "NotUsable") {
- return FomodInstallerDialog::TYPE_NOTUSABLE;
- } else if (typeString == "CouldBeUsable") {
- return FomodInstallerDialog::TYPE_COULDBEUSABLE;
- } else {
- qCritical("invalid plugin type %s", typeString.toUtf8().constData());
- return FomodInstallerDialog::TYPE_OPTIONAL;
- }
-}
-
-
-void FomodInstallerDialog::readFileList(QXmlStreamReader &reader, std::vector<FileDescriptor*> &fileList)
-{
- QStringRef openTag = reader.name();
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == openTag))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if ((reader.name() == "folder") ||
- (reader.name() == "file")) {
- QXmlStreamAttributes attributes = reader.attributes();
- FileDescriptor *file = new FileDescriptor(this);
- file->m_Source = attributes.value("source").toString();
- file->m_Destination = attributes.hasAttribute("destination") ? attributes.value("destination").toString()
- : file->m_Source;
- file->m_Priority = attributes.hasAttribute("priority") ? attributes.value("priority").string()->toInt()
- : 0;
- file->m_IsFolder = reader.name() == "folder";
- file->m_InstallIfUsable = attributes.hasAttribute("installIfUsable") ? (attributes.value("installIfUsable").compare("true") == 0)
- : false;
- file->m_AlwaysInstall = attributes.hasAttribute("alwaysInstall") ? (attributes.value("alwaysInstall").compare("true") == 0)
- : false;
-
- fileList.push_back(file);
- }
- }
- }
-}
-
-
-void FomodInstallerDialog::readPluginType(QXmlStreamReader &reader, Plugin &plugin)
-{
- plugin.m_Type = TYPE_OPTIONAL;
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "typeDescriptor"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "type") {
- plugin.m_Type = getPluginType(reader.attributes().value("name").toString());
- }
- }
- }
-}
-
-
-void FomodInstallerDialog::readConditionFlags(QXmlStreamReader &reader, Plugin &plugin)
-{
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "conditionFlags"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "flag") {
- QString name = reader.attributes().value("name").toString();
- plugin.m_Conditions.push_back(Condition(name, readContent(reader)));
- }
- }
- }
-}
-
-
-bool FomodInstallerDialog::byPriority(const FileDescriptor *LHS, const FileDescriptor *RHS)
-{
- return LHS->m_Priority < RHS->m_Priority;
-}
-
-
-FomodInstallerDialog::Plugin FomodInstallerDialog::readPlugin(QXmlStreamReader &reader)
-{
- Plugin result;
- result.m_Name = reader.attributes().value("name").toString();
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "plugin"))) {
-// QXmlStreamReader::TokenType type = reader.tokenType();
-// QString name = reader.name().toUtf8();
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "description") {
- result.m_Description = readContent(reader).trimmed();
- } else if (reader.name() == "image") {
- result.m_ImagePath = reader.attributes().value("path").toString();
- } else if (reader.name() == "typeDescriptor") {
- readPluginType(reader, result);
- } else if (reader.name() == "conditionFlags") {
- readConditionFlags(reader, result);
- } else if (reader.name() == "files") {
- readFileList(reader, result.m_Files);
- }
- }
- }
-
- std::sort(result.m_Files.begin(), result.m_Files.end(), byPriority);
-
- return result;
-}
-
-
-void FomodInstallerDialog::readPlugins(QXmlStreamReader &reader, GroupType groupType, QLayout *layout)
-{
- ItemOrder pluginOrder = reader.attributes().hasAttribute("order") ? getItemOrder(reader.attributes().value("order").toString())
- : ORDER_ASCENDING;
- bool first = true;
- bool maySelectMore = true;
-
- std::vector<QAbstractButton*> controls;
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "plugins"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "plugin") {
- Plugin plugin = readPlugin(reader);
- QAbstractButton *newControl = NULL;
- switch (groupType) {
- case TYPE_SELECTATLEASTONE:
- case TYPE_SELECTANY: {
- newControl = new QCheckBox(plugin.m_Name);
- } break;
- case TYPE_SELECTATMOSTONE: {
- newControl = new QRadioButton(plugin.m_Name);
- } break;
- case TYPE_SELECTEXACTLYONE: {
- newControl = new QRadioButton(plugin.m_Name);
- if (first) {
- newControl->setChecked(true);
- }
- } break;
- case TYPE_SELECTALL: {
- newControl = new QCheckBox(plugin.m_Name);
- newControl->setChecked(true);
- newControl->setEnabled(false);
- } break;
- }
- newControl->setObjectName("choice");
- switch (plugin.m_Type) {
- case TYPE_REQUIRED: {
- newControl->setChecked(true);
- newControl->setEnabled(false);
- newControl->setToolTip(tr("This component is required"));
- } break;
- case TYPE_RECOMMENDED: {
- if (maySelectMore) {
- newControl->setChecked(true);
- }
- newControl->setToolTip(tr("It is recommended you enable this component"));
- if ((groupType == TYPE_SELECTATMOSTONE) || (groupType == TYPE_SELECTEXACTLYONE)) {
- maySelectMore = false;
- }
- } break;
- case TYPE_OPTIONAL: {
- newControl->setToolTip(tr("Optional component"));
- } break;
- case TYPE_NOTUSABLE: {
- newControl->setChecked(false);
- newControl->setEnabled(false);
- newControl->setToolTip(tr("This component is not usable in combination with other installed plugins"));
- } break;
- case TYPE_COULDBEUSABLE: {
- newControl->setCheckable(true);
- newControl->setIcon(QIcon(":/new/guiresources/resources/dialog-warning_16.png"));
- newControl->setToolTip(tr("You may be experiencing instability in combination with other installed plugins"));
- } break;
- }
-
- newControl->setProperty("plugintype", plugin.m_Type);
- newControl->setProperty("screenshot", plugin.m_ImagePath);
- newControl->setProperty("description", plugin.m_Description);
- QVariantList fileList;
- for (std::vector<FileDescriptor*>::iterator iter = plugin.m_Files.begin(); iter != plugin.m_Files.end(); ++iter) {
- fileList.append(qVariantFromValue(*iter));
- }
- newControl->setProperty("files", fileList);
- QVariantList conditionFlags;
- for (std::vector<Condition>::const_iterator iter = plugin.m_Conditions.begin(); iter != plugin.m_Conditions.end(); ++iter) {
- if (iter->m_Name.length() != 0) {
- conditionFlags.append(qVariantFromValue(Condition(iter->m_Name, iter->m_Value)));
- }
- }
- newControl->setProperty("conditionFlags", conditionFlags);
- newControl->installEventFilter(this);
- controls.push_back(newControl);
- first = false;
- }
- }
- }
-
- if (pluginOrder == ORDER_ASCENDING) {
- std::sort(controls.begin(), controls.end(), ControlsAscending);
- } else if (pluginOrder == ORDER_DESCENDING) {
- std::sort(controls.begin(), controls.end(), ControlsDescending);
- }
-
- for (std::vector<QAbstractButton*>::const_iterator iter = controls.begin(); iter != controls.end(); ++iter) {
- layout->addWidget(*iter);
- }
-
- if (groupType == TYPE_SELECTATMOSTONE) {
- QRadioButton *newButton = new QRadioButton(tr("None"));
- if (maySelectMore) {
- newButton->setChecked(true);
- }
- layout->addWidget(newButton);
- }
-}
-
-
-void FomodInstallerDialog::readGroup(QXmlStreamReader &reader, QLayout *layout)
-{
- //FileGroup result;
- QString name = reader.attributes().value("name").toString();
- GroupType type = getGroupType(reader.attributes().value("type").toString());
-
- if (type == TYPE_SELECTATLEASTONE) {
- QLabel *label = new QLabel(tr("Select one or more of these options:"));
- layout->addWidget(label);
- }
-
- QGroupBox *groupBox = new QGroupBox(name);
-
- QVBoxLayout *groupLayout = new QVBoxLayout;
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "group"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "plugins") {
- readPlugins(reader, type, groupLayout);
- }
- }
- }
-
- groupBox->setLayout(groupLayout);
- layout->addWidget(groupBox);
-}
-
-
-void FomodInstallerDialog::readGroups(QXmlStreamReader &reader, QLayout *layout)
-{
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "optionalFileGroups"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "group") {
- readGroup(reader, layout);
- }
- }
- }
-}
-
-
-void FomodInstallerDialog::readVisible(QXmlStreamReader &reader, QVariantList &conditions)
-{
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "visible"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "flagDependency") {
- Condition condition(reader.attributes().value("flag").toString(),
- reader.attributes().value("value").toString());
- conditions.append(qVariantFromValue(condition));
- }
- }
- }
-}
-
-QGroupBox *FomodInstallerDialog::readInstallerStep(QXmlStreamReader &reader)
-{
- QString name = reader.attributes().value("name").toString();
- QGroupBox *page = new QGroupBox(name);
- QVBoxLayout *pageLayout = new QVBoxLayout;
- QScrollArea *scrollArea = new QScrollArea;
- QFrame *scrolledArea = new QFrame;
- QVBoxLayout *scrollLayout = new QVBoxLayout;
-
- QVariantList conditions;
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "installStep"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "optionalFileGroups") {
- readGroups(reader, scrollLayout);
- } else if (reader.name() == "visible") {
- readVisible(reader, conditions);
- }
- }
- }
- if (conditions.length() != 0) {
- page->setProperty("conditions", conditions);
- }
-
- scrolledArea->setLayout(scrollLayout);
- scrollArea->setWidget(scrolledArea);
- scrollArea->setWidgetResizable(true);
- pageLayout->addWidget(scrollArea);
- page->setLayout(pageLayout);
- return page;
-}
-
-
-void FomodInstallerDialog::readInstallerSteps(QXmlStreamReader &reader)
-{
- ItemOrder stepOrder = reader.attributes().hasAttribute("order") ? getItemOrder(reader.attributes().value("order").toString())
- : ORDER_ASCENDING;
-
- std::vector<QGroupBox*> pages;
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "installSteps"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "installStep") {
- pages.push_back(readInstallerStep(reader));
- }
- }
- }
-
- if (stepOrder == ORDER_ASCENDING) {
- std::sort(pages.begin(), pages.end(), PagesAscending);
- } else if (stepOrder == ORDER_DESCENDING) {
- std::sort(pages.begin(), pages.end(), PagesDescending);
- }
-
- for (std::vector<QGroupBox*>::const_iterator iter = pages.begin(); iter != pages.end(); ++iter) {
- ui->stepsStack->addWidget(*iter);
- }
-}
-
-
-FomodInstallerDialog::ConditionalInstall FomodInstallerDialog::readConditionalPattern(QXmlStreamReader &reader)
-{
- ConditionalInstall result;
- result.m_Operator = ConditionalInstall::OP_AND;
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "pattern"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "dependencies") {
- QStringRef dependencyOperator = reader.attributes().value("operator");
- if (dependencyOperator == "And") {
- result.m_Operator = ConditionalInstall::OP_AND;
- } else if (dependencyOperator == "Or") {
- result.m_Operator = ConditionalInstall::OP_OR;
- } // otherwise operator is not set (which we can ignore) or invalid (which we should report actually)
-
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "dependencies"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "flagDependency") {
- result.m_Conditions.push_back(Condition(reader.attributes().value("flag").toString(),
- reader.attributes().value("value").toString()));
- }
- }
- }
- } else if (reader.name() == "files") {
- readFileList(reader, result.m_Files);
- }
- }
- }
- return result;
-}
-
-
-void FomodInstallerDialog::readConditionalFileInstalls(QXmlStreamReader &reader)
-{
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "conditionalFileInstalls"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "patterns") {
- while (!((reader.readNext() == QXmlStreamReader::EndElement) &&
- (reader.name() == "patterns"))) {
- if (reader.tokenType() == QXmlStreamReader::StartElement) {
- if (reader.name() == "pattern") {
- m_ConditionalInstalls.push_back(readConditionalPattern(reader));
- }
- }
- }
- }
- }
- }
-}
-
-
-void FomodInstallerDialog::parseModuleConfig(const QByteArray &data)
-{
- QXmlStreamReader reader(data);
- while (!reader.atEnd() && !reader.hasError()) {
- switch (reader.readNext()) {
- case QXmlStreamReader::StartElement: {
- if (reader.name() == "installSteps") {
- readInstallerSteps(reader);
- } else if (reader.name() == "requiredInstallFiles") {
- readFileList(reader, m_RequiredFiles);
- } else if (reader.name() == "conditionalFileInstalls") {
- readConditionalFileInstalls(reader);
- }
- } break;
- default: {} break;
- }
- }
- if (reader.hasError()) {
- reportError(tr("failed to parse ModuleConfig.xml: %1 - %2").arg(reader.errorString()).arg(reader.lineNumber()));
- }
- activateCurrentPage();
-}
-
-
-void FomodInstallerDialog::on_manualBtn_clicked()
-{
- m_Manual = true;
- this->reject();
-}
-
-void FomodInstallerDialog::on_cancelBtn_clicked()
-{
- this->reject();
-}
-
-
-void FomodInstallerDialog::on_websiteLabel_linkActivated(const QString &link)
-{
- ::ShellExecuteW(NULL, L"open", ToWString(link).c_str(), NULL, NULL, SW_SHOWNORMAL);
-}
-
-
-void FomodInstallerDialog::activateCurrentPage()
-{
- QList<QAbstractButton*> choices = ui->stepsStack->currentWidget()->findChildren<QAbstractButton*>("choice");
- if (choices.count() > 0) {
- highlightControl(choices.at(0));
- }
-}
-
-
-bool FomodInstallerDialog::testCondition(int maxIndex, const QString &flag, const QString &value)
-{
- // iterate through all set condition flags on all activated controls on all visible pages if one of them matches the condition
- for (int i = 0; i < maxIndex; ++i) {
- if (testVisible(i)) {
- QWidget *page = ui->stepsStack->widget(i);
- QList<QAbstractButton*> choices = page->findChildren<QAbstractButton*>("choice");
- foreach (QAbstractButton* choice, choices) {
- if (choice->isChecked()) {
- QVariant temp = choice->property("conditionFlags");
- if (temp.isValid()) {
- QVariantList conditionFlags = temp.toList();
- for (QVariantList::const_iterator iter = conditionFlags.begin(); iter != conditionFlags.end(); ++iter) {
- Condition condition = iter->value<Condition>();
- if ((condition.m_Name == flag) && (condition.m_Value == value)) {
- return true;
- }
- }
- }
- }
- }
- }
- }
- return false;
-}
-
-
-bool FomodInstallerDialog::testVisible(int pageIndex)
-{
- QWidget *page = ui->stepsStack->widget(pageIndex);
- QVariant temp = page->property("conditions");
- if (temp.isValid()) {
- QVariantList conditions = temp.toList();
- for (QVariantList::const_iterator iter = conditions.begin(); iter != conditions.end(); ++iter) {
- Condition condition = iter->value<Condition>();
- if (!testCondition(pageIndex, condition.m_Name, condition.m_Value)) {
- return false;
- }
- }
- return true;
- } else {
- return true;
- }
-}
-
-
-bool FomodInstallerDialog::nextPage()
-{
- int index = ui->stepsStack->currentIndex() + 1;
- while (index < ui->stepsStack->count()) {
- if (testVisible(index)) {
- ui->stepsStack->setCurrentIndex(index);
- return true;
- }
- ++index;
- }
- // no more visible pages -> install
- return false;
-}
-
-
-void FomodInstallerDialog::on_nextBtn_clicked()
-{
- if (ui->stepsStack->currentIndex() == ui->stepsStack->count() - 1) {
- this->accept();
- } else {
- if (nextPage()) {
- if (ui->stepsStack->currentIndex() == ui->stepsStack->count() - 1) {
- ui->nextBtn->setText(tr("Install"));
- }
- ui->prevBtn->setEnabled(true);
- activateCurrentPage();
- } else {
- this->accept();
- }
- }
-}
-
-void FomodInstallerDialog::on_prevBtn_clicked()
-{
- if (ui->stepsStack->currentIndex() != 0) {
- ui->stepsStack->setCurrentIndex(ui->stepsStack->currentIndex() - 1);
- ui->nextBtn->setText(tr("Next"));
- }
- if (ui->stepsStack->currentIndex() == 0) {
- ui->prevBtn->setEnabled(false);
- }
- activateCurrentPage();
-}
diff --git a/src/fomodinstallerdialog.h b/src/fomodinstallerdialog.h
deleted file mode 100644
index be4e34b2..00000000
--- a/src/fomodinstallerdialog.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef FOMODINSTALLERDIALOG_H
-#define FOMODINSTALLERDIALOG_H
-
-#include "installdialog.h"
-#include <QDialog>
-#include <QAbstractButton>
-#include <QXmlStreamReader>
-#include <QGroupBox>
-#include <QSharedPointer>
-
-namespace Ui {
-class FomodInstallerDialog;
-}
-
-
-class Condition : public QObject {
- Q_OBJECT
-public:
- Condition(QObject *parent = NULL) : QObject(parent) { }
- Condition(const Condition &reference) : QObject(reference.parent()), m_Name(reference.m_Name), m_Value(reference.m_Value) { }
- Condition(const QString &name, const QString &value) : QObject(), m_Name(name), m_Value(value) { }
- QString m_Name;
- QString m_Value;
-private:
- Condition &operator=(const Condition&);
-};
-
-Q_DECLARE_METATYPE(Condition)
-
-
-class FileDescriptor : public QObject {
- Q_OBJECT
-public:
- FileDescriptor(QObject *parent)
- : QObject(parent), m_Source(), m_Destination(), m_Priority(0), m_IsFolder(false), m_AlwaysInstall(false),
- m_InstallIfUsable(false) {}
- FileDescriptor(const FileDescriptor &reference)
- : QObject(reference.parent()), m_Source(reference.m_Source), m_Destination(reference.m_Destination),
- m_Priority(reference.m_Priority), m_IsFolder(reference.m_IsFolder), m_AlwaysInstall(reference.m_AlwaysInstall),
- m_InstallIfUsable(reference.m_InstallIfUsable) {}
- QString m_Source;
- QString m_Destination;
- int m_Priority;
- bool m_IsFolder;
- bool m_AlwaysInstall;
- bool m_InstallIfUsable;
-private:
- FileDescriptor &operator=(const FileDescriptor&);
-};
-
-Q_DECLARE_METATYPE(FileDescriptor*)
-
-
-class FomodInstallerDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- explicit FomodInstallerDialog(const QString &modName, bool nameWasGuessed, const QString &fomodPath, QWidget *parent = 0);
- ~FomodInstallerDialog();
-
- void initData();
-
- /**
- * @return bool true if the user requested the manual dialog
- **/
- bool manualRequested() const { return m_Manual; }
-
- /**
- * @return QString the (user-modified) name to be used for the mod
- **/
- QString getName() const;
-
- /**
- * @brief retrieve the updated archive tree from the dialog. The caller is responsible to delete the returned tree.
- *
- * @note This call is destructive on the input tree!
- *
- * @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
- * @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
- **/
- MOBase::DirectoryTree *updateTree(MOBase::DirectoryTree *tree);
-
-protected:
-
- virtual bool eventFilter(QObject *object, QEvent *event);
-
-private slots:
-
- void on_cancelBtn_clicked();
-
- void on_manualBtn_clicked();
-
- void on_websiteLabel_linkActivated(const QString &link);
-
- void on_nextBtn_clicked();
-
- void on_prevBtn_clicked();
-
-private:
-
- enum ItemOrder {
- ORDER_ASCENDING,
- ORDER_DESCENDING,
- ORDER_EXPLICIT
- };
-
- enum GroupType {
- TYPE_SELECTATLEASTONE,
- TYPE_SELECTATMOSTONE,
- TYPE_SELECTEXACTLYONE,
- TYPE_SELECTANY,
- TYPE_SELECTALL
- };
-
- enum PluginType {
- TYPE_REQUIRED,
- TYPE_RECOMMENDED,
- TYPE_OPTIONAL,
- TYPE_NOTUSABLE,
- TYPE_COULDBEUSABLE
- };
-
- struct Plugin {
- QString m_Name;
- QString m_Description;
- QString m_ImagePath;
- PluginType m_Type;
- std::vector<Condition> m_Conditions;
- std::vector<FileDescriptor*> m_Files;
- };
-
- struct ConditionalInstall {
- enum {
- OP_AND,
- OP_OR
- } m_Operator;
- std::vector<Condition> m_Conditions;
- std::vector<FileDescriptor*> m_Files;
- };
-
-private:
-
- static int bomOffset(const QByteArray &buffer);
-
- QString readContent(QXmlStreamReader &reader);
- void parseInfo(const QByteArray &data);
-
- static ItemOrder getItemOrder(const QString &orderString);
- static GroupType getGroupType(const QString &typeString);
- static PluginType getPluginType(const QString &typeString);
- static bool byPriority(const FileDescriptor *LHS, const FileDescriptor *RHS);
-
- bool copyFileIterator(MOBase::DirectoryTree *sourceTree, MOBase::DirectoryTree *destinationTree, FileDescriptor *descriptor);
- void readFileList(QXmlStreamReader &reader, std::vector<FileDescriptor*> &fileList);
- void readPluginType(QXmlStreamReader &reader, Plugin &plugin);
- void readConditionFlags(QXmlStreamReader &reader, Plugin &plugin);
- FomodInstallerDialog::Plugin readPlugin(QXmlStreamReader &reader);
- void readPlugins(QXmlStreamReader &reader, GroupType groupType, QLayout *layout);
- void readGroup(QXmlStreamReader &reader, QLayout *layout);
- void readGroups(QXmlStreamReader &reader, QLayout *layout);
- void readVisible(QXmlStreamReader &reader, QVariantList &conditions);
- QGroupBox *readInstallerStep(QXmlStreamReader &reader);
- ConditionalInstall readConditionalPattern(QXmlStreamReader &reader);
- void readConditionalFileInstalls(QXmlStreamReader &reader);
- void readInstallerSteps(QXmlStreamReader &reader);
- void parseModuleConfig(const QByteArray &data);
- void highlightControl(QAbstractButton *button);
-
- bool testCondition(int maxIndex, const QString &flag, const QString &value);
- bool testVisible(int pageIndex);
- bool nextPage();
- void activateCurrentPage();
- void moveTree(MOBase::DirectoryTree::Node *target, MOBase::DirectoryTree::Node *source);
- MOBase::DirectoryTree::Node *findNode(MOBase::DirectoryTree::Node *node, const QString &path, bool create);
- void copyLeaf(MOBase::DirectoryTree::Node *sourceTree, const QString &sourcePath,
- MOBase::DirectoryTree::Node *destinationTree, const QString &destinationPath);
-
-private:
-
- Ui::FomodInstallerDialog *ui;
-
- bool m_NameWasGuessed;
-
- QString m_FomodPath;
- bool m_Manual;
-
-// ItemOrder m_StepOrder;
-// std::vector<InstallationStep> m_Steps;
- std::vector<FileDescriptor*> m_RequiredFiles;
- std::vector<ConditionalInstall> m_ConditionalInstalls;
-
-};
-
-#endif // FOMODINSTALLERDIALOG_H
diff --git a/src/icondelegate.cpp b/src/icondelegate.cpp
index 859311fc..2540e1d5 100644
--- a/src/icondelegate.cpp
+++ b/src/icondelegate.cpp
@@ -17,61 +17,65 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "icondelegate.h"
-#include <QHBoxLayout>
-#include <QLabel>
-#include <QPainter>
-
-
-IconDelegate::IconDelegate(QAbstractProxyModel *proxyModel, QObject *parent)
- : QStyledItemDelegate(parent), m_ProxyModel(proxyModel)
-{
-}
-
-
-QIcon IconDelegate::getFlagIcon(ModInfo::EFlag flag) const
-{
- switch (flag) {
- case ModInfo::FLAG_BACKUP: return QIcon(":/MO/gui/emblem_backup");
- case ModInfo::FLAG_INVALID: return QIcon(":/MO/gui/emblem_problem");
- case ModInfo::FLAG_NOTENDORSED: return QIcon(":/MO/gui/emblem_notendorsed");
- case ModInfo::FLAG_NOTES: return QIcon(":/MO/gui/emblem_notes");
- case ModInfo::FLAG_CONFLICT_OVERWRITE: return QIcon(":/MO/gui/emblem_conflict_overwrite");
- case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return QIcon(":/MO/gui/emblem_conflict_overwritten");
- case ModInfo::FLAG_CONFLICT_MIXED: return QIcon(":/MO/gui/emblem_conflict_mixed");
- case ModInfo::FLAG_CONFLICT_REDUNDANT: return QIcon(":MO/gui/emblem_conflict_redundant");
- default: return QIcon();
- }
-}
-
-
-void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
-{
- QStyledItemDelegate::paint(painter, option, index);
- ModInfo::Ptr info = ModInfo::getByIndex(m_ProxyModel->mapToSource(index).row());
- std::vector<ModInfo::EFlag> flags = info->getFlags();
-
- int x = 4;
- painter->save();
- painter->translate(option.rect.topLeft());
- for (auto iter = flags.begin(); iter != flags.end(); ++iter) {
- QIcon temp = getFlagIcon(*iter);
- painter->drawPixmap(x, 2, 16, 16, temp.pixmap(QSize(16, 16)));
- x += 20;
- }
-
- painter->restore();
-}
-
-
-QSize IconDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex &modelIndex) const
-{
- unsigned int index = m_ProxyModel->mapToSource(modelIndex).row();
- if (index < ModInfo::getNumMods()) {
- ModInfo::Ptr info = ModInfo::getByIndex(index);
- return QSize(info->getFlags().size() * 20, 20);
- } else {
- return QSize(1, 20);
- }
-}
-
+#include "icondelegate.h"
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPainter>
+
+
+IconDelegate::IconDelegate(QObject *parent)
+ : QStyledItemDelegate(parent)
+{
+}
+
+
+QIcon IconDelegate::getFlagIcon(ModInfo::EFlag flag) const
+{
+ switch (flag) {
+ case ModInfo::FLAG_BACKUP: return QIcon(":/MO/gui/emblem_backup");
+ case ModInfo::FLAG_INVALID: return QIcon(":/MO/gui/emblem_problem");
+ case ModInfo::FLAG_NOTENDORSED: return QIcon(":/MO/gui/emblem_notendorsed");
+ case ModInfo::FLAG_NOTES: return QIcon(":/MO/gui/emblem_notes");
+ case ModInfo::FLAG_CONFLICT_OVERWRITE: return QIcon(":/MO/gui/emblem_conflict_overwrite");
+ case ModInfo::FLAG_CONFLICT_OVERWRITTEN: return QIcon(":/MO/gui/emblem_conflict_overwritten");
+ case ModInfo::FLAG_CONFLICT_MIXED: return QIcon(":/MO/gui/emblem_conflict_mixed");
+ case ModInfo::FLAG_CONFLICT_REDUNDANT: return QIcon(":MO/gui/emblem_conflict_redundant");
+ default: return QIcon();
+ }
+}
+
+
+void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ QStyledItemDelegate::paint(painter, option, index);
+ QVariant modid = index.data(Qt::UserRole + 1);
+ if (!modid.isValid()) {
+ return;
+ }
+ ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt());
+ std::vector<ModInfo::EFlag> flags = info->getFlags();
+
+ int x = 4;
+ painter->save();
+ painter->translate(option.rect.topLeft());
+ for (auto iter = flags.begin(); iter != flags.end(); ++iter) {
+ QIcon temp = getFlagIcon(*iter);
+ painter->drawPixmap(x, 2, 16, 16, temp.pixmap(QSize(16, 16)));
+ x += 20;
+ }
+
+ painter->restore();
+}
+
+
+QSize IconDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex &modelIndex) const
+{
+ unsigned int index = modelIndex.data(Qt::UserRole + 1).toInt();
+ if (index < ModInfo::getNumMods()) {
+ ModInfo::Ptr info = ModInfo::getByIndex(index);
+ return QSize(info->getFlags().size() * 20, 20);
+ } else {
+ return QSize(1, 20);
+ }
+}
+
diff --git a/src/icondelegate.h b/src/icondelegate.h
index de49d810..dd9f9dfc 100644
--- a/src/icondelegate.h
+++ b/src/icondelegate.h
@@ -17,35 +17,33 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef ICONDELEGATE_H
-#define ICONDELEGATE_H
-
-#include "modinfo.h"
-#include <QStyledItemDelegate>
-#include <QAbstractProxyModel>
-
-
-class IconDelegate : public QStyledItemDelegate
-{
- Q_OBJECT
-public:
-
- explicit IconDelegate(QAbstractProxyModel *proxyModel, QObject *parent = 0);
-
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
- virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
-signals:
-
-public slots:
-
-private:
-
- QIcon getFlagIcon(ModInfo::EFlag flag) const;
-
-private:
-
- QAbstractProxyModel *m_ProxyModel;
-
-};
-
-#endif // ICONDELEGATE_H
+#ifndef ICONDELEGATE_H
+#define ICONDELEGATE_H
+
+#include "modinfo.h"
+#include <QStyledItemDelegate>
+#include <QAbstractProxyModel>
+
+
+class IconDelegate : public QStyledItemDelegate
+{
+ Q_OBJECT
+public:
+
+ explicit IconDelegate(QObject *parent = 0);
+
+ virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
+signals:
+
+public slots:
+
+private:
+
+ QIcon getFlagIcon(ModInfo::EFlag flag) const;
+
+private:
+
+};
+
+#endif // ICONDELEGATE_H
diff --git a/src/installationmanager.cpp b/src/installationmanager.cpp
index d92cae15..96d3743f 100644
--- a/src/installationmanager.cpp
+++ b/src/installationmanager.cpp
@@ -20,10 +20,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "installationmanager.h"
#include "utility.h"
-#include "installdialog.h"
-#include "simpleinstalldialog.h"
-#include "baincomplexinstallerdialog.h"
-#include "fomodinstallerdialog.h"
+
#include "report.h"
#include "categories.h"
#include "questionboxmemory.h"
@@ -268,11 +265,11 @@ QStringList InstallationManager::extractFiles(const QStringList &filesOrig)
return result;
}
-IPluginInstaller::EInstallResult InstallationManager::installArchive(const QString &modName, const QString &archiveName)
+IPluginInstaller::EInstallResult InstallationManager::installArchive(GuessedValue<QString> &modName, const QString &archiveName)
{
- QString temp = modName;
+ GuessedValue<QString> temp(modName);
bool iniTweaks;
- if (install(archiveName, "", "modsdir", false, true, temp, iniTweaks)) {
+ if (install(archiveName, "modsdir", temp, iniTweaks)) {
return IPluginInstaller::RESULT_SUCCESS;
} else {
return IPluginInstaller::RESULT_FAILED;
@@ -484,7 +481,7 @@ QString InstallationManager::generateBackupName(const QString &directoryName)
}
-bool InstallationManager::testOverwrite(const QString &modsDirectory, QString &modName)
+bool InstallationManager::testOverwrite(const QString &modsDirectory, GuessedValue<QString> &modName)
{
QString targetDirectory = QDir::fromNativeSeparators(modsDirectory.mid(0).append("\\").append(modName));
@@ -503,7 +500,7 @@ bool InstallationManager::testOverwrite(const QString &modsDirectory, QString &m
QString name = QInputDialog::getText(m_ParentWidget, tr("Mod Name"), tr("Name"),
QLineEdit::Normal, modName, &ok);
if (ok && !name.isEmpty()) {
- modName = name;
+ modName.update(name, GUESS_USER);
if (!ensureValidModName(modName)) {
return false;
}
@@ -547,45 +544,48 @@ bool InstallationManager::testOverwrite(const QString &modsDirectory, QString &m
return true;
}
-
-void InstallationManager::fixModName(QString &name)
+/*
+bool InstallationManager::fixModName(QString &name)
{
-// name = name.remove("^[ ]*").trimmed();
- name = name.simplified();
- while (name.endsWith('.')) name.chop(1);
-
- name.replace(QRegExp("[<>:\"/\\|?*]"), "");
+ QString temp = name.simplified();
+ while (temp.endsWith('.')) temp.chop(1);
+ temp.replace(QRegExp("[<>:\"/\\|?*]"), "");
static QString invalidNames[] = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
for (int i = 0; i < sizeof(invalidNames) / sizeof(QString); ++i) {
- if (name == invalidNames[i]) {
- name = "";
+ if (temp == invalidNames[i]) {
+ temp = "";
break;
}
}
-}
+ if (temp.length() > 1) {
+ name = temp;
+ return true;
+ } else {
+ return false;
+ }
+}
+*/
-bool InstallationManager::ensureValidModName(QString &name)
+bool InstallationManager::ensureValidModName(GuessedValue<QString> &name)
{
- fixModName(name);
-
- while (name.isEmpty()) {
+ while (name->isEmpty()) {
bool ok;
- name = QInputDialog::getText(m_ParentWidget, tr("Invalid name"),
- tr("The name you entered is invalid, please enter a different one."),
- QLineEdit::Normal, "", &ok);
+ name.update(QInputDialog::getText(m_ParentWidget, tr("Invalid name"),
+ tr("The name you entered is invalid, please enter a different one."),
+ QLineEdit::Normal, "", &ok),
+ GUESS_USER);
if (!ok) {
return false;
}
- fixModName(name);
}
return true;
}
-bool InstallationManager::doInstall(const QString &modsDirectory, QString &modName, int modID,
+bool InstallationManager::doInstall(const QString &modsDirectory, GuessedValue<QString> &modName, int modID,
const QString &version, const QString &newestVersion, int categoryID)
{
if (!ensureValidModName(modName)) {
@@ -906,7 +906,7 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
errorOccured = true;
}
} // if it's a directory and the target exists that isn't really a problem
- // TODO: use shellRename?
+
if (!QFile::rename(fileInfo.absoluteFilePath(), newName)) {
// moving doesn't work when merging
if (!copyDir(fileInfo.absoluteFilePath(), newName, true)) {
@@ -924,7 +924,7 @@ bool InstallationManager::installFomodExternal(const QString &fileName, const QS
}
QString dataDir = modDirectory.mid(0).append("/Data");
- if (!shellDelete(QStringList(dataDir), NULL)) {
+ if (!removeDir(dataDir)) {
qCritical("failed to remove data directory from %s", dataDir.toUtf8().constData());
errorOccured = true;
}
@@ -953,8 +953,8 @@ bool InstallationManager::wasCancelled()
}
-bool InstallationManager::install(const QString &fileName, const QString &pluginsFileName, const QString &modsDirectory,
- bool preferIntegrated, bool enableQuickInstall, QString &modName, bool &hasIniTweaks)
+bool InstallationManager::install(const QString &fileName, const QString &modsDirectory,
+ GuessedValue<QString> &modName, bool &hasIniTweaks)
{
QFileInfo fileInfo(fileName);
bool success = false;
@@ -963,6 +963,8 @@ bool InstallationManager::install(const QString &fileName, const QString &plugin
return false;
}
+ modName.setFilter(&fixDirectoryName);
+
// read out meta information from the download if available
int modID = 0;
QString version = "";
@@ -974,14 +976,9 @@ bool InstallationManager::install(const QString &fileName, const QString &plugin
if (QFile(metaName).exists()) {
QSettings metaFile(metaName, QSettings::IniFormat);
modID = metaFile.value("modID", 0).toInt();
- if (modName.isEmpty()) {
- modName = metaFile.value("modName", "").toString();
- // it is possible we have a file-name but not the correct mod name. in this case,
- // the stored mod name may be "\0"
- if (modName.isEmpty() || (modName.length() < 2)) {
- modName = metaFile.value("name", "").toString();
- }
- }
+ modName.update(metaFile.value("name", "").toString(), GUESS_FALLBACK);
+ modName.update(metaFile.value("modName", "").toString(), GUESS_META);
+
version = metaFile.value("version", "").toString();
newestVersion = metaFile.value("newestVersion", "").toString();
unsigned int categoryIndex = CategoryFactory::instance().resolveNexusID(metaFile.value("category", 0).toInt());
@@ -997,15 +994,10 @@ bool InstallationManager::install(const QString &fileName, const QString &plugin
} else if (modID != guessedModID) {
qDebug("passed mod id: %d, guessed id: %d", modID, guessedModID);
}
-
- if (modName.isEmpty()) {
- modName = guessedModName;
- nameGuessed = true;
- }
+ modName.update(guessedModName, GUESS_GOOD);
}
- fixModName(modName);
- qDebug("using mod name \"%s\" (id %d)", modName.toUtf8().constData(), modID);
+ qDebug("using mod name \"%s\" (id %d)", modName->toUtf8().constData(), modID);
m_CurrentFile = fileInfo.fileName();
// open the archive and construct the directory tree the installers work on
@@ -1014,7 +1006,7 @@ bool InstallationManager::install(const QString &fileName, const QString &plugin
DirectoryTree *filesTree = archiveOpen ? createFilesTree() : NULL;
-/* IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED;
+ IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED;
std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) {
return LHS->priority() > RHS->priority();
@@ -1080,216 +1072,7 @@ bool InstallationManager::install(const QString &fileName, const QString &plugin
}
reportError(tr("None of the available installer plugins were able to handle that archive"));
- return false;*/
-
-
-
- hasIniTweaks = false;
-
-
- DirectoryTree::Node *baseNode = NULL;
- bool manualRequest = false;
-
- if (!archiveOpen) {
- reportError(tr("Failed to open \"%1\": %2").arg(QDir::toNativeSeparators(fileName)).arg(getErrorString(m_CurrentArchive->getLastError())));
- return false;
- }
-
- // bundled fomod?
- if ((baseNode == NULL) && !manualRequest) {
- QStringList bundledFomods;
- for (DirectoryTree::const_leaf_iterator fileIter = filesTree->leafsBegin(); fileIter != filesTree->leafsEnd(); ++fileIter) {
- if (fileIter->getName().endsWith(".fomod", Qt::CaseInsensitive)) {
- bundledFomods.append(fileIter->getName());
- }
- }
- QString bundledFomodInst;
- if (bundledFomods.count() > 1) {
- SelectionDialog selection(tr("This seems like a bundle of fomods, which one do you want to install?"), m_ParentWidget);
- foreach (const QString &fomod, bundledFomods) {
- selection.addChoice(fomod, fomod, QVariant());
- }
- if (selection.exec() == QDialog::Accepted) {
- bundledFomodInst = selection.getChoiceString();
- } else {
- return false;
- }
- } else if (bundledFomods.count() == 1) {
- bundledFomodInst = bundledFomods.at(0);
- qDebug("archive contains fomod: %s", qPrintable(bundledFomodInst));
- }
- if (!bundledFomodInst.isEmpty()) {
- unpackSingleFile(bundledFomodInst);
- m_CurrentArchive->close();
- return install(QDir::tempPath().append("/").append(bundledFomodInst), pluginsFileName, modsDirectory, preferIntegrated,
- enableQuickInstall, modName, hasIniTweaks);
- }
- }
-
- // fomod installer?
- if ((baseNode == NULL) && !manualRequest) {
- QString fomodPath;
- bool xmlInstaller = false;
- if (checkFomodPackage(filesTree, fomodPath, xmlInstaller)) {
- baseNode = filesTree;
- bool nmmInstaller = checkNMMInstaller();
-
- if (xmlInstaller || nmmInstaller) {
- if (!xmlInstaller || (nmmInstaller && !preferIntegrated)) {
- if (!ensureValidModName(modName) ||
- !testOverwrite(modsDirectory, modName)) {
- return false;
- }
-
- QString targetDirectory = QDir::fromNativeSeparators(modsDirectory.mid(0).append("\\").append(modName));
-
- if (installFomodExternal(fileName, pluginsFileName, targetDirectory)) {
- QSettings settingsFile(targetDirectory.mid(0).append("/meta.ini"), QSettings::IniFormat);
-
- // overwrite settings only if they are actually are available or haven't been set before
- if ((modID != 0) || !settingsFile.contains("modid")) {
- settingsFile.setValue("modid", modID);
- }
- if (!settingsFile.contains("version") ||
- (!version.isEmpty() &&
- (VersionInfo(version) >= VersionInfo(settingsFile.value("version").toString())))) {
- settingsFile.setValue("version", version);
- }
- if (!newestVersion.isEmpty() || !settingsFile.contains("newestVersion")) {
- settingsFile.setValue("newestVersion", newestVersion);
- }
- if (!settingsFile.contains("category")) {
- settingsFile.setValue("category", QString::number(categoryID));
- }
- settingsFile.setValue("installationFile", m_CurrentFile);
-
- success = true;
- }
- } else {
- if (installFomodInternal(baseNode, fomodPath, modsDirectory,
- modID, version, newestVersion, categoryID,
- modName, nameGuessed, manualRequest)) {
- success = true;
- }
- }
- if (success) {
- DirectoryTree::node_iterator iniTweakNode = baseNode->nodeFind(DirectoryTreeInformation("INI Tweaks"));
- hasIniTweaks = (iniTweakNode != baseNode->nodesEnd()) &&
- ((*iniTweakNode)->numLeafs() != 0);
- }
- if (baseNode != filesTree) {
- delete baseNode;
- }
- } else {
- if (QuestionBoxMemory::query(m_ParentWidget, Settings::instance().directInterface(), "missingNCC",
- tr("Installer missing"),
- tr("This package contains a scripted installer. To use this installer "
- "you need the optional \"NCC\"-package and the .net runtime. "
- "Do you want to continue, treating this as a manual installer?"),
- QDialogButtonBox::Yes | QDialogButtonBox::Cancel) == QMessageBox::Yes) {
- manualRequest = true;
- } else {
- MessageDialog::showMessage(tr("Please install NCC"), m_ParentWidget);
- }
- }
- }
- }
-
- // simple installer?
- if ((baseNode == NULL) && enableQuickInstall && !manualRequest) {
- baseNode = getSimpleArchiveBase(filesTree);
- if (baseNode != NULL) {
- qDebug("treating as simple archive (%d)", baseNode->numLeafs());
- SimpleInstallDialog dialog(modName, m_ParentWidget);
- if (dialog.exec() == QDialog::Accepted) {
- mapToArchive(baseNode);
- modName = dialog.getName();
- if (doInstall(modsDirectory, modName, modID, version, newestVersion, categoryID)) {
- success = true;
-
- DirectoryTree::node_iterator iniTweakNode = baseNode->nodeFind(DirectoryTreeInformation("INI Tweaks"));
- hasIniTweaks = (iniTweakNode != baseNode->nodesEnd()) &&
- ((*iniTweakNode)->numLeafs() != 0);
- }
- } else {
- if (dialog.manualRequested()) {
- manualRequest = true;
- modName = dialog.getName();
- }
- }
- }
- }
-
- // bain complex package?
- if ((baseNode == NULL) && !manualRequest) {
- if (checkBainPackage(filesTree)) {
- bool hasPackageTXT = unpackPackageTXT();
-
- baseNode = filesTree;
- qDebug("treating as complex archive (%d)", filesTree->numNodes());
- BainComplexInstallerDialog dialog(filesTree, modName, hasPackageTXT, m_ParentWidget);
- if (dialog.exec() == QDialog::Accepted) {
- modName = dialog.getName();
- // create a new tree with the selected directories mapped to the
- // base directory. This is destructive on the original tree
- baseNode = dialog.updateTree(baseNode);
- mapToArchive(baseNode);
-
- if (doInstall(modsDirectory, modName, modID, version, newestVersion, categoryID)) {
- success = true;
-
- DirectoryTree::node_iterator iniTweakNode = baseNode->nodeFind(DirectoryTreeInformation("INI Tweaks"));
- hasIniTweaks = (iniTweakNode != baseNode->nodesEnd()) &&
- ((*iniTweakNode)->numLeafs() != 0);
- }
- delete baseNode;
- } else {
- if (dialog.manualRequested()) {
- manualRequest = true;
- modName = dialog.getName();
- }
- }
- QFile::remove(QDir::tempPath().append("/package.txt"));
- }
- }
-
- // final option: manual installer
- if ((baseNode == NULL) || manualRequest) {
- qDebug("offering installation dialog");
- InstallDialog dialog(filesTree, modName, m_ParentWidget);
- connect(&dialog, SIGNAL(openFile(QString)), this, SLOT(openFile(QString)));
- if (dialog.exec() == QDialog::Accepted) {
- modName = dialog.getModName();
- baseNode = dialog.getDataTree();
- mapToArchive(baseNode);
- if (doInstall(modsDirectory, modName, modID, version, newestVersion, categoryID)) {
- success = true;
-
- DirectoryTree::node_iterator iniTweakNode = baseNode->nodeFind(DirectoryTreeInformation("INI Tweaks"));
- hasIniTweaks = (iniTweakNode != baseNode->nodesEnd()) &&
- ((*iniTweakNode)->numLeafs() != 0);
- }
- delete baseNode; // baseNode is a new tree, independent of filesTree
- }
- }
-
- delete filesTree;
- m_CurrentArchive->close();
-
- for (std::set<QString>::iterator iter = m_FilesToDelete.begin();
- iter != m_FilesToDelete.end(); ++iter) {
- QFile(*iter).remove();
- }
- m_FilesToDelete.clear();
-
- for (std::set<QString>::iterator iter = m_TempFilesToDelete.begin();
- iter != m_TempFilesToDelete.end(); ++iter) {
- QFile(QDir::tempPath().append("/").append(*iter)).remove();
- }
-
- m_TempFilesToDelete.clear();
-
- return success;
+ return false;
}
diff --git a/src/installationmanager.h b/src/installationmanager.h
index 7d864647..5bf64e94 100644
--- a/src/installationmanager.h
+++ b/src/installationmanager.h
@@ -20,10 +20,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef INSTALLATIONMANAGER_H
#define INSTALLATIONMANAGER_H
-#include "installdialog.h"
#include <iinstallationmanager.h>
#include <iplugininstaller.h>
+#include <guessedvalue.h>
#include <QObject>
#define WIN32_LEAN_AND_MEAN
@@ -61,12 +61,12 @@ public:
* @brief install a mod from an archive
*
* @param fileName absolute file name of the archive to install
+ * @param modsDirectory directory to install mods to
* @param modName suggested name of the mod. If this is empty (the default), a name will be guessed based on the filename. The user will always have a chance to rename the mod
- * @param preferIntegrated if true, integrated installers are chosen over external installers
* @return true if the archive was installed, false if installation failed or was refused
* @exception std::exception an exception may be thrown if the archive can't be opened (maybe the format is invalid or the file is damaged)
**/
- bool install(const QString &fileName, const QString &pluginsFileName, const QString &modsDirectory, bool preferIntegrated, bool enableQuickInstall, QString &modName, bool &hasIniTweaks);
+ bool install(const QString &fileName, const QString &modsDirectory, MOBase::GuessedValue<QString> &modName, bool &hasIniTweaks);
/**
* @return true if the installation was canceled
@@ -123,7 +123,7 @@ public:
* @param archiveFile path to the archive to install
* @return the installation result
*/
- virtual MOBase::IPluginInstaller::EInstallResult installArchive(const QString &modName, const QString &archiveName);
+ virtual MOBase::IPluginInstaller::EInstallResult installArchive(MOBase::GuessedValue<QString> &modName, const QString &archiveName);
private:
@@ -152,11 +152,11 @@ private:
bool checkFomodPackage(MOBase::DirectoryTree *dataTree, QString &offset, bool &xmlInstaller);
bool checkNMMInstaller();
- void fixModName(QString &name);
+// static bool fixModName(QString &name);
- bool testOverwrite(const QString &modsDirectory, QString &modName);
+ bool testOverwrite(const QString &modsDirectory, MOBase::GuessedValue<QString> &modName);
- bool doInstall(const QString &modsDirectory, QString &modName,
+ bool doInstall(const QString &modsDirectory, MOBase::GuessedValue<QString> &modName,
int modID, const QString &version, const QString &newestVersion, int categoryID);
bool installFomodExternal(const QString &fileName, const QString &pluginsFileName, const QString &modDirectory);
@@ -165,7 +165,7 @@ private:
int categoryID, QString &modName, bool nameGuessed, bool &manualRequest);
QString generateBackupName(const QString &directoryName);
- bool ensureValidModName(QString &name);
+ bool ensureValidModName(MOBase::GuessedValue<QString> &name);
private slots:
diff --git a/src/installdialog.cpp b/src/installdialog.cpp
deleted file mode 100644
index ac508516..00000000
--- a/src/installdialog.cpp
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "installdialog.h"
-#include "ui_installdialog.h"
-
-#include "report.h"
-#include "utility.h"
-#include "installationtester.h"
-
-#include <QMenu>
-#include <QInputDialog>
-#include <QMessageBox>
-
-
-using namespace MOBase;
-
-
-InstallDialog::InstallDialog(DirectoryTree *tree, const QString &modName, QWidget *parent)
- : TutorableDialog("InstallDialog", parent), ui(new Ui::InstallDialog),
- m_DataTree(tree), m_TreeRoot(NULL), m_DataRoot(NULL), m_TreeSelection(NULL),
- m_Updating(false)
-{
- ui->setupUi(this);
-
- QLineEdit *editName = findChild<QLineEdit*>("editName");
- editName->setText(modName);
-
- m_Tree = findChild<ArchiveTree*>("treeContent");
-
- m_ProblemLabel = findChild<QLabel*>("problemLabel");
-
- connect(m_Tree, SIGNAL(changed()), this, SLOT(treeChanged()));
-
- updatePreview();
-}
-
-InstallDialog::~InstallDialog()
-{
- delete ui;
-}
-
-
-QString InstallDialog::getModName() const
-{
- QLineEdit *editName = findChild<QLineEdit*>("editName");
- return editName->text();
-}
-
-
-void InstallDialog::mapDataNode(DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const
-{
- for (int i = 0; i < baseItem->childCount(); ++i) {
- QTreeWidgetItem *currentItem = baseItem->child(i);
-
- if (currentItem->checkState(0) != Qt::Unchecked) {
- if (currentItem->data(0, Qt::UserRole).isNull()) {
- DirectoryTree::Node *newNode = new DirectoryTree::Node;
- newNode->setData(currentItem->text(0));
- mapDataNode(newNode, currentItem);
- node->addNode(newNode, true);
- } else {
- node->addLeaf(FileTreeInformation(currentItem->text(0), currentItem->data(0, Qt::UserRole).toInt()));
- }
- }
- }
-}
-
-
-DirectoryTree *InstallDialog::getDataTree() const
-{
- DirectoryTree *base = new DirectoryTree;
-
- mapDataNode(base, m_Tree->topLevelItem(0));
- return base;
-}
-
-
-QString InstallDialog::getFullPath(const DirectoryTree::Node *node)
-{
- QString result(node->getData().name);
- const DirectoryTree::Node *parent = node->getParent();
- while (parent != NULL) {
- if (parent->getParent() != NULL) {
- result.prepend("\\");
- }
- result.prepend(parent->getData().name);
- parent = parent->getParent();
- }
- return result;
-}
-
-
-void InstallDialog::addDataToTree(DirectoryTree::Node *node, QTreeWidgetItem *treeItem)
-{
- QString path = getFullPath(node);
-
- // add directory elements
- for (DirectoryTree::node_iterator iter = node->nodesBegin(); iter != node->nodesEnd(); ++iter) {
- QStringList fields((*iter)->getData().name);
- QTreeWidgetItem *newNodeItem = new QTreeWidgetItem(treeItem, fields);
- newNodeItem->setFlags(newNodeItem->flags() | Qt::ItemIsUserCheckable | Qt::ItemIsTristate);
- newNodeItem->setCheckState(0, Qt::Checked);
- addDataToTree(*iter, newNodeItem);
- treeItem->addChild(newNodeItem);
- }
-
- // add file elements
- for (DirectoryTree::leaf_iterator iter = node->leafsBegin(); iter != node->leafsEnd(); ++iter) {
- QStringList fields(iter->getName());
-
- QTreeWidgetItem *newLeafItem = new QTreeWidgetItem(treeItem, fields);
- newLeafItem->setFlags(newLeafItem->flags() | Qt::ItemIsUserCheckable);
- newLeafItem->setCheckState(0, Qt::Checked);
- if (path.size() != 0) {
- newLeafItem->setToolTip(0, path.mid(0).append("\\").append(iter->getName()));
- } else {
- newLeafItem->setToolTip(0, iter->getName());
- }
- newLeafItem->setData(0, Qt::UserRole, iter->getIndex());
-
- treeItem->addChild(newLeafItem);
- }
-}
-
-
-void InstallDialog::updatePreview()
-{
- m_Updating = true;
- m_Tree->clear();
- delete m_TreeRoot;
-
- m_TreeRoot = new QTreeWidgetItem(m_Tree, QStringList("<data>"));
-
- addDataToTree(m_DataTree, m_TreeRoot);
-
- setDataRoot(m_TreeRoot);
- m_Updating = false;
- updateProblems();
-}
-
-
-bool InstallDialog::testForProblem()
-{
- bool ok = false;
- QTreeWidgetItem *tlWidget = m_Tree->topLevelItem(0);
- for (int i = 0; i < tlWidget->childCount(); ++i) {
- QTreeWidgetItem *widget = tlWidget->child(i);
- if (widget->checkState(0) == Qt::Unchecked) {
- continue;
- }
-
- if (widget->data(0, Qt::UserRole).isValid()) {
- // file
- if (InstallationTester::isTopLevelSuffix(widget->text(0))) {
- ok = true;
- break;
- }
- } else {
- // directory
- if (InstallationTester::isTopLevelDirectory(widget->text(0))) {
- ok = true;
- break;
- }
- }
- }
- return ok;
-}
-
-
-void InstallDialog::updateProblems()
-{
-
- if (testForProblem()) {
- m_ProblemLabel->setText(tr("Looks good"));
- m_ProblemLabel->setToolTip(tr("No problem detected"));
- m_ProblemLabel->setStyleSheet("color: darkGreen;");
- } else {
- m_ProblemLabel->setText(tr("No game data on top level"));
- m_ProblemLabel->setToolTip(tr("There is no esp/esm file or asset directory (textures, meshes, interface, ...) "
- "on the top level."));
- m_ProblemLabel->setStyleSheet("color: red;");
- }
-}
-
-
-void InstallDialog::setDataRoot(QTreeWidgetItem* root)
-{
- if (root != NULL) {
- m_DataRoot = root;
-
- m_Tree->takeTopLevelItem(0);
- QTreeWidgetItem *temp = root->clone();
-// temp->setCheckState(0, Qt::Checked);
- temp->setFlags(temp->flags() & ~(Qt::ItemIsUserCheckable | Qt::ItemIsTristate));
- temp->setText(0, "<data>");
- temp->setData(0, Qt::CheckStateRole, QVariant());
- m_Tree->addTopLevelItem(temp);
- temp->setExpanded(true);
- } else {
- m_Tree->takeTopLevelItem(0);
- }
- updateProblems();
-}
-
-
-void InstallDialog::use_as_data()
-{
- if (m_TreeSelection != NULL) {
- setDataRoot(m_TreeSelection);
- m_TreeSelection = NULL;
- }
- updateProblems();
-}
-
-
-void InstallDialog::unset_data()
-{
- m_TreeSelection = NULL;
-
- setDataRoot(m_TreeRoot);
- updateProblems();
-}
-
-
-void InstallDialog::create_directory()
-{
- bool ok = false;
- QString result = QInputDialog::getText(this, tr("Enter a directory name"), tr("Name"),
- QLineEdit::Normal, QString(), &ok);
- if (ok && !result.isEmpty()) {
- for (int i = 0; i < m_TreeSelection->childCount(); ++i) {
- if (m_TreeSelection->child(i)->text(0) == result) {
- reportError(tr("A directory with that name exists"));
- return;
- }
- }
- QStringList fields(result);
- QTreeWidgetItem *newItem = new QTreeWidgetItem(m_TreeSelection, fields);
- newItem->setFlags(newItem->flags() | Qt::ItemIsUserCheckable);
- newItem->setCheckState(0, Qt::Checked);
- m_TreeSelection->addChild(newItem);
- updateProblems();
- }
-}
-
-void InstallDialog::open_file()
-{
- emit openFile(m_TreeSelection->toolTip(0));
-}
-
-
-void InstallDialog::on_treeContent_customContextMenuRequested(QPoint pos)
-{
- m_TreeSelection = m_Tree->itemAt(pos);
- if (m_TreeSelection == 0) {
- return;
- }
-
- QMenu menu;
- menu.addAction(tr("Set data directory"), this, SLOT(use_as_data()));
- menu.addAction(tr("Unset data directory"), this, SLOT(unset_data()));
- if (m_TreeSelection->data(0, Qt::UserRole).isNull()) {
- menu.addAction(tr("Create directory..."), this, SLOT(create_directory()));
- } else {
- menu.addAction(tr("&Open"), this, SLOT(open_file()));
- }
- menu.exec(m_Tree->mapToGlobal(pos));
-}
-
-
-void InstallDialog::treeChanged()
-{
- updateProblems();
-}
-
-
-void InstallDialog::on_okButton_clicked()
-{
- if (!testForProblem()) {
- if (QMessageBox::question(this, tr("Continue?"), tr("This mod was probably NOT set up correctly, most likely it will NOT work. Really continue?"),
- QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) {
- return;
- }
- }
- this->accept();
-}
-
-void InstallDialog::on_cancelButton_clicked()
-{
- this->reject();
-}
diff --git a/src/installdialog.h b/src/installdialog.h
deleted file mode 100644
index e44d7eee..00000000
--- a/src/installdialog.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef INSTALLDIALOG_H
-#define INSTALLDIALOG_H
-
-#include "mytree.h"
-#include "archivetree.h"
-#include "tutorabledialog.h"
-#include <QDialog>
-#include <QUuid>
-#include <QTreeWidgetItem>
-#include <QProgressDialog>
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-#include <archive.h>
-#include <directorytree.h>
-
-
-namespace Ui {
- class InstallDialog;
-}
-
-
-/**
- * a dialog presented to manually define how a mod is to be installed. It provides
- * a tree view of the file contents that can modified directly
- **/
-class InstallDialog : public MOBase::TutorableDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief constructor
- *
- * @param tree tree structure describing the vanilla archive structure. The InstallDialog does NOT take custody of this pointer!
- * @param modName name of the mod. The name can be modified through the dialog
- * @param parent parent widget
- **/
- explicit InstallDialog(MOBase::DirectoryTree *tree, const QString &modName, QWidget *parent = 0);
- ~InstallDialog();
-
- /**
- * @brief retrieve the (modified) mod name
- *
- * @return updated mod name
- **/
- QString getModName() const;
-
- /**
- * @brief retrieve the user-modified directory structure
- *
- * @return modified data structure. This is a NEW datatree object for which the caller takes custody
- **/
- MOBase::DirectoryTree *getDataTree() const;
-
-signals:
-
- void openFile(const QString fileName);
-
-private:
-
- void updatePreview();
- bool testForProblem();
- void updateProblems();
-
- void setDataRoot(QTreeWidgetItem* root);
-
- void updateFileList(QTreeWidgetItem *item, QString targetName, FileData* const *fileData, size_t size) const;
-
- void updateCheckState(QTreeWidgetItem *item);
-// void recursiveCheck(QTreeWidgetItem *item);
-// void recursiveUncheck(QTreeWidgetItem *item);
-
- void addDataToTree(MOBase::DirectoryTree::Node *node, QTreeWidgetItem *treeItem);
-
- void mapDataNode(MOBase::DirectoryTree::Node *node, QTreeWidgetItem *baseItem) const;
-
- static QString getFullPath(const MOBase::DirectoryTree::Node *node);
-
-private slots:
-
- void on_treeContent_customContextMenuRequested(QPoint pos);
-
- void unset_data();
- void use_as_data();
- void create_directory();
- void open_file();
-
- void treeChanged();
-
- void on_cancelButton_clicked();
-
- void on_okButton_clicked();
-
-private:
- Ui::InstallDialog *ui;
-
- MOBase::DirectoryTree *m_DataTree;
-
- ArchiveTree *m_Tree;
- QLabel *m_ProblemLabel;
- QTreeWidgetItem *m_TreeRoot;
- QTreeWidgetItem *m_DataRoot;
- QTreeWidgetItem *m_TreeSelection;
- bool m_Updating;
-
-};
-
-#endif // INSTALLDIALOG_H
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f2f3af65..2f6710cc 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -23,9 +23,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "spawn.h"
#include "report.h"
#include "modlist.h"
+#include "modlistsortproxy.h"
+#include "qtgroupingproxy.h"
#include "profile.h"
#include "pluginlist.h"
-#include "installdialog.h"
#include "profilesdialog.h"
#include "editexecutablesdialog.h"
#include "categories.h"
@@ -43,7 +44,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "syncoverwritedialog.h"
#include "logbuffer.h"
#include "downloadlistsortproxy.h"
-#include "modlistsortproxy.h"
#include "motddialog.h"
#include "filedialogmemory.h"
#include "questionboxmemory.h"
@@ -84,6 +84,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QPluginLoader>
#include <QRadioButton>
#include <QDesktopWidget>
+#include <QIdentityProxyModel>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <Psapi.h>
@@ -99,7 +100,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
: QMainWindow(parent), ui(new Ui::MainWindow), m_Tutorial(this, "MainWindow"),
m_ExeName(exeName), m_OldProfileIndex(-1),
m_DirectoryStructure(new DirectoryEntry(L"data", NULL, 0)),
- m_ModList(NexusInterface::instance()),
+ m_ModList(NexusInterface::instance()), m_ModListSortProxy(NULL),
m_OldExecutableIndex(-1), m_GamePath(ToQString(GameInfo::instance().getGameDirectory())),
m_NexusDialog(NexusInterface::instance()->getAccessManager(), NULL),
m_DownloadManager(NexusInterface::instance(), this),
@@ -132,13 +133,17 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
// set up mod list
m_ModListSortProxy = new ModListSortProxy(m_CurrentProfile, this);
m_ModListSortProxy->setSourceModel(&m_ModList);
- ui->modList->setModel(m_ModListSortProxy);
+
+ QAbstractProxyModel *proxyModel = new QIdentityProxyModel(this);
+ proxyModel->setSourceModel(m_ModListSortProxy);
+ ui->modList->setModel(proxyModel);
ui->modList->sortByColumn(ModList::COL_PRIORITY, Qt::AscendingOrder);
- ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new IconDelegate(m_ModListSortProxy));
+ ui->modList->setItemDelegateForColumn(ModList::COL_FLAGS, new IconDelegate(ui->modList));
ui->modList->header()->installEventFilter(&m_ModList);
ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray());
ui->modList->installEventFilter(&m_ModList);
+
// restoreState also seems to restores the resize mode from previous session,
// I don't really like that
#if QT_VERSION >= 0x50000
@@ -205,7 +210,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(ui->modFilterEdit, SIGNAL(textChanged(QString)), m_ModListSortProxy, SLOT(updateFilter(QString)));
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), m_PluginListSortProxy, SLOT(updateFilter(QString)));
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString)));
- connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), m_ModListSortProxy, SLOT(invalidate()));
+ connect(&m_ModList, SIGNAL(modlist_changed(int)), this, SLOT(modlistChanged(int)));
connect(&m_ModList, SIGNAL(modlist_changed(QModelIndex, int)), this, SLOT(modlistChanged(QModelIndex, int)));
connect(&m_ModList, SIGNAL(removeSelectedMods()), this, SLOT(removeMod_clicked()));
@@ -269,6 +274,25 @@ void MainWindow::resizeEvent(QResizeEvent *event)
}
+static QModelIndex mapToModel(const QAbstractItemModel *targetModel, QModelIndex idx)
+{
+ QModelIndex result = idx;
+ const QAbstractItemModel *model = idx.model();
+ while (model != targetModel) {
+ if (model == NULL) {
+ return QModelIndex();
+ }
+ const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel*>(model);
+ if (proxyModel == NULL) {
+ return QModelIndex();
+ }
+ result = proxyModel->mapToSource(result);
+ model = proxyModel->sourceModel();
+ }
+ return result;
+}
+
+
void MainWindow::actionToToolButton(QAction *&sourceAction)
{
QToolButton *button = new QToolButton(ui->toolBar);
@@ -1188,11 +1212,11 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
}
if (ui->profileBox->currentIndex() == 0) {
- ui->profileBox->setCurrentIndex(previousIndex);
ProfilesDialog(m_GamePath).exec();
while (!refreshProfiles()) {
ProfilesDialog(m_GamePath).exec();
}
+ ui->profileBox->setCurrentIndex(previousIndex);
} else {
activateSelectedProfile();
}
@@ -1625,6 +1649,9 @@ void MainWindow::readSettings()
languageChange(m_Settings.language());
int selectedExecutable = settings.value("selected_executable").toInt();
setExecutableIndex(selectedExecutable);
+
+ int grouping = settings.value("group_state").toInt();
+ ui->groupCombo->setCurrentIndex(grouping);
}
@@ -1645,8 +1672,10 @@ void MainWindow::storeSettings()
}
settings.setValue("mod_list_state", ui->modList->header()->saveState());
-
settings.setValue("plugin_list_state", ui->espList->header()->saveState());
+
+ settings.setValue("group_state", ui->groupCombo->currentIndex());
+
settings.setValue("compact_downloads", ui->compactBox->isChecked());
settings.setValue("ask_for_nexuspw", m_AskForNexusPW);
@@ -1712,14 +1741,13 @@ void MainWindow::on_tabWidget_currentChanged(int index)
void MainWindow::installMod(const QString &fileName)
{
bool hasIniTweaks = false;
- QString modName;
+ GuessedValue<QString> modName;
m_CurrentProfile->writeModlistNow();
- if (m_InstallationManager.install(fileName, m_CurrentProfile->getPluginsFileName(), m_Settings.getModDirectory(), m_Settings.preferIntegratedInstallers(),
- m_Settings.enableQuickInstaller(), modName, hasIniTweaks)) {
+ if (m_InstallationManager.install(fileName, m_Settings.getModDirectory(), modName, hasIniTweaks)) {
MessageDialog::showMessage(tr("Installation successful"), this);
refreshModList();
- QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, modName);
+ QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName));
if (posList.count() == 1) {
ui->modList->scrollTo(posList.at(0));
}
@@ -2220,6 +2248,12 @@ void MainWindow::modRenamed(const QString &oldName, const QString &newName)
}
+void MainWindow::modlistChanged(int)
+{
+ m_ModListSortProxy->invalidate();
+}
+
+
QTreeWidgetItem *MainWindow::addFilterItem(QTreeWidgetItem *root, const QString &name, int categoryID)
{
QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(name));
@@ -2299,9 +2333,11 @@ void MainWindow::refreshFilters()
void MainWindow::renameMod_clicked()
{
try {
- QModelIndex treeIdx = m_ModListSortProxy->mapFromSource(m_ModList.index(m_ContextRow, 0));
+/* QModelIndex treeIdx = m_ModListGroupProxy->mapFromSource(m_ModListSortProxy->mapFromSource(m_ModList.index(m_ContextRow, 0)));
ui->modList->setCurrentIndex(treeIdx);
- ui->modList->edit(treeIdx);
+ ui->modList->edit(treeIdx);*/
+
+ ui->modList->edit(ui->modList->currentIndex());
} catch (const std::exception &e) {
reportError(tr("failed to rename mod: %1").arg(e.what()));
}
@@ -2364,7 +2400,8 @@ void MainWindow::removeMod_clicked()
QString mods;
QStringList modNames;
foreach (QModelIndex idx, selection->selectedRows()) {
- QString name = ModInfo::getByIndex(m_ModListSortProxy->mapToSource(idx).row())->name();
+// QString name = ModInfo::getByIndex(m_ModListGroupProxy->mapToSource(idx).row())->name();
+ QString name = idx.data().toString();
mods += "<li>" + name + "</li>";
modNames.append(name);
}
@@ -2647,9 +2684,19 @@ void MainWindow::cancelModListEditor()
void MainWindow::on_modList_doubleClicked(const QModelIndex &index)
{
+ if (!index.isValid()) {
+ return;
+ }
+// QModelIndex sourceIdx = m_ModListGroupProxy->mapToSource(index);
+ QModelIndex sourceIdx = mapToModel(&m_ModList, index);
+ if (!sourceIdx.isValid()) {
+ return;
+ }
+
try {
m_ContextRow = m_ModListSortProxy->mapToSource(index).row();
- displayModInformation(m_ModListSortProxy->mapToSource(index).row());
+// displayModInformation(m_ModListSortProxy->mapToSource(index).row());
+ displayModInformation(sourceIdx.row());
// workaround to cancel the editor that might have opened because of
// selection-click
ui->modList->closePersistentEditor(index);
@@ -2889,13 +2936,12 @@ void MainWindow::exportModListCSV()
}
}
-
void MainWindow::on_modList_customContextMenuRequested(const QPoint &pos)
{
try {
QTreeView *modList = findChild<QTreeView*>("modList");
- m_ContextRow = m_ModListSortProxy->mapToSource(modList->indexAt(pos)).row();
+ m_ContextRow = mapToModel(&m_ModList, modList->indexAt(pos)).row();
QMenu menu;
menu.addAction(tr("Install Mod..."), this, SLOT(installMod_clicked()));
@@ -2971,6 +3017,7 @@ void MainWindow::on_categoriesList_currentItemChanged(QTreeWidgetItem *current,
int filter = current->data(0, Qt::UserRole).toInt();
m_ModListSortProxy->setCategoryFilter(filter);
ui->currentCategoryLabel->setText(QString("(%1)").arg(current->text(0)));
+ ui->modList->reset();
}
}
@@ -3278,32 +3325,29 @@ void MainWindow::installDownload(int index)
try {
QString fileName = m_DownloadManager.getFilePath(index);
int modID = m_DownloadManager.getModID(index);
- QString modName;
+ GuessedValue<QString> modName;
// see if there already are mods with the specified mod id
if (modID != 0) {
- ModInfo::Ptr modInfo = ModInfo::getByModID(modID, true);
- if (!modInfo.isNull()) {
- std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
+ std::vector<ModInfo::Ptr> modInfo = ModInfo::getByModID(modID);
+ for (auto iter = modInfo.begin(); iter != modInfo.end(); ++iter) {
+ std::vector<ModInfo::EFlag> flags = (*iter)->getFlags();
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_BACKUP) == flags.end()) {
- modName = modInfo->name();
- modInfo->saveMeta();
+ modName.update((*iter)->name(), GUESS_PRESET);
+ (*iter)->saveMeta();
}
}
- // TODO there may be multiple mods with the same id!
-// modName = m_ModList.getModByModID(modID);
}
m_CurrentProfile->writeModlistNow();
bool hasIniTweaks = false;
- if (m_InstallationManager.install(fileName, m_CurrentProfile->getPluginsFileName(), m_Settings.getModDirectory(), m_Settings.preferIntegratedInstallers(),
- m_Settings.enableQuickInstaller(), modName, hasIniTweaks)) {
+ if (m_InstallationManager.install(fileName, m_Settings.getModDirectory(), modName, hasIniTweaks)) {
MessageDialog::showMessage(tr("Installation successful"), this);
refreshModList();
- QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, modName);
+ QModelIndexList posList = m_ModList.match(m_ModList.index(0, 0), Qt::DisplayRole, static_cast<const QString&>(modName));
if (posList.count() == 1) {
ui->modList->scrollTo(posList.at(0));
}
@@ -3702,13 +3746,13 @@ void MainWindow::nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant us
ui->actionEndorseMO->setVisible(true);
}
} else {
- ModInfo::Ptr info = ModInfo::getByModID(result["id"].toInt(), true);
- if (!info.isNull()) {
- info->setNewestVersion(VersionInfo(result["version"].toString()));
- info->setNexusDescription(result["description"].toString());
+ std::vector<ModInfo::Ptr> info = ModInfo::getByModID(result["id"].toInt());
+ for (auto iter = info.begin(); iter != info.end(); ++iter) {
+ (*iter)->setNewestVersion(VersionInfo(result["version"].toString()));
+ (*iter)->setNexusDescription(result["description"].toString());
if (NexusInterface::instance()->getAccessManager()->loggedIn()) {
// don't use endorsement info if we're not logged in
- info->setIsEndorsed(result["voted_by_user"].toBool());
+ (*iter)->setIsEndorsed(result["voted_by_user"].toBool());
}
}
}
@@ -4013,3 +4057,31 @@ void MainWindow::on_espList_customContextMenuRequested(const QPoint &pos)
reportError(tr("Unknown exception"));
}
}
+
+void MainWindow::on_groupCombo_currentIndexChanged(int index)
+{
+ QAbstractProxyModel *newModel = NULL;
+ switch (index) {
+ case 1: {
+ newModel = new QtGroupingProxy(m_ModListSortProxy, QModelIndex(), ModList::COL_CATEGORY);
+ } break;
+ case 2: {
+ newModel = new QtGroupingProxy(m_ModListSortProxy, QModelIndex(), ModList::COL_MODID);
+ } break;
+ default: {
+ newModel = NULL;
+ } break;
+ }
+
+ if (newModel != NULL) {
+ newModel->setSourceModel(m_ModListSortProxy);
+ connect(ui->modList, SIGNAL(expanded(QModelIndex)),newModel, SLOT(expanded(QModelIndex)));
+ connect(ui->modList, SIGNAL(collapsed(QModelIndex)), newModel, SLOT(collapsed(QModelIndex)));
+ connect(newModel, SIGNAL(expandItem(QModelIndex)), ui->modList, SLOT(expand(QModelIndex)));
+
+ ui->modList->setModel(newModel);
+ } else {
+ ui->modList->setModel(m_ModListSortProxy);
+ }
+ // ui->modList->setSelectionMode(QAbstractItemView::ExtendedSelection);
+}
diff --git a/src/mainwindow.h b/src/mainwindow.h
index 1e6eef44..91c2b17d 100644
--- a/src/mainwindow.h
+++ b/src/mainwindow.h
@@ -56,6 +56,8 @@ namespace Ui {
}
class QToolButton;
+class ModListSortProxy;
+class ModListGroupCategoriesProxy;
class MainWindow : public QMainWindow, public MOBase::IOrganizer
{
@@ -239,6 +241,7 @@ private:
ModList m_ModList;
ModListSortProxy *m_ModListSortProxy;
+
PluginList m_PluginList;
PluginListSortProxy *m_PluginListSortProxy;
@@ -365,6 +368,7 @@ private slots:
void addPrimaryCategoryCandidates();
void modDetailsUpdated(bool success);
+ void modlistChanged(int row);
void nxmUpdatesAvailable(const std::vector<int> &modIDs, QVariant userData, QVariant resultData, int requestID);
void nxmEndorsementToggled(int, QVariant, QVariant resultData, int);
@@ -435,7 +439,7 @@ private slots: // ui slots
void on_espList_customContextMenuRequested(const QPoint &pos);
void on_displayCategoriesBtn_toggled(bool checked);
-
+ void on_groupCombo_currentIndexChanged(int index);
};
#endif // MAINWINDOW_H
diff --git a/src/mainwindow.ui b/src/mainwindow.ui
index 74f8c686..e9fa8db6 100644
--- a/src/mainwindow.ui
+++ b/src/mainwindow.ui
@@ -322,7 +322,7 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0,1,1">
+ <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0,1,1,1">
<item>
<widget class="QPushButton" name="displayCategoriesBtn">
<property name="maximumSize">
@@ -372,6 +372,25 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
+ <widget class="QComboBox" name="groupCombo">
+ <item>
+ <property name="text">
+ <string>No groups</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Categories</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Nexus IDs</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
<widget class="MOBase::LineEditClear" name="modFilterEdit">
<property name="placeholderText">
<string>Namefilter</string>
@@ -669,6 +688,9 @@ p, li { white-space: pre-wrap; }
<property name="indentation">
<number>0</number>
</property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index aebab059..d84a6aa0 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -44,7 +44,7 @@ using namespace MOShared;
std::vector<ModInfo::Ptr> ModInfo::s_Collection;
std::map<QString, unsigned int> ModInfo::s_ModsByName;
-std::map<int, unsigned int> ModInfo::s_ModsByModID;
+std::map<int, std::vector<unsigned int> > ModInfo::s_ModsByModID;
int ModInfo::s_NextID;
QMutex ModInfo::s_Mutex(QMutex::Recursive);
@@ -128,20 +128,21 @@ ModInfo::Ptr ModInfo::getByIndex(unsigned int index)
}
-ModInfo::Ptr ModInfo::getByModID(int modID, bool missingAcceptable)
+std::vector<ModInfo::Ptr> ModInfo::getByModID(int modID)
{
QMutexLocker locker(&s_Mutex);
- std::map<int, unsigned int>::iterator iter = s_ModsByModID.find(modID);
+ auto iter = s_ModsByModID.find(modID);
if (iter == s_ModsByModID.end()) {
- if (missingAcceptable) {
- return ModInfo::Ptr();
- } else {
- throw MyException(tr("invalid mod id %1").arg(modID));
- }
+ return std::vector<ModInfo::Ptr>();
+ }
+
+ std::vector<ModInfo::Ptr> result;
+ for (auto idxIter = iter->second.begin(); idxIter != iter->second.end(); ++idxIter) {
+ result.push_back(getByIndex(*idxIter));
}
- return getByIndex(iter->second);
+ return result;
}
@@ -157,12 +158,11 @@ bool ModInfo::removeMod(unsigned int index)
ModInfo::Ptr modInfo = s_Collection[index];
s_ModsByName.erase(s_ModsByName.find(modInfo->name()));
- //TODO this is a bit more complicated since multiple mods may have the
- // same mod id but only one appears in the index
- std::map<int, unsigned int>::iterator iter = s_ModsByModID.find(modInfo->getNexusID()) ;
- if ((iter != s_ModsByModID.end()) &&
- (iter->second == index)) {
- s_ModsByModID.erase(iter);
+ auto iter = s_ModsByModID.find(modInfo->getNexusID());
+ if (iter != s_ModsByModID.end()) {
+ std::vector<unsigned int> indices = iter->second;
+ std::remove(indices.begin(), indices.end(), index);
+ s_ModsByModID[modInfo->getNexusID()] = indices;
}
// physically remove the mod directory
@@ -234,12 +234,7 @@ void ModInfo::updateIndices()
QString modName = s_Collection[i]->name();
int modID = s_Collection[i]->getNexusID();
s_ModsByName[modName] = i;
-
- // don't overwrite a modid-entry with a backup entry. This is a bit of a workaround
- if ((s_ModsByModID.find(modID) == s_ModsByModID.end()) ||
- !backupRegEx.exactMatch(modName)) {
- s_ModsByModID[modID] = i;
- }
+ s_ModsByModID[modID].push_back(i);
}
}
diff --git a/src/modinfo.h b/src/modinfo.h
index 4c5cfd50..adf194dd 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -126,12 +126,11 @@ public:
* @brief retrieve a ModInfo object based on its nexus mod id
*
* @param modID the nexus mod id to look up
- * @param missingAcceptable if true, this function will return a null-pointer if no mod has the specified mod id, otherwise an exception is thrown
* @return a reference counting pointer to the mod info
* @todo in its current form, this function is broken! There may be multiple mods with the same nexus id,
* this function will return only one of them
**/
- static ModInfo::Ptr getByModID(int modID, bool missingAcceptable);
+ static std::vector<ModInfo::Ptr> getByModID(int modID);
/**
* @brief remove a mod by index
@@ -396,14 +395,14 @@ public:
bool categorySet(int categoryID) const;
/**
- * @brief retrive the whole list of categories this mod belongs to
+ * @brief retrive the whole list of categories (as ids) this mod belongs to
*
* @return list of categories
**/
const std::set<int> &getCategories() const { return m_Categories; }
/**
- * @return the primary category of this mod
+ * @return id of the primary category of this mod
*/
int getPrimaryCategory() const { return m_PrimaryCategory; }
@@ -465,7 +464,7 @@ protected:
private:
static QMutex s_Mutex;
- static std::map<int, unsigned int> s_ModsByModID;
+ static std::map<int, std::vector<unsigned int> > s_ModsByModID;
static int s_NextID;
bool m_Valid;
diff --git a/src/modlist.cpp b/src/modlist.cpp
index 1d5db5a0..84e76b75 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -121,6 +121,7 @@ QString ModList::getFlagText(ModInfo::EFlag flag, ModInfo::Ptr modInfo) const
QVariant ModList::data(const QModelIndex &modelIndex, int role) const
{
if (m_Profile == NULL) return QVariant();
+ if (!modelIndex.isValid()) return QVariant();
unsigned int modIndex = modelIndex.row();
int column = modelIndex.column();
@@ -196,6 +197,8 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
}
} else if (role == Qt::UserRole) {
return modInfo->getNexusID();
+ } else if (role == Qt::UserRole + 1) {
+ return modIndex;
} else if (role == Qt::FontRole) {
QFont result;
if (column == COL_NAME) {
@@ -632,10 +635,20 @@ bool ModList::eventFilter(QObject *obj, QEvent *event)
(keyEvent->modifiers() == Qt::ControlModifier) &&
((keyEvent->key() == Qt::Key_Up) || (keyEvent->key() == Qt::Key_Down))) {
QItemSelectionModel *selectionModel = itemView->selectionModel();
- const QSortFilterProxyModel *proxyModel = qobject_cast<const QSortFilterProxyModel*>(selectionModel->model());
+ const QAbstractProxyModel *proxyModel = qobject_cast<const QAbstractProxyModel*>(selectionModel->model());
+ const QSortFilterProxyModel *filterModel = NULL;
+ while ((filterModel == NULL) && (proxyModel != NULL)) {
+ filterModel = qobject_cast<const QSortFilterProxyModel*>(proxyModel);
+ if (filterModel == NULL) {
+ proxyModel = qobject_cast<const QAbstractProxyModel*>(proxyModel->sourceModel());
+ }
+ }
+ if (filterModel == NULL) {
+ return true;
+ }
int diff = -1;
- if (((keyEvent->key() == Qt::Key_Up) && (proxyModel->sortOrder() == Qt::DescendingOrder)) ||
- ((keyEvent->key() == Qt::Key_Down) && (proxyModel->sortOrder() == Qt::AscendingOrder))) {
+ if (((keyEvent->key() == Qt::Key_Up) && (filterModel->sortOrder() == Qt::DescendingOrder)) ||
+ ((keyEvent->key() == Qt::Key_Down) && (filterModel->sortOrder() == Qt::AscendingOrder))) {
diff = 1;
}
QModelIndexList rows = selectionModel->selectedRows();
@@ -645,8 +658,8 @@ bool ModList::eventFilter(QObject *obj, QEvent *event)
}
}
foreach (QModelIndex idx, rows) {
- if (proxyModel != NULL) {
- idx = proxyModel->mapToSource(idx);
+ if (filterModel != NULL) {
+ idx = filterModel->mapToSource(idx);
}
int newPriority = m_Profile->getModPriority(idx.row()) + diff;
if ((newPriority >= 0) && (newPriority < static_cast<int>(m_Profile->numRegularMods()))) {
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index a18174c6..071e0384 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -108,6 +108,7 @@ void ModListSortProxy::enableAllVisible()
int modID = mapToSource(index(i, 0)).row();
m_Profile->setModEnabled(modID, true);
}
+ invalidate();
}
@@ -124,8 +125,8 @@ void ModListSortProxy::disableAllVisible()
bool ModListSortProxy::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
- int leftIndex = left.internalId();
- int rightIndex = right.internalId();
+ int leftIndex = left.row();
+ int rightIndex = right.row();
ModInfo::Ptr leftMod = ModInfo::getByIndex(leftIndex);
ModInfo::Ptr rightMod = ModInfo::getByIndex(rightIndex);
@@ -202,7 +203,7 @@ bool ModListSortProxy::filterMatches(ModInfo::Ptr info, bool enabled) const
((m_CategoryFilter == CategoryFactory::CATEGORY_SPECIAL_UNCHECKED) && !enabled) ||
((m_CategoryFilter == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) && info->updateAvailable()) ||
((m_CategoryFilter == CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY) && (info->getCategories().size() == 0)) ||
- ((m_CategoryFilter == CategoryFactory::CATEGORY_SPECIAL_CONFLICT) && (hasConflictFlag(info->getFlags()))));
+ ((m_CategoryFilter == CategoryFactory::CATEGORY_SPECIAL_CONFLICT) && (hasConflictFlag(info->getFlags()))));
}
diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h
index d1e3e238..8b86b222 100644
--- a/src/modlistsortproxy.h
+++ b/src/modlistsortproxy.h
@@ -54,6 +54,8 @@ public:
bool filterMatches(ModInfo::Ptr info, bool enabled) const;
+//virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
+
public slots:
void displayColumnSelection(const QPoint &pos);
diff --git a/src/organizer.pro b/src/organizer.pro
index 076bcde9..560394a4 100644
--- a/src/organizer.pro
+++ b/src/organizer.pro
@@ -18,7 +18,6 @@ SOURCES += \
syncoverwritedialog.cpp \
spawn.cpp \
singleinstance.cpp \
- simpleinstalldialog.cpp \
settingsdialog.cpp \
settings.cpp \
selfupdater.cpp \
@@ -53,10 +52,8 @@ SOURCES += \
lockeddialog.cpp \
loadmechanism.cpp \
json.cpp \
- installdialog.cpp \
installationmanager.cpp \
helper.cpp \
- fomodinstallerdialog.cpp \
finddialog.cpp \
filedialogmemory.cpp \
executableslist.cpp \
@@ -72,7 +69,6 @@ SOURCES += \
categoriesdialog.cpp \
categories.cpp \
bbcode.cpp \
- baincomplexinstallerdialog.cpp \
archivetree.cpp \
activatemodsdialog.cpp \
moapplication.cpp \
@@ -80,14 +76,14 @@ SOURCES += \
icondelegate.cpp \
gameinfoimpl.cpp \
csvbuilder.cpp \
- savetextasdialog.cpp
+ savetextasdialog.cpp \
+ qtgroupingproxy.cpp
HEADERS += \
transfersavesdialog.h \
syncoverwritedialog.h \
spawn.h \
singleinstance.h \
- simpleinstalldialog.h \
settingsdialog.h \
settings.h \
selfupdater.h \
@@ -121,10 +117,8 @@ HEADERS += \
lockeddialog.h \
loadmechanism.h \
json.h \
- installdialog.h \
installationmanager.h \
helper.h \
- fomodinstallerdialog.h \
finddialog.h \
filedialogmemory.h \
executableslist.h \
@@ -140,7 +134,6 @@ HEADERS += \
categoriesdialog.h \
categories.h \
bbcode.h \
- baincomplexinstallerdialog.h \
archivetree.h \
activatemodsdialog.h \
moapplication.h \
@@ -148,7 +141,8 @@ HEADERS += \
icondelegate.h \
gameinfoimpl.h \
csvbuilder.h \
- savetextasdialog.h
+ savetextasdialog.h \
+ qtgroupingproxy.h
FORMS += \
transfersavesdialog.ui \
diff --git a/src/pluginlist.cpp b/src/pluginlist.cpp
index 93f511a4..dfac8ccb 100644
--- a/src/pluginlist.cpp
+++ b/src/pluginlist.cpp
@@ -340,7 +340,6 @@ void PluginList::readLockedOrderFrom(const QString &fileName)
}
}
}
-
file.close();
}
@@ -525,7 +524,6 @@ void PluginList::syncLoadOrder()
void PluginList::refreshLoadOrder()
{
syncLoadOrder();
-
// set priorities according to locked load order
std::map<int, QString> lockedLoadOrder;
std::for_each(m_LockedOrder.begin(), m_LockedOrder.end(),
@@ -545,10 +543,16 @@ void PluginList::refreshLoadOrder()
++targetPrio;
}
}
+
+ if (static_cast<size_t>(targetPrio) >= m_ESPs.size()) {
+ continue;
+ }
+
int temp = targetPrio;
- if (m_ESPs[nameIter->second].m_Priority != temp) {
- setPluginPriority(nameIter->second, temp);
- m_ESPs[nameIter->second].m_LoadOrder = iter->first;
+ int index = nameIter->second;
+ if (m_ESPs[index].m_Priority != temp) {
+ setPluginPriority(index, temp);
+ m_ESPs[index].m_LoadOrder = iter->first;
syncLoadOrder();
startSaveTime();
}
diff --git a/src/profile.cpp b/src/profile.cpp
index d67c6b8f..204a71aa 100644
--- a/src/profile.cpp
+++ b/src/profile.cpp
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modinfo.h"
#include <utility.h>
#include <util.h>
+#include <error_report.h>
#include <appconfig.h>
#include <QMessageBox>
#include <QApplication>
@@ -50,14 +51,19 @@ Profile::Profile(const QString &name, bool useDefaultSettings)
QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()));
QDir profileBase(profilesDir);
- if (!profileBase.exists() || !profileBase.mkdir(name)) {
- throw std::runtime_error(QObject::tr("failed to create %1").arg(name).toUtf8().constData());
+ QString fixedName = name;
+ if (!fixDirectoryName(fixedName)) {
+ throw MyException(tr("invalid profile name %1").arg(name));
}
- QString fullPath = profilesDir + "/" + name;
+
+ if (!profileBase.exists() || !profileBase.mkdir(fixedName)) {
+ throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData());
+ }
+ QString fullPath = profilesDir + "/" + fixedName;
m_Directory = QDir(fullPath);
QFile modList(m_Directory.filePath("modlist.txt"));
if (!modList.open(QIODevice::ReadWrite)) {
- profileBase.rmdir(name);
+ profileBase.rmdir(fixedName);
throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath("modlist.txt")).toUtf8().constData());
}
modList.close();
@@ -66,7 +72,7 @@ Profile::Profile(const QString &name, bool useDefaultSettings)
GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings);
} catch (...) {
// clean up in case of an error
- shellDelete(QStringList(profileBase.absoluteFilePath(name)), NULL);
+ shellDelete(QStringList(profileBase.absoluteFilePath(fixedName)), NULL);
throw;
}
refreshModStatus();
@@ -137,11 +143,16 @@ void Profile::writeModlistNow(bool onlyOnTimer) const
if (onlyOnTimer && !m_SaveTimer->isActive()) return;
m_SaveTimer->stop();
+ if (!m_Directory.exists()) return;
#pragma message("right now, this is doing unnecessary saves. Need a flag that says that mod priority, enabled-state or name of a mod has changed")
QString fileName = getModlistFileName();
QFile file(fileName);
- file.open(QIODevice::WriteOnly);
+ if (!file.open(QIODevice::WriteOnly)) {
+ reportError(tr("failed to open \"%1\" for writing").arg(fileName));
+ return;
+ }
+
file.resize(0);
file.write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
if (m_ModStatus.empty()) {
@@ -172,9 +183,7 @@ void Profile::writeModlistNow(bool onlyOnTimer) const
void Profile::createTweakedIniFile()
{
- QFileInfo iniInfo(getIniFileName());
-
- QString tweakedIni = iniInfo.absolutePath() + "/initweaks.ini";
+ QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini");
if (!shellDelete(QStringList(tweakedIni), NULL)) {
reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError())));
@@ -187,6 +196,28 @@ void Profile::createTweakedIniFile()
mergeTweaks(modInfo, tweakedIni);
}
}
+
+
+ bool error = false;
+ if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) {
+ error = true;
+ }
+
+ if (localSavesEnabled()) {
+ if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str())) {
+ error = true;
+ }
+
+ if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath",
+ AppConfig::localSavePlaceholder(),
+ ToWString(tweakedIni).c_str())) {
+ error = true;
+ }
+ }
+
+ if (error) {
+ reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str()));
+ }
}
@@ -427,6 +458,14 @@ Profile Profile::createFrom(const QString &name, const Profile &reference)
}
+Profile *Profile::createPtrFrom(const QString &name, const Profile &reference)
+{
+ QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name);
+ reference.copyFilesTo(profileDirectory);
+ return new Profile(QDir(profileDirectory));
+}
+
+
void Profile::copyFilesTo(QString &target) const
{
copyDir(m_Directory.absolutePath(), target, false);
@@ -499,16 +538,6 @@ void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const
iter != iniTweaks.end(); ++iter) {
mergeTweak(*iter, tweakedIni);
}
-
- ::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str());
-
- if (localSavesEnabled()) {
- ::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"1", ToWString(tweakedIni).c_str());
-
- ::WritePrivateProfileStringW(L"General", L"SLocalSavePath",
- AppConfig::localSavePlaceholder(),
- ToWString(tweakedIni).c_str());
- }
}
@@ -667,7 +696,6 @@ QString Profile::getPluginsFileName() const
return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt"));
}
-
QString Profile::getLoadOrderFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt"));
@@ -701,3 +729,10 @@ QString Profile::getPath() const
{
return QDir::cleanPath(m_Directory.absolutePath());
}
+
+void Profile::rename(const QString &newName)
+{
+ QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())));
+ profileDir.rename(getName(), newName);
+ m_Directory = profileDir.absoluteFilePath(newName);
+}
diff --git a/src/profile.h b/src/profile.h
index 32647591..9c678a23 100644
--- a/src/profile.h
+++ b/src/profile.h
@@ -41,6 +41,10 @@ class Profile : public QObject
public:
+ typedef boost::shared_ptr<Profile> Ptr;
+
+public:
+
/**
* @brief default constructor
* @todo This constructor initialised nothing, the resulting object is not usable
@@ -76,14 +80,18 @@ public:
bool exists() const;
/**
- * @brief copy constructor
- *
* @param name of the new profile
* @param reference profile to copy from
**/
static Profile createFrom(const QString &name, const Profile &reference);
/**
+ * @param name of the new profile
+ * @param reference profile to copy from
+ **/
+ static Profile *createPtrFrom(const QString &name, const Profile &reference);
+
+ /**
* @brief write out the modlist.txt
**/
void writeModlist() const;
@@ -174,6 +182,8 @@ public:
**/
QString getPath() const;
+ void rename(const QString &newName);
+
/**
* @brief create the ini file to be used by the game
*
@@ -302,7 +312,6 @@ private:
unsigned int m_NumRegularMods;
QTimer *m_SaveTimer;
-
};
Q_DECLARE_METATYPE(Profile)
diff --git a/src/profilesdialog.cpp b/src/profilesdialog.cpp
index ac2c534f..54e9147b 100644
--- a/src/profilesdialog.cpp
+++ b/src/profilesdialog.cpp
@@ -37,6 +37,8 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
using namespace MOShared;
+Q_DECLARE_METATYPE(Profile::Ptr)
+
ProfilesDialog::ProfilesDialog(const QString &gamePath, QWidget *parent)
: TutorableDialog("Profiles", parent), ui(new Ui::ProfilesDialog), m_GamePath(gamePath), m_FailState(false)
@@ -89,11 +91,11 @@ void ProfilesDialog::on_closeButton_clicked()
void ProfilesDialog::addItem(const QString &name)
{
try {
- QVariant temp;
+// QVariant temp;
QDir profileDir(name);
- temp.setValue(Profile(profileDir));
+// temp.setValue(Profile(profileDir));
QListWidgetItem *newItem = new QListWidgetItem(profileDir.dirName(), m_ProfilesList);
- newItem->setData(Qt::UserRole, temp);
+ newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(profileDir))));
m_FailState = false;
} catch (const std::exception& e) {
reportError(tr("failed to create profile: %1").arg(e.what()));
@@ -104,11 +106,11 @@ void ProfilesDialog::addItem(const QString &name)
void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings)
{
try {
- QVariant temp;
- temp.setValue(Profile(name, useDefaultSettings));
+// QVariant temp;
+// temp.setValue(Profile(name, useDefaultSettings));
QListWidget *profilesList = findChild<QListWidget*>("profilesList");
QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
- newItem->setData(Qt::UserRole, temp);
+ newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(new Profile(name, useDefaultSettings))));
profilesList->addItem(newItem);
m_FailState = false;
} catch (const std::exception&) {
@@ -121,13 +123,13 @@ void ProfilesDialog::createProfile(const QString &name, bool useDefaultSettings)
void ProfilesDialog::createProfile(const QString &name, const Profile &reference)
{
try {
- Profile newProfile = Profile::createFrom(name, reference);
+// Profile newProfile = Profile::createFrom(name, reference);
- QVariant temp;
- temp.setValue(newProfile);
+// QVariant temp;
+// temp.setValue(newProfile);
QListWidget *profilesList = findChild<QListWidget*>("profilesList");
QListWidgetItem *newItem = new QListWidgetItem(name, profilesList);
- newItem->setData(Qt::UserRole, temp);
+ newItem->setData(Qt::UserRole, QVariant::fromValue(Profile::Ptr(Profile::createPtrFrom(name, reference))));
profilesList->addItem(newItem);
m_FailState = false;
} catch (const std::exception&) {
@@ -160,8 +162,8 @@ void ProfilesDialog::on_copyProfileButton_clicked()
QListWidget *profilesList = findChild<QListWidget*>("profilesList");
try {
- const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- createProfile(name, currentProfile);
+ const Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile::Ptr>();
+ createProfile(name, *currentProfile);
} catch (const std::exception &e) {
reportError(tr("failed to copy profile: %1").arg(e.what()));
}
@@ -176,11 +178,11 @@ void ProfilesDialog::on_removeProfileButton_clicked()
if (confirmBox.exec() == QMessageBox::Yes) {
QListWidget *profilesList = findChild<QListWidget*>("profilesList");
- const Profile &currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile>();
+ Profile::Ptr currentProfile = profilesList->currentItem()->data(Qt::UserRole).value<Profile::Ptr>();
// on destruction, the profile object would write the profile.ini file again, so
// we have to get rid of the it before deleting the directory
- QString profilePath = currentProfile.getPath();
+ QString profilePath = currentProfile->getPath();
QListWidgetItem* item = profilesList->takeItem(profilesList->currentRow());
if (item != NULL) {
delete item;
@@ -190,6 +192,33 @@ void ProfilesDialog::on_removeProfileButton_clicked()
}
+void ProfilesDialog::on_renameButton_clicked()
+{
+ Profile::Ptr currentProfile = ui->profilesList->currentItem()->data(Qt::UserRole).value<Profile::Ptr>();
+
+ bool valid = false;
+ QString name;
+
+ while (!valid) {
+ bool ok = false;
+ name = QInputDialog::getText(this, tr("Rename Profile"), tr("New Name"),
+ QLineEdit::Normal, currentProfile->getName(),
+ &ok);
+ valid = fixDirectoryName(name);
+ if (!ok) {
+ return;
+ }
+ }
+
+ ui->profilesList->currentItem()->setText(name);
+ currentProfile->rename(name);
+
+// QVariant temp;
+// temp.setValue(currentProfile);
+// ui->profilesList->currentItem()->setData(Qt::UserRole, temp);
+}
+
+
void ProfilesDialog::on_invalidationBox_stateChanged(int state)
{
QListWidget *profilesList = findChild<QListWidget*>("profilesList");
@@ -206,11 +235,11 @@ void ProfilesDialog::on_invalidationBox_stateChanged(int state)
if (!currentProfileVariant.isValid() || currentProfileVariant.isNull()) {
return;
}
- const Profile &currentProfile = currentItem->data(Qt::UserRole).value<Profile>();
+ const Profile::Ptr currentProfile = currentItem->data(Qt::UserRole).value<Profile::Ptr>();
if (state == Qt::Unchecked) {
- currentProfile.deactivateInvalidation();
+ currentProfile->deactivateInvalidation();
} else {
- currentProfile.activateInvalidation(m_GamePath + "/data");
+ currentProfile->activateInvalidation(m_GamePath + "/data");
}
} catch (const std::exception &e) {
reportError(tr("failed to change archive invalidation state: %1").arg(e.what()));
@@ -225,18 +254,19 @@ void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current
QPushButton *copyButton = findChild<QPushButton*>("copyProfileButton");
QPushButton *removeButton = findChild<QPushButton*>("removeProfileButton");
QPushButton *transferButton = findChild<QPushButton*>("transferButton");
+ QPushButton *renameButton = findChild<QPushButton*>("renameButton");
if (current != NULL) {
- const Profile &currentProfile = current->data(Qt::UserRole).value<Profile>();
+ const Profile::Ptr currentProfile = current->data(Qt::UserRole).value<Profile::Ptr>();
try {
bool invalidationSupported = false;
invalidationBox->blockSignals(true);
- invalidationBox->setChecked(currentProfile.invalidationActive(&invalidationSupported));
+ invalidationBox->setChecked(currentProfile->invalidationActive(&invalidationSupported));
invalidationBox->setEnabled(invalidationSupported);
invalidationBox->blockSignals(false);
- bool localSaves = currentProfile.localSavesEnabled();
+ bool localSaves = currentProfile->localSavesEnabled();
transferButton->setEnabled(localSaves);
// prevent the stateChanged-event for the saves-box from triggering, otherwise it may think local saves
// were disabled and delete the files/rename the dir
@@ -246,23 +276,27 @@ void ProfilesDialog::on_profilesList_currentItemChanged(QListWidgetItem *current
copyButton->setEnabled(true);
removeButton->setEnabled(true);
+ renameButton->setEnabled(true);
} catch (const std::exception& E) {
reportError(tr("failed to determine if invalidation is active: %1").arg(E.what()));
copyButton->setEnabled(false);
removeButton->setEnabled(false);
+ renameButton->setEnabled(false);
invalidationBox->setChecked(false);
}
} else {
invalidationBox->setChecked(false);
copyButton->setEnabled(false);
removeButton->setEnabled(false);
+ renameButton->setEnabled(false);
}
}
void ProfilesDialog::on_localSavesBox_stateChanged(int state)
{
- Profile currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- if (currentProfile.enableLocalSaves(state == Qt::Checked)) {
+ Profile::Ptr currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile::Ptr>();
+
+ if (currentProfile->enableLocalSaves(state == Qt::Checked)) {
ui->transferButton->setEnabled(state == Qt::Checked);
} else {
// revert checkbox-state
@@ -272,7 +306,7 @@ void ProfilesDialog::on_localSavesBox_stateChanged(int state)
void ProfilesDialog::on_transferButton_clicked()
{
- const Profile &currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile>();
- TransferSavesDialog transferDialog(currentProfile, this);
+ const Profile::Ptr currentProfile = m_ProfilesList->currentItem()->data(Qt::UserRole).value<Profile::Ptr>();
+ TransferSavesDialog transferDialog(*currentProfile, this);
transferDialog.exec();
}
diff --git a/src/profilesdialog.h b/src/profilesdialog.h
index df08aa45..f864a7e0 100644
--- a/src/profilesdialog.h
+++ b/src/profilesdialog.h
@@ -83,6 +83,8 @@ private slots:
void on_transferButton_clicked();
+ void on_renameButton_clicked();
+
private:
Ui::ProfilesDialog *ui;
QString m_GamePath;
diff --git a/src/profilesdialog.ui b/src/profilesdialog.ui
index a2983436..0c952877 100644
--- a/src/profilesdialog.ui
+++ b/src/profilesdialog.ui
@@ -115,6 +115,16 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
+ <widget class="QPushButton" name="renameButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Rename</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QPushButton" name="transferButton">
<property name="enabled">
<bool>false</bool>
diff --git a/src/qtgroupingproxy.cpp b/src/qtgroupingproxy.cpp
new file mode 100644
index 00000000..5c1e0275
--- /dev/null
+++ b/src/qtgroupingproxy.cpp
@@ -0,0 +1,897 @@
+/****************************************************************************************
+ * Copyright (c) 2007-2011 Bart Cerneels <bart.cerneels@kde.org> *
+ * *
+ * This program 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 2 of the License, or (at your option) any later *
+ * version. *
+ * *
+ * This program 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 *
+ * this program. If not, see <http://www.gnu.org/licenses/>. *
+ ****************************************************************************************/
+
+#include "QtGroupingProxy.h"
+
+#include <QDebug>
+#include <QIcon>
+#include <QInputDialog>
+
+/*!
+ \class QtGroupingProxy
+ \brief The QtGroupingProxy class will group source model rows by adding a new top tree-level.
+ The source model can be flat or tree organized, but only the original top level rows are used
+ for determining the grouping.
+ \ingroup model-view
+*/
+
+QtGroupingProxy::QtGroupingProxy( QAbstractItemModel *model, QModelIndex rootNode, int groupedColumn )
+ : QAbstractProxyModel()
+ , m_rootNode( rootNode )
+ , m_groupedColumn( 0 )
+{
+ setSourceModel( model );
+
+ // signal proxies
+ connect( sourceModel(),
+ SIGNAL( dataChanged( const QModelIndex&, const QModelIndex& ) ),
+ this, SLOT( modelDataChanged( const QModelIndex&, const QModelIndex& ) )
+ );
+ connect( sourceModel(), SIGNAL( rowsInserted( const QModelIndex&, int, int ) ),
+ SLOT( modelRowsInserted( const QModelIndex &, int, int ) ) );
+ connect( sourceModel(), SIGNAL(rowsAboutToBeInserted( const QModelIndex &, int ,int )),
+ SLOT(modelRowsAboutToBeInserted( const QModelIndex &, int ,int )));
+ connect( sourceModel(), SIGNAL( rowsRemoved( const QModelIndex&, int, int ) ),
+ SLOT( modelRowsRemoved( const QModelIndex&, int, int ) ) );
+ connect( sourceModel(), SIGNAL(rowsAboutToBeRemoved( const QModelIndex &, int ,int )),
+ SLOT(modelRowsAboutToBeRemoved(QModelIndex,int,int)) );
+ connect( sourceModel(), SIGNAL(layoutChanged()), SLOT(buildTree()) );
+ connect( sourceModel(), SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ SLOT(modelDataChanged(QModelIndex,QModelIndex)) );
+
+ if( groupedColumn != -1 )
+ setGroupedColumn( groupedColumn );
+}
+
+QtGroupingProxy::~QtGroupingProxy()
+{
+}
+
+void
+QtGroupingProxy::setGroupedColumn( int groupedColumn )
+{
+ m_groupedColumn = groupedColumn;
+ buildTree();
+}
+
+/** Maps to what groups the source row belongs by returning the data of those groups.
+ *
+ * @returns a list of data for the rows the argument belongs to. In common cases this list will
+ * contain only one entry. An empty list means that the source item will be placed in the root of
+ * this proxyModel. There is no support for hiding source items.
+ *
+ * Group data can be pre-loaded in the return value so it's added to the cache maintained by this
+ * class. This is required if you want to have data that is not present in the source model.
+ */
+QList<RowData>
+QtGroupingProxy::belongsTo( const QModelIndex &idx )
+{
+ //qDebug() << __FILE__ << __FUNCTION__;
+ QList<RowData> rowDataList;
+
+ //get all the data for this index from the model
+ ItemData itemData = sourceModel()->itemData( idx );
+ QMapIterator<int, QVariant> i( itemData );
+ while( i.hasNext() )
+ {
+ i.next();
+ int role = i.key();
+ QVariant variant = i.value();
+ // qDebug() << "role " << role << " : (" << variant.typeName() << ") : "<< variant;
+ if( variant.type() == QVariant::List )
+ {
+ //a list of variants get's expanded to multiple rows
+ QVariantList list = variant.toList();
+ for( int i = 0; i < list.length(); i++ )
+ {
+ //take an existing row data or create a new one
+ RowData rowData = (rowDataList.count() > i) ? rowDataList.takeAt( i )
+ : RowData();
+
+ //we only gather data for the first column
+ ItemData indexData = rowData.contains( 0 ) ? rowData.take( 0 ) : ItemData();
+ indexData.insert( role, list.value( i ) );
+ rowData.insert( 0, indexData );
+ //for the grouped column the data should not be gathered from the children
+ //this will allow filtering on the content of this column with a
+ //QSortFilterProxyModel
+ rowData.insert( m_groupedColumn, indexData );
+ rowDataList.insert( i, rowData );
+ }
+ }
+ else if( !variant.isNull() )
+ {
+ //it's just a normal item. Copy all the data and break this loop.
+ RowData rowData;
+ rowData.insert( 0, itemData );
+ rowDataList << rowData;
+ break;
+ }
+ }
+
+ return rowDataList;
+}
+
+/* m_groupHash layout
+* key : index of the group in m_groupMaps
+* value : a QList of the original rows in sourceModel() for the children of this group
+*
+* key = -1 contains a QList of the non-grouped indexes
+*
+* TODO: sub-groups
+*/
+void
+QtGroupingProxy::buildTree()
+{
+ if( !sourceModel() )
+ return;
+ beginResetModel();
+
+ m_groupHash.clear();
+ //don't clear the data maps since most of it will probably be needed again.
+ m_parentCreateList.clear();
+
+ int max = sourceModel()->rowCount( m_rootNode );
+ //qDebug() << QString("building tree with %1 leafs.").arg( max );
+ //WARNING: these have to be added in order because the addToGroups function is optimized for
+ //modelRowsInserted(). Failure to do so will result in wrong data shown in the view at best.
+ for( int row = 0; row < max; row++ )
+ {
+ QModelIndex idx = sourceModel()->index( row, m_groupedColumn, m_rootNode );
+ addSourceRow( idx );
+ }
+ //dumpGroups();
+
+ endResetModel();
+
+ for( int row = 0; row < rowCount(); row++ ) {
+ QModelIndex idx = index( row, 0, QModelIndex() );
+ if (m_expandedItems.contains(idx.data(Qt::UserRole).toString())) {
+ emit expandItem(idx);
+ }
+ }
+}
+
+QList<int>
+QtGroupingProxy::addSourceRow( const QModelIndex &idx )
+{
+ QList<int> updatedGroups;
+
+ QList<RowData> groupData = belongsTo( idx );
+ //an empty list here means it's supposed to go in root.
+ if( groupData.isEmpty() )
+ {
+ updatedGroups << -1;
+ if( !m_groupHash.keys().contains( std::numeric_limits<quint32>::max() ) )
+ m_groupHash.insert( std::numeric_limits<quint32>::max(), QList<int>() ); //add an empty placeholder
+ }
+
+ //an item can be in multiple groups
+ foreach( RowData data, groupData )
+ {
+ int updatedGroup = -1;
+ if( !data.isEmpty() )
+ {
+ // qDebug() << QString("index %1 belongs to group %2").arg( row )
+ // .arg( data[0][Qt::DisplayRole].toString() );
+
+ foreach( const RowData &cachedData, m_groupMaps )
+ {
+ //when this matches the index belongs to an existing group
+ if( data[0][Qt::DisplayRole] == cachedData[0][Qt::DisplayRole] )
+ {
+ data = cachedData;
+ break;
+ }
+ }
+
+ updatedGroup = m_groupMaps.indexOf( data );
+ //-1 means not found
+ if( updatedGroup == -1 )
+ {
+ //new groups are added to the end of the existing list
+ m_groupMaps << data;
+ updatedGroup = m_groupMaps.count() - 1;
+ }
+
+ if( !m_groupHash.keys().contains( updatedGroup ) )
+ m_groupHash.insert( updatedGroup, QList<int>() ); //add an empty placeholder
+ }
+
+ if( !updatedGroups.contains( updatedGroup ) )
+ updatedGroups << updatedGroup;
+ }
+
+
+ //update m_groupHash to the new source-model layout (one row added)
+ QMutableHashIterator<quint32, QList<int> > i( m_groupHash );
+ while( i.hasNext() )
+ {
+ i.next();
+ QList<int> &groupList = i.value();
+ int insertedProxyRow = groupList.count();
+ for( ; insertedProxyRow > 0 ; insertedProxyRow-- )
+ {
+ int &rowValue = groupList[insertedProxyRow-1];
+ if( idx.row() <= rowValue )
+ {
+ //increment the rows that come after the new row since they moved one place up.
+ rowValue++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( updatedGroups.contains( i.key() ) )
+ {
+ //the row needs to be added to this group
+ beginInsertRows( index( i.key() ), insertedProxyRow, insertedProxyRow );
+ groupList.insert( insertedProxyRow, idx.row() );
+ endInsertRows();
+ }
+ }
+
+ return updatedGroups;
+}
+
+/** Each ModelIndex has in it's internalId a position in the parentCreateList.
+ * struct ParentCreate are the instructions to recreate the parent index.
+ * It contains the proxy row number of the parent and the postion in this list of the grandfather.
+ * This function creates the ParentCreate structs and saves them in a list.
+ */
+int
+QtGroupingProxy::indexOfParentCreate( const QModelIndex &parent ) const
+{
+ if( !parent.isValid() )
+ return -1;
+
+ struct ParentCreate pc;
+ for( int i = 0 ; i < m_parentCreateList.size() ; i++ )
+ {
+ pc = m_parentCreateList[i];
+ if( pc.parentCreateIndex == parent.internalId() && pc.row == parent.row() )
+ return i;
+ }
+ //there is no parentCreate yet for this index, so let's create one.
+ pc.parentCreateIndex = parent.internalId();
+ pc.row = parent.row();
+ m_parentCreateList << pc;
+
+ //dumpParentCreateList();
+ // qDebug() << QString( "m_parentCreateList: (%1)" ).arg( m_parentCreateList.size() );
+ // for( int i = 0 ; i < m_parentCreateList.size() ; i++ )
+ // {
+ // qDebug() << i << " : " << m_parentCreateList[i].parentCreateIndex <<
+ // " | " << m_parentCreateList[i].row;
+ // }
+
+ return m_parentCreateList.size() - 1;
+}
+
+QModelIndex
+QtGroupingProxy::index( int row, int column, const QModelIndex &parent ) const
+{
+ // qDebug() << "index requested for: (" << row << "," << column << "), " << parent;
+ if( !hasIndex(row, column, parent) )
+ return QModelIndex();
+
+ if( parent.column() > 0 )
+ return QModelIndex();
+
+ /* We save the instructions to make the parent of the index in a struct.
+ * The place of the struct in the list is stored in the internalId
+ */
+ int parentCreateIndex = indexOfParentCreate( parent );
+
+ return createIndex( row, column, parentCreateIndex );
+}
+
+QModelIndex
+QtGroupingProxy::parent( const QModelIndex &index ) const
+{
+ //qDebug() << "parent: " << index;
+ if( !index.isValid() )
+ return QModelIndex();
+
+ int parentCreateIndex = index.internalId();
+ //qDebug() << "parentCreateIndex: " << parentCreateIndex;
+ if( parentCreateIndex == -1 || parentCreateIndex >= m_parentCreateList.count() )
+ return QModelIndex();
+
+ struct ParentCreate pc = m_parentCreateList[parentCreateIndex];
+ //qDebug() << "parentCreate: (" << pc.parentCreateIndex << "," << pc.row << ")";
+ //only items at column 0 have children
+ return createIndex( pc.row, 0, pc.parentCreateIndex );
+}
+
+int
+QtGroupingProxy::rowCount( const QModelIndex &index ) const
+{
+ //qDebug() << "rowCount: " << index;
+ if( !index.isValid() )
+ {
+ //the number of top level groups + the number of non-grouped items
+ int rows = m_groupMaps.count() + m_groupHash.value( std::numeric_limits<quint32>::max() ).count();
+ //qDebug() << rows << " in root group";
+ return rows;
+ }
+
+ //TODO:group in group support.
+ if( isGroup( index ) )
+ {
+ qint64 groupIndex = index.row();
+ int rows = m_groupHash.value( groupIndex ).count();
+ //qDebug() << rows << " in group " << m_groupMaps[groupIndex];
+ return rows;
+ }
+
+ QModelIndex originalIndex = mapToSource( index );
+ int rowCount = sourceModel()->rowCount( originalIndex );
+ //qDebug() << "original item: rowCount == " << rowCount;
+ return rowCount;
+}
+
+int
+QtGroupingProxy::columnCount( const QModelIndex &index ) const
+{
+ if( !index.isValid() )
+ return sourceModel()->columnCount( m_rootNode );
+
+ if( index.column() != 0 )
+ return 0;
+
+ return sourceModel()->columnCount( mapToSource( index ) );
+}
+
+QVariant
+QtGroupingProxy::data( const QModelIndex &index, int role ) const
+{
+ if( !index.isValid() )
+ return QVariant();
+ // qDebug() << __FUNCTION__ << index << " role: " << role;
+ int row = index.row();
+ int column = index.column();
+ if( isGroup( index ) )
+ {
+ if (column != 0) return QVariant();
+
+ //qDebug() << __FUNCTION__ << "is a group";
+ //use cached or precalculated data
+ if( m_groupMaps[row][column].contains( Qt::DisplayRole ) )
+ {
+ //qDebug() << "Using cached data";
+ switch (role) {
+ case Qt::DisplayRole: {
+ QString value = m_groupMaps[row][column].value( role ).toString();
+ return QString("----- %1 -----").arg(value.isEmpty() ? tr("<unset>") : value);
+ } break;
+ case Qt::ForegroundRole: {
+ return QBrush(Qt::gray);
+ } break;
+ case Qt::FontRole: {
+ QFont font(m_groupMaps[row][column].value(Qt::FontRole).value<QFont>());
+ font.setItalic(true);
+ return font;
+ } break;
+ case Qt::TextAlignmentRole: {
+ return Qt::AlignHCenter;
+ } break;
+ case Qt::UserRole: {
+ return m_groupMaps[row][column].value( Qt::DisplayRole ).toString();
+ } break;
+ default: {
+ return QVariant();
+ // return m_groupMaps[row][column].value( role );
+ } break;
+ }
+ }
+
+ //for column 0 we gather data from the grouped column instead
+ if( column == 0 )
+ column = m_groupedColumn;
+
+ //map all data from children to columns of group to allow grouping one level up
+ QVariantList variantsOfChildren;
+ int childCount = m_groupHash.value( row ).count();
+ if( childCount == 0 )
+ return QVariant();
+
+ //qDebug() << __FUNCTION__ << "childCount: " << childCount;
+ //Need a parentIndex with column == 0 because only those have children.
+ QModelIndex parentIndex = this->index( row, 0, index.parent() );
+ for( int childRow = 0; childRow < childCount; childRow++ )
+ {
+ QModelIndex childIndex = this->index( childRow, column, parentIndex );
+ QVariant data = mapToSource( childIndex ).data( role );
+ //qDebug() << __FUNCTION__ << data << QVariant::typeToName(data.type());
+ if( data.isValid() && !variantsOfChildren.contains( data ) )
+ variantsOfChildren << data;
+ }
+ //qDebug() << "gathered this data from children: " << variantsOfChildren;
+ //saving in cache
+ ItemData roleMap = m_groupMaps[row].value( column );
+ foreach( const QVariant &variant, variantsOfChildren )
+ {
+ if( roleMap[ role ] != variant )
+ roleMap.insert( role, variantsOfChildren );
+ }
+
+ //qDebug() << QString("roleMap[%1]:").arg(role) << roleMap[role];
+ //only one unique variant? No need to return a list
+ if( variantsOfChildren.count() == 1 )
+ return variantsOfChildren.first();
+
+ if( variantsOfChildren.count() == 0 )
+ return QVariant();
+
+ return variantsOfChildren;
+ }
+
+ return mapToSource( index ).data( role );
+}
+
+bool
+QtGroupingProxy::setData( const QModelIndex &idx, const QVariant &value, int role )
+{
+ if( !idx.isValid() )
+ return false;
+
+ //no need to set data to exactly the same value
+ if( idx.data( role ) == value )
+ return false;
+
+ if( isGroup( idx ) )
+ {
+ ItemData columnData = m_groupMaps[idx.row()][idx.column()];
+
+ columnData.insert( role, value );
+ //QItemDelegate will always use Qt::EditRole
+ if( role == Qt::EditRole )
+ columnData.insert( Qt::DisplayRole, value );
+
+ //and make sure it's stored in the map
+ m_groupMaps[idx.row()].insert( idx.column(), columnData );
+
+ int columnToChange = idx.column() ? idx.column() : m_groupedColumn;
+ foreach( int originalRow, m_groupHash.value( idx.row() ) )
+ {
+ QModelIndex childIdx = sourceModel()->index( originalRow, columnToChange,
+ m_rootNode );
+ if( childIdx.isValid() )
+ sourceModel()->setData( childIdx, value, role );
+ }
+ //TODO: we might need to reload the data from the children at this point
+
+ emit dataChanged( idx, idx );
+ return true;
+ }
+
+ return sourceModel()->setData( mapToSource( idx ), value, role );
+}
+
+bool
+QtGroupingProxy::isGroup( const QModelIndex &index ) const
+{
+ int parentCreateIndex = index.internalId();
+ if( parentCreateIndex == -1 && index.row() < m_groupMaps.count() )
+ return true;
+ return false;
+}
+
+QModelIndex
+QtGroupingProxy::mapToSource( const QModelIndex &index ) const
+{
+ //qDebug() << "mapToSource: " << index;
+ if( !index.isValid() )
+ return m_rootNode;
+
+ if( isGroup( index ) )
+ {
+ //qDebug() << "is a group: " << index.data( Qt::DisplayRole ).toString();
+ return m_rootNode;
+ }
+
+ QModelIndex proxyParent = index.parent();
+ //qDebug() << "parent: " << proxyParent;
+ QModelIndex originalParent = mapToSource( proxyParent );
+ //qDebug() << "originalParent: " << originalParent;
+ int originalRow = index.row();
+ if( originalParent == m_rootNode )
+ {
+ int indexInGroup = index.row();
+ if( !proxyParent.isValid() )
+ indexInGroup -= m_groupMaps.count();
+ //qDebug() << "indexInGroup" << indexInGroup;
+ QList<int> childRows = m_groupHash.value( proxyParent.row() );
+ if( childRows.isEmpty() || indexInGroup >= childRows.count() || indexInGroup < 0 )
+ return QModelIndex();
+
+ originalRow = childRows.at( indexInGroup );
+ //qDebug() << "originalRow: " << originalRow;
+ }
+ return sourceModel()->index( originalRow, index.column(), originalParent );
+}
+
+QModelIndexList
+QtGroupingProxy::mapToSource( const QModelIndexList& list ) const
+{
+ QModelIndexList originalList;
+ foreach( const QModelIndex &index, list )
+ {
+ QModelIndex originalIndex = mapToSource( index );
+ if( originalIndex.isValid() )
+ originalList << originalIndex;
+ }
+ return originalList;
+}
+
+QModelIndex
+QtGroupingProxy::mapFromSource( const QModelIndex &idx ) const
+{
+ if( !idx.isValid() )
+ return QModelIndex();
+
+ QModelIndex proxyParent;
+ QModelIndex sourceParent = idx.parent();
+ //qDebug() << "sourceParent: " << sourceParent;
+ int proxyRow = idx.row();
+ int sourceRow = idx.row();
+
+ if( sourceParent.isValid() && ( sourceParent != m_rootNode ) )
+ {
+ //idx is a child of one of the items in the source model
+ proxyParent = mapFromSource( sourceParent );
+ }
+ else
+ {
+ //idx is an item in the top level of the source model (child of the rootnode)
+ int groupRow = -1;
+ QHashIterator<quint32, QList<int> > iterator( m_groupHash );
+ while( iterator.hasNext() )
+ {
+ iterator.next();
+ if( iterator.value().contains( sourceRow ) )
+ {
+ groupRow = iterator.key();
+ break;
+ }
+ }
+
+ if( groupRow != -1 ) //it's in a group, let's find the correct row.
+ {
+ proxyParent = this->index( groupRow, 0, QModelIndex() );
+ proxyRow = m_groupHash.value( groupRow ).indexOf( sourceRow );
+ }
+ else
+ {
+ proxyParent = QModelIndex();
+ // if the proxy item is not in a group it will be below the groups.
+ int groupLength = m_groupMaps.count();
+ //qDebug() << "groupNames length: " << groupLength;
+ int i = m_groupHash.value( std::numeric_limits<quint32>::max() ).indexOf( sourceRow );
+ //qDebug() << "index in hash: " << i;
+ proxyRow = groupLength + i;
+ }
+ }
+
+ //qDebug() << "proxyParent: " << proxyParent;
+ //qDebug() << "proxyRow: " << proxyRow;
+ return this->index( proxyRow, 0, proxyParent );
+}
+
+Qt::ItemFlags
+QtGroupingProxy::flags( const QModelIndex &idx ) const
+{
+ if( !idx.isValid() )
+ {
+ Qt::ItemFlags rootFlags = sourceModel()->flags( m_rootNode );
+ if( rootFlags.testFlag( Qt::ItemIsDropEnabled ) )
+ return Qt::ItemFlags( Qt::ItemIsDropEnabled );
+
+ return 0;
+ }
+ //only if the grouped column has the editable flag set allow the
+ //actions leading to setData on the source (edit & drop)
+ // qDebug() << idx;
+ if( isGroup( idx ) )
+ {
+ // dumpGroups();
+ // Qt::ItemFlags defaultFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
+ Qt::ItemFlags defaultFlags(Qt::NoItemFlags);
+ bool groupIsEditable = true;
+
+ //it's possible to have empty groups
+ if( m_groupHash.value( idx.row() ).count() == 0 )
+ {
+ //check the flags of this column with the root node
+ QModelIndex originalRootNode = sourceModel()->index( m_rootNode.row(), m_groupedColumn,
+ m_rootNode.parent() );
+ groupIsEditable = originalRootNode.flags().testFlag( Qt::ItemIsEditable );
+ }
+ else
+ {
+ foreach( int originalRow, m_groupHash.value( idx.row() ) )
+ {
+ QModelIndex originalIdx = sourceModel()->index( originalRow, m_groupedColumn,
+ m_rootNode );
+
+ groupIsEditable = groupIsEditable
+ ? originalIdx.flags().testFlag( Qt::ItemIsEditable )
+ : false;
+ if( !groupIsEditable ) //all children need to have an editable grouped column
+ break;
+ }
+ }
+
+ if( groupIsEditable )
+ return ( defaultFlags | Qt::ItemIsEditable | Qt::ItemIsDropEnabled );
+ return defaultFlags;
+ }
+
+ QModelIndex originalIdx = mapToSource( idx );
+ Qt::ItemFlags originalItemFlags = sourceModel()->flags( originalIdx );
+
+ //check the source model to see if the grouped column is editable;
+ QModelIndex groupedColumnIndex =
+ sourceModel()->index( originalIdx.row(), m_groupedColumn, originalIdx.parent() );
+ bool groupIsEditable = sourceModel()->flags( groupedColumnIndex ).testFlag( Qt::ItemIsEditable );
+ if( groupIsEditable )
+ return originalItemFlags | Qt::ItemIsDragEnabled;
+
+ return originalItemFlags;
+}
+
+QVariant
+QtGroupingProxy::headerData( int section, Qt::Orientation orientation, int role ) const
+{
+ return sourceModel()->headerData( section, orientation, role );
+}
+
+bool
+QtGroupingProxy::canFetchMore( const QModelIndex &parent ) const
+{
+ if( !parent.isValid() )
+ return false;
+
+ if( isGroup( parent ) )
+ return false;
+
+ return sourceModel()->canFetchMore( mapToSource( parent ) );
+}
+
+void
+QtGroupingProxy::fetchMore ( const QModelIndex & parent )
+{
+ if( !parent.isValid() )
+ return;
+
+ if( isGroup( parent ) )
+ return;
+
+ return sourceModel()->fetchMore( mapToSource( parent ) );
+}
+
+QModelIndex
+QtGroupingProxy::addEmptyGroup( const RowData &data )
+{
+ int newRow = m_groupMaps.count();
+ beginInsertRows( QModelIndex(), newRow, newRow );
+ m_groupMaps << data;
+ endInsertRows();
+ return index( newRow, 0, QModelIndex() );
+}
+
+bool
+QtGroupingProxy::removeGroup( const QModelIndex &idx )
+{
+ beginRemoveRows( idx.parent(), idx.row(), idx.row() );
+ m_groupHash.remove( idx.row() );
+ m_groupMaps.removeAt( idx.row() );
+ m_parentCreateList.removeAt( idx.internalId() );
+ endRemoveRows();
+
+ //TODO: only true if all data could be unset.
+ return true;
+}
+
+bool
+QtGroupingProxy::hasChildren( const QModelIndex &parent ) const
+{
+ if( !parent.isValid() )
+ return true;
+
+ if( isGroup( parent ) )
+ return !m_groupHash.value( parent.row() ).isEmpty();
+
+ return sourceModel()->hasChildren( mapToSource( parent ) );
+}
+
+void
+QtGroupingProxy::modelRowsAboutToBeInserted( const QModelIndex &parent, int start, int end )
+{
+ if( parent != m_rootNode )
+ {
+ //an item will be added to an original index, remap and pass it on
+ // qDebug() << parent;
+ QModelIndex proxyParent = mapFromSource( parent );
+ // qDebug() << proxyParent;
+ beginInsertRows( proxyParent, start, end );
+ }
+}
+
+void
+QtGroupingProxy::modelRowsInserted( const QModelIndex &parent, int start, int end )
+{
+ if( parent == m_rootNode )
+ {
+ //top level of the model changed, these new rows need to be put in groups
+ for( int modelRow = start; modelRow <= end ; modelRow++ )
+ {
+ addSourceRow( sourceModel()->index( modelRow, m_groupedColumn, m_rootNode ) );
+ }
+ }
+ else
+ {
+ //an item was added to an original index, remap and pass it on
+ QModelIndex proxyParent = mapFromSource( parent );
+ qDebug() << proxyParent;
+ //beginInsertRows had to be called in modelRowsAboutToBeInserted()
+ endInsertRows();
+ }
+}
+
+void
+QtGroupingProxy::modelRowsAboutToBeRemoved( const QModelIndex &parent, int start, int end )
+{
+ if( parent == m_rootNode )
+ {
+ QHash<quint32, QList<int> >::const_iterator i;
+ //HACK, we are going to call beginRemoveRows() multiple times without
+ // endRemoveRows() if a source index is in multiple groups.
+ // This can be a problem for some views/proxies, but Q*Views can handle it.
+ // TODO: investigate a queue for applying proxy model changes in the correct order
+ for( i = m_groupHash.constBegin(); i != m_groupHash.constEnd(); ++i )
+ {
+ int groupIndex = i.key();
+ const QList<int> &groupList = i.value();
+ QModelIndex proxyParent = index( groupIndex, 0 );
+ foreach( int originalRow, groupList )
+ {
+ if( originalRow >= start && originalRow <= end )
+ {
+ int proxyRow = groupList.indexOf( originalRow );
+ if( groupIndex == -1 ) //adjust for non-grouped (root level) original items
+ proxyRow += m_groupMaps.count();
+ //TODO: optimize for continues original rows in the same group
+ beginRemoveRows( proxyParent, proxyRow, proxyRow );
+ }
+ }
+ }
+ }
+ else
+ {
+ //child item(s) of an original item will be removed, remap and pass it on
+ // qDebug() << parent;
+ QModelIndex proxyParent = mapFromSource( parent );
+ // qDebug() << proxyParent;
+ beginRemoveRows( proxyParent, start, end );
+ }
+}
+
+void
+QtGroupingProxy::modelRowsRemoved( const QModelIndex &parent, int start, int end )
+{
+ if( parent == m_rootNode )
+ {
+ //TODO: can be optimised by iterating over m_groupHash and checking start <= r < end
+
+ //rather than increasing i we change the stored sourceRows in-place and reuse argument start
+ //X-times (where X = end - start).
+ for( int i = start; i <= end; i++ )
+ {
+ //HACK: we are going to iterate the hash in reverse so calls to endRemoveRows()
+ // are matched up with the beginRemoveRows() in modelRowsAboutToBeRemoved()
+ //NOTE: easier to do reverse with java style iterator
+ QMutableHashIterator<quint32, QList<int> > iter( m_groupHash );
+ iter.toBack();
+ while( iter.hasPrevious() )
+ {
+ iter.previous();
+ int groupIndex = iter.key();
+ //has to be a modifiable reference for remove and replace operations
+ QList<int> &groupList = iter.value();
+ int rowIndex = groupList.indexOf( start );
+ if( rowIndex != -1 )
+ {
+ QModelIndex proxyParent = index( groupIndex, 0 );
+ groupList.removeAt( rowIndex );
+ }
+ //Now decrement all source rows that are after the removed row
+ for( int j = 0; j < groupList.count(); j++ )
+ {
+ int sourceRow = groupList.at( j );
+ if( sourceRow > start )
+ groupList.replace( j, sourceRow-1 );
+ }
+ if( rowIndex != -1)
+ endRemoveRows(); //end remove operation only after group was updated.
+ }
+ }
+
+ return;
+ }
+
+ //beginRemoveRows had to be called in modelRowsAboutToBeRemoved();
+ endRemoveRows();
+}
+
+void
+QtGroupingProxy::modelDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight )
+{
+ //TODO: need to look in the groupedColumn and see if it changed and changed grouping accordingly
+ QModelIndex proxyTopLeft = mapFromSource( topLeft );
+ if( !proxyTopLeft.isValid() )
+ return;
+
+ if( topLeft == bottomRight )
+ {
+ emit dataChanged( proxyTopLeft, proxyTopLeft );
+ }
+ else
+ {
+ QModelIndex proxyBottomRight = mapFromSource( bottomRight );
+ emit dataChanged( proxyTopLeft, proxyBottomRight );
+ }
+}
+
+bool
+QtGroupingProxy::isAGroupSelected( const QModelIndexList& list ) const
+{
+ foreach( const QModelIndex &index, list )
+ {
+ if( isGroup( index ) )
+ return true;
+ }
+ return false;
+}
+
+void
+QtGroupingProxy::dumpGroups() const
+{
+ qDebug() << "m_groupHash: ";
+ for( int groupIndex = -1; groupIndex < m_groupHash.keys().count() - 1; groupIndex++ )
+ {
+ qDebug() << groupIndex << " : " << m_groupHash.value( groupIndex );
+ }
+
+ qDebug() << "m_groupMaps: ";
+ for( int groupIndex = 0; groupIndex < m_groupMaps.count(); groupIndex++ )
+ qDebug() << m_groupMaps[groupIndex] << ": " << m_groupHash.value( groupIndex );
+ qDebug() << m_groupHash.value( std::numeric_limits<quint32>::max() );
+}
+
+
+void QtGroupingProxy::expanded(const QModelIndex &index)
+{
+ m_expandedItems.insert(index.data(Qt::UserRole).toString());
+}
+
+void QtGroupingProxy::collapsed(const QModelIndex &index)
+{
+ m_expandedItems.remove(index.data(Qt::UserRole).toString());
+}
diff --git a/src/qtgroupingproxy.h b/src/qtgroupingproxy.h
new file mode 100644
index 00000000..6ec34481
--- /dev/null
+++ b/src/qtgroupingproxy.h
@@ -0,0 +1,144 @@
+/****************************************************************************************
+ * Copyright (c) 2007-2010 Bart Cerneels <bart.cerneels@kde.org> *
+ * *
+ * This program 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 2 of the License, or (at your option) any later *
+ * version. *
+ * *
+ * This program 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 *
+ * this program. If not, see <http://www.gnu.org/licenses/>. *
+ ****************************************************************************************/
+
+#ifndef GROUPINGPROXY_H
+#define GROUPINGPROXY_H
+
+#include <QAbstractProxyModel>
+#include <QModelIndex>
+#include <QMultiHash>
+#include <QStringList>
+#include <QIcon>
+#include <QSet>
+
+typedef QMap<int, QVariant> ItemData;
+typedef QMap<int, ItemData> RowData;
+
+class QtGroupingProxy : public QAbstractProxyModel
+{
+ Q_OBJECT
+public:
+ explicit QtGroupingProxy( QAbstractItemModel *model, QModelIndex rootNode = QModelIndex(),
+ int groupedColumn = -1 );
+ ~QtGroupingProxy();
+
+ void setGroupedColumn( int groupedColumn );
+
+ /* QAbstractProxyModel methods */
+ virtual QModelIndex index( int, int c = 0,
+ const QModelIndex& parent = QModelIndex() ) const;
+ virtual Qt::ItemFlags flags( const QModelIndex &idx ) const;
+ virtual QModelIndex parent( const QModelIndex &idx ) const;
+ virtual int rowCount( const QModelIndex &idx = QModelIndex() ) const;
+ virtual int columnCount( const QModelIndex &idx ) const;
+ virtual QModelIndex mapToSource( const QModelIndex &idx ) const;
+ virtual QModelIndexList mapToSource( const QModelIndexList &list ) const;
+ virtual QModelIndex mapFromSource( const QModelIndex &idx ) const;
+ virtual QVariant data( const QModelIndex &idx, int role ) const;
+ virtual bool setData( const QModelIndex &index, const QVariant &value,
+ int role = Qt::EditRole );
+ virtual QVariant headerData ( int section, Qt::Orientation orientation,
+ int role ) const;
+ virtual bool canFetchMore( const QModelIndex &parent ) const;
+ virtual void fetchMore( const QModelIndex &parent );
+ virtual bool hasChildren( const QModelIndex &parent = QModelIndex() ) const;
+
+ /* QtGroupingProxy methods */
+ virtual QModelIndex addEmptyGroup( const RowData &data );
+ virtual bool removeGroup( const QModelIndex &idx );
+
+ QStringList expandedState();
+
+signals:
+ void expandItem(const QModelIndex &index);
+
+public slots:
+ /**
+ * @brief update expanded state
+ * @param index index of the expanded/collapsed item (from the base model!)
+ */
+ void expanded(const QModelIndex &index);
+ /**
+ * @brief update expanded state
+ * @param index index of the expanded/collapsed item (from the base model!)
+ */
+ void collapsed(const QModelIndex &index);
+protected slots:
+ virtual void buildTree();
+
+private slots:
+ void modelDataChanged( const QModelIndex &, const QModelIndex & );
+ void modelRowsAboutToBeInserted( const QModelIndex &, int ,int );
+ void modelRowsInserted( const QModelIndex &, int, int );
+ void modelRowsAboutToBeRemoved( const QModelIndex &, int ,int );
+ void modelRowsRemoved( const QModelIndex &, int, int );
+
+protected:
+ /** Maps an item to a group.
+ * The return value is a list because an item can put in multiple groups.
+ * Inside the list is a 2 dimensional map.
+ * Mapped to column-number is another map of role-number to QVariant.
+ * This data prepolulates the group-data cache. The rest is gathered on demand
+ * from the children of the group.
+ */
+ virtual QList<RowData> belongsTo( const QModelIndex &idx );
+
+ /**
+ * calls belongsTo(), checks cached data and adds the index to existing or new groups.
+ * @returns the groups this index was added to where -1 means it was added to the root.
+ */
+ QList<int> addSourceRow( const QModelIndex &idx );
+
+ bool isGroup( const QModelIndex &index ) const;
+ bool isAGroupSelected( const QModelIndexList &list ) const;
+
+ /** Maintains the group -> sourcemodel row mapping
+ * The reason a QList<int> is use instead of a QMultiHash is that the values have to be
+ * reordered when rows are inserted or removed.
+ * TODO:use some auto-incrementing container class (steveire's?) for the list
+ */
+ QHash<quint32, QList<int> > m_groupHash;
+ /** The data cache of the groups.
+ * This can be pre-loaded with data in belongsTo()
+ */
+ QList<RowData> m_groupMaps;
+
+ /** "instuctions" how to create an item in the tree.
+ * This is used by parent( QModelIndex )
+ */
+ struct ParentCreate
+ {
+ int parentCreateIndex;
+ int row;
+ };
+ mutable QList<struct ParentCreate> m_parentCreateList;
+ /** @returns index of the "instructions" to recreate the parent. Will create new if it doesn't exist yet.
+ */
+ int indexOfParentCreate( const QModelIndex &parent ) const;
+
+ QModelIndexList m_selectedGroups;
+
+ QModelIndex m_rootNode;
+ int m_groupedColumn;
+
+ /* debug function */
+ void dumpGroups() const;
+
+private:
+ QSet<QString> m_expandedItems;
+};
+
+#endif //GROUPINGPROXY_H
diff --git a/src/simpleinstalldialog.cpp b/src/simpleinstalldialog.cpp
deleted file mode 100644
index ef694780..00000000
--- a/src/simpleinstalldialog.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#include "simpleinstalldialog.h"
-#include "ui_simpleinstalldialog.h"
-
-SimpleInstallDialog::SimpleInstallDialog(const QString &preset, QWidget *parent) :
- QDialog(parent), ui(new Ui::SimpleInstallDialog), m_Manual(false)
-{
- ui->setupUi(this);
- ui->nameEdit->setText(preset);
- setWindowFlags(windowFlags() & (~Qt::WindowContextHelpButtonHint));
-}
-
-SimpleInstallDialog::~SimpleInstallDialog()
-{
- delete ui;
-}
-
-QString SimpleInstallDialog::getName() const
-{
- return ui->nameEdit->text();
-}
-
-void SimpleInstallDialog::on_okBtn_clicked()
-{
- this->accept();
-}
-
-void SimpleInstallDialog::on_cancelBtn_clicked()
-{
- this->reject();
-}
-
-void SimpleInstallDialog::on_manualBtn_clicked()
-{
- m_Manual = true;
- this->reject();
-}
diff --git a/src/simpleinstalldialog.h b/src/simpleinstalldialog.h
deleted file mode 100644
index 5e8765de..00000000
--- a/src/simpleinstalldialog.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-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 <http://www.gnu.org/licenses/>.
-*/
-
-#ifndef SIMPLEINSTALLDIALOG_H
-#define SIMPLEINSTALLDIALOG_H
-
-#include <QDialog>
-
-namespace Ui {
- class SimpleInstallDialog;
-}
-
-/**
- * @brief Dialog for the installation of a simple archive
- * a simple archive is one that doesn't require any manual changes to work correctly
- **/
-class SimpleInstallDialog : public QDialog
-{
- Q_OBJECT
-
-public:
- /**
- * @brief constructor
- *
- * @param preset suggested name for the mod
- * @param parent parent widget
- **/
- explicit SimpleInstallDialog(const QString &preset, QWidget *parent = 0);
- ~SimpleInstallDialog();
-
- /**
- * @return true if the user requested the manual installation dialog
- **/
- bool manualRequested() const { return m_Manual; }
- /**
- * @return the (user-modified) mod name
- **/
- QString getName() const;
-
-private slots:
-
- void on_okBtn_clicked();
-
- void on_cancelBtn_clicked();
-
- void on_manualBtn_clicked();
-
-private:
- Ui::SimpleInstallDialog *ui;
- bool m_Manual;
-};
-
-#endif // SIMPLEINSTALLDIALOG_H