mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Launcher now supports submods. See forum thread for details.
This commit is contained in:
		| @@ -106,6 +106,18 @@ QString CModEntry::getName() const | ||||
|  | ||||
| QVariant CModEntry::getValue(QString value) const | ||||
| { | ||||
| 	if (repository.contains(value) && localData.contains(value)) | ||||
| 	{ | ||||
| 		// value is present in both repo and locally installed. Select one from latest version | ||||
| 		QString installedVer = localData["installedVersion"].toString(); | ||||
| 		QString availableVer = repository["latestVersion"].toString(); | ||||
|  | ||||
| 		if (compareVersions(installedVer, availableVer)) | ||||
| 			return repository[value]; | ||||
| 		else | ||||
| 			return localData[value]; | ||||
| 	} | ||||
|  | ||||
| 	if (repository.contains(value)) | ||||
| 		return repository[value]; | ||||
|  | ||||
| @@ -149,15 +161,36 @@ void CModList::setModSettings(QVariant data) | ||||
| 	modSettings = data.toMap(); | ||||
| } | ||||
|  | ||||
| void CModList::modChanged(QString modID) | ||||
| { | ||||
| } | ||||
|  | ||||
| static QVariant getValue(QVariantMap input, QString path) | ||||
| { | ||||
| 	if (path.size() > 1) | ||||
| 	{ | ||||
| 		QString entryName = path.section('/', 0, 1); | ||||
| 		QString remainder = "/" + path.section('/', 2, -1); | ||||
|  | ||||
| 		entryName.remove(0, 1); | ||||
| 		return getValue(input.value(entryName).toMap(), remainder); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return input; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| CModEntry CModList::getMod(QString modname) const | ||||
| { | ||||
| 	assert(hasMod(modname)); | ||||
|  | ||||
| 	QVariantMap repo; | ||||
| 	QVariantMap local = localModList[modname].toMap(); | ||||
| 	QVariantMap settings; | ||||
|  | ||||
| 	QVariant conf = modSettings[modname]; | ||||
| 	QString path = modname; | ||||
| 	path = "/" + path.replace(".", "/mods/"); | ||||
| 	QVariant conf = getValue(modSettings, path); | ||||
|  | ||||
| 	if (conf.isNull()) | ||||
| 	{ | ||||
| 		settings["active"] = true; // default | ||||
| @@ -165,22 +198,22 @@ CModEntry CModList::getMod(QString modname) const | ||||
| 	else | ||||
| 	{ | ||||
| 		if (conf.canConvert<QVariantMap>()) | ||||
| 			settings = modSettings[modname].toMap(); | ||||
| 			settings = conf.toMap(); | ||||
| 		else | ||||
| 			settings.insert("active", conf); | ||||
| 	} | ||||
|  | ||||
| 	for (auto entry : repositories) | ||||
| 	{ | ||||
| 		if (entry.contains(modname)) | ||||
| 		QVariant repoVal = getValue(entry, path); | ||||
| 		if (repoVal.isValid()) | ||||
| 		{ | ||||
| 			if (repo.empty()) | ||||
| 				repo = entry[modname].toMap(); | ||||
| 				repo = repoVal.toMap(); | ||||
| 			else | ||||
| 			{ | ||||
| 				if (CModEntry::compareVersions(repo["version"].toString(), | ||||
| 				                               entry[modname].toMap()["version"].toString())) | ||||
| 					repo = entry[modname].toMap(); | ||||
| 				if (CModEntry::compareVersions(repo["version"].toString(), repoVal.toMap()["version"].toString())) | ||||
| 					repo = repoVal.toMap(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -65,6 +65,7 @@ public: | ||||
| 	virtual void addRepository(QVariantMap data); | ||||
| 	virtual void setLocalModList(QVariantMap data); | ||||
| 	virtual void setModSettings(QVariant data); | ||||
| 	virtual void modChanged(QString modID); | ||||
|  | ||||
| 	// returns mod by name. Note: mod MUST exist | ||||
| 	CModEntry getMod(QString modname) const; | ||||
|   | ||||
| @@ -7,10 +7,10 @@ namespace ModFields | ||||
| { | ||||
| 	static const QString names [ModFields::COUNT] = | ||||
| 	{ | ||||
| 		"name", | ||||
| 		"", | ||||
| 		"", | ||||
| 		"modType", | ||||
| 		"name", | ||||
| 		"version", | ||||
| 		"size", | ||||
| 		"author" | ||||
| @@ -18,10 +18,10 @@ namespace ModFields | ||||
|  | ||||
| 	static const QString header [ModFields::COUNT] = | ||||
| 	{ | ||||
| 		"Name", | ||||
| 		"", // status icon | ||||
| 		"", // status icon | ||||
| 		"Type", | ||||
| 		"Name", | ||||
| 		"Version", | ||||
| 		"Size", | ||||
| 		"Author" | ||||
| @@ -38,13 +38,17 @@ namespace ModStatus | ||||
| } | ||||
|  | ||||
| CModListModel::CModListModel(QObject *parent) : | ||||
|     QAbstractTableModel(parent) | ||||
| 	QAbstractItemModel(parent) | ||||
| { | ||||
| } | ||||
|  | ||||
| QString CModListModel::modIndexToName(int index) const | ||||
| QString CModListModel::modIndexToName(const QModelIndex & index) const | ||||
| { | ||||
| 	return indexToName[index]; | ||||
| 	if (index.isValid()) | ||||
| 	{ | ||||
| 		return modNameToID.at(index.internalId()); | ||||
| 	} | ||||
| 	return ""; | ||||
| } | ||||
|  | ||||
| QVariant CModListModel::getValue(const CModEntry &mod, int field) const | ||||
| @@ -95,30 +99,34 @@ QVariant CModListModel::getTextAlign(int field) const | ||||
| { | ||||
| 	if (field == ModFields::SIZE) | ||||
| 		return QVariant(Qt::AlignRight | Qt::AlignVCenter); | ||||
| 	else | ||||
| 		return QVariant(Qt::AlignLeft  | Qt::AlignVCenter); | ||||
| 	//if (field == ModFields::NAME) | ||||
| 	//	return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); | ||||
| 	return QVariant(Qt::AlignLeft  | Qt::AlignVCenter); | ||||
| } | ||||
|  | ||||
| QVariant CModListModel::data(const QModelIndex &index, int role) const | ||||
| { | ||||
| 	if (index.isValid()) | ||||
| 	{ | ||||
| 		auto mod = getMod(modIndexToName(index.row())); | ||||
| 		auto mod = getMod(modIndexToName(index)); | ||||
|  | ||||
| 		switch (role) | ||||
| 		{ | ||||
| 			case Qt::DecorationRole:    return getIcon(mod, index.column()); | ||||
| 			case Qt::DisplayRole:       return getText(mod, index.column()); | ||||
| 			case Qt::UserRole:          return getValue(mod, index.column()); | ||||
| 			case Qt::TextAlignmentRole: return getTextAlign(index.column()); | ||||
| 			case ModRoles::ValueRole:   return getValue(mod, index.column()); | ||||
| 			case ModRoles::ModNameRole: return mod.getName(); | ||||
| 		} | ||||
| 	} | ||||
| 	return QVariant(); | ||||
| } | ||||
|  | ||||
| int CModListModel::rowCount(const QModelIndex &) const | ||||
| int CModListModel::rowCount(const QModelIndex & index) const | ||||
| { | ||||
| 	return indexToName.size(); | ||||
| 	if (index.isValid()) | ||||
| 		return modIndex[modIndexToName(index)].size(); | ||||
| 	return modIndex[""].size(); | ||||
| } | ||||
|  | ||||
| int CModListModel::columnCount(const QModelIndex &) const | ||||
| @@ -152,26 +160,60 @@ void CModListModel::addRepository(QVariantMap data) | ||||
| 	endResetModel(); | ||||
| } | ||||
|  | ||||
| void CModListModel::setLocalModList(QVariantMap data) | ||||
| void CModListModel::modChanged(QString modID) | ||||
| { | ||||
| 	beginResetModel(); | ||||
| 	CModList::setLocalModList(data); | ||||
| 	endResetModel(); | ||||
| } | ||||
|  | ||||
| void CModListModel::setModSettings(QVariant data) | ||||
| { | ||||
| 	beginResetModel(); | ||||
| 	CModList::setModSettings(data); | ||||
| 	endResetModel(); | ||||
| 	int index = modNameToID.indexOf(modID); | ||||
| 	QModelIndex parent  = this->parent(createIndex(0, 0, index)); | ||||
| 	int row = modIndex[modIndexToName(parent)].indexOf(modID); | ||||
| 	emit dataChanged(createIndex(row, 0, index), createIndex(row, 4, index)); | ||||
| } | ||||
|  | ||||
| void CModListModel::endResetModel() | ||||
| { | ||||
| 	indexToName = getModList(); | ||||
| 	modNameToID = getModList(); | ||||
| 	modIndex.clear(); | ||||
| 	for (const QString & str : modNameToID) | ||||
| 	{ | ||||
| 		if (str.contains('.')) | ||||
| 		{ | ||||
| 			modIndex[str.section('.', 0, -2)].append(str); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			modIndex[""].append(str); | ||||
| 		} | ||||
| 	} | ||||
| 	QAbstractItemModel::endResetModel(); | ||||
| } | ||||
|  | ||||
| QModelIndex CModListModel::index(int row, int column, const QModelIndex &parent) const | ||||
| { | ||||
| 	if (parent.isValid()) | ||||
| 	{ | ||||
| 		if (modIndex[modIndexToName(parent)].size() > row) | ||||
| 			return createIndex(row, column, modNameToID.indexOf(modIndex[modIndexToName(parent)][row])); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (modIndex[""].size() > row) | ||||
| 			return createIndex(row, column, modNameToID.indexOf(modIndex[""][row])); | ||||
| 	} | ||||
| 	return QModelIndex(); | ||||
| } | ||||
|  | ||||
| QModelIndex CModListModel::parent(const QModelIndex &child) const | ||||
| { | ||||
| 	QString modID = modNameToID[child.internalId()]; | ||||
| 	for (auto entry = modIndex.begin(); entry != modIndex.end(); entry++) // because using range-for entry type is QMap::value_type oO | ||||
| 	{ | ||||
| 		if (entry.key() != "" && entry.value().indexOf(modID) != -1) | ||||
| 		{ | ||||
| 			return createIndex(entry.value().indexOf(modID), child.column(), modNameToID.indexOf(entry.key())); | ||||
| 		} | ||||
| 	} | ||||
| 	return QModelIndex(); | ||||
| } | ||||
|  | ||||
| void CModFilterModel::setTypeFilter(int filteredType, int filterMask) | ||||
| { | ||||
| 	this->filterMask = filterMask; | ||||
| @@ -179,17 +221,35 @@ void CModFilterModel::setTypeFilter(int filteredType, int filterMask) | ||||
| 	invalidateFilter(); | ||||
| } | ||||
|  | ||||
| bool CModFilterModel::filterMatches(int modIndex) const | ||||
| bool CModFilterModel::filterMatchesThis(const QModelIndex &source) const | ||||
| { | ||||
| 	CModEntry mod = base->getMod(base->modIndexToName(modIndex)); | ||||
|  | ||||
| 	return (mod.getModStatus() & filterMask) == filteredType; | ||||
| 	CModEntry mod = base->getMod(source.data(ModRoles::ModNameRole).toString()); | ||||
| 	return (mod.getModStatus() & filterMask) == filteredType && | ||||
| 			QSortFilterProxyModel::filterAcceptsRow(source.row(), source.parent()); | ||||
| } | ||||
|  | ||||
| bool CModFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const | ||||
| { | ||||
| 	if (filterMatches(source_row)) | ||||
| 		return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); | ||||
| 	QModelIndex index = base->index(source_row, 0, source_parent); | ||||
|  | ||||
| 	if (filterMatchesThis(index)) | ||||
| 	{ | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i=0; i<base->rowCount(index); i++) | ||||
| 	{ | ||||
| 		if (filterMatchesThis(index.child(i, 0))) | ||||
| 			return true; | ||||
| 	} | ||||
|  | ||||
| 	QModelIndex parent = source_parent; | ||||
| 	while (parent.isValid()) | ||||
| 	{ | ||||
| 		if (filterMatchesThis(parent)) | ||||
| 			return true; | ||||
| 		parent = parent.parent(); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| @@ -200,5 +260,5 @@ CModFilterModel::CModFilterModel(CModListModel * model, QObject * parent): | ||||
|     filterMask(ModStatus::MASK_NONE) | ||||
| { | ||||
| 	setSourceModel(model); | ||||
| 	setSortRole(Qt::UserRole); | ||||
| 	setSortRole(ModRoles::ValueRole); | ||||
| } | ||||
|   | ||||
| @@ -9,10 +9,10 @@ namespace ModFields | ||||
| { | ||||
| 	enum EModFields | ||||
| 	{ | ||||
| 		NAME, | ||||
| 		STATUS_ENABLED, | ||||
| 		STATUS_UPDATE, | ||||
| 		TYPE, | ||||
| 		NAME, | ||||
| 		VERSION, | ||||
| 		SIZE, | ||||
| 		AUTHOR, | ||||
| @@ -20,36 +20,50 @@ namespace ModFields | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| class CModListModel : public QAbstractTableModel, public CModList | ||||
| namespace ModRoles | ||||
| { | ||||
| 	enum EModRoles | ||||
| 	{ | ||||
| 		ValueRole = Qt::UserRole, | ||||
| 		ModNameRole | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| class CModListModel : public QAbstractItemModel, public CModList | ||||
| { | ||||
| 	Q_OBJECT | ||||
|  | ||||
| 	QVector<QString> indexToName; | ||||
| 	QVector<QString> modNameToID; | ||||
| 	// contains mapping mod -> numbered list of submods | ||||
| 	// mods that have no parent located under "" key (empty string) | ||||
| 	QMap<QString, QVector<QString>> modIndex; | ||||
|  | ||||
| 	void endResetModel(); | ||||
|  | ||||
| 	QString modIndexToName(const QModelIndex & index) const; | ||||
|  | ||||
| 	QVariant getTextAlign(int field) const; | ||||
| 	QVariant getValue(const CModEntry & mod, int field) const; | ||||
| 	QVariant getText(const CModEntry & mod, int field) const; | ||||
| 	QVariant getIcon(const CModEntry & mod, int field) const; | ||||
| public: | ||||
| 	/// CModListContainer overrides | ||||
| 	void resetRepositories(); | ||||
| 	void addRepository(QVariantMap data); | ||||
| 	void setLocalModList(QVariantMap data); | ||||
| 	void setModSettings(QVariant data); | ||||
|  | ||||
| 	QString modIndexToName(int index) const; | ||||
|  | ||||
| 	explicit CModListModel(QObject *parent = 0); | ||||
| 	 | ||||
| 	QVariant data(const QModelIndex &index, int role) const; | ||||
| 	QVariant headerData(int section, Qt::Orientation orientation, int role) const; | ||||
|  | ||||
| 	int rowCount(const QModelIndex &parent) const; | ||||
| 	int columnCount(const QModelIndex &parent) const; | ||||
| 	/// CModListContainer overrides | ||||
| 	void resetRepositories() override; | ||||
| 	void addRepository(QVariantMap data) override; | ||||
| 	void modChanged(QString modID); | ||||
|  | ||||
| 	Qt::ItemFlags flags(const QModelIndex &index) const; | ||||
| 	QVariant data(const QModelIndex &index, int role) const override; | ||||
| 	QVariant headerData(int section, Qt::Orientation orientation, int role) const override; | ||||
|  | ||||
| 	int rowCount(const QModelIndex &parent) const override; | ||||
| 	int columnCount(const QModelIndex &parent) const override; | ||||
|  | ||||
| 	QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; | ||||
| 	QModelIndex parent(const QModelIndex &child) const override; | ||||
|  | ||||
| 	Qt::ItemFlags flags(const QModelIndex &index) const override; | ||||
| signals: | ||||
| 	 | ||||
| public slots: | ||||
| @@ -62,7 +76,7 @@ class CModFilterModel : public QSortFilterProxyModel | ||||
| 	int filteredType; | ||||
| 	int filterMask; | ||||
|  | ||||
| 	bool filterMatches(int modIndex) const; | ||||
| 	bool filterMatchesThis(const QModelIndex & source) const; | ||||
|  | ||||
| 	bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; | ||||
| public: | ||||
|   | ||||
| @@ -24,6 +24,7 @@ void CModListView::setupFilterModel() | ||||
|  | ||||
| 	filterModel->setFilterKeyColumn(-1); // filter across all columns | ||||
| 	filterModel->setSortCaseSensitivity(Qt::CaseInsensitive); // to make it more user-friendly | ||||
| 	filterModel->setDynamicSortFilter(true); | ||||
| } | ||||
|  | ||||
| void CModListView::setupModsView() | ||||
| @@ -31,20 +32,26 @@ void CModListView::setupModsView() | ||||
| 	ui->allModsView->setModel(filterModel); | ||||
| 	// input data is not sorted - sort it before display | ||||
| 	ui->allModsView->sortByColumn(ModFields::TYPE, Qt::AscendingOrder); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::NAME, 185); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::STATUS_ENABLED, 30); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::STATUS_UPDATE, 30); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::TYPE, 80); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::NAME, 180); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::TYPE, 75); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::SIZE, 80); | ||||
| 	ui->allModsView->setColumnWidth(ModFields::VERSION, 60); | ||||
|  | ||||
| 	ui->allModsView->header()->setSectionResizeMode(ModFields::STATUS_ENABLED, QHeaderView::Fixed); | ||||
| 	ui->allModsView->header()->setSectionResizeMode(ModFields::STATUS_UPDATE,  QHeaderView::Fixed); | ||||
|  | ||||
| 	ui->allModsView->setUniformRowHeights(true); | ||||
|  | ||||
| 	connect( ui->allModsView->selectionModel(), SIGNAL( currentRowChanged( const QModelIndex &, const QModelIndex & )), | ||||
| 	         this, SLOT( modSelected( const QModelIndex &, const QModelIndex & ))); | ||||
|  | ||||
| 	connect( filterModel, SIGNAL( modelReset()), | ||||
| 	         this, SLOT( modelReset())); | ||||
|  | ||||
| 	selectMod(filterModel->rowCount() > 0 ? 0 : -1); | ||||
| 	connect( modModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), | ||||
| 			 this, SLOT(dataChanged(QModelIndex,QModelIndex))); | ||||
| } | ||||
|  | ||||
| CModListView::CModListView(QWidget *parent) : | ||||
| @@ -61,7 +68,8 @@ CModListView::CModListView(QWidget *parent) : | ||||
|  | ||||
| 	ui->progressWidget->setVisible(false); | ||||
| 	dlManager = nullptr; | ||||
| 	loadRepositories(); | ||||
| 	//loadRepositories(); | ||||
| 	hideModInfo(); | ||||
| } | ||||
|  | ||||
| void CModListView::loadRepositories() | ||||
| @@ -133,39 +141,45 @@ QString CModListView::genModInfoText(CModEntry &mod) | ||||
| 	QString lineTemplate = prefix + "%2</p>"; | ||||
| 	QString urlTemplate  = prefix + "<a href=\"%2\"><span style=\" text-decoration: underline; color:#0000ff;\">%2</span></a></p>"; | ||||
| 	QString textTemplate = prefix + "</p><p align=\"justify\">%2</p>"; | ||||
| 	QString noteTemplate = "<p align=\"justify\">%1: %2</p>"; | ||||
| 	QString listTemplate = "<p align=\"justify\">%1: %2</p>"; | ||||
| 	QString noteTemplate = "<p align=\"justify\">%1</p>"; | ||||
|  | ||||
| 	QString result; | ||||
|  | ||||
| 	result += "<html><body>"; | ||||
| 	result += replaceIfNotEmpty(mod.getValue("name"), lineTemplate.arg("Mod name")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("installedVersion"), lineTemplate.arg("Installed version")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("latestVersion"), lineTemplate.arg("Latest version")); | ||||
| 	result += replaceIfNotEmpty(CModEntry::sizeToString(mod.getValue("size").toDouble()), lineTemplate.arg("Download size")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("author"), lineTemplate.arg("Authors")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("contact"), urlTemplate.arg("Home")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("depends"), lineTemplate.arg("Required mods")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("conflicts"), lineTemplate.arg("Conflicting mods")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg("Description")); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("name"), lineTemplate.arg(tr("Mod name"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("installedVersion"), lineTemplate.arg(tr("Installed version"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("latestVersion"), lineTemplate.arg(tr("Latest version"))); | ||||
| 	if (mod.getValue("size").toDouble() != 0) | ||||
| 		result += replaceIfNotEmpty(CModEntry::sizeToString(mod.getValue("size").toDouble()), lineTemplate.arg(tr("Download size"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("author"), lineTemplate.arg(tr("Authors"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("contact"), urlTemplate.arg(tr("Home"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("depends"), lineTemplate.arg(tr("Required mods"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("conflicts"), lineTemplate.arg(tr("Conflicting mods"))); | ||||
| 	result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg(tr("Description"))); | ||||
|  | ||||
| 	result += "<p></p>"; // to get some empty space | ||||
|  | ||||
| 	QString unknownDeps  = "This mod can not be installed or enabled because following dependencies are not present"; | ||||
| 	QString blockingMods = "This mod can not be enabled because following mods are incompatible with this mod"; | ||||
| 	QString hasActiveDependentMods = "This mod can not be disabled because it is required to run following mods"; | ||||
| 	QString hasDependentMods = "This mod can not be uninstalled or updated because it is required to run following mods"; | ||||
| 	QString unknownDeps  = tr("This mod can not be installed or enabled because following dependencies are not present"); | ||||
| 	QString blockingMods = tr("This mod can not be enabled because following mods are incompatible with this mod"); | ||||
| 	QString hasActiveDependentMods = tr("This mod can not be disabled because it is required to run following mods"); | ||||
| 	QString hasDependentMods = tr("This mod can not be uninstalled or updated because it is required to run following mods"); | ||||
| 	QString thisIsSubmod = tr("This is submod and it can not be installed or uninstalled separately from parent mod"); | ||||
|  | ||||
| 	QString notes; | ||||
|  | ||||
| 	notes += replaceIfNotEmpty(findInvalidDependencies(mod.getName()), noteTemplate.arg(unknownDeps)); | ||||
| 	notes += replaceIfNotEmpty(findBlockingMods(mod.getName()), noteTemplate.arg(blockingMods)); | ||||
| 	notes += replaceIfNotEmpty(findInvalidDependencies(mod.getName()), listTemplate.arg(unknownDeps)); | ||||
| 	notes += replaceIfNotEmpty(findBlockingMods(mod.getName()), listTemplate.arg(blockingMods)); | ||||
| 	if (mod.isEnabled()) | ||||
| 		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), true), noteTemplate.arg(hasActiveDependentMods)); | ||||
| 		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), true), listTemplate.arg(hasActiveDependentMods)); | ||||
| 	if (mod.isInstalled()) | ||||
| 		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), false), noteTemplate.arg(hasDependentMods)); | ||||
| 		notes += replaceIfNotEmpty(findDependentMods(mod.getName(), false), listTemplate.arg(hasDependentMods)); | ||||
|  | ||||
| 	if (mod.getName().contains('.')) | ||||
| 		notes += noteTemplate.arg(thisIsSubmod); | ||||
|  | ||||
| 	if (notes.size()) | ||||
| 		result += textTemplate.arg("Notes").arg(notes); | ||||
| 		result += textTemplate.arg(tr("Notes")).arg(notes); | ||||
|  | ||||
| 	result += "</body></html>"; | ||||
| 	return result; | ||||
| @@ -183,28 +197,31 @@ void CModListView::disableModInfo() | ||||
| 	ui->hideModInfoButton->setEnabled(false); | ||||
| } | ||||
|  | ||||
| void CModListView::selectMod(int index) | ||||
| void CModListView::dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight) | ||||
| { | ||||
| 	if (index < 0) | ||||
| 	selectMod(ui->allModsView->currentIndex()); | ||||
| } | ||||
|  | ||||
| void CModListView::selectMod(const QModelIndex & index) | ||||
| { | ||||
| 	if (!index.isValid()) | ||||
| 	{ | ||||
| 		disableModInfo(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		enableModInfo(); | ||||
|  | ||||
| 		auto mod = modModel->getMod(modModel->modIndexToName(index)); | ||||
| 		auto mod = modModel->getMod(index.data(ModRoles::ModNameRole).toString()); | ||||
|  | ||||
| 		ui->textBrowser->setHtml(genModInfoText(mod)); | ||||
|  | ||||
| 		bool hasInvalidDeps = !findInvalidDependencies(modModel->modIndexToName(index)).empty(); | ||||
| 		bool hasBlockingMods = !findBlockingMods(modModel->modIndexToName(index)).empty(); | ||||
| 		bool hasDependentMods = !findDependentMods(modModel->modIndexToName(index), true).empty(); | ||||
| 		bool hasInvalidDeps = !findInvalidDependencies(index.data(ModRoles::ModNameRole).toString()).empty(); | ||||
| 		bool hasBlockingMods = !findBlockingMods(index.data(ModRoles::ModNameRole).toString()).empty(); | ||||
| 		bool hasDependentMods = !findDependentMods(index.data(ModRoles::ModNameRole).toString(), true).empty(); | ||||
|  | ||||
| 		ui->disableButton->setVisible(mod.isEnabled()); | ||||
| 		ui->enableButton->setVisible(mod.isDisabled()); | ||||
| 		ui->installButton->setVisible(mod.isAvailable()); | ||||
| 		ui->uninstallButton->setVisible(mod.isInstalled()); | ||||
| 		ui->installButton->setVisible(mod.isAvailable() && !mod.getName().contains('.')); | ||||
| 		ui->uninstallButton->setVisible(mod.isInstalled() && !mod.getName().contains('.')); | ||||
| 		ui->updateButton->setVisible(mod.isUpdateable()); | ||||
|  | ||||
| 		// Block buttons if action is not allowed at this time | ||||
| @@ -232,7 +249,7 @@ void CModListView::keyPressEvent(QKeyEvent * event) | ||||
|  | ||||
| void CModListView::modSelected(const QModelIndex & current, const QModelIndex & ) | ||||
| { | ||||
| 	selectMod(filterModel->mapToSource(current).row()); | ||||
| 	selectMod(current); | ||||
| } | ||||
|  | ||||
| void CModListView::on_hideModInfoButton_clicked() | ||||
| @@ -243,10 +260,10 @@ void CModListView::on_hideModInfoButton_clicked() | ||||
| 		showModInfo(); | ||||
| } | ||||
|  | ||||
| void CModListView::on_allModsView_doubleClicked(const QModelIndex &index) | ||||
| void CModListView::on_allModsView_activated(const QModelIndex &index) | ||||
| { | ||||
| 	showModInfo(); | ||||
| 	selectMod(filterModel->mapToSource(index).row()); | ||||
| 	selectMod(index); | ||||
| } | ||||
|  | ||||
| void CModListView::on_lineEdit_textChanged(const QString &arg1) | ||||
| @@ -319,7 +336,7 @@ QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled) | ||||
|  | ||||
| void CModListView::on_enableButton_clicked() | ||||
| { | ||||
| 	QString modName = modModel->modIndexToName(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); | ||||
|  | ||||
| 	assert(findBlockingMods(modName).empty()); | ||||
| 	assert(findInvalidDependencies(modName).empty()); | ||||
| @@ -332,7 +349,7 @@ void CModListView::on_enableButton_clicked() | ||||
|  | ||||
| void CModListView::on_disableButton_clicked() | ||||
| { | ||||
| 	QString modName = modModel->modIndexToName(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); | ||||
|  | ||||
| 	if (modModel->hasMod(modName) && | ||||
| 	    modModel->getMod(modName).isEnabled()) | ||||
| @@ -343,7 +360,7 @@ void CModListView::on_disableButton_clicked() | ||||
|  | ||||
| void CModListView::on_updateButton_clicked() | ||||
| { | ||||
| 	QString modName = modModel->modIndexToName(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); | ||||
|  | ||||
| 	assert(findInvalidDependencies(modName).empty()); | ||||
|  | ||||
| @@ -358,7 +375,7 @@ void CModListView::on_updateButton_clicked() | ||||
|  | ||||
| void CModListView::on_uninstallButton_clicked() | ||||
| { | ||||
| 	QString modName = modModel->modIndexToName(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); | ||||
| 	// NOTE: perhaps add "manually installed" flag and uninstall those dependencies that don't have it? | ||||
|  | ||||
| 	if (modModel->hasMod(modName) && | ||||
| @@ -373,7 +390,7 @@ void CModListView::on_uninstallButton_clicked() | ||||
|  | ||||
| void CModListView::on_installButton_clicked() | ||||
| { | ||||
| 	QString modName = modModel->modIndexToName(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString(); | ||||
|  | ||||
| 	assert(findInvalidDependencies(modName).empty()); | ||||
|  | ||||
| @@ -537,8 +554,8 @@ void CModListView::on_pushButton_clicked() | ||||
|  | ||||
| void CModListView::modelReset() | ||||
| { | ||||
| 	//selectMod(filterModel->mapToSource(ui->allModsView->currentIndex()).row()); | ||||
| 	selectMod(filterModel->rowCount() > 0 ? 0 : -1); | ||||
| 	if (ui->modInfoWidget->isVisible()) | ||||
| 		selectMod(filterModel->rowCount() > 0 ? filterModel->index(0,0) : QModelIndex()); | ||||
| } | ||||
|  | ||||
| void CModListView::checkManagerErrors() | ||||
|   | ||||
| @@ -61,9 +61,10 @@ public: | ||||
| 	void enableModInfo(); | ||||
| 	void disableModInfo(); | ||||
|  | ||||
| 	void selectMod(int index); | ||||
| 	void selectMod(const QModelIndex & index); | ||||
|  | ||||
| private slots: | ||||
| 	void dataChanged(const QModelIndex & topleft, const QModelIndex & bottomRight); | ||||
| 	void modSelected(const QModelIndex & current, const QModelIndex & previous); | ||||
| 	void downloadProgress(qint64 current, qint64 max); | ||||
| 	void downloadFinished(QStringList savedFiles, QStringList failedFiles, QStringList errors); | ||||
| @@ -72,8 +73,6 @@ private slots: | ||||
|  | ||||
| 	void on_hideModInfoButton_clicked(); | ||||
|  | ||||
| 	void on_allModsView_doubleClicked(const QModelIndex &index); | ||||
|  | ||||
| 	void on_lineEdit_textChanged(const QString &arg1); | ||||
|  | ||||
| 	void on_comboBox_currentIndexChanged(int index); | ||||
| @@ -90,6 +89,8 @@ private slots: | ||||
|  | ||||
| 	void on_pushButton_clicked(); | ||||
|  | ||||
| 	void on_allModsView_activated(const QModelIndex &index); | ||||
|  | ||||
| private: | ||||
| 	Ui::CModListView *ui; | ||||
| }; | ||||
|   | ||||
| @@ -109,7 +109,7 @@ | ||||
|       </widget> | ||||
|      </item> | ||||
|      <item row="1" column="0" colspan="2"> | ||||
|       <widget class="QTableView" name="allModsView"> | ||||
|       <widget class="QTreeView" name="allModsView"> | ||||
|        <property name="selectionMode"> | ||||
|         <enum>QAbstractItemView::SingleSelection</enum> | ||||
|        </property> | ||||
| @@ -119,7 +119,7 @@ | ||||
|        <property name="iconSize"> | ||||
|         <size> | ||||
|          <width>32</width> | ||||
|          <height>20</height> | ||||
|          <height>32</height> | ||||
|         </size> | ||||
|        </property> | ||||
|        <property name="verticalScrollMode"> | ||||
| @@ -131,12 +131,9 @@ | ||||
|        <property name="sortingEnabled"> | ||||
|         <bool>true</bool> | ||||
|        </property> | ||||
|        <attribute name="horizontalHeaderStretchLastSection"> | ||||
|         <bool>true</bool> | ||||
|        </attribute> | ||||
|        <attribute name="verticalHeaderVisible"> | ||||
|        <property name="expandsOnDoubleClick"> | ||||
|         <bool>false</bool> | ||||
|        </attribute> | ||||
|        </property> | ||||
|       </widget> | ||||
|      </item> | ||||
|     </layout> | ||||
| @@ -220,8 +217,8 @@ | ||||
|          <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> | ||||
| <html><head><meta name="qrichtext" content="1" /><style type="text/css"> | ||||
| p, li { white-space: pre-wrap; } | ||||
| </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> | ||||
| <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string> | ||||
| </style></head><body style=" font-family:'Ubuntu'; font-size:10pt; font-weight:400; font-style:normal;"> | ||||
| <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p></body></html></string> | ||||
|         </property> | ||||
|         <property name="openExternalLinks"> | ||||
|          <bool>true</bool> | ||||
|   | ||||
| @@ -66,7 +66,7 @@ void CModManager::loadMods() | ||||
|  | ||||
| 	for (auto modname : installedMods) | ||||
| 	{ | ||||
| 		ResourceID resID("Mods/" + modname + "/mod.json"); | ||||
| 		ResourceID resID(CModInfo::getModFile(modname)); | ||||
| 		if (CResourceHandler::get()->existsResource(resID)) | ||||
| 		{ | ||||
| 			std::string name = *CResourceHandler::get()->getResourceName(resID); | ||||
| @@ -114,6 +114,9 @@ bool CModManager::canInstallMod(QString modname) | ||||
| { | ||||
| 	auto mod = modList->getMod(modname); | ||||
|  | ||||
| 	if (mod.getName().contains('.')) | ||||
| 		return addError(modname, "Can not install submod"); | ||||
|  | ||||
| 	if (mod.isInstalled()) | ||||
| 		return addError(modname, "Mod is already installed"); | ||||
|  | ||||
| @@ -126,6 +129,9 @@ bool CModManager::canUninstallMod(QString modname) | ||||
| { | ||||
| 	auto mod = modList->getMod(modname); | ||||
|  | ||||
| 	if (mod.getName().contains('.')) | ||||
| 		return addError(modname, "Can not uninstall submod"); | ||||
|  | ||||
| 	if (!mod.isInstalled()) | ||||
| 		return addError(modname, "Mod is not installed"); | ||||
|  | ||||
| @@ -191,17 +197,32 @@ bool CModManager::canDisableMod(QString modname) | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static QVariant writeValue(QString path, QVariantMap input, QVariant value) | ||||
| { | ||||
| 	if (path.size() > 1) | ||||
| 	{ | ||||
|  | ||||
| 		QString entryName = path.section('/', 0, 1); | ||||
| 		QString remainder = "/" + path.section('/', 2, -1); | ||||
|  | ||||
| 		entryName.remove(0, 1); | ||||
| 		input.insert(entryName, writeValue(remainder, input.value(entryName).toMap(), value)); | ||||
| 		return input; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		return value; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool CModManager::doEnableMod(QString mod, bool on) | ||||
| { | ||||
| 	QVariant value(on); | ||||
| 	QVariantMap list = modSettings["activeMods"].toMap(); | ||||
| 	QVariantMap modData = list[mod].toMap(); | ||||
|  | ||||
| 	modData.insert("active", value); | ||||
| 	list.insert(mod, modData); | ||||
| 	modSettings.insert("activeMods", list); | ||||
| 	QString path = mod; | ||||
| 	path = "/activeMods/" + path.replace(".", "/mods/") + "/active"; | ||||
|  | ||||
| 	modSettings = writeValue(path, modSettings, QVariant(on)).toMap(); | ||||
| 	modList->setModSettings(modSettings["activeMods"]); | ||||
| 	modList->modChanged(mod); | ||||
|  | ||||
| 	JsonUtils::JsonToFile(settingsPath(), modSettings); | ||||
|  | ||||
| @@ -215,11 +236,6 @@ bool CModManager::doInstallMod(QString modname, QString archivePath) | ||||
| 	if (!QFile(archivePath).exists()) | ||||
| 		return addError(modname, "Mod archive is missing"); | ||||
|  | ||||
| 	// FIXME: recheck wog/vcmi data behavior - they have bits of data in our trunk | ||||
| 	// FIXME: breaks when there is Era mod with same name | ||||
| 	//if (QDir(destDir + modname).exists()) | ||||
| 	//	return addError(modname, "Mod with such name is already installed"); | ||||
|  | ||||
| 	if (localMods.contains(modname)) | ||||
| 		return addError(modname, "Mod with such name is already installed"); | ||||
|  | ||||
| @@ -237,6 +253,7 @@ bool CModManager::doInstallMod(QString modname, QString archivePath) | ||||
|  | ||||
| 	localMods.insert(modname, json); | ||||
| 	modList->setLocalModList(localMods); | ||||
| 	modList->modChanged(modname); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
| @@ -258,6 +275,7 @@ bool CModManager::doUninstallMod(QString modname) | ||||
|  | ||||
| 	localMods.remove(modname); | ||||
| 	modList->setLocalModList(localMods); | ||||
| 	modList->modChanged(modname); | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|   | ||||
| @@ -668,7 +668,7 @@ std::vector<std::string> CModHandler::getModList(std::string path) | ||||
| 	return foundMods; | ||||
| } | ||||
|  | ||||
| void CModHandler::loadMods(std::string path, std::string parent, const JsonNode & modSettings) | ||||
| void CModHandler::loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods) | ||||
| { | ||||
| 	for (std::string modName : getModList(path)) | ||||
| 	{ | ||||
| @@ -682,11 +682,10 @@ void CModHandler::loadMods(std::string path, std::string parent, const JsonNode | ||||
| 				mod.dependencies.insert(parent); | ||||
|  | ||||
| 			allMods[modFullName] = mod; | ||||
| 			if (mod.enabled) | ||||
| 			{ | ||||
| 			if (mod.enabled && enableMods) | ||||
| 				activeMods.push_back(modFullName); | ||||
| 				loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"]); | ||||
| 			} | ||||
|  | ||||
| 			loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.enabled); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -695,7 +694,7 @@ void CModHandler::loadMods() | ||||
| { | ||||
| 	const JsonNode modConfig = loadModSettings("config/modSettings.json"); | ||||
|  | ||||
| 	loadMods("", "", modConfig["activeMods"]); | ||||
| 	loadMods("", "", modConfig["activeMods"], true); | ||||
|  | ||||
| 	coreMod = CModInfo("core", modConfig["core"], JsonNode(ResourceID("config/gameConfig.json"))); | ||||
| 	coreMod.name = "Original game files"; | ||||
|   | ||||
| @@ -214,7 +214,7 @@ class DLL_LINKAGE CModHandler | ||||
| 	std::vector <TModID> resolveDependencies(std::vector<TModID> input) const; | ||||
|  | ||||
| 	std::vector<std::string> getModList(std::string path); | ||||
| 	void loadMods(std::string path, std::string namePrefix, const JsonNode & modSettings); | ||||
| 	void loadMods(std::string path, std::string namePrefix, const JsonNode & modSettings, bool enableMods); | ||||
| public: | ||||
|  | ||||
| 	CIdentifierStorage identifiers; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user