Refactor icon lists heavily
* Icon list now uses a filesystem watcher for updates * Icon folder is user-customizable * All the little details. ALL OF THEM.
This commit is contained in:
		| @@ -372,8 +372,6 @@ logic/LegacyFTBInstance.cpp | ||||
| # Lists | ||||
| logic/lists/InstanceList.h | ||||
| logic/lists/InstanceList.cpp | ||||
| logic/lists/IconList.h | ||||
| logic/lists/IconList.cpp | ||||
| logic/lists/BaseVersionList.h | ||||
| logic/lists/BaseVersionList.cpp | ||||
| logic/lists/MinecraftVersionList.h | ||||
| @@ -385,6 +383,13 @@ logic/lists/ForgeVersionList.cpp | ||||
| logic/lists/JavaVersionList.h | ||||
| logic/lists/JavaVersionList.cpp | ||||
|  | ||||
| # Icons | ||||
| logic/icons/MMCIcon.h | ||||
| logic/icons/MMCIcon.cpp | ||||
| logic/icons/IconList.h | ||||
| logic/icons/IconList.cpp | ||||
|  | ||||
|  | ||||
| # misc model/view | ||||
| logic/EnabledItemFilter.h | ||||
| logic/EnabledItemFilter.cpp | ||||
|   | ||||
							
								
								
									
										12
									
								
								MultiMC.cpp
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								MultiMC.cpp
									
									
									
									
									
								
							| @@ -13,7 +13,7 @@ | ||||
| #include "gui/dialogs/VersionSelectDialog.h" | ||||
| #include "logic/lists/InstanceList.h" | ||||
| #include "logic/auth/MojangAccountList.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
| #include "logic/lists/LwjglVersionList.h" | ||||
| #include "logic/lists/MinecraftVersionList.h" | ||||
| #include "logic/lists/ForgeVersionList.h" | ||||
| @@ -382,6 +382,7 @@ void MultiMC::initGlobalSettings() | ||||
| 	m_settings->registerSetting(new Setting("InstanceDir", "instances")); | ||||
| 	m_settings->registerSetting(new Setting("CentralModsDir", "mods")); | ||||
| 	m_settings->registerSetting(new Setting("LWJGLDir", "lwjgl")); | ||||
| 	m_settings->registerSetting(new Setting("IconsDir", "icons")); | ||||
|  | ||||
| 	// Editors | ||||
| 	m_settings->registerSetting(new Setting("JsonEditor", QString())); | ||||
| @@ -420,15 +421,6 @@ void MultiMC::initGlobalSettings() | ||||
| 	m_settings->registerSetting(new Setting("InstSortMode", "Name")); | ||||
| 	m_settings->registerSetting(new Setting("SelectedInstance", QString())); | ||||
|  | ||||
| 	// Persistent value for the client ID | ||||
| 	m_settings->registerSetting(new Setting("YggdrasilClientToken", "")); | ||||
| 	QString currentYggID = m_settings->get("YggdrasilClientToken").toString(); | ||||
| 	if (currentYggID.isEmpty()) | ||||
| 	{ | ||||
| 		QUuid uuid = QUuid::createUuid(); | ||||
| 		m_settings->set("YggdrasilClientToken", uuid.toString()); | ||||
| 	} | ||||
|  | ||||
| 	// Window state and geometry | ||||
| 	m_settings->registerSetting(new Setting("MainWindowState", "")); | ||||
| 	m_settings->registerSetting(new Setting("MainWindowGeometry", "")); | ||||
|   | ||||
| @@ -66,7 +66,7 @@ | ||||
| #include "logic/lists/InstanceList.h" | ||||
| #include "logic/lists/MinecraftVersionList.h" | ||||
| #include "logic/lists/LwjglVersionList.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
| #include "logic/lists/JavaVersionList.h" | ||||
|  | ||||
| #include "logic/auth/flows/AuthenticateTask.h" | ||||
| @@ -165,6 +165,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi | ||||
| 	connect(view->selectionModel(), | ||||
| 			SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, | ||||
| 			SLOT(instanceChanged(const QModelIndex &, const QModelIndex &))); | ||||
|  | ||||
| 	// track icon changes and update the toolbar! | ||||
| 	connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString))); | ||||
|  | ||||
| 	// model reset -> selection is invalid. All the instance pointers are wrong. | ||||
| 	// FIXME: stop using POINTERS everywhere | ||||
| 	connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad())); | ||||
| @@ -635,11 +639,27 @@ void MainWindow::on_actionChangeInstIcon_triggered() | ||||
| 	if (dlg.result() == QDialog::Accepted) | ||||
| 	{ | ||||
| 		m_selectedInstance->setIconKey(dlg.selectedIconKey); | ||||
| 		/* | ||||
| 		auto ico = MMC->icons()->getIcon(dlg.selectedIconKey); | ||||
| 		ui->actionChangeInstIcon->setIcon(ico); | ||||
| 		*/ | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MainWindow::iconUpdated(QString icon) | ||||
| { | ||||
| 	if(icon == m_currentInstIcon) | ||||
| 	{ | ||||
| 		ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void MainWindow::updateInstanceToolIcon(QString new_icon) | ||||
| { | ||||
| 	m_currentInstIcon = new_icon; | ||||
| 	ui->actionChangeInstIcon->setIcon(MMC->icons()->getIcon(m_currentInstIcon)); | ||||
| } | ||||
|  | ||||
| void MainWindow::on_actionChangeInstGroup_triggered() | ||||
| { | ||||
| 	if (!m_selectedInstance) | ||||
| @@ -1095,7 +1115,6 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & | ||||
| 							.value<void *>())) | ||||
| 	{ | ||||
| 		ui->instanceToolBar->setEnabled(true); | ||||
| 		QString iconKey = m_selectedInstance->iconKey(); | ||||
| 		renameButton->setText(m_selectedInstance->name()); | ||||
| 		ui->actionChangeInstLWJGLVersion->setEnabled( | ||||
| 			m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion")); | ||||
| @@ -1104,8 +1123,7 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & | ||||
| 		ui->actionChangeInstMCVersion->setEnabled( | ||||
| 			m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion")); | ||||
| 		m_statusLeft->setText(m_selectedInstance->getStatusbarDescription()); | ||||
| 		auto ico = MMC->icons()->getIcon(iconKey); | ||||
| 		ui->actionChangeInstIcon->setIcon(ico); | ||||
| 		updateInstanceToolIcon(m_selectedInstance->iconKey()); | ||||
|  | ||||
| 		MMC->settings()->set("SelectedInstance", m_selectedInstance->id()); | ||||
| 	} | ||||
| @@ -1120,12 +1138,11 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & | ||||
| void MainWindow::selectionBad() | ||||
| { | ||||
| 	m_selectedInstance = nullptr; | ||||
| 	QString iconKey = "infinity"; | ||||
|  | ||||
| 	statusBar()->clearMessage(); | ||||
| 	ui->instanceToolBar->setEnabled(false); | ||||
| 	renameButton->setText(tr("Rename Instance")); | ||||
| 	auto ico = MMC->icons()->getIcon(iconKey); | ||||
| 	ui->actionChangeInstIcon->setIcon(ico); | ||||
| 	updateInstanceToolIcon("infinity"); | ||||
| } | ||||
|  | ||||
| void MainWindow::on_actionEditInstNotes_triggered() | ||||
|   | ||||
| @@ -145,6 +145,9 @@ slots: | ||||
| 	void assetsFailed(); | ||||
| 	void assetsFinished(); | ||||
|  | ||||
| 	// called when an icon is changed in the icon model. | ||||
| 	void iconUpdated(QString); | ||||
|  | ||||
| public | ||||
| slots: | ||||
| 	void instanceActivated(QModelIndex); | ||||
| @@ -171,6 +174,7 @@ slots: | ||||
| protected: | ||||
| 	bool eventFilter(QObject *obj, QEvent *ev); | ||||
| 	void setCatBackground(bool enabled); | ||||
| 	void updateInstanceToolIcon(QString new_icon); | ||||
|  | ||||
| private: | ||||
| 	Ui::MainWindow *ui; | ||||
| @@ -180,9 +184,9 @@ private: | ||||
| 	MinecraftProcess *proc; | ||||
| 	ConsoleWindow *console; | ||||
| 	LabeledToolButton *renameButton; | ||||
| 	QToolButton *changeIconButton; | ||||
|  | ||||
| 	BaseInstance *m_selectedInstance; | ||||
| 	QString m_currentInstIcon; | ||||
|  | ||||
| 	Task *m_versionLoadTask; | ||||
|  | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|  | ||||
| #include "logic/InstanceFactory.h" | ||||
| #include "logic/BaseVersion.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
| #include "logic/lists/MinecraftVersionList.h" | ||||
| #include "logic/tasks/Task.h" | ||||
| #include "logic/BaseInstance.h" | ||||
|   | ||||
| @@ -25,7 +25,7 @@ | ||||
| #include "gui/Platform.h" | ||||
| #include "gui/widgets/InstanceDelegate.h" | ||||
|  | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
|  | ||||
| IconPickerDialog::IconPickerDialog(QWidget *parent) | ||||
| 	: QDialog(parent), ui(new Ui::IconPickerDialog) | ||||
|   | ||||
| @@ -19,7 +19,7 @@ | ||||
|  | ||||
| #include "logic/InstanceFactory.h" | ||||
| #include "logic/BaseVersion.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
| #include "logic/lists/MinecraftVersionList.h" | ||||
| #include "logic/tasks/Task.h" | ||||
|  | ||||
|   | ||||
| @@ -102,6 +102,18 @@ void SettingsDialog::on_instDirBrowseBtn_clicked() | ||||
| 		ui->instDirTextBox->setText(cooked_dir); | ||||
| 	} | ||||
| } | ||||
| void SettingsDialog::on_iconsDirBrowseBtn_clicked() | ||||
| { | ||||
| 	QString raw_dir = QFileDialog::getExistingDirectory(this, tr("Icons Directory"), | ||||
| 														ui->iconsDirTextBox->text()); | ||||
| 	QString cooked_dir = NormalizePath(raw_dir); | ||||
|  | ||||
| 	// do not allow current dir - it's dirty. Do not allow dirs that don't exist | ||||
| 	if (!cooked_dir.isEmpty() && QDir(cooked_dir).exists()) | ||||
| 	{ | ||||
| 		ui->iconsDirTextBox->setText(cooked_dir); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void SettingsDialog::on_modsDirBrowseBtn_clicked() | ||||
| { | ||||
| @@ -205,6 +217,7 @@ void SettingsDialog::applySettings(SettingsObject *s) | ||||
| 	s->set("InstanceDir", ui->instDirTextBox->text()); | ||||
| 	s->set("CentralModsDir", ui->modsDirTextBox->text()); | ||||
| 	s->set("LWJGLDir", ui->lwjglDirTextBox->text()); | ||||
| 	s->set("IconsDir", ui->iconsDirTextBox->text()); | ||||
|  | ||||
| 	// Editors | ||||
| 	QString jsonEditor = ui->jsonEditorTextBox->text(); | ||||
| @@ -271,6 +284,7 @@ void SettingsDialog::loadSettings(SettingsObject *s) | ||||
| 	ui->instDirTextBox->setText(s->get("InstanceDir").toString()); | ||||
| 	ui->modsDirTextBox->setText(s->get("CentralModsDir").toString()); | ||||
| 	ui->lwjglDirTextBox->setText(s->get("LWJGLDir").toString()); | ||||
| 	ui->iconsDirTextBox->setText(s->get("IconsDir").toString()); | ||||
|  | ||||
| 	// Editors | ||||
| 	ui->jsonEditorTextBox->setText(s->get("JsonEditor").toString()); | ||||
|   | ||||
| @@ -55,8 +55,11 @@ slots: | ||||
|  | ||||
| 	void on_lwjglDirBrowseBtn_clicked(); | ||||
|  | ||||
|  | ||||
| 	void on_jsonEditorBrowseBtn_clicked(); | ||||
|  | ||||
| 	void on_iconsDirBrowseBtn_clicked(); | ||||
|  | ||||
| 	void on_maximizedCheckBox_clicked(bool checked); | ||||
|  | ||||
| 	void on_buttonBox_accepted(); | ||||
|   | ||||
| @@ -215,6 +215,9 @@ | ||||
|           <item row="1" column="1"> | ||||
|            <widget class="QLineEdit" name="modsDirTextBox"/> | ||||
|           </item> | ||||
|           <item row="2" column="1"> | ||||
|            <widget class="QLineEdit" name="lwjglDirTextBox"/> | ||||
|           </item> | ||||
|           <item row="1" column="2"> | ||||
|            <widget class="QToolButton" name="modsDirBrowseBtn"> | ||||
|             <property name="text"> | ||||
| @@ -229,9 +232,6 @@ | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="2" column="1"> | ||||
|            <widget class="QLineEdit" name="lwjglDirTextBox"/> | ||||
|           </item> | ||||
|           <item row="2" column="2"> | ||||
|            <widget class="QToolButton" name="lwjglDirBrowseBtn"> | ||||
|             <property name="text"> | ||||
| @@ -239,6 +239,23 @@ | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="3" column="1"> | ||||
|            <widget class="QLineEdit" name="iconsDirTextBox"/> | ||||
|           </item> | ||||
|           <item row="3" column="0"> | ||||
|            <widget class="QLabel" name="labelIconsDir"> | ||||
|             <property name="text"> | ||||
|              <string>Icons:</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|           <item row="3" column="2"> | ||||
|            <widget class="QToolButton" name="iconsDirBrowseBtn"> | ||||
|             <property name="text"> | ||||
|              <string>...</string> | ||||
|             </property> | ||||
|            </widget> | ||||
|           </item> | ||||
|          </layout> | ||||
|         </widget> | ||||
|        </item> | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
|  | ||||
| #include "pathutils.h" | ||||
| #include "lists/MinecraftVersionList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
|  | ||||
| BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, | ||||
| 						   SettingsObject *settings_obj, QObject *parent) | ||||
| @@ -38,6 +39,7 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, | ||||
|  | ||||
| 	settings().registerSetting(new Setting("name", "Unnamed Instance")); | ||||
| 	settings().registerSetting(new Setting("iconKey", "default")); | ||||
| 	connect(MMC->icons().get(), SIGNAL(iconUpdated(QString)), SLOT(iconUpdated(QString))); | ||||
| 	settings().registerSetting(new Setting("notes", "")); | ||||
| 	settings().registerSetting(new Setting("lastLaunchTime", 0)); | ||||
|  | ||||
| @@ -93,6 +95,14 @@ BaseInstance::BaseInstance(BaseInstancePrivate *d_in, const QString &rootDir, | ||||
| 		"AutoCloseConsole", globalSettings->getSetting("AutoCloseConsole"))); | ||||
| } | ||||
|  | ||||
| void BaseInstance::iconUpdated(QString key) | ||||
| { | ||||
| 	if(iconKey() == key) | ||||
| 	{ | ||||
| 		emit propertiesChanged(this); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void BaseInstance::nuke() | ||||
| { | ||||
| 	QDir(instanceRoot()).removeRecursively(); | ||||
|   | ||||
| @@ -184,6 +184,9 @@ signals: | ||||
| 	 */ | ||||
| 	void nuked(BaseInstance *inst); | ||||
|  | ||||
| protected slots: | ||||
| 	void iconUpdated(QString key); | ||||
|  | ||||
| protected: | ||||
| 	std::shared_ptr<BaseInstancePrivate> inst_d; | ||||
| }; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ | ||||
|  | ||||
| #include "logic/MinecraftProcess.h" | ||||
| #include "logic/LegacyUpdate.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
|  | ||||
| #include "gui/dialogs/LegacyModEditDialog.h" | ||||
|  | ||||
|   | ||||
							
								
								
									
										351
									
								
								logic/icons/IconList.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								logic/icons/IconList.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,351 @@ | ||||
| /* Copyright 2013 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include "IconList.h" | ||||
| #include <pathutils.h> | ||||
| #include <settingsobject.h> | ||||
| #include <QMap> | ||||
| #include <QEventLoop> | ||||
| #include <QMimeData> | ||||
| #include <QUrl> | ||||
| #include <QFileSystemWatcher> | ||||
| #include <MultiMC.h> | ||||
| #include <setting.h> | ||||
|  | ||||
| #define MAX_SIZE 1024 | ||||
|  | ||||
| IconList::IconList(QObject *parent) : QAbstractListModel(parent) | ||||
| { | ||||
| 	// add builtin icons | ||||
| 	QDir instance_icons(":/icons/instances/"); | ||||
| 	auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); | ||||
| 	for (auto file_info : file_info_list) | ||||
| 	{ | ||||
| 		QString key = file_info.baseName(); | ||||
| 		addIcon(key, key, file_info.absoluteFilePath(), MMCIcon::Builtin); | ||||
| 	} | ||||
|  | ||||
| 	m_watcher.reset(new QFileSystemWatcher()); | ||||
| 	is_watching = false; | ||||
| 	connect(m_watcher.get(), SIGNAL(directoryChanged(QString)), | ||||
| 			SLOT(directoryChanged(QString))); | ||||
| 	connect(m_watcher.get(), SIGNAL(fileChanged(QString)), SLOT(fileChanged(QString))); | ||||
| 	 | ||||
| 	auto setting = MMC->settings()->getSetting("IconsDir"); | ||||
| 	QString path = setting->get().toString(); | ||||
| 	connect(setting, SIGNAL(settingChanged(const Setting &, QVariant)), | ||||
| 			SLOT(settingChanged(const Setting &, QVariant))); | ||||
| 	directoryChanged(path); | ||||
| } | ||||
|  | ||||
| void IconList::directoryChanged(const QString &path) | ||||
| { | ||||
| 	QDir new_dir (path); | ||||
| 	if(m_dir.absolutePath() != new_dir.absolutePath()) | ||||
| 	{ | ||||
| 		m_dir.setPath(path); | ||||
| 		m_dir.refresh(); | ||||
| 		if(is_watching) | ||||
| 			stopWatching(); | ||||
| 		startWatching(); | ||||
| 	} | ||||
| 	if(!m_dir.exists()) | ||||
| 		if(!ensureFolderPathExists(m_dir.absolutePath())) | ||||
| 			return; | ||||
| 	m_dir.refresh(); | ||||
| 	auto new_list = m_dir.entryList(QDir::Files, QDir::Name); | ||||
| 	for (auto it = new_list.begin(); it != new_list.end(); it++) | ||||
| 	{ | ||||
| 		QString &foo = (*it); | ||||
| 		foo = m_dir.filePath(foo); | ||||
| 	} | ||||
| 	auto new_set = new_list.toSet(); | ||||
| 	QList<QString> current_list; | ||||
| 	for (auto &it : icons) | ||||
| 	{ | ||||
| 		if (!it.has(MMCIcon::FileBased)) | ||||
| 			continue; | ||||
| 		current_list.push_back(it.m_images[MMCIcon::FileBased].filename); | ||||
| 	} | ||||
| 	QSet<QString> current_set = current_list.toSet(); | ||||
|  | ||||
| 	QSet<QString> to_remove = current_set; | ||||
| 	to_remove -= new_set; | ||||
|  | ||||
| 	QSet<QString> to_add = new_set; | ||||
| 	to_add -= current_set; | ||||
|  | ||||
| 	for (auto remove : to_remove) | ||||
| 	{ | ||||
| 		QLOG_INFO() << "Removing " << remove; | ||||
| 		QFileInfo rmfile(remove); | ||||
| 		QString key = rmfile.baseName(); | ||||
| 		int idx = getIconIndex(key); | ||||
| 		if (idx == -1) | ||||
| 			continue; | ||||
| 		icons[idx].remove(MMCIcon::FileBased); | ||||
| 		if (icons[idx].type() == MMCIcon::ToBeDeleted) | ||||
| 		{ | ||||
| 			beginRemoveRows(QModelIndex(), idx, idx); | ||||
| 			icons.remove(idx); | ||||
| 			reindex(); | ||||
| 			endRemoveRows(); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			dataChanged(index(idx), index(idx)); | ||||
| 		} | ||||
| 		m_watcher->removePath(remove); | ||||
| 		emit iconUpdated(key); | ||||
| 	} | ||||
|  | ||||
| 	for (auto add : to_add) | ||||
| 	{ | ||||
| 		QLOG_INFO() << "Adding " << add; | ||||
| 		QFileInfo addfile(add); | ||||
| 		QString key = addfile.baseName(); | ||||
| 		if (addIcon(key, QString(), addfile.filePath(), MMCIcon::FileBased)) | ||||
| 		{ | ||||
| 			m_watcher->addPath(add); | ||||
| 			emit iconUpdated(key); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IconList::fileChanged(const QString &path) | ||||
| { | ||||
| 	QLOG_INFO() << "Checking " << path; | ||||
| 	QFileInfo checkfile(path); | ||||
| 	if (!checkfile.exists()) | ||||
| 		return; | ||||
| 	QString key = checkfile.baseName(); | ||||
| 	int idx = getIconIndex(key); | ||||
| 	if (idx == -1) | ||||
| 		return; | ||||
| 	QIcon icon(path); | ||||
| 	if (!icon.availableSizes().size()) | ||||
| 		return; | ||||
|  | ||||
| 	icons[idx].m_images[MMCIcon::FileBased].icon = icon; | ||||
| 	dataChanged(index(idx), index(idx)); | ||||
| 	emit iconUpdated(key); | ||||
| } | ||||
|  | ||||
| void IconList::settingChanged(const Setting &setting, QVariant value) | ||||
| { | ||||
| 	if(setting.configKey() != "IconsDir") | ||||
| 		return; | ||||
|  | ||||
| 	directoryChanged(value.toString()); | ||||
| } | ||||
|  | ||||
| void IconList::startWatching() | ||||
| { | ||||
| 	auto abs_path = m_dir.absolutePath(); | ||||
| 	ensureFolderPathExists(abs_path); | ||||
| 	is_watching = m_watcher->addPath(abs_path); | ||||
| 	if (is_watching) | ||||
| 	{ | ||||
| 		QLOG_INFO() << "Started watching " << abs_path; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QLOG_INFO() << "Failed to start watching " << abs_path; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IconList::stopWatching() | ||||
| { | ||||
| 	m_watcher->removePaths(m_watcher->files()); | ||||
| 	m_watcher->removePaths(m_watcher->directories()); | ||||
| 	is_watching = false; | ||||
| } | ||||
|  | ||||
| QStringList IconList::mimeTypes() const | ||||
| { | ||||
| 	QStringList types; | ||||
| 	types << "text/uri-list"; | ||||
| 	return types; | ||||
| } | ||||
| Qt::DropActions IconList::supportedDropActions() const | ||||
| { | ||||
| 	return Qt::CopyAction; | ||||
| } | ||||
|  | ||||
| bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, | ||||
| 							const QModelIndex &parent) | ||||
| { | ||||
| 	if (action == Qt::IgnoreAction) | ||||
| 		return true; | ||||
| 	// check if the action is supported | ||||
| 	if (!data || !(action & supportedDropActions())) | ||||
| 		return false; | ||||
|  | ||||
| 	// files dropped from outside? | ||||
| 	if (data->hasUrls()) | ||||
| 	{ | ||||
| 		auto urls = data->urls(); | ||||
| 		QStringList iconFiles; | ||||
| 		for (auto url : urls) | ||||
| 		{ | ||||
| 			// only local files may be dropped... | ||||
| 			if (!url.isLocalFile()) | ||||
| 				continue; | ||||
| 			iconFiles += url.toLocalFile(); | ||||
| 		} | ||||
| 		installIcons(iconFiles); | ||||
| 		return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| Qt::ItemFlags IconList::flags(const QModelIndex &index) const | ||||
| { | ||||
| 	Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); | ||||
| 	if (index.isValid()) | ||||
| 		return Qt::ItemIsDropEnabled | defaultFlags; | ||||
| 	else | ||||
| 		return Qt::ItemIsDropEnabled | defaultFlags; | ||||
| } | ||||
|  | ||||
| QVariant IconList::data(const QModelIndex &index, int role) const | ||||
| { | ||||
| 	if (!index.isValid()) | ||||
| 		return QVariant(); | ||||
|  | ||||
| 	int row = index.row(); | ||||
|  | ||||
| 	if (row < 0 || row >= icons.size()) | ||||
| 		return QVariant(); | ||||
|  | ||||
| 	switch (role) | ||||
| 	{ | ||||
| 	case Qt::DecorationRole: | ||||
| 		return icons[row].icon(); | ||||
| 	case Qt::DisplayRole: | ||||
| 		return icons[row].name(); | ||||
| 	case Qt::UserRole: | ||||
| 		return icons[row].m_key; | ||||
| 	default: | ||||
| 		return QVariant(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int IconList::rowCount(const QModelIndex &parent) const | ||||
| { | ||||
| 	return icons.size(); | ||||
| } | ||||
|  | ||||
| void IconList::installIcons(QStringList iconFiles) | ||||
| { | ||||
| 	for (QString file : iconFiles) | ||||
| 	{ | ||||
| 		QFileInfo fileinfo(file); | ||||
| 		if (!fileinfo.isReadable() || !fileinfo.isFile()) | ||||
| 			continue; | ||||
| 		QString target = PathCombine("icons", fileinfo.fileName()); | ||||
|  | ||||
| 		QString suffix = fileinfo.suffix(); | ||||
| 		if (suffix != "jpeg" && suffix != "png" && suffix != "jpg") | ||||
| 			continue; | ||||
|  | ||||
| 		if (!QFile::copy(file, target)) | ||||
| 			continue; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool IconList::deleteIcon(QString key) | ||||
| { | ||||
| 	int iconIdx = getIconIndex(key); | ||||
| 	if (iconIdx == -1) | ||||
| 		return false; | ||||
| 	auto &iconEntry = icons[iconIdx]; | ||||
| 	if (iconEntry.has(MMCIcon::FileBased)) | ||||
| 	{ | ||||
| 		return QFile::remove(iconEntry.m_images[MMCIcon::FileBased].filename); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| bool IconList::addIcon(QString key, QString name, QString path, MMCIcon::Type type) | ||||
| { | ||||
| 	// replace the icon even? is the input valid? | ||||
| 	QIcon icon(path); | ||||
| 	if (!icon.availableSizes().size()) | ||||
| 		return false; | ||||
| 	auto iter = name_index.find(key); | ||||
| 	if (iter != name_index.end()) | ||||
| 	{ | ||||
| 		auto &oldOne = icons[*iter]; | ||||
| 		oldOne.replace(type, icon, path); | ||||
| 		dataChanged(index(*iter), index(*iter)); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		// add a new icon | ||||
| 		beginInsertRows(QModelIndex(), icons.size(), icons.size()); | ||||
| 		{ | ||||
| 			MMCIcon mmc_icon; | ||||
| 			mmc_icon.m_name = name; | ||||
| 			mmc_icon.m_key = key; | ||||
| 			mmc_icon.replace(type, icon, path); | ||||
| 			icons.push_back(mmc_icon); | ||||
| 			name_index[key] = icons.size() - 1; | ||||
| 		} | ||||
| 		endInsertRows(); | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IconList::reindex() | ||||
| { | ||||
| 	name_index.clear(); | ||||
| 	int i = 0; | ||||
| 	for (auto &iter : icons) | ||||
| 	{ | ||||
| 		name_index[iter.m_key] = i; | ||||
| 		i++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QIcon IconList::getIcon(QString key) | ||||
| { | ||||
| 	int icon_index = getIconIndex(key); | ||||
|  | ||||
| 	if (icon_index != -1) | ||||
| 		return icons[icon_index].icon(); | ||||
|  | ||||
| 	// Fallback for icons that don't exist. | ||||
| 	icon_index = getIconIndex("infinity"); | ||||
|  | ||||
| 	if (icon_index != -1) | ||||
| 		return icons[icon_index].icon(); | ||||
| 	return QIcon(); | ||||
| } | ||||
|  | ||||
| int IconList::getIconIndex(QString key) | ||||
| { | ||||
| 	if (key == "default") | ||||
| 		key = "infinity"; | ||||
|  | ||||
| 	auto iter = name_index.find(key); | ||||
| 	if (iter != name_index.end()) | ||||
| 		return *iter; | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| //#include "IconList.moc" | ||||
| @@ -17,15 +17,21 @@ | ||||
| 
 | ||||
| #include <QMutex> | ||||
| #include <QAbstractListModel> | ||||
| #include <QFile> | ||||
| #include <QDir> | ||||
| #include <QtGui/QIcon> | ||||
| #include <memory> | ||||
| #include "MMCIcon.h" | ||||
| #include "setting.h" | ||||
| 
 | ||||
| class Private; | ||||
| class QFileSystemWatcher; | ||||
| 
 | ||||
| class IconList : public QAbstractListModel | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	IconList(); | ||||
| 	virtual ~IconList(); | ||||
| 	explicit IconList(QObject *parent = 0); | ||||
| 	virtual ~IconList() {}; | ||||
| 
 | ||||
| 	QIcon getIcon(QString key); | ||||
| 	int getIconIndex(QString key); | ||||
| @@ -33,7 +39,7 @@ public: | ||||
| 	virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; | ||||
| 	virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; | ||||
| 
 | ||||
| 	bool addIcon(QString key, QString name, QString path, bool is_builtin = false); | ||||
| 	bool addIcon(QString key, QString name, QString path, MMCIcon::Type type); | ||||
| 	bool deleteIcon(QString key); | ||||
| 
 | ||||
| 	virtual QStringList mimeTypes() const; | ||||
| @@ -44,11 +50,28 @@ public: | ||||
| 
 | ||||
| 	void installIcons(QStringList iconFiles); | ||||
| 
 | ||||
| 	void startWatching(); | ||||
| 	void stopWatching(); | ||||
| 
 | ||||
| signals: | ||||
| 	void iconUpdated(QString key); | ||||
| 
 | ||||
| private: | ||||
| 	// hide copy constructor
 | ||||
| 	IconList(const IconList &) = delete; | ||||
| 	// hide assign op
 | ||||
| 	IconList &operator=(const IconList &) = delete; | ||||
| 	void reindex(); | ||||
| 	Private *d; | ||||
| 
 | ||||
| protected | ||||
| slots: | ||||
| 	void directoryChanged(const QString &path); | ||||
| 	void fileChanged(const QString &path); | ||||
| 	void settingChanged(const Setting & setting, QVariant value); | ||||
| private: | ||||
| 	std::shared_ptr<QFileSystemWatcher> m_watcher; | ||||
| 	bool is_watching; | ||||
| 	QMap<QString, int> name_index; | ||||
| 	QVector<MMCIcon> icons; | ||||
| 	QDir m_dir; | ||||
| }; | ||||
							
								
								
									
										89
									
								
								logic/icons/MMCIcon.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								logic/icons/MMCIcon.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| /* Copyright 2013 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include "MMCIcon.h" | ||||
| #include <QFileInfo> | ||||
|  | ||||
| MMCIcon::Type operator--(MMCIcon::Type &t, int) | ||||
| { | ||||
| 	MMCIcon::Type temp = t; | ||||
| 	switch (t) | ||||
| 	{ | ||||
| 	case MMCIcon::Type::Builtin: | ||||
| 		t = MMCIcon::Type::ToBeDeleted; | ||||
| 		break; | ||||
| 	case MMCIcon::Type::Transient: | ||||
| 		t = MMCIcon::Type::Builtin; | ||||
| 		break; | ||||
| 	case MMCIcon::Type::FileBased: | ||||
| 		t = MMCIcon::Type::Transient; | ||||
| 		break; | ||||
| 	default: | ||||
| 	{ | ||||
| 	} | ||||
| 	} | ||||
| 	return temp; | ||||
| } | ||||
|  | ||||
| MMCIcon::Type MMCIcon::type() const | ||||
| { | ||||
| 	return m_current_type; | ||||
| } | ||||
|  | ||||
| QString MMCIcon::name() const | ||||
| { | ||||
| 	if (m_name.size()) | ||||
| 		return m_name; | ||||
| 	return m_key; | ||||
| } | ||||
|  | ||||
| bool MMCIcon::has(MMCIcon::Type _type) const | ||||
| { | ||||
| 	return m_images[_type].present(); | ||||
| } | ||||
|  | ||||
| QIcon MMCIcon::icon() const | ||||
| { | ||||
| 	if (m_current_type == Type::ToBeDeleted) | ||||
| 		return QIcon(); | ||||
| 	return m_images[m_current_type].icon; | ||||
| } | ||||
|  | ||||
| void MMCIcon::remove(Type rm_type) | ||||
| { | ||||
| 	m_images[rm_type].filename = QString(); | ||||
| 	m_images[rm_type].icon = QIcon(); | ||||
| 	for (auto iter = rm_type; iter != Type::ToBeDeleted; iter--) | ||||
| 	{ | ||||
| 		if (m_images[iter].present()) | ||||
| 		{ | ||||
| 			m_current_type = iter; | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	m_current_type = Type::ToBeDeleted; | ||||
| } | ||||
|  | ||||
| void MMCIcon::replace(MMCIcon::Type new_type, QIcon icon, QString path) | ||||
| { | ||||
| 	QFileInfo foo(path); | ||||
| 	if (new_type > m_current_type || m_current_type == MMCIcon::ToBeDeleted) | ||||
| 	{ | ||||
| 		m_current_type = new_type; | ||||
| 	} | ||||
| 	m_images[new_type].icon = icon; | ||||
| 	m_images[new_type].changed = foo.lastModified(); | ||||
| 	m_images[new_type].filename = path; | ||||
| } | ||||
							
								
								
									
										52
									
								
								logic/icons/MMCIcon.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								logic/icons/MMCIcon.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| /* Copyright 2013 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
| #include <QString> | ||||
| #include <QDateTime> | ||||
| #include <QIcon> | ||||
| struct MMCImage | ||||
| { | ||||
| 	QIcon icon; | ||||
| 	QString filename; | ||||
| 	QDateTime changed; | ||||
| 	bool present() const | ||||
| 	{ | ||||
| 		return !icon.isNull(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| struct MMCIcon | ||||
| { | ||||
| 	enum Type : unsigned | ||||
| 	{ | ||||
| 		Builtin, | ||||
| 		Transient, | ||||
| 		FileBased, | ||||
| 		ICONS_TOTAL, | ||||
| 		ToBeDeleted | ||||
| 	}; | ||||
| 	QString m_key; | ||||
| 	QString m_name; | ||||
| 	MMCImage m_images[ICONS_TOTAL]; | ||||
| 	Type m_current_type = ToBeDeleted; | ||||
|  | ||||
| 	Type type() const; | ||||
| 	QString name() const; | ||||
| 	bool has(Type _type) const; | ||||
| 	QIcon icon() const; | ||||
| 	void remove(Type rm_type); | ||||
| 	void replace(Type new_type, QIcon icon, QString path = QString()); | ||||
| }; | ||||
| @@ -1,271 +0,0 @@ | ||||
| /* Copyright 2013 MultiMC Contributors | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  *     http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| #include "IconList.h" | ||||
| #include <pathutils.h> | ||||
| #include <QMap> | ||||
| #include <QEventLoop> | ||||
| #include <QDir> | ||||
| #include <QMimeData> | ||||
| #include <QUrl> | ||||
| #define MAX_SIZE 1024 | ||||
|  | ||||
| struct entry | ||||
| { | ||||
| 	QString key; | ||||
| 	QString name; | ||||
| 	QIcon icon; | ||||
| 	bool is_builtin; | ||||
| 	QString filename; | ||||
| }; | ||||
|  | ||||
| class Private : public QObject | ||||
| { | ||||
| 	Q_OBJECT | ||||
| public: | ||||
| 	QMap<QString, int> index; | ||||
| 	QVector<entry> icons; | ||||
| 	Private() | ||||
| 	{ | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| IconList::IconList() : QAbstractListModel(), d(new Private()) | ||||
| { | ||||
| 	QDir instance_icons(":/icons/instances/"); | ||||
| 	auto file_info_list = instance_icons.entryInfoList(QDir::Files, QDir::Name); | ||||
| 	for (auto file_info : file_info_list) | ||||
| 	{ | ||||
| 		QString key = file_info.baseName(); | ||||
| 		addIcon(key, key, file_info.absoluteFilePath(), true); | ||||
| 	} | ||||
|  | ||||
| 	// FIXME: get from settings | ||||
| 	ensureFolderPathExists("icons"); | ||||
| 	QDir user_icons("icons"); | ||||
| 	file_info_list = user_icons.entryInfoList(QDir::Files, QDir::Name); | ||||
| 	for (auto file_info : file_info_list) | ||||
| 	{ | ||||
| 		QString filename = file_info.absoluteFilePath(); | ||||
| 		QString key = file_info.baseName(); | ||||
| 		addIcon(key, key, filename); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| IconList::~IconList() | ||||
| { | ||||
| 	delete d; | ||||
| 	d = nullptr; | ||||
| } | ||||
|  | ||||
| QStringList IconList::mimeTypes() const | ||||
| { | ||||
| 	QStringList types; | ||||
| 	types << "text/uri-list"; | ||||
| 	return types; | ||||
| } | ||||
| Qt::DropActions IconList::supportedDropActions() const | ||||
| { | ||||
| 	return Qt::CopyAction; | ||||
| } | ||||
|  | ||||
| bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, | ||||
| 							const QModelIndex &parent) | ||||
| { | ||||
| 	if (action == Qt::IgnoreAction) | ||||
| 		return true; | ||||
| 	// check if the action is supported | ||||
| 	if (!data || !(action & supportedDropActions())) | ||||
| 		return false; | ||||
|  | ||||
| 	// files dropped from outside? | ||||
| 	if (data->hasUrls()) | ||||
| 	{ | ||||
| 		/* | ||||
| 		bool was_watching = is_watching; | ||||
| 		if(was_watching) | ||||
| 			stopWatching(); | ||||
| 		*/ | ||||
| 		auto urls = data->urls(); | ||||
| 		QStringList iconFiles; | ||||
| 		for (auto url : urls) | ||||
| 		{ | ||||
| 			// only local files may be dropped... | ||||
| 			if (!url.isLocalFile()) | ||||
| 				continue; | ||||
| 			iconFiles += url.toLocalFile(); | ||||
| 		} | ||||
| 		installIcons(iconFiles); | ||||
| 		/* | ||||
| 		if(was_watching) | ||||
| 			startWatching(); | ||||
| 		*/ | ||||
| 		return true; | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| Qt::ItemFlags IconList::flags(const QModelIndex &index) const | ||||
| { | ||||
| 	Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); | ||||
| 	if (index.isValid()) | ||||
| 		return Qt::ItemIsDropEnabled | defaultFlags; | ||||
| 	else | ||||
| 		return Qt::ItemIsDropEnabled | defaultFlags; | ||||
| } | ||||
|  | ||||
| QVariant IconList::data(const QModelIndex &index, int role) const | ||||
| { | ||||
| 	if (!index.isValid()) | ||||
| 		return QVariant(); | ||||
|  | ||||
| 	int row = index.row(); | ||||
|  | ||||
| 	if (row < 0 || row >= d->icons.size()) | ||||
| 		return QVariant(); | ||||
|  | ||||
| 	switch (role) | ||||
| 	{ | ||||
| 	case Qt::DecorationRole: | ||||
| 		return d->icons[row].icon; | ||||
| 	case Qt::DisplayRole: | ||||
| 		return d->icons[row].name; | ||||
| 	case Qt::UserRole: | ||||
| 		return d->icons[row].key; | ||||
| 	default: | ||||
| 		return QVariant(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int IconList::rowCount(const QModelIndex &parent) const | ||||
| { | ||||
| 	return d->icons.size(); | ||||
| } | ||||
|  | ||||
| void IconList::installIcons(QStringList iconFiles) | ||||
| { | ||||
| 	for (QString file : iconFiles) | ||||
| 	{ | ||||
| 		QFileInfo fileinfo(file); | ||||
| 		if (!fileinfo.isReadable() || !fileinfo.isFile()) | ||||
| 			continue; | ||||
| 		QString target = PathCombine("icons", fileinfo.fileName()); | ||||
|  | ||||
| 		QString suffix = fileinfo.suffix(); | ||||
| 		if (suffix != "jpeg" && suffix != "png" && suffix != "jpg") | ||||
| 			continue; | ||||
|  | ||||
| 		if (!QFile::copy(file, target)) | ||||
| 			continue; | ||||
|  | ||||
| 		QString key = fileinfo.baseName(); | ||||
| 		addIcon(key, key, target); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| bool IconList::deleteIcon(QString key) | ||||
| { | ||||
| 	int iconIdx = getIconIndex(key); | ||||
| 	if (iconIdx == -1) | ||||
| 		return false; | ||||
| 	auto &iconEntry = d->icons[iconIdx]; | ||||
| 	if (iconEntry.is_builtin) | ||||
| 		return false; | ||||
| 	if (QFile::remove(iconEntry.filename)) | ||||
| 	{ | ||||
| 		beginRemoveRows(QModelIndex(), iconIdx, iconIdx); | ||||
| 		d->icons.remove(iconIdx); | ||||
| 		reindex(); | ||||
| 		endRemoveRows(); | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool IconList::addIcon(QString key, QString name, QString path, bool is_builtin) | ||||
| { | ||||
| 	auto iter = d->index.find(key); | ||||
| 	if (iter != d->index.end()) | ||||
| 	{ | ||||
| 		if (d->icons[*iter].is_builtin) | ||||
| 			return false; | ||||
|  | ||||
| 		QIcon icon(path); | ||||
| 		if (icon.isNull()) | ||||
| 			return false; | ||||
|  | ||||
| 		auto &oldOne = d->icons[*iter]; | ||||
|  | ||||
| 		if (!QFile::remove(oldOne.filename)) | ||||
| 			return false; | ||||
|  | ||||
| 		// replace the icon | ||||
| 		oldOne = {key, name, icon, is_builtin, path}; | ||||
| 		dataChanged(index(*iter), index(*iter)); | ||||
| 		return true; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		QIcon icon(path); | ||||
| 		if (icon.isNull()) | ||||
| 			return false; | ||||
|  | ||||
| 		// add a new icon | ||||
| 		beginInsertRows(QModelIndex(), d->icons.size(), d->icons.size()); | ||||
| 		d->icons.push_back({key, name, icon, is_builtin, path}); | ||||
| 		d->index[key] = d->icons.size() - 1; | ||||
| 		endInsertRows(); | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void IconList::reindex() | ||||
| { | ||||
| 	d->index.clear(); | ||||
| 	int i = 0; | ||||
| 	for (auto &iter : d->icons) | ||||
| 	{ | ||||
| 		d->index[iter.key] = i; | ||||
| 		i++; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| QIcon IconList::getIcon(QString key) | ||||
| { | ||||
| 	int icon_index = getIconIndex(key); | ||||
|  | ||||
| 	if (icon_index != -1) | ||||
| 		return d->icons[icon_index].icon; | ||||
|  | ||||
| 	// Fallback for icons that don't exist. | ||||
| 	icon_index = getIconIndex("infinity"); | ||||
|  | ||||
| 	if (icon_index != -1) | ||||
| 		return d->icons[icon_index].icon; | ||||
| 	return QIcon(); | ||||
| } | ||||
|  | ||||
| int IconList::getIconIndex(QString key) | ||||
| { | ||||
| 	if (key == "default") | ||||
| 		key = "infinity"; | ||||
|  | ||||
| 	auto iter = d->index.find(key); | ||||
| 	if (iter != d->index.end()) | ||||
| 		return *iter; | ||||
|  | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| #include "IconList.moc" | ||||
| @@ -28,7 +28,7 @@ | ||||
|  | ||||
| #include "MultiMC.h" | ||||
| #include "logic/lists/InstanceList.h" | ||||
| #include "logic/lists/IconList.h" | ||||
| #include "logic/icons/IconList.h" | ||||
| #include "logic/lists/MinecraftVersionList.h" | ||||
| #include "logic/BaseInstance.h" | ||||
| #include "logic/InstanceFactory.h" | ||||
| @@ -356,12 +356,13 @@ void InstanceList::loadForgeInstances(QMap<QString, QString> groupMap) | ||||
|  | ||||
| 		QString iconKey = record.logo; | ||||
| 		iconKey.remove(QRegularExpression("\\..*")); | ||||
| 		MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), true); | ||||
| 		MMC->icons()->addIcon(iconKey, iconKey, PathCombine(templateDir, record.logo), | ||||
| 							  MMCIcon::Transient); | ||||
|  | ||||
| 		if (!QFileInfo(PathCombine(instanceDir, "instance.cfg")).exists()) | ||||
| 		{ | ||||
| 			BaseInstance *instPtr = NULL; | ||||
| 			auto & factory = InstanceFactory::get(); | ||||
| 			auto &factory = InstanceFactory::get(); | ||||
| 			auto version = MMC->minecraftlist()->findVersion(record.mcVersion); | ||||
| 			if (!version) | ||||
| 			{ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user