Compare commits

..

31 Commits

Author SHA1 Message Date
janrupf
b09f3448f9 NOISSUE Try on fixing overflowing text 2019-06-22 00:27:43 +02:00
Petr Mrázek
ce12f1a734 Merge pull request #2694 from Janrupf/fix-disabled-mods
Operate on copy of QString instead of appending
2019-06-17 23:21:26 +02:00
janrupf
c1953739d9 NOISSUE Don't append .disabled to string 2019-06-17 15:49:26 +02:00
Petr Mrázek
e8bf9cef24 Merge branch 'develop' of https://github.com/lelandliu/MultiMC5 into develop 2019-06-15 22:47:36 +02:00
Petr Mrázek
8aa4b9dac5 NOISSUE limit fabric intermediary selection options in UI by minecraft version 2019-06-15 22:45:56 +02:00
Petr Mrázek
4836ba22cd NOISSUE fix fabric tooltip string 2019-06-15 22:29:34 +02:00
Petr Mrázek
6c30076b6c GH-2639 Add simple fabric loader installation support 2019-06-15 21:25:23 +02:00
Petr Mrázek
83c48a0f1a Merge pull request #2690 from asiekierka/fabric-modparse
Add Fabric mod JSON parsing support
2019-06-15 20:41:42 +02:00
asie
d251097545 fix author name parsing 2019-06-15 16:36:13 +02:00
asie
c35dbd972e Add Fabric mod JSON parsing support 2019-06-15 13:54:20 +02:00
Leland Liu
0c5d121452 Fixed adding a disabled duplicate 2019-06-14 15:48:24 -04:00
Petr Mrázek
53ad5cb436 Merge pull request #2684 from isoraqathedh/patch-1
Pass command line arguments through to the executable
2019-06-08 15:53:01 +02:00
isoraqathedh
d87bd4b588 Update run.sh
Pass command line arguments through to the executable
2019-06-08 21:21:22 +08:00
Petr Mrázek
86850ef5d0 NOISSUE fix macOS build, remove bundled dependencies on linux
Your copy of MultiMC might stop working after this update
because we no longer bundle Qt and other system libraries.

Contact us at https://discord.gg/0k2zsXGNHs0fE4Wm if
you need help with installing Qt.

Qt 5.4.x or newer is required.
2019-06-08 15:08:24 +02:00
Petr Mrázek
30fba4d407 NOISSUE make the global settings button context sensitive 2019-06-01 18:05:42 +02:00
Petr Mrázek
932160818e NOISSUE add option to open global settings from instance settings
This should hopefully giude people towards using the right thing.
2019-06-01 12:28:53 +02:00
Petr Mrázek
59e1ed3d87 NOISSUE add the pocket fox icon 2019-06-01 00:59:02 +02:00
Petr Mrázek
3470a3df96 NOISSUE improve icon handling while importing and exporting instances
Now it handles formats other than png.
2019-05-31 21:53:58 +02:00
Petr Mrázek
61913daaf3 Merge pull request #2603 from AlexTMjugador/develop
Support launching profiler in offline mode
2019-04-14 23:34:10 +02:00
Alejandro González
cb71dfa605 Tweak strings as per peterix review 2019-04-14 23:02:01 +02:00
AlexTMjugador
2be98baaba Conform to existing code style 2019-04-15 00:44:59 +02:00
AlexTMjugador
0ce637bf0e Support launching profiler in offline mode
This commit adds the necessary GUI controls to let the user choose
whether they want to launch Minecraft in offline mode with a configured profiler.
2019-04-15 00:42:06 +02:00
Petr Mrázek
70ed30f9e6 GH-2591 less std::shared_ptr and more shared_qobject_ptr
This eliminates some weird crashes.
2019-04-07 23:59:04 +02:00
Petr Mrázek
414946cad9 Merge remote-tracking branch 'origin/feature/trim_server_details' into develop 2019-04-02 23:39:13 +02:00
Elise
1fc199697c GH-2551 Trim at serialization 2019-03-29 11:25:37 -04:00
Petr Mrázek
9292d846b6 Merge pull request #2565 from MultiMC/feature/deletion_harder
GH-2487 Make deleting instances harder
2019-03-23 14:23:45 +01:00
Elise
22db95ce3b Merge branch 'develop' into feature/deletion_harder 2019-03-19 16:29:40 -04:00
Elise
597fe50d37 GH-2551 Trim server name and IP strings 2019-03-19 13:28:11 -04:00
Petr Mrázek
bf93ba02e9 NOISSUE fix metadata URL 2019-03-08 02:04:08 +01:00
Petr Mrázek
07c1685ff1 NOISSUE create translations folder before starting to watch it for changes
Fixes issue where on first run, the translations don't show up.
2019-03-08 01:21:04 +01:00
Elise
a5e531d4f1 GH-2487 Make NO the default button 2019-02-26 16:56:08 -05:00
59 changed files with 738 additions and 197 deletions

View File

@@ -187,8 +187,7 @@ Qt::DropActions IconList::supportedDropActions() const
return Qt::CopyAction;
}
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column,
const QModelIndex &parent)
bool IconList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
if (action == Qt::IgnoreAction)
return true;

View File

@@ -80,7 +80,7 @@ protected slots:
void fileChanged(const QString &path);
void SettingChanged(const Setting & setting, QVariant value);
private:
std::shared_ptr<QFileSystemWatcher> m_watcher;
shared_qobject_ptr<QFileSystemWatcher> m_watcher;
bool is_watching;
QMap<QString, int> name_index;
QVector<MMCIcon> icons;

View File

@@ -102,3 +102,17 @@ void MMCIcon::replace(IconType new_type, const QString& key)
m_images[new_type].filename = QString();
m_images[new_type].key = key;
}
QString MMCIcon::getFilePath() const
{
if(m_current_type == IconType::ToBeDeleted){
return QString();
}
return m_images[m_current_type].filename;
}
bool MMCIcon::isBuiltIn() const
{
return m_current_type == IconType::Builtin;
}

View File

@@ -46,4 +46,6 @@ struct MULTIMC_GUI_EXPORT MMCIcon
void remove(IconType rm_type);
void replace(IconType new_type, QIcon icon, QString path = QString());
void replace(IconType new_type, const QString &key);
bool isBuiltIn() const;
QString getFilePath() const;
};

View File

@@ -175,11 +175,6 @@ QString BaseInstance::instanceRoot() const
return m_rootDir;
}
InstancePtr BaseInstance::getSharedPtr()
{
return shared_from_this();
}
SettingsObjectPtr BaseInstance::settings() const
{
return m_settings;
@@ -253,7 +248,7 @@ QStringList BaseInstance::extraArguments() const
return Commandline::splitArgs(settings()->get("JvmArgs").toString());
}
std::shared_ptr<LaunchTask> BaseInstance::getLaunchTask()
shared_qobject_ptr<LaunchTask> BaseInstance::getLaunchTask()
{
return m_launchProcess;
}

View File

@@ -134,8 +134,6 @@ public:
/// Sets the last launched time to 'val' milliseconds since epoch
void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch());
InstancePtr getSharedPtr();
/*!
* \brief Gets this instance's settings object.
* This settings object stores instance-specific settings.
@@ -147,10 +145,10 @@ public:
virtual shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) = 0;
/// returns a valid launcher (task container)
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) = 0;
/// returns the current launch task (if any)
std::shared_ptr<LaunchTask> getLaunchTask();
shared_qobject_ptr<LaunchTask> getLaunchTask();
/*!
* Create envrironment variables for running the instance
@@ -241,7 +239,7 @@ signals:
*/
void propertiesChanged(BaseInstance *inst);
void launchTaskChanged(std::shared_ptr<LaunchTask>);
void launchTaskChanged(shared_qobject_ptr<LaunchTask>);
void runningStatusChanged(bool running);
@@ -255,7 +253,7 @@ protected: /* data */
SettingsObjectPtr m_settings;
// InstanceFlags m_flags;
bool m_isRunning = false;
std::shared_ptr<LaunchTask> m_launchProcess;
shared_qobject_ptr<LaunchTask> m_launchProcess;
QDateTime m_timeStarted;
private: /* data */
@@ -265,6 +263,6 @@ private: /* data */
bool m_hasBrokenVersion = false;
};
Q_DECLARE_METATYPE(std::shared_ptr<BaseInstance>)
Q_DECLARE_METATYPE(shared_qobject_ptr<BaseInstance>)
//Q_DECLARE_METATYPE(BaseInstance::InstanceFlag)
//Q_DECLARE_OPERATORS_FOR_FLAGS(BaseInstance::InstanceFlags)

View File

@@ -182,9 +182,11 @@ set(NEWS_SOURCES
# Icon interface
set(ICONS_SOURCES
# News System
# Icons System and related code
icons/IIconList.h
icons/IIconList.cpp
icons/IconUtils.h
icons/IconUtils.cpp
)
# Minecraft services status checker

View File

@@ -6,6 +6,7 @@
#include "NullInstance.h"
#include "settings/INISettingsObject.h"
#include "icons/IIconList.h"
#include "icons/IconUtils.h"
#include <QtConcurrentRun>
// FIXME: this does not belong here, it's Minecraft/Flame specific
@@ -393,8 +394,9 @@ void InstanceImportTask::processMultiMC()
else
{
m_instIcon = instance.iconKey();
auto importIconPath = FS::PathCombine(instance.instanceRoot(), m_instIcon + ".png");
if (QFile::exists(importIconPath))
auto importIconPath = IconUtils::findBestIconIn(instance.instanceRoot(), m_instIcon);
if (!importIconPath.isNull() && QFile::exists(importIconPath))
{
// import icon
auto iconList = ENV.icons();

View File

@@ -1,5 +1,6 @@
#pragma once
#include "BaseInstance.h"
#include "launch/LaunchTask.h"
class NullInstance: public BaseInstance
{
@@ -11,46 +12,46 @@ public:
setVersionBroken(true);
}
virtual ~NullInstance() {};
virtual void saveNow() override
void saveNow() override
{
}
virtual QString getStatusbarDescription() override
QString getStatusbarDescription() override
{
return tr("Unknown instance type");
};
virtual QSet< QString > traits() const override
QSet< QString > traits() const override
{
return {};
};
virtual QString instanceConfigFolder() const override
QString instanceConfigFolder() const override
{
return instanceRoot();
};
virtual std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr) override
{
return nullptr;
}
virtual shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
shared_qobject_ptr< Task > createUpdateTask(Net::Mode mode) override
{
return nullptr;
}
virtual QProcessEnvironment createEnvironment() override
QProcessEnvironment createEnvironment() override
{
return QProcessEnvironment();
}
virtual QMap<QString, QString> getVariables() const override
QMap<QString, QString> getVariables() const override
{
return QMap<QString, QString>();
}
virtual IPathMatcher::Ptr getLogFileMatcher() override
IPathMatcher::Ptr getLogFileMatcher() override
{
return nullptr;
}
virtual QString getLogFileRoot() override
QString getLogFileRoot() override
{
return instanceRoot();
}
virtual QString typeName() const override
QString typeName() const override
{
return "Null";
}

View File

@@ -0,0 +1,62 @@
#include "IconUtils.h"
#include "FileSystem.h"
#include <QDirIterator>
#include <array>
namespace {
std::array<const char *, 6> validIconExtensions = {{
"svg",
"png",
"ico",
"gif",
"jpg",
"jpeg"
}};
}
namespace IconUtils{
QString findBestIconIn(const QString &folder, const QString & iconKey) {
int best_found = validIconExtensions.size();
QString best_filename;
QDirIterator it(folder, QDir::NoDotAndDotDot | QDir::Files, QDirIterator::NoIteratorFlags);
while (it.hasNext()) {
it.next();
auto fileInfo = it.fileInfo();
if(fileInfo.completeBaseName() != iconKey)
continue;
auto extension = fileInfo.suffix();
for(int i = 0; i < best_found; i++) {
if(extension == validIconExtensions[i]) {
best_found = i;
qDebug() << i << " : " << fileInfo.fileName();
best_filename = fileInfo.fileName();
}
}
}
return FS::PathCombine(folder, best_filename);
}
QString getIconFilter() {
QString out;
QTextStream stream(&out);
stream << '(';
for(size_t i = 0; i < validIconExtensions.size() - 1; i++) {
if(i > 0) {
stream << " ";
}
stream << "*." << validIconExtensions[i];
}
stream << " *." << validIconExtensions[validIconExtensions.size() - 1];
stream << ')';
return out;
}
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <QString>
#include "multimc_logic_export.h"
namespace IconUtils {
// Given a folder and an icon key, find 'best' of the icons with the given key in there and return its path
MULTIMC_LOGIC_EXPORT QString findBestIconIn(const QString &folder, const QString & iconKey);
// Get icon file type filter for file browser dialogs
MULTIMC_LOGIC_EXPORT QString getIconFilter();
}

View File

@@ -75,8 +75,8 @@ void JavaChecker::stderrReady()
void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
{
killTimer.stop();
QProcessPtr _process;
_process.swap(process);
QProcessPtr _process = process;
process.reset();
JavaCheckResult result;
{

View File

@@ -3,6 +3,8 @@
#include <QTimer>
#include <memory>
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
#include "JavaVersion.h"
@@ -27,8 +29,8 @@ struct MULTIMC_LOGIC_EXPORT JavaCheckResult
} validity = Validity::Errored;
};
typedef std::shared_ptr<QProcess> QProcessPtr;
typedef std::shared_ptr<JavaChecker> JavaCheckerPtr;
typedef shared_qobject_ptr<QProcess> QProcessPtr;
typedef shared_qobject_ptr<JavaChecker> JavaCheckerPtr;
class MULTIMC_LOGIC_EXPORT JavaChecker : public QObject
{
Q_OBJECT

View File

@@ -20,7 +20,7 @@
#include "tasks/Task.h"
class JavaCheckerJob;
typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr;
typedef shared_qobject_ptr<JavaCheckerJob> JavaCheckerJobPtr;
// FIXME: this just seems horribly redundant
class JavaCheckerJob : public Task

View File

@@ -149,7 +149,7 @@ void JavaListLoadTask::executeTask()
JavaUtils ju;
QList<QString> candidate_paths = ju.FindJavaPaths();
m_job = std::shared_ptr<JavaCheckerJob>(new JavaCheckerJob("Java detection"));
m_job = new JavaCheckerJob("Java detection");
connect(m_job.get(), &Task::finished, this, &JavaListLoadTask::javaCheckerFinished);
connect(m_job.get(), &Task::progress, this, &Task::setProgress);

View File

@@ -24,6 +24,8 @@
#include "JavaCheckerJob.h"
#include "JavaInstall.h"
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
class JavaListLoadTask;
@@ -75,7 +77,7 @@ public slots:
void javaCheckerFinished();
protected:
std::shared_ptr<JavaCheckerJob> m_job;
shared_qobject_ptr<JavaCheckerJob> m_job;
JavaInstallList *m_list;
JavaInstall *m_currentRecommended;
};

View File

@@ -60,7 +60,7 @@ void CheckJava::executeTask()
// if timestamps are not the same, or something is missing, check!
if (javaUnixTime != storedUnixTime || storedVersion.size() == 0 || storedArchitecture.size() == 0)
{
m_JavaChecker = std::make_shared<JavaChecker>();
m_JavaChecker = new JavaChecker();
emit logLine(tr("Checking Java version..."), MessageLevel::MultiMC);
connect(m_JavaChecker.get(), &JavaChecker::checkFinished, this, &CheckJava::checkJavaFinished);
m_JavaChecker->m_path = realJavaPath;

View File

@@ -33,9 +33,9 @@ void LaunchTask::init()
m_instance->setRunning(true);
}
std::shared_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
shared_qobject_ptr<LaunchTask> LaunchTask::create(InstancePtr inst)
{
std::shared_ptr<LaunchTask> proc(new LaunchTask(inst));
shared_qobject_ptr<LaunchTask> proc(new LaunchTask(inst));
proc->init();
return proc;
}
@@ -44,12 +44,12 @@ LaunchTask::LaunchTask(InstancePtr instance): m_instance(instance)
{
}
void LaunchTask::appendStep(std::shared_ptr<LaunchStep> step)
void LaunchTask::appendStep(shared_qobject_ptr<LaunchStep> step)
{
m_steps.append(step);
}
void LaunchTask::prependStep(std::shared_ptr<LaunchStep> step)
void LaunchTask::prependStep(shared_qobject_ptr<LaunchStep> step)
{
m_steps.prepend(step);
}

View File

@@ -45,11 +45,11 @@ public:
};
public: /* methods */
static std::shared_ptr<LaunchTask> create(InstancePtr inst);
static shared_qobject_ptr<LaunchTask> create(InstancePtr inst);
virtual ~LaunchTask() {};
void appendStep(std::shared_ptr<LaunchStep> step);
void prependStep(std::shared_ptr<LaunchStep> step);
void appendStep(shared_qobject_ptr<LaunchStep> step);
void prependStep(shared_qobject_ptr<LaunchStep> step);
void setCensorFilter(QMap<QString, QString> filter);
InstancePtr instance()
@@ -117,7 +117,7 @@ private: /*methods */
protected: /* data */
InstancePtr m_instance;
shared_qobject_ptr<LogModel> m_logModel;
QList <std::shared_ptr<LaunchStep>> m_steps;
QList <shared_qobject_ptr<LaunchStep>> m_steps;
QMap<QString, QString> m_censorFilter;
int currentStep = -1;
State state = NotStarted;

View File

@@ -74,7 +74,7 @@ Meta::BaseEntity::~BaseEntity()
QUrl Meta::BaseEntity::url() const
{
return QUrl("https://v1.meta.multimc.org").resolved(localFilename());
return QUrl("https://meta.multimc.org/v1/").resolved(localFilename());
}
bool Meta::BaseEntity::loadLocalFile()

View File

@@ -586,6 +586,15 @@ void ComponentUpdateTask::resolveDependencies(bool checkOnly)
{
component->m_version = "3.1.2";
}
else if (add.uid == "net.fabricmc.intermediary")
{
auto minecraft = std::find_if(components.begin(), components.end(), [](ComponentPtr & cmp){
return cmp->getID() == "net.minecraft";
});
if(minecraft != components.end()) {
component->m_version = (*minecraft)->getVersion();
}
}
}
// HACK HACK HACK HACK FIXME: this is a placeholder for deciding what version to use. For now, it is hardcoded.
// ############################################################################################################

View File

@@ -777,22 +777,22 @@ shared_qobject_ptr<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
return nullptr;
}
std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session)
{
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(getSharedPtr()));
// FIXME: get rid of shared_from_this ...
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
auto pptr = process.get();
ENV.icons()->saveIcon(iconKey(), FS::PathCombine(gameRoot(), "icon.png"), "PNG");
// print a header
{
process->appendStep(std::make_shared<TextPrint>(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::MultiMC));
process->appendStep(new TextPrint(pptr, "Minecraft folder is:\n" + gameRoot() + "\n\n", MessageLevel::MultiMC));
}
// check java
{
auto step = std::make_shared<CheckJava>(pptr);
process->appendStep(step);
process->appendStep(new CheckJava(pptr));
}
// check launch method
@@ -800,14 +800,14 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
QString method = launchMethod();
if(!validMethods.contains(method))
{
process->appendStep(std::make_shared<TextPrint>(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
process->appendStep(new TextPrint(pptr, "Selected launch method \"" + method + "\" is not valid.\n", MessageLevel::Fatal));
return process;
}
// run pre-launch command if that's needed
if(getPreLaunchCommand().size())
{
auto step = std::make_shared<PreLaunchCommand>(pptr);
auto step = new PreLaunchCommand(pptr);
step->setWorkingDirectory(gameRoot());
process->appendStep(step);
}
@@ -815,42 +815,37 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
// if we aren't in offline mode,.
if(session->status != AuthSession::PlayableOffline)
{
process->appendStep(std::make_shared<ClaimAccount>(pptr, session));
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Online));
process->appendStep(new ClaimAccount(pptr, session));
process->appendStep(new Update(pptr, Net::Mode::Online));
}
else
{
process->appendStep(std::make_shared<Update>(pptr, Net::Mode::Offline));
process->appendStep(new Update(pptr, Net::Mode::Offline));
}
// if there are any jar mods
{
auto step = std::make_shared<ModMinecraftJar>(pptr);
process->appendStep(step);
process->appendStep(new ModMinecraftJar(pptr));
}
// print some instance info here...
{
auto step = std::make_shared<PrintInstanceInfo>(pptr, session);
process->appendStep(step);
process->appendStep(new PrintInstanceInfo(pptr, session));
}
// create the server-resource-packs folder (workaround for Minecraft bug MCL-3732)
{
auto step = std::make_shared<CreateServerResourcePacksFolder>(pptr);
process->appendStep(step);
process->appendStep(new CreateServerResourcePacksFolder(pptr));
}
// extract native jars if needed
{
auto step = std::make_shared<ExtractNatives>(pptr);
process->appendStep(step);
process->appendStep(new ExtractNatives(pptr));
}
// reconstruct assets if needed
{
auto step = std::make_shared<ReconstructAssets>(pptr);
process->appendStep(step);
process->appendStep(new ReconstructAssets(pptr));
}
{
@@ -858,14 +853,14 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
auto method = launchMethod();
if(method == "LauncherPart")
{
auto step = std::make_shared<LauncherPartLaunch>(pptr);
auto step = new LauncherPartLaunch(pptr);
step->setWorkingDirectory(gameRoot());
step->setAuthSession(session);
process->appendStep(step);
}
else if (method == "DirectJava")
{
auto step = std::make_shared<DirectJavaLaunch>(pptr);
auto step = new DirectJavaLaunch(pptr);
step->setWorkingDirectory(gameRoot());
step->setAuthSession(session);
process->appendStep(step);
@@ -875,7 +870,7 @@ std::shared_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr s
// run post-exit command if that's needed
if(getPostExitCommand().size())
{
auto step = std::make_shared<PostLaunchCommand>(pptr);
auto step = new PostLaunchCommand(pptr);
step->setWorkingDirectory(gameRoot());
process->appendStep(step);
}

View File

@@ -78,7 +78,7 @@ public:
////// Launch stuff //////
shared_qobject_ptr<Task> createUpdateTask(Net::Mode mode) override;
std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override;
QStringList extraArguments() const override;
QStringList verboseDescription(AuthSessionPtr session) override;
QList<Mod> getJarMods() const;

View File

@@ -96,6 +96,19 @@ void Mod::repath(const QFileInfo &file)
zip.close();
return;
}
else if (zip.setCurrentFile("fabric.mod.json"))
{
if (!file.open(QIODevice::ReadOnly))
{
zip.close();
return;
}
ReadFabricModInfo(file.readAll());
file.close();
zip.close();
return;
}
else if (zip.setCurrentFile("forgeversion.properties"))
{
if (!file.open(QIODevice::ReadOnly))
@@ -224,6 +237,48 @@ void Mod::ReadMCModInfo(QByteArray contents)
}
}
// https://fabricmc.net/wiki/documentation:fabric_mod_json
void Mod::ReadFabricModInfo(QByteArray contents)
{
QJsonParseError jsonError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError);
auto object = jsonDoc.object();
auto schemaVersion = object.contains("schemaVersion") ? object.value("schemaVersion").toInt(0) : 0;
m_mod_id = object.value("id").toString();
m_version = object.value("version").toString();
m_name = object.contains("name") ? object.value("name").toString() : m_mod_id;
m_description = object.value("description").toString();
if (schemaVersion >= 1)
{
QJsonArray authors = object.value("authors").toArray();
m_authors = "";
for (int i = 0; i < authors.size(); i++)
{
QString author_name = authors.at(i).isObject()
? authors.at(i).toObject().value("name").toString()
: authors.at(i).toString();
if (i > 0)
m_authors += ", " + author_name;
else {
m_authors += author_name;
}
}
if (object.contains("contact"))
{
QJsonObject contact = object.value("contact").toObject();
if (contact.contains("homepage"))
m_homeurl = contact.value("homepage").toString();
}
}
}
void Mod::ReadForgeInfo(QByteArray contents)
{
// Read the data

View File

@@ -113,6 +113,7 @@ public:
private:
void ReadMCModInfo(QByteArray contents);
void ReadFabricModInfo(QByteArray contents);
void ReadForgeInfo(QByteArray contents);
void ReadLiteModInfo(QByteArray contents);

View File

@@ -123,8 +123,8 @@ bool SimpleModList::installMod(const QString &filename)
qDebug() << "Cannot recognize mod type of" << originalPath << ", ignoring it.";
return false;
}
auto newpath = FS::NormalizePath(FS::PathCombine(m_dir.path(), fileinfo.fileName()));
auto newpath = FS::NormalizePath(FS::PathCombine(m_dir.path(), fileinfo.fileName()));
if(originalPath == newpath)
{
qDebug() << "Overwriting the mod (" << originalPath << ") with itself makes no sense...";
@@ -133,7 +133,7 @@ bool SimpleModList::installMod(const QString &filename)
if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE || type == Mod::MOD_LITEMOD)
{
if(QFile::exists(newpath))
if(QFile::exists(newpath) || QFile::exists(newpath + QString(".disabled")))
{
if(!QFile::remove(newpath))
{

View File

@@ -17,6 +17,7 @@
#include "BaseInstance.h"
#include "minecraft/Mod.h"
#include "launch/LaunchTask.h"
#include "multimc_logic_export.h"
@@ -112,7 +113,7 @@ public:
{
return false;
}
std::shared_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account) override
{
return nullptr;
}

View File

@@ -1,4 +1,5 @@
#include "BaseProfiler.h"
#include "QObjectPtr.h"
#include <QProcess>
@@ -7,7 +8,7 @@ BaseProfiler::BaseProfiler(SettingsObjectPtr settings, InstancePtr instance, QOb
{
}
void BaseProfiler::beginProfiling(std::shared_ptr<LaunchTask> process)
void BaseProfiler::beginProfiling(shared_qobject_ptr<LaunchTask> process)
{
beginProfilingImpl(process);
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "BaseExternalTool.h"
#include "QObjectPtr.h"
#include "multimc_logic_export.h"
@@ -17,13 +18,13 @@ public:
public
slots:
void beginProfiling(std::shared_ptr<LaunchTask> process);
void beginProfiling(shared_qobject_ptr<LaunchTask> process);
void abortProfiling();
protected:
QProcess *m_profilerProcess;
virtual void beginProfilingImpl(std::shared_ptr<LaunchTask> process) = 0;
virtual void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process) = 0;
virtual void abortProfilingImpl();
signals:

View File

@@ -17,7 +17,7 @@ private slots:
void profilerFinished(int exit, QProcess::ExitStatus status);
protected:
void beginProfilingImpl(std::shared_ptr<LaunchTask> process);
void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
private:
int listeningPort = 0;
@@ -47,7 +47,7 @@ void JProfiler::profilerFinished(int exit, QProcess::ExitStatus status)
}
}
void JProfiler::beginProfilingImpl(std::shared_ptr<LaunchTask> process)
void JProfiler::beginProfilingImpl(shared_qobject_ptr<LaunchTask> process)
{
listeningPort = globalSettings->get("JProfilerPort").toInt();
QProcess *profiler = new QProcess(this);

View File

@@ -18,7 +18,7 @@ private slots:
void profilerFinished(int exit, QProcess::ExitStatus status);
protected:
void beginProfilingImpl(std::shared_ptr<LaunchTask> process);
void beginProfilingImpl(shared_qobject_ptr<LaunchTask> process);
};
@@ -45,7 +45,7 @@ void JVisualVM::profilerFinished(int exit, QProcess::ExitStatus status)
}
}
void JVisualVM::beginProfilingImpl(std::shared_ptr<LaunchTask> process)
void JVisualVM::beginProfilingImpl(shared_qobject_ptr<LaunchTask> process)
{
QProcess *profiler = new QProcess(this);
QStringList profilerArgs =

View File

@@ -133,6 +133,7 @@ TranslationsModel::TranslationsModel(QString path, QObject* parent): QAbstractLi
{
d.reset(new Private);
d->m_dir.setPath(path);
FS::ensureFolderPathExists(path);
reloadLocalFiles();
d->watcher = new QFileSystemWatcher(this);
@@ -151,7 +152,7 @@ void TranslationsModel::translationDirChanged(const QString& path)
selectLanguage(selectedLanguage());
}
void TranslationsModel::indexRecieved()
void TranslationsModel::indexReceived()
{
qDebug() << "Got translations index!";
d->m_index_job.reset();
@@ -558,7 +559,7 @@ void TranslationsModel::downloadIndex()
d->m_index_task = Net::Download::makeCached(QUrl("https://files.multimc.org/translations/index_v2.json"), entry);
d->m_index_job->addNetAction(d->m_index_task);
connect(d->m_index_job.get(), &NetJob::failed, this, &TranslationsModel::indexFailed);
connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexRecieved);
connect(d->m_index_job.get(), &NetJob::succeeded, this, &TranslationsModel::indexReceived);
d->m_index_job->start();
}

View File

@@ -52,7 +52,7 @@ private:
TranslationsModel &operator=(const TranslationsModel &) = delete;
private slots:
void indexRecieved();
void indexReceived();
void indexFailed(QString reason);
void dlFailed(QString reason);
void dlGood();

View File

@@ -64,9 +64,6 @@ SET(MULTIMC_SOURCES
themes/SystemTheme.cpp
themes/SystemTheme.h
# GUI - settings-specific wrappers for paged dialog
SettingsUI.h
# Processes
LaunchController.h
LaunchController.cpp

View File

@@ -144,7 +144,7 @@ void InstanceWindow::on_btnLaunchMinecraftOffline_clicked()
MMC->launch(m_instance, false, nullptr);
}
void InstanceWindow::on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc)
void InstanceWindow::on_InstanceLaunchTask_changed(shared_qobject_ptr<LaunchTask> proc)
{
m_proc = proc;
}

View File

@@ -52,7 +52,7 @@ slots:
void on_btnKillMinecraft_clicked();
void on_btnLaunchMinecraftOffline_clicked();
void on_InstanceLaunchTask_changed(std::shared_ptr<LaunchTask> proc);
void on_InstanceLaunchTask_changed(shared_qobject_ptr<LaunchTask> proc);
void on_RunningState_changed(bool running);
void on_instanceStatusChanged(BaseInstance::Status, BaseInstance::Status newStatus);
@@ -63,7 +63,7 @@ private:
void updateLaunchButtons();
private:
std::shared_ptr<LaunchTask> m_proc;
shared_qobject_ptr<LaunchTask> m_proc;
InstancePtr m_instance;
bool m_doNotSave = false;
PageContainer *m_container = nullptr;

View File

@@ -9,7 +9,6 @@
#include "InstanceWindow.h"
#include "BuildConfig.h"
#include "JavaCommon.h"
#include "SettingsUI.h"
#include <QLineEdit>
#include <QInputDialog>
#include <tasks/Task.h>
@@ -53,7 +52,7 @@ void LaunchController::login()
if (reply == QMessageBox::Yes)
{
// Open the account manager.
SettingsUI::ShowPageDialog(MMC->globalSettingsPages(), m_parentWidget, "accounts");
MMC->ShowGlobalSettings(m_parentWidget, "accounts");
}
}
else if (account.get() == nullptr)
@@ -218,7 +217,7 @@ void LaunchController::launchInstance()
connect(m_launcher.get(), &LaunchTask::requestProgress, this, &LaunchController::onProgressRequested);
m_launcher->prependStep(std::make_shared<TextPrint>(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
m_launcher->prependStep(new TextPrint(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
m_launcher->start();
}

View File

@@ -57,5 +57,5 @@ private:
QWidget * m_parentWidget = nullptr;
InstanceWindow *m_console = nullptr;
AuthSessionPtr m_session;
std::shared_ptr <LaunchTask> m_launcher;
shared_qobject_ptr<LaunchTask> m_launcher;
};

View File

@@ -70,7 +70,6 @@
#include "InstanceProxyModel.h"
#include "JavaCommon.h"
#include "LaunchController.h"
#include "SettingsUI.h"
#include "groupview/GroupView.h"
#include "groupview/InstanceDelegate.h"
#include "widgets/LabeledToolButton.h"
@@ -662,6 +661,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
// Create the instance list widget
{
view = new GroupView(ui->centralWidget);
view->setTextElideMode(Qt::TextElideMode::ElideRight);
view->setSelectionMode(QAbstractItemView::SingleSelection);
// FIXME: leaks ListViewDelegate
@@ -703,6 +703,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
// model reset -> selection is invalid. All the instance pointers are wrong.
connect(MMC->instances().get(), &InstanceList::dataIsInvalid, this, &MainWindow::selectionBad);
// When the global settings page closes, we want to know about it and update our state
connect(MMC, &MultiMC::globalSettingsClosed, this, &MainWindow::globalSettingsClosed);
m_statusLeft = new QLabel(tr("No instance selected"), this);
m_statusRight = new ServerStatus(this);
statusBar()->addPermanentWidget(m_statusLeft, 1);
@@ -902,15 +905,21 @@ void MainWindow::showInstanceContextMenu(const QPoint &pos)
void MainWindow::updateToolsMenu()
{
QToolButton *launchButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstance));
QToolButton *launchOfflineButton = dynamic_cast<QToolButton*>(ui->instanceToolBar->widgetForAction(ui->actionLaunchInstanceOffline));
if(!m_selectedInstance || m_selectedInstance->isRunning())
{
ui->actionLaunchInstance->setMenu(nullptr);
ui->actionLaunchInstanceOffline->setMenu(nullptr);
launchButton->setPopupMode(QToolButton::InstantPopup);
launchOfflineButton->setPopupMode(QToolButton::InstantPopup);
return;
}
QMenu *launchMenu = ui->actionLaunchInstance->menu();
QMenu *launchOfflineMenu = ui->actionLaunchInstanceOffline->menu();
launchButton->setPopupMode(QToolButton::MenuButtonPopup);
launchOfflineButton->setPopupMode(QToolButton::MenuButtonPopup);
if (launchMenu)
{
launchMenu->clear();
@@ -919,21 +928,39 @@ void MainWindow::updateToolsMenu()
{
launchMenu = new QMenu(this);
}
if (launchOfflineMenu) {
launchOfflineMenu->clear();
}
else
{
launchOfflineMenu = new QMenu(this);
}
QAction *normalLaunch = launchMenu->addAction(tr("Launch"));
QAction *normalLaunchOffline = launchOfflineMenu->addAction(tr("Launch Offline"));
connect(normalLaunch, &QAction::triggered, [this]()
{
MMC->launch(m_selectedInstance);
MMC->launch(m_selectedInstance, true);
});
launchMenu->addSeparator()->setText(tr("Profilers"));
connect(normalLaunchOffline, &QAction::triggered, [this]()
{
MMC->launch(m_selectedInstance, false);
});
QString profilersTitle = tr("Profilers");
launchMenu->addSeparator()->setText(profilersTitle);
launchOfflineMenu->addSeparator()->setText(profilersTitle);
for (auto profiler : MMC->profilers().values())
{
QAction *profilerAction = launchMenu->addAction(profiler->name());
QAction *profilerOfflineAction = launchOfflineMenu->addAction(profiler->name());
QString error;
if (!profiler->check(&error))
{
profilerAction->setDisabled(true);
profilerAction->setToolTip(tr("Profiler not setup correctly. Go into settings, \"External Tools\"."));
profilerOfflineAction->setDisabled(true);
QString profilerToolTip = tr("Profiler not setup correctly. Go into settings, \"External Tools\".");
profilerAction->setToolTip(profilerToolTip);
profilerOfflineAction->setToolTip(profilerToolTip);
}
else
{
@@ -941,9 +968,14 @@ void MainWindow::updateToolsMenu()
{
MMC->launch(m_selectedInstance, true, profiler.get());
});
connect(profilerOfflineAction, &QAction::triggered, [this, profiler]()
{
MMC->launch(m_selectedInstance, false, profiler.get());
});
}
}
ui->actionLaunchInstance->setMenu(launchMenu);
ui->actionLaunchInstanceOffline->setMenu(launchOfflineMenu);
}
QString profileInUseFilter(const QString & profile, bool used)
@@ -1543,7 +1575,11 @@ void MainWindow::checkForUpdates()
void MainWindow::on_actionSettings_triggered()
{
SettingsUI::ShowPageDialog(MMC->globalSettingsPages(), this, "global-settings");
MMC->ShowGlobalSettings(this, "global-settings");
}
void MainWindow::globalSettingsClosed()
{
// FIXME: quick HACK to make this work. improve, optimize.
MMC->instances()->loadList();
proxymodel->invalidate();
@@ -1579,7 +1615,7 @@ void MainWindow::on_actionScreenshots_triggered()
void MainWindow::on_actionManageAccounts_triggered()
{
SettingsUI::ShowPageDialog(MMC->globalSettingsPages(), this, "accounts");
MMC->ShowGlobalSettings(this, "accounts");
}
void MainWindow::on_actionReportBug_triggered()
@@ -1634,7 +1670,8 @@ void MainWindow::on_actionDeleteInstance_triggered()
tr("CAREFUL!"),
tr("About to delete: %1\nThis is permanent and will completely delete the instance.\n\nAre you sure?").arg(m_selectedInstance->name()),
QMessageBox::Warning,
QMessageBox::Yes | QMessageBox::No
QMessageBox::Yes | QMessageBox::No,
QMessageBox::No
)->exec();
if (response == QMessageBox::Yes)
{

View File

@@ -181,6 +181,8 @@ private slots:
void konamiTriggered();
void globalSettingsClosed();
private:
void addInstance(QString url = QString());
void activateInstance(InstancePtr instance);

View File

@@ -66,6 +66,8 @@
#include <ganalytics.h>
#include <sys.h>
#include "pagedialog/PageDialog.h"
#if defined Q_OS_WIN32
#ifndef WIN32_LEAN_AND_MEAN
@@ -552,7 +554,8 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
{
":/icons/multimc/32x32/instances/",
":/icons/multimc/50x50/instances/",
":/icons/multimc/128x128/instances/"
":/icons/multimc/128x128/instances/",
":/icons/multimc/scalable/instances/"
};
m_icons.reset(new IconList(instFolders, setting->get().toString()));
connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value)
@@ -1087,6 +1090,20 @@ void MultiMC::controllerFailed(const QString& error)
}
}
void MultiMC::ShowGlobalSettings(class QWidget* parent, QString open_page)
{
if(!m_globalSettingsProvider) {
return;
}
emit globalSettingsAboutToOpen();
{
SettingsObject::Lock lock(MMC->settings());
PageDialog dlg(m_globalSettingsProvider.get(), open_page, parent);
dlg.exec();
}
emit globalSettingsClosed();
}
MainWindow* MultiMC::showMainWindow(bool minimized)
{
if(m_mainWindow)

View File

@@ -65,11 +65,6 @@ public:
return m_settings;
}
std::shared_ptr<GenericPageProvider> globalSettingsPages() const
{
return m_globalSettingsProvider;
}
qint64 timeSinceStart() const
{
return startTime.msecsTo(QDateTime::currentDateTime());
@@ -146,8 +141,12 @@ public:
void updateIsRunning(bool running);
bool updatesAreAllowed();
void ShowGlobalSettings(class QWidget * parent, QString open_page = QString());
signals:
void updateAllowedChanged(bool status);
void globalSettingsAboutToOpen();
void globalSettingsClosed();
public slots:
bool launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr);

View File

@@ -1,26 +0,0 @@
#pragma once
#include "pages/BasePageProvider.h"
#include "MultiMC.h"
#include "pagedialog/PageDialog.h"
#include "InstancePageProvider.h"
#include <settings/SettingsObject.h>
#include <BaseInstance.h>
/*
* FIXME: this is a fragment. find a better place for it.
*/
namespace SettingsUI
{
template <typename T>
void ShowPageDialog(T raw_provider, QWidget * parent, QString open_page = QString())
{
auto provider = std::dynamic_pointer_cast<BasePageProvider>(raw_provider);
if(!provider)
return;
{
SettingsObject::Lock lock(MMC->settings());
PageDialog dlg(provider.get(), open_page, parent);
dlg.exec();
}
}
}

View File

@@ -343,43 +343,37 @@ void SaveIcon(InstancePtr m_instance)
auto iconKey = m_instance->iconKey();
auto iconList = MMC->icons();
auto mmcIcon = iconList->icon(iconKey);
if(mmcIcon)
if(!mmcIcon || mmcIcon->isBuiltIn()) {
return;
}
auto path = mmcIcon->getFilePath();
if(!path.isNull()) {
QFileInfo inInfo (path);
FS::copy(path, FS::PathCombine(m_instance->instanceRoot(), inInfo.fileName())) ();
return;
}
auto & image = mmcIcon->m_images[mmcIcon->type()];
auto & icon = image.icon;
auto sizes = icon.availableSizes();
if(sizes.size() == 0)
{
bool saveIcon = false;
switch(mmcIcon->type())
return;
}
auto areaOf = [](QSize size)
{
return size.width() * size.height();
};
QSize largest = sizes[0];
// find variant with largest area
for(auto size: sizes)
{
if(areaOf(largest) < areaOf(size))
{
case IconType::FileBased:
case IconType::Transient:
saveIcon = true;
default:
break;
}
if(saveIcon)
{
auto & image = mmcIcon->m_images[mmcIcon->type()];
auto & icon = image.icon;
auto sizes = icon.availableSizes();
if(sizes.size() == 0)
{
return;
}
auto areaOf = [](QSize size)
{
return size.width() * size.height();
};
QSize largest = sizes[0];
// find variant with largest area
for(auto size: sizes)
{
if(areaOf(largest) < areaOf(size))
{
largest = size;
}
}
auto pixmap = icon.pixmap(largest);
pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png"));
largest = size;
}
}
auto pixmap = icon.pixmap(largest);
pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png"));
}
bool ExportInstanceDialog::doExport()

View File

@@ -25,6 +25,7 @@
#include "groupview/InstanceDelegate.h"
#include "icons/IconList.h"
#include "icons/IconUtils.h"
#include <DesktopServices.h>
IconPickerDialog::IconPickerDialog(QWidget *parent)
@@ -103,8 +104,8 @@ void IconPickerDialog::addNewIcon()
//: The title of the select icons open file dialog
QString selectIcons = tr("Select Icons");
//: The type of icon files
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
tr("Icons") + "(*.png *.jpg *.jpeg *.ico *.svg *.gif)");
auto filter = IconUtils::getIconFilter();
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(), tr("Icons %1").arg(filter));
MMC->icons()->installIcons(fileNames);
}

View File

@@ -34,7 +34,6 @@ static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &he
height = 0;
widthUsed = 0;
textLayout.beginLayout();
QString str = textLayout.text();
while (true)
{
QTextLine line = textLayout.createLine();
@@ -190,11 +189,13 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
// FIXME: Things go really weird with long instance names
// const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize);
const int iconSize = 48;
QRect iconbox = opt.rect;
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1;
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, opt.widget) + 1;
QRect textRect = opt.rect;
textRect.setWidth(qMin(textRect.width(), iconbox.width()));
QRect textHighlightRect = textRect;
// clip the decoration on top, remove width padding
textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0);
@@ -299,7 +300,7 @@ void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
const int lineCount = textLayout.lineCount();
const QRect layoutRect = QStyle::alignedRect(
opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect);
opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect);
const QPointF position = layoutRect.topLeft();
for (int i = 0; i < lineCount; ++i)
{

View File

@@ -22,12 +22,12 @@ deploy() {
runmmc() {
cd ${INSTDIR}
./MultiMC
./MultiMC "$@"
}
if [[ ! -f ${INSTDIR}/MultiMC ]]; then
deploy
runmmc
runmmc "$@"
else
runmmc
runmmc "$@"
fi

View File

@@ -21,6 +21,9 @@ InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
ui->setupUi(this);
auto sysMB = Sys::getSystemRam() / Sys::megabyte;
ui->maxMemSpinBox->setMaximum(sysMB);
connect(ui->openGlobalJavaSettingsButton, &QCommandLinkButton::clicked, this, &InstanceSettingsPage::globalSettingsButtonClicked);
connect(MMC, &MultiMC::globalSettingsAboutToOpen, this, &InstanceSettingsPage::applySettings);
connect(MMC, &MultiMC::globalSettingsClosed, this, &InstanceSettingsPage::loadSettings);
loadSettings();
}
@@ -34,6 +37,21 @@ InstanceSettingsPage::~InstanceSettingsPage()
delete ui;
}
void InstanceSettingsPage::globalSettingsButtonClicked(bool)
{
switch(ui->settingsTabs->currentIndex()) {
case 0:
MMC->ShowGlobalSettings(this, "java-settings");
return;
case 1:
MMC->ShowGlobalSettings(this, "minecraft-settings");
return;
case 2:
MMC->ShowGlobalSettings(this, "custom-commands");
return;
}
}
bool InstanceSettingsPage::apply()
{
applySettings();

View File

@@ -66,6 +66,8 @@ private slots:
void checkerFinished();
void globalSettingsButtonClicked(bool checked);
private:
Ui::InstanceSettingsPage *ui;
BaseInstance *m_instance;

View File

@@ -6,23 +6,21 @@
<rect>
<x>0</x>
<y>0</y>
<width>553</width>
<height>522</height>
<width>738</width>
<height>804</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QCommandLinkButton" name="openGlobalJavaSettingsButton">
<property name="text">
<string>Open Global Settings</string>
</property>
<property name="description">
<string>The settings here are overrides for global settings.</string>
</property>
</widget>
</item>
<item>
<widget class="QTabWidget" name="settingsTabs">
<property name="tabShape">
@@ -367,6 +365,7 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>openGlobalJavaSettingsButton</tabstop>
<tabstop>settingsTabs</tabstop>
<tabstop>javaSettingsGroupBox</tabstop>
<tabstop>javaPathTextBox</tabstop>

View File

@@ -192,7 +192,7 @@ void LogPage::UIToModelState()
m_model->suspend(ui->trackLogCheckbox->checkState() != Qt::Checked);
}
void LogPage::setInstanceLaunchTaskChanged(std::shared_ptr<LaunchTask> proc, bool initial)
void LogPage::setInstanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc, bool initial)
{
m_process = proc;
if(m_process)
@@ -215,7 +215,7 @@ void LogPage::setInstanceLaunchTaskChanged(std::shared_ptr<LaunchTask> proc, boo
}
}
void LogPage::onInstanceLaunchTaskChanged(std::shared_ptr<LaunchTask> proc)
void LogPage::onInstanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc)
{
setInstanceLaunchTaskChanged(proc, false);
}

View File

@@ -69,17 +69,17 @@ private slots:
void findNextActivated();
void findPreviousActivated();
void onInstanceLaunchTaskChanged(std::shared_ptr<LaunchTask> proc);
void onInstanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc);
private:
void modelStateToUI();
void UIToModelState();
void setInstanceLaunchTaskChanged(std::shared_ptr<LaunchTask> proc, bool initial);
void setInstanceLaunchTaskChanged(shared_qobject_ptr<LaunchTask> proc, bool initial);
private:
Ui::LogPage *ui;
InstancePtr m_instance;
std::shared_ptr<LaunchTask> m_process;
shared_qobject_ptr<LaunchTask> m_process;
LogFormatProxyModel * m_proxy;
shared_qobject_ptr <LogModel> m_model;

View File

@@ -64,8 +64,8 @@ struct Server
void serialize(nbt::tag_compound& server)
{
server.insert("name", m_name.toUtf8().toStdString());
server.insert("ip", m_address.toUtf8().toStdString());
server.insert("name", m_name.trimmed().toUtf8().toStdString());
server.insert("ip", m_address.trimmed().toUtf8().toStdString());
if(m_icon.size())
{
server.insert("icon", m_icon.toBase64().toStdString());

View File

@@ -180,6 +180,7 @@ void VersionPage::packageCurrent(const QModelIndex &current, const QModelIndex &
void VersionPage::updateVersionControls()
{
ui->fabricBtn->setEnabled(true);
ui->forgeBtn->setEnabled(true);
ui->liteloaderBtn->setEnabled(true);
updateButtons();
@@ -187,6 +188,7 @@ void VersionPage::updateVersionControls()
void VersionPage::disableVersionControls()
{
ui->fabricBtn->setEnabled(false);
ui->forgeBtn->setEnabled(false);
ui->liteloaderBtn->setEnabled(false);
ui->reloadBtn->setEnabled(false);
@@ -316,6 +318,12 @@ void VersionPage::on_changeVersionBtn_clicked()
return;
}
VersionSelectDialog vselect(list.get(), tr("Change %1 version").arg(name), this);
if (uid == "net.fabricmc.intermediary")
{
vselect.setEmptyString(tr("No Fabric Loader versions are currently available."));
vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!"));
vselect.setExactFilter(BaseVersionList::ParentVersionRole, m_profile->getComponentVersion("net.minecraft"));
}
auto currentVersion = patch->getVersion();
if(!currentVersion.isEmpty())
{
@@ -389,6 +397,33 @@ void VersionPage::on_forgeBtn_clicked()
}
}
void VersionPage::on_fabricBtn_clicked()
{
auto vlist = ENV.metadataIndex()->get("net.fabricmc.fabric-loader");
if(!vlist)
{
return;
}
VersionSelectDialog vselect(vlist.get(), tr("Select Fabric Loader version"), this);
vselect.setEmptyString(tr("No Fabric Loader versions are currently available."));
vselect.setEmptyErrorString(tr("Couldn't load or download the Fabric Loader version lists!"));
auto currentVersion = m_profile->getComponentVersion("net.fabricmc.fabric-loader");
if(!currentVersion.isEmpty())
{
vselect.setCurrentVersion(currentVersion);
}
if (vselect.exec() && vselect.selectedVersion())
{
auto vsn = vselect.selectedVersion();
m_profile->setComponentVersion("net.fabricmc.fabric-loader", vsn->descriptor());
m_profile->resolve(Net::Mode::Online);
preselect(m_profile->rowCount(QModelIndex())-1);
m_container->refreshContainer();
}
}
void VersionPage::on_addEmptyBtn_clicked()
{
NewComponentDialog compdialog(QString(), QString(), this);

View File

@@ -49,6 +49,7 @@ public:
virtual bool shouldDisplay() const override;
private slots:
void on_fabricBtn_clicked();
void on_forgeBtn_clicked();
void on_addEmptyBtn_clicked();
void on_liteloaderBtn_clicked();

View File

@@ -170,6 +170,16 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fabricBtn">
<property name="toolTip">
<string>Install the Fabric Loader package.</string>
</property>
<property name="text">
<string>Install Fabric</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="liteloaderBtn">
<property name="toolTip">
@@ -298,6 +308,7 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>packageView</tabstop>
<tabstop>changeVersionBtn</tabstop>
<tabstop>moveUpBtn</tabstop>
@@ -307,6 +318,7 @@
<tabstop>editBtn</tabstop>
<tabstop>revertBtn</tabstop>
<tabstop>forgeBtn</tabstop>
<tabstop>fabricBtn</tabstop>
<tabstop>liteloaderBtn</tabstop>
<tabstop>modBtn</tabstop>
<tabstop>jarmodBtn</tabstop>
@@ -314,7 +326,6 @@
<tabstop>addEmptyBtn</tabstop>
<tabstop>reloadBtn</tabstop>
<tabstop>downloadBtn</tabstop>
<tabstop>tabWidget</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@@ -2,7 +2,7 @@
Name=multimc
Comment=MultiMC Default Icons
Inherits=default
Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable
Directories=8x8,16x16,22x22,24x24,32x32,32x32/instances,48x48,50x50/instances,64x64,128x128/instances,256x256,scalable,scalable/instances
[8x8]
Size=8
@@ -51,3 +51,8 @@ Size=48
Type=Scalable
MinSize=16
MaxSize=256
[scalable/instances]
Size=128
MinSize=16
MaxSize=256

View File

@@ -308,5 +308,7 @@
<file>32x32/instances/tnt.png</file>
<file>50x50/instances/enderman.png</file>
<file>scalable/instances/fox.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,290 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
id="svg8"
version="1.1"
viewBox="0 0 33.866666 33.866666"
height="128"
width="128">
<defs
id="defs2" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-263.13334)"
id="layer1">
<path
id="rect4750"
d="m 4.233333,267.36667 v 6.35 6.35 3.175 3.175 3.175 3.175 h 25.4 v -3.175 -3.175 -3.175 -3.175 -6.35 -6.35 h -6.35 v 6.35 h -12.7 v -3.175 -3.175 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:0.79375;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<g
id="g4748">
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f4f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4553"
width="6.3500032"
height="6.3499975"
x="4.233326"
y="267.36667" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#b48f83;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4553-6-2"
width="3.1750014"
height="3.1749992"
x="7.4083276"
y="270.54166" />
<rect
y="267.36667"
x="23.283335"
height="6.3499975"
width="6.3500032"
id="rect4623"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f4f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
y="270.54166"
x="23.283335"
height="3.1749992"
width="3.1750014"
id="rect4627"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#b48f83;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#e27c21;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4672"
width="25.400013"
height="15.875009"
x="4.233326"
y="273.71667" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#cc6920;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4629"
width="3.1750016"
height="6.3500032"
x="4.2333255"
y="273.71667" />
<rect
y="273.71667"
x="26.458338"
height="6.3500032"
width="3.1750016"
id="rect4631"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#cc6920;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#e78f41;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4636"
width="6.3500032"
height="3.1750016"
x="13.758331"
y="283.24167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#b05122;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4638"
width="3.1750016"
height="3.1750016"
x="4.2333255"
y="280.06668" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#cc6920;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4640"
width="3.1750016"
height="3.1750016"
x="7.4083276"
y="280.06668" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f4f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.84189939;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4644"
width="6.3500028"
height="3.1750016"
x="4.233326"
y="283.24167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#06040e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4642"
width="3.1750016"
height="3.1750016"
x="4.2333255"
y="283.24167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f4f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.84189939;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4646"
width="6.3500028"
height="3.1750016"
x="23.283337"
y="283.24167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#06040e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4648"
width="3.1750016"
height="3.1750016"
x="26.45834"
y="283.24167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#cc6920;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4650"
width="3.1750016"
height="3.1750016"
x="23.283337"
y="280.06668" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#b05122;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4652"
width="3.1750016"
height="3.1750016"
x="26.45834"
y="280.06668" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#cc6920;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4654"
width="25.400013"
height="3.1750016"
x="4.2333255"
y="286.41666" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#e7d9d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.59531283;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4656"
width="25.400013"
height="3.1750016"
x="4.2333255"
y="289.59167" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.5612604;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4917"
width="25.4"
height="0.26457807"
x="4.2333331"
y="273.71667" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.48607069;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4921"
width="0.2645835"
height="19.049997"
x="4.2333331"
y="273.71667" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f9f4f4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062555;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4658"
width="12.700007"
height="6.3500013"
x="10.583333"
y="286.41666" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#06040e;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062555;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4660"
width="6.3500037"
height="3.1750007"
x="13.758333"
y="286.41669" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#e7d9d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062555;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4662"
width="3.1750019"
height="3.1750007"
x="10.583333"
y="286.41669" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#e7d9d3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.19062555;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4664"
width="3.1750019"
height="3.1750007"
x="20.108334"
y="286.41669" />
<rect
y="286.41669"
x="10.583333"
height="6.3499832"
width="0.2645835"
id="rect4923"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2806327;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.39686465;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4951"
width="12.699996"
height="0.26456967"
x="10.583333"
y="286.41669" />
<rect
y="273.71667"
x="29.36875"
height="19.049982"
width="0.26458356"
id="rect4953"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.48607057;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
y="292.50208"
x="23.283333"
height="0.26457682"
width="6.3499994"
id="rect4957"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28062955;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2806325;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4959"
width="0.2645838"
height="6.3499656"
x="23.018749"
y="286.41669" />
<rect
y="292.50208"
x="4.2333331"
height="0.26457968"
width="6.3499999"
id="rect4961"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063107;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
y="267.36667"
x="4.2333331"
height="6.3499956"
width="0.2645835"
id="rect4963"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063297;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063536;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4965"
width="6.349998"
height="0.26458791"
x="4.2333331"
y="267.36667" />
<rect
y="267.36667"
x="23.283333"
height="6.3499956"
width="0.2645835"
id="rect4963-9"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.280633;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063536;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4965-1"
width="6.349998"
height="0.26458791"
x="23.283333"
y="267.36667" />
<rect
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063306;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
id="rect4985"
width="0.26458356"
height="6.3499975"
x="29.36875"
y="267.36667" />
<rect
y="267.36667"
x="10.318749"
height="6.3499975"
width="0.26458356"
id="rect4987"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.25;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.28063306;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB