Compare commits

..

5 Commits

Author SHA1 Message Date
Petr Mrázek
75edfc8d08 WIP smash 2019-08-15 01:22:38 +02:00
Petr Mrázek
0272d6d7f7 WIP remove jank previously added for a minimal forge hack 2019-08-13 23:58:15 +02:00
Petr Mrázek
406203e76b WIP clean Library a bit 2019-08-13 19:54:50 +02:00
Petr Mrázek
2bcd255909 HACK add option to launch without a main jar 2019-08-10 20:13:59 +02:00
Petr Mrázek
d5d710b89e HACK bare minimum to make forge 1.13 load
Probably introduces 100 bugs and 200 corner cases. \o/
2019-08-10 20:13:59 +02:00
105 changed files with 2235 additions and 1513 deletions

5
.arcconfig Normal file
View File

@@ -0,0 +1,5 @@
{
"project_id": "MultiMC5",
"conduit_uri": "http://ph.multimc.org"
}

25
.clang-format Normal file
View File

@@ -0,0 +1,25 @@
UseTab: false
IndentWidth: 4
TabWidth: 4
ConstructorInitializerIndentWidth: 4
AccessModifierOffset: -4
IndentCaseLabels: false
IndentFunctionDeclarationAfterType: false
NamespaceIndentation: None
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 160
MaxEmptyLinesToKeep: 1
Standard: Cpp11
Cpp11BracedListStyle: true
SpacesInParentheses: false
SpaceInEmptyParentheses: false
SpacesInCStyleCastParentheses: false
SpaceAfterControlStatementKeyword: true
AlignTrailingComments: true
SpacesBeforeTrailingComments: 1

5
.gitignore vendored
View File

@@ -1,5 +1,5 @@
Thumbs.db
*.kdev4
.kdev4
.user
.directory
resources/CMakeFiles
@@ -9,7 +9,8 @@ resources/MultiMCLauncher.jar
html/
# Project Files
*.pro.user
MultiMC5.kdev4
MultiMC.pro.user
CMakeLists.txt.user
CMakeLists.txt.user.*
/.project

38
.travis.yml Normal file
View File

@@ -0,0 +1,38 @@
# General set up
language: cpp
cache: apt
matrix:
include:
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.4.2
- os: linux
dist: precise
sudo: required
compiler: gcc
env: TRAVIS_DIST=precise QT_VERSION=5.6.2
- os: linux
dist: trusty
sudo: required
compiler: gcc
env: TRAVIS_DIST=trusty QT_VERSION=5.4.2
- os: linux
dist: trusty
sudo: required
compiler: gcc
env: TRAVIS_DIST=trusty QT_VERSION=5.6.2
# Install dependencies
install:
- source travis/prepare.sh # installs qt and cmake. need to source because some env vars are set from there
# Actual work
before_script:
- mkdir build
- cd build
- cmake -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ..
script:
- make -j4 && make test ARGS="-V"

View File

@@ -46,7 +46,7 @@ set(MultiMC_NEWS_RSS_URL "https://multimc.org/rss.xml" CACHE STRING "URL to fetc
######## Set version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 6)
set(MultiMC_VERSION_HOTFIX 11)
set(MultiMC_VERSION_HOTFIX 7)
# Build number
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
@@ -77,7 +77,6 @@ set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MIN
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
add_custom_target(tcversion echo "\\#\\#teamcity[setParameter name=\\'env.MULTIMC_VERSION\\' value=\\'${MultiMC_RELEASE_VERSION_NAME}\\']")
################################ 3rd Party Libs ################################

View File

@@ -13,22 +13,21 @@ The project uses C++ and Qt5 as the language and base framework. This might seem
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from the github issues [workflowy](https://github.com/MultiMC/MultiMC5/issues) - there is always plenty of ideas around.
If you want to contribute, either talk to us on [Discord](https://discord.gg/0k2zsXGNHs0fE4Wm), [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC) or pick up some item from [workflowy](https://workflowy.com/s/2EyDMcp7CU) - there are many.
### Building
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
### Code formatting
Just follow the existing formatting.
The ci server is running at [ci.multimc.org](http://ci.multimc.org), where you can watch the builds happen in (or very close to) real time. It can also serve as a nice reference on how to set up the build environment on your end.
In general:
* Indent with 4 space unless it's in a submodule
* Keep lists (of arguments, parameters, initializators...) as lists, not paragraphs.
* Prefer readability over dogma.
According to travis.ci, the builds are currently [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](https://travis-ci.org/MultiMC/MultiMC5)
### Code formatting
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted.
## Translations
Translations can be done [on crowdin](https://translate.multimc.org).
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5-translate). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC).
## Forking/Redistributing
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
@@ -44,36 +43,3 @@ Copyright © 2013-2019 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](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.
## Build status
### Linux (Intel32)
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux32_Build&guest=1">
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux32_Build)/statusIcon"/>
</a>
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux32_Deploy&guest=1">
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux32_Deploy)/statusIcon"/>
</a>
### Linux (AMD64)
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux64_Build&guest=1">
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux64_Build)/statusIcon"/>
</a>
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Linux64_Deploy&guest=1">
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Linux64_Deploy)/statusIcon"/>
</a>
### macOS (AMD64)
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_MacOS_Build&guest=1">
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_MacOS_Build)/statusIcon"/>
</a>
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_MacOS_Deploy&guest=1">
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_MacOS_Deploy)/statusIcon"/>
</a>
### Windows (Intel32)
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Windows_Build&guest=1">
Build: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Windows_Build)/statusIcon"/>
</a>
<a href="https://teamcity.multimc.org/viewType.html?buildTypeId=MultiMC_Launcher_Windows_Deploy&guest=1">
Deploy: <img src="https://teamcity.multimc.org/app/rest/builds/buildType:(id:MultiMC_Launcher_Windows_Deploy)/statusIcon"/>
</a>

View File

@@ -267,8 +267,6 @@ set(MINECRAFT_SOURCES
minecraft/MinecraftLoadAndCheck.cpp
minecraft/MinecraftUpdate.h
minecraft/MinecraftUpdate.cpp
minecraft/MojangVersionFormat.cpp
minecraft/MojangVersionFormat.h
minecraft/Rule.cpp
minecraft/Rule.h
minecraft/OneSixVersionFormat.cpp
@@ -319,12 +317,6 @@ add_unit_test(GradleSpecifier
LIBS MultiMC_logic
)
add_unit_test(MojangVersionFormat
SOURCES minecraft/MojangVersionFormat_test.cpp
LIBS MultiMC_logic
DATA minecraft/testdata
)
add_unit_test(Library
SOURCES minecraft/Library_test.cpp
LIBS MultiMC_logic
@@ -439,14 +431,15 @@ set(META_SOURCES
)
set(FTB_SOURCES
modplatform/legacy_ftb/PackFetchTask.h
modplatform/legacy_ftb/PackFetchTask.cpp
modplatform/legacy_ftb/PackInstallTask.h
modplatform/legacy_ftb/PackInstallTask.cpp
modplatform/legacy_ftb/PrivatePackManager.h
modplatform/legacy_ftb/PrivatePackManager.cpp
modplatform/ftb/FtbPackFetchTask.h
modplatform/ftb/FtbPackFetchTask.cpp
modplatform/ftb/FtbPackInstallTask.h
modplatform/ftb/FtbPackInstallTask.cpp
modplatform/legacy_ftb/PackHelpers.h
modplatform/ftb/FtbPrivatePackManager.h
modplatform/ftb/FtbPrivatePackManager.cpp
modplatform/ftb/PackHelpers.h
)
set(FLAME_SOURCES
@@ -455,6 +448,8 @@ set(FLAME_SOURCES
modplatform/flame/PackManifest.cpp
modplatform/flame/FileResolvingTask.h
modplatform/flame/FileResolvingTask.cpp
modplatform/flame/UrlResolvingTask.h
modplatform/flame/UrlResolvingTask.cpp
)
add_unit_test(Index

View File

@@ -69,8 +69,8 @@ namespace ArgumentStyle
{
enum Enum
{
Space, /**< --option value */
Equals, /**< --option=value */
Space, /**< --option=value */
Equals, /**< --option value */
SpaceAndEquals, /**< --option[= ]value */
#ifdef Q_OS_WIN32
Default = Equals

View File

@@ -299,7 +299,7 @@ QString NormalizePath(QString path)
}
}
QString badFilenameChars = "\"\\/?<>:;*|!+\r\n";
QString badFilenameChars = "\"\\/?<>:*|!+\r\n";
QString RemoveInvalidFilenameChars(QString string, QChar replaceWith)
{

View File

@@ -5,10 +5,9 @@
#include "pathmatcher/RegexpMatcher.h"
#include <QtConcurrentRun>
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime)
InstanceCopyTask::InstanceCopyTask(InstancePtr origInstance, bool copySaves)
{
m_origInstance = origInstance;
m_keepPlaytime = keepPlaytime;
if(!copySaves)
{
@@ -47,9 +46,6 @@ void InstanceCopyTask::copyFinished()
InstancePtr inst(new NullInstance(m_globalSettings, instanceSettings, m_stagingPath));
inst->setName(m_instName);
inst->setIconKey(m_instIcon);
if(!m_keepPlaytime) {
inst->resetTimePlayed();
}
emitSucceeded();
}

View File

@@ -15,7 +15,7 @@ class MULTIMC_LOGIC_EXPORT InstanceCopyTask : public InstanceTask
{
Q_OBJECT
public:
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves, bool keepPlaytime);
explicit InstanceCopyTask(InstancePtr origInstance, bool copySaves);
protected:
//! Entry point for tasks.
@@ -28,5 +28,4 @@ private: /* data */
QFuture<bool> m_copyFuture;
QFutureWatcher<bool> m_copyFutureWatcher;
std::unique_ptr<IPathMatcher> m_matcher;
bool m_keepPlaytime;
};

View File

@@ -160,8 +160,8 @@ GroupId InstanceList::getInstanceGroup(const InstanceId& id) const
{
return GroupId();
}
auto iter = m_instanceGroupIndex.find(inst->id());
if(iter != m_instanceGroupIndex.end())
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
return *iter;
}
@@ -178,8 +178,8 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
}
bool changed = false;
auto iter = m_instanceGroupIndex.find(inst->id());
if(iter != m_instanceGroupIndex.end())
auto iter = m_groupMap.find(inst->id());
if(iter != m_groupMap.end())
{
if(*iter != name)
{
@@ -190,12 +190,12 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
else
{
changed = true;
m_instanceGroupIndex[id] = name;
m_groupMap[id] = name;
}
if(changed)
{
m_groupNameCache.insert(name);
m_groups.insert(name);
auto idx = getInstIndex(inst.get());
emit dataChanged(index(idx), index(idx), {GroupRole});
saveGroupList();
@@ -204,7 +204,7 @@ void InstanceList::setInstanceGroup(const InstanceId& id, const GroupId& name)
QStringList InstanceList::getGroups()
{
return m_groupNameCache.toList();
return m_groups.toList();
}
void InstanceList::deleteGroup(const QString& name)
@@ -217,7 +217,7 @@ void InstanceList::deleteGroup(const QString& name)
auto instGroupName = getInstanceGroup(instID);
if(instGroupName == name)
{
m_instanceGroupIndex.remove(instID);
m_groupMap.remove(instID);
qDebug() << "Remove" << instID << "from group" << name;
removed = true;
auto idx = getInstIndex(instance.get());
@@ -233,11 +233,6 @@ void InstanceList::deleteGroup(const QString& name)
}
}
bool InstanceList::isGroupCollapsed(const QString& group)
{
return m_collapsedGroups.contains(group);
}
void InstanceList::deleteInstance(const InstanceId& id)
{
auto inst = getInstanceById(id);
@@ -247,7 +242,7 @@ void InstanceList::deleteInstance(const InstanceId& id)
return;
}
if(m_instanceGroupIndex.remove(id))
if(m_groupMap.remove(id))
{
saveGroupList();
}
@@ -520,7 +515,7 @@ void InstanceList::saveGroupList()
WatchLock foo(m_watcher, m_instDir);
QString groupFileName = m_instDir + "/instgroups.json";
QMap<QString, QSet<QString>> reverseGroupMap;
for (auto iter = m_instanceGroupIndex.begin(); iter != m_instanceGroupIndex.end(); iter++)
for (auto iter = m_groupMap.begin(); iter != m_groupMap.end(); iter++)
{
QString id = iter.key();
QString group = iter.value();
@@ -553,7 +548,7 @@ void InstanceList::saveGroupList()
auto name = iter.key();
QJsonObject groupObj;
QJsonArray instanceArr;
groupObj.insert("hidden", QJsonValue(m_collapsedGroups.contains(name)));
groupObj.insert("hidden", QJsonValue(QString("false")));
for (auto item : list)
{
instanceArr.append(QJsonValue(item));
@@ -577,6 +572,7 @@ void InstanceList::saveGroupList()
void InstanceList::loadGroupList()
{
qDebug() << "Will load group list now.";
QSet<QString> groupSet;
QString groupFileName = m_instDir + "/instgroups.json";
@@ -627,8 +623,7 @@ void InstanceList::loadGroupList()
return;
}
QSet<QString> groupSet;
m_instanceGroupIndex.clear();
m_groupMap.clear();
// Iterate through all the groups.
QJsonObject groupMapping = rootObj.value("groups").toObject();
@@ -639,35 +634,37 @@ void InstanceList::loadGroupList()
// If not an object, complain and skip to the next one.
if (!iter.value().isObject())
{
qWarning() << QString("Group '%1' in the group list should be an object.").arg(groupName).toUtf8();
qWarning() << QString("Group '%1' in the group list should "
"be an object.")
.arg(groupName)
.toUtf8();
continue;
}
QJsonObject groupObj = iter.value().toObject();
if (!groupObj.value("instances").isArray())
{
qWarning() << QString("Group '%1' in the group list is invalid. It should contain an array called 'instances'.").arg(groupName).toUtf8();
qWarning() << QString("Group '%1' in the group list is invalid. "
"It should contain an array "
"called 'instances'.")
.arg(groupName)
.toUtf8();
continue;
}
// keep a list/set of groups for choosing
groupSet.insert(groupName);
auto hidden = groupObj.value("hidden").toBool(false);
if(hidden) {
m_collapsedGroups.insert(groupName);
}
// Iterate through the list of instances in the group.
QJsonArray instancesArray = groupObj.value("instances").toArray();
for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); iter2++)
{
m_instanceGroupIndex[(*iter2).toString()] = groupName;
m_groupMap[(*iter2).toString()] = groupName;
}
}
m_groupsLoaded = true;
m_groupNameCache.unite(groupSet);
m_groups.unite(groupSet);
qDebug() << "Group list loaded.";
}
@@ -692,17 +689,6 @@ void InstanceList::on_InstFolderChanged(const Setting &setting, QVariant value)
}
}
void InstanceList::on_GroupStateChanged(const QString& group, bool collapsed)
{
qDebug() << "Group" << group << (collapsed ? "collapsed" : "expanded");
if(collapsed) {
m_collapsedGroups.insert(group);
} else {
m_collapsedGroups.remove(group);
}
saveGroupList();
}
class InstanceStaging : public Task
{
Q_OBJECT
@@ -833,9 +819,9 @@ bool InstanceList::commitStagedInstance(const QString& path, const QString& inst
qWarning() << "Failed to move" << path << "to" << destination;
return false;
}
m_instanceGroupIndex[instID] = groupName;
m_groupMap[instID] = groupName;
instanceSet.insert(instID);
m_groupNameCache.insert(groupName);
m_groups.insert(groupName);
emit instancesChanged();
emit instanceSelectRequest(instID);
}

View File

@@ -99,8 +99,6 @@ public:
InstancePtr getInstanceById(QString id) const;
QModelIndex getInstanceIndexById(const QString &id) const;
QStringList getGroups();
bool isGroupCollapsed(const QString &groupName);
GroupId getInstanceGroup(const InstanceId & id) const;
void setInstanceGroup(const InstanceId & id, const GroupId& name);
@@ -136,7 +134,6 @@ signals:
public slots:
void on_InstFolderChanged(const Setting &setting, QVariant value);
void on_GroupStateChanged(const QString &group, bool collapsed);
private slots:
void propertiesChanged(BaseInstance *inst);
@@ -157,14 +154,12 @@ private:
int m_watchLevel = 0;
bool m_dirty = false;
QList<InstancePtr> m_instances;
QSet<QString> m_groupNameCache;
QSet<QString> m_groups;
SettingsObjectPtr m_globalSettings;
QString m_instDir;
QFileSystemWatcher * m_watcher;
// FIXME: this is so inefficient that looking at it is almost painful.
QSet<QString> m_collapsedGroups;
QMap<InstanceId, GroupId> m_instanceGroupIndex;
QMap<InstanceId, GroupId> m_groupMap;
QSet<InstanceId> instanceSet;
bool m_groupsLoaded = false;
bool m_instancesProbed = false;

View File

@@ -991,7 +991,7 @@ bool ComponentList::removeComponent_internal(ComponentPtr patch)
// FIXME: we need a generic way of removing local resources, not just jar mods...
auto preRemoveJarMod = [&](LibraryPtr jarMod) -> bool
{
if (!jarMod->isLocal())
if (!jarMod->isInstanceLocal())
{
return true;
}

View File

@@ -451,17 +451,13 @@ static bool getTrivialComponentChanges(const ComponentIndex & index, const Requi
auto & comp = (*compIter);
if(comp->getVersion() != req.equalsVersion)
{
if(comp->isCustom()) {
if(comp->m_dependencyOnly)
{
decision = Decision::VersionNotSame;
}
else
{
decision = Decision::LockedVersionNotSame;
} else {
if(comp->m_dependencyOnly)
{
decision = Decision::VersionNotSame;
}
else
{
decision = Decision::LockedVersionNotSame;
}
}
break;
}

View File

@@ -117,7 +117,7 @@ struct GradleSpecifier
}
bool matchName(const GradleSpecifier & other) const
{
return other.artifactId() == artifactId() && other.groupId() == groupId();
return other.artifactId() == artifactId() && other.groupId() == groupId() && other.classifier() == classifier();
}
bool operator==(const GradleSpecifier & other) const
{

View File

@@ -11,7 +11,6 @@ void LaunchProfile::clear()
m_mainClass.clear();
m_appletClass.clear();
m_libraries.clear();
m_mavenFiles.clear();
m_traits.clear();
m_jarMods.clear();
m_mainJar.reset();
@@ -158,33 +157,25 @@ void LaunchProfile::applyLibrary(LibraryPtr library)
}
}
void LaunchProfile::applyMavenFile(LibraryPtr mavenFile)
{
if(!mavenFile->isActive())
{
return;
}
if(mavenFile->isNative())
{
return;
}
// unlike libraries, we do not keep only one version or try to dedupe them
m_mavenFiles.append(Library::limitedCopy(mavenFile));
}
const LibraryPtr LaunchProfile::getMainJar() const
{
return m_mainJar;
}
void LaunchProfile::applyMainJar(LibraryPtr jar)
void LaunchProfile::applyMainJar(LibraryPtr jar, bool removeMainJar)
{
if(jar)
if(removeMainJar)
{
m_mainJar.reset();
}
else if(jar)
{
m_mainJar = jar;
}
else
{
// mainJar was not specified, NOOP
}
}
void LaunchProfile::applyProblemSeverity(ProblemSeverity severity)
@@ -270,11 +261,6 @@ const QList<LibraryPtr> & LaunchProfile::getNativeLibraries() const
return m_nativeLibraries;
}
const QList<LibraryPtr> & LaunchProfile::getMavenFiles() const
{
return m_mavenFiles;
}
void LaunchProfile::getLibraryFiles(
const QString& architecture,
QStringList& jars,

View File

@@ -20,8 +20,7 @@ public: /* application of profile variables from patches */
void applyJarMods(const QList<LibraryPtr> &jarMods);
void applyMods(const QList<LibraryPtr> &jarMods);
void applyLibrary(LibraryPtr library);
void applyMavenFile(LibraryPtr library);
void applyMainJar(LibraryPtr jar);
void applyMainJar(LibraryPtr jar, bool removeMainJar);
void applyProblemSeverity(ProblemSeverity severity);
/// clear the profile
void clear();
@@ -38,7 +37,6 @@ public: /* getters for profile variables */
const QList<LibraryPtr> & getJarMods() const;
const QList<LibraryPtr> & getLibraries() const;
const QList<LibraryPtr> & getNativeLibraries() const;
const QList<LibraryPtr> & getMavenFiles() const;
const LibraryPtr getMainJar() const;
void getLibraryFiles(
const QString & architecture,
@@ -81,13 +79,10 @@ private:
/// the list of libraries
QList<LibraryPtr> m_libraries;
/// the list of maven files to be placed in the libraries folder, but not acted upon
QList<LibraryPtr> m_mavenFiles;
/// the main jar
LibraryPtr m_mainJar;
/// the list of native libraries
/// the list of libraries
QList<LibraryPtr> m_nativeLibraries;
/// traits, collected from all the version files (version files can only add)

View File

@@ -8,13 +8,19 @@
#include <FileSystem.h>
void Library::getApplicableFiles(OpSys system, QStringList& jar, QStringList& native, QStringList& native32,
QStringList& native64, const QString &overridePath) const
void Library::getApplicableFiles(
OpSys system,
QStringList& jar,
QStringList& native,
QStringList& native32,
QStringList& native64,
const QString &overridePath
) const
{
bool local = isLocal();
bool local = isInstanceLocal();
auto actualPath = [&](QString relPath)
{
QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
QFileInfo out(FS::PathCombine("libraries", relPath));
if(local && !overridePath.isEmpty())
{
QString fileName = out.fileName();
@@ -54,9 +60,9 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(
{
QList<NetActionPtr> out;
bool stale = isAlwaysStale();
bool local = isLocal();
bool instanceLocal = isInstanceLocal();
auto check_local_file = [&](QString storage)
auto check_instance_local_file = [&](QString storage)
{
QFileInfo fileinfo(storage);
QString fileName = fileinfo.fileName();
@@ -72,9 +78,9 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(
auto add_download = [&](QString storage, QString url, QString sha1)
{
if(local)
if(instanceLocal)
{
return check_local_file(storage);
return check_instance_local_file(storage);
}
auto entry = cache->resolveEntry("libraries", storage);
if(stale)
@@ -82,7 +88,9 @@ QList< std::shared_ptr< NetAction > > Library::getDownloads(
entry->setStale(true);
}
if (!entry->isStale())
{
return true;
}
Net::Download::Options options;
if(stale)
{
@@ -233,7 +241,7 @@ bool Library::isActive() const
return result;
}
bool Library::isLocal() const
bool Library::isInstanceLocal() const
{
return m_hint == "local";
}
@@ -248,25 +256,6 @@ bool Library::isForge() const
return m_hint == "forge-pack-xz";
}
void Library::setStoragePrefix(QString prefix)
{
m_storagePrefix = prefix;
}
QString Library::defaultStoragePrefix()
{
return "libraries/";
}
QString Library::storagePrefix() const
{
if(m_storagePrefix.isEmpty())
{
return defaultStoragePrefix();
}
return m_storagePrefix;
}
QString Library::filename(OpSys system) const
{
if(!m_filename.isEmpty())

View File

@@ -46,7 +46,6 @@ public:
newlib->m_extractExcludes = base->m_extractExcludes;
newlib->m_nativeClassifiers = base->m_nativeClassifiers;
newlib->m_rules = base->m_rules;
newlib->m_storagePrefix = base->m_storagePrefix;
newlib->m_mojangDownloads = base->m_mojangDownloads;
newlib->m_filename = base->m_filename;
return newlib;
@@ -93,8 +92,6 @@ public: /* methods */
return m_nativeClassifiers.size() != 0;
}
void setStoragePrefix(QString prefix = QString());
/// Set the url base for downloads
void setRepositoryURL(const QString &base_url)
{
@@ -146,7 +143,15 @@ public: /* methods */
bool isActive() const;
/// Returns true if the library is contained in an instance and false if it is shared
bool isLocal() const;
bool isInstanceLocal() const;
/// Returns true if the library cannot be downloaded (was built locally somehow)
bool isLocalBuilt() const;
bool isLocal() const
{
return isInstanceLocal() || isLocalBuilt();
}
/// Returns true if the library is to always be checked for updates
bool isAlwaysStale() const;
@@ -159,11 +164,6 @@ public: /* methods */
QStringList & failedLocalFiles, const QString & overridePath) const;
private: /* methods */
/// the default storage prefix used by MultiMC
static QString defaultStoragePrefix();
/// Get the prefix - root of the storage to be used
QString storagePrefix() const;
/// Get the relative file path where the library should be saved
QString storageSuffix(OpSys system) const;

View File

@@ -1,7 +1,6 @@
#include <QTest>
#include "TestUtil.h"
#include "minecraft/MojangVersionFormat.h"
#include "minecraft/OneSixVersionFormat.h"
#include "minecraft/Library.h"
#include "net/HttpMetaCache.h"
@@ -18,7 +17,7 @@ private:
jsonFile.open(QIODevice::ReadOnly);
auto data = jsonFile.readAll();
jsonFile.close();
return MojangVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file);
return OneSixVersionFormat::libraryFromJson(QJsonDocument::fromJson(data).object(), file);
}
// get absolute path to expected storage, assuming default cache prefix
QStringList getStorage(QString relative)

View File

@@ -36,7 +36,7 @@ struct MojangLibraryDownloadInfo
{
return artifact.get();
}
return classifiers[classifier].get();
}

View File

@@ -1,379 +0,0 @@
#include "MojangVersionFormat.h"
#include "OneSixVersionFormat.h"
#include "MojangDownloadInfo.h"
#include "Json.h"
using namespace Json;
#include "ParseUtils.h"
static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18;
static MojangAssetIndexInfo::Ptr assetIndexFromJson (const QJsonObject &obj);
static MojangDownloadInfo::Ptr downloadInfoFromJson (const QJsonObject &obj);
static MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson (const QJsonObject &libObj);
static QJsonObject assetIndexToJson (MojangAssetIndexInfo::Ptr assetidxinfo);
static QJsonObject libDownloadInfoToJson (MojangLibraryDownloadInfo::Ptr libinfo);
static QJsonObject downloadInfoToJson (MojangDownloadInfo::Ptr info);
namespace Bits
{
static void readString(const QJsonObject &root, const QString &key, QString &variable)
{
if (root.contains(key))
{
variable = requireString(root.value(key));
}
}
static void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj)
{
// optional, not used
readString(obj, "path", out->path);
// required!
out->sha1 = requireString(obj, "sha1");
out->url = requireString(obj, "url");
out->size = requireInteger(obj, "size");
}
static void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj)
{
out->totalSize = requireInteger(obj, "totalSize");
out->id = requireString(obj, "id");
// out->known = true;
}
}
MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj)
{
auto out = std::make_shared<MojangDownloadInfo>();
Bits::readDownloadInfo(out, obj);
return out;
}
MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj)
{
auto out = std::make_shared<MojangAssetIndexInfo>();
Bits::readDownloadInfo(out, obj);
Bits::readAssetIndex(out, obj);
return out;
}
QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info)
{
QJsonObject out;
if(!info->path.isNull())
{
out.insert("path", info->path);
}
out.insert("sha1", info->sha1);
out.insert("size", info->size);
out.insert("url", info->url);
return out;
}
MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj)
{
auto out = std::make_shared<MojangLibraryDownloadInfo>();
auto dlObj = requireObject(libObj.value("downloads"));
if(dlObj.contains("artifact"))
{
out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact"));
}
if(dlObj.contains("classifiers"))
{
auto classifiersObj = requireObject(dlObj, "classifiers");
for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++)
{
auto classifier = iter.key();
auto classifierObj = requireObject(iter.value());
out->classifiers[classifier] = downloadInfoFromJson(classifierObj);
}
}
return out;
}
QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo)
{
QJsonObject out;
if(libinfo->artifact)
{
out.insert("artifact", downloadInfoToJson(libinfo->artifact));
}
if(libinfo->classifiers.size())
{
QJsonObject classifiersOut;
for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++)
{
classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value()));
}
out.insert("classifiers", classifiersOut);
}
return out;
}
QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info)
{
QJsonObject out;
if(!info->path.isNull())
{
out.insert("path", info->path);
}
out.insert("sha1", info->sha1);
out.insert("size", info->size);
out.insert("url", info->url);
out.insert("totalSize", info->totalSize);
out.insert("id", info->id);
return out;
}
void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out)
{
Bits::readString(in, "id", out->minecraftVersion);
Bits::readString(in, "mainClass", out->mainClass);
Bits::readString(in, "minecraftArguments", out->minecraftArguments);
if(out->minecraftArguments.isEmpty())
{
QString processArguments;
Bits::readString(in, "processArguments", processArguments);
QString toCompare = processArguments.toLower();
if (toCompare == "legacy")
{
out->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
else if (toCompare == "username_session")
{
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
}
else if (toCompare == "username_session_version")
{
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
}
else if (!toCompare.isEmpty())
{
out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
}
}
Bits::readString(in, "type", out->type);
Bits::readString(in, "assets", out->assets);
if(in.contains("assetIndex"))
{
out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
}
else if (!out->assets.isNull())
{
out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
}
out->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
out->updateTime = timeFromS3Time(in.value("time").toString(""));
if (in.contains("minimumLauncherVersion"))
{
out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion"));
if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{
out->addProblem(
ProblemSeverity::Warning,
QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
.arg(out->minimumLauncherVersion)
.arg(CURRENT_MINIMUM_LAUNCHER_VERSION));
}
}
if(in.contains("downloads"))
{
auto downloadsObj = requireObject(in, "downloads");
for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++)
{
auto classifier = iter.key();
auto classifierObj = requireObject(iter.value());
out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj);
}
}
}
VersionFilePtr MojangVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename)
{
VersionFilePtr out(new VersionFile());
if (doc.isEmpty() || doc.isNull())
{
throw JSONValidationError(filename + " is empty or null");
}
if (!doc.isObject())
{
throw JSONValidationError(filename + " is not an object");
}
QJsonObject root = doc.object();
readVersionProperties(root, out.get());
out->name = "Minecraft";
out->uid = "net.minecraft";
out->version = out->minecraftVersion;
// out->filename = filename;
if (root.contains("libraries"))
{
for (auto libVal : requireArray(root.value("libraries")))
{
auto libObj = requireObject(libVal);
auto lib = MojangVersionFormat::libraryFromJson(libObj, filename);
out->libraries.append(lib);
}
}
return out;
}
void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out)
{
writeString(out, "id", in->minecraftVersion);
writeString(out, "mainClass", in->mainClass);
writeString(out, "minecraftArguments", in->minecraftArguments);
writeString(out, "type", in->type);
if(!in->releaseTime.isNull())
{
writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
}
if(!in->updateTime.isNull())
{
writeString(out, "time", timeToS3Time(in->updateTime));
}
if(in->minimumLauncherVersion != -1)
{
out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
}
writeString(out, "assets", in->assets);
if(in->mojangAssetIndex && in->mojangAssetIndex->known)
{
out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
}
if(in->mojangDownloads.size())
{
QJsonObject downloadsOut;
for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++)
{
downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value()));
}
out.insert("downloads", downloadsOut);
}
}
QJsonDocument MojangVersionFormat::versionFileToJson(const VersionFilePtr &patch)
{
QJsonObject root;
writeVersionProperties(patch.get(), root);
if (!patch->libraries.isEmpty())
{
QJsonArray array;
for (auto value: patch->libraries)
{
array.append(MojangVersionFormat::libraryToJson(value.get()));
}
root.insert("libraries", array);
}
// write the contents to a json document.
{
QJsonDocument out;
out.setObject(root);
return out;
}
}
LibraryPtr MojangVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename)
{
LibraryPtr out(new Library());
if (!libObj.contains("name"))
{
throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field");
}
out->m_name = libObj.value("name").toString();
Bits::readString(libObj, "url", out->m_repositoryURL);
if (libObj.contains("extract"))
{
out->m_hasExcludes = true;
auto extractObj = requireObject(libObj.value("extract"));
for (auto excludeVal : requireArray(extractObj.value("exclude")))
{
out->m_extractExcludes.append(requireString(excludeVal));
}
}
if (libObj.contains("natives"))
{
QJsonObject nativesObj = requireObject(libObj.value("natives"));
for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
{
if (!it.value().isString())
{
qWarning() << filename << "contains an invalid native (skipping)";
}
OpSys opSys = OpSys_fromString(it.key());
if (opSys != Os_Other)
{
out->m_nativeClassifiers[opSys] = it.value().toString();
}
}
}
if (libObj.contains("rules"))
{
out->applyRules = true;
out->m_rules = rulesFromJsonV4(libObj);
}
if (libObj.contains("downloads"))
{
out->m_mojangDownloads = libDownloadInfoFromJson(libObj);
}
return out;
}
QJsonObject MojangVersionFormat::libraryToJson(Library *library)
{
QJsonObject libRoot;
libRoot.insert("name", (QString)library->m_name);
if (!library->m_repositoryURL.isEmpty())
{
libRoot.insert("url", library->m_repositoryURL);
}
if (library->isNative())
{
QJsonObject nativeList;
auto iter = library->m_nativeClassifiers.begin();
while (iter != library->m_nativeClassifiers.end())
{
nativeList.insert(OpSys_toString(iter.key()), iter.value());
iter++;
}
libRoot.insert("natives", nativeList);
if (library->m_extractExcludes.size())
{
QJsonArray excludes;
QJsonObject extract;
for (auto exclude : library->m_extractExcludes)
{
excludes.append(exclude);
}
extract.insert("exclude", excludes);
libRoot.insert("extract", extract);
}
}
if (library->m_rules.size())
{
QJsonArray allRules;
for (auto &rule : library->m_rules)
{
QJsonObject ruleObj = rule->toJson();
allRules.append(ruleObj);
}
libRoot.insert("rules", allRules);
}
if(library->m_mojangDownloads)
{
auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads);
libRoot.insert("downloads", downloadsObj);
}
return libRoot;
}

View File

@@ -1,25 +0,0 @@
#pragma once
#include <minecraft/VersionFile.h>
#include <minecraft/Library.h>
#include <QJsonDocument>
#include "multimc_logic_export.h"
class MULTIMC_LOGIC_EXPORT MojangVersionFormat
{
friend class OneSixVersionFormat;
protected:
// does not include libraries
static void readVersionProperties(const QJsonObject& in, VersionFile* out);
// does not include libraries
static void writeVersionProperties(const VersionFile* in, QJsonObject& out);
public:
// version files / profile patches
static VersionFilePtr versionFileFromJson(const QJsonDocument &doc, const QString &filename);
static QJsonDocument versionFileToJson(const VersionFilePtr &patch);
// libraries
static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
static QJsonObject libraryToJson(Library *library);
};

View File

@@ -1,55 +0,0 @@
#include <QTest>
#include <QDebug>
#include "TestUtil.h"
#include "minecraft/MojangVersionFormat.h"
class MojangVersionFormatTest : public QObject
{
Q_OBJECT
static QJsonDocument readJson(const char *file)
{
auto path = QFINDTESTDATA(file);
QFile jsonFile(path);
jsonFile.open(QIODevice::ReadOnly);
auto data = jsonFile.readAll();
jsonFile.close();
return QJsonDocument::fromJson(data);
}
static void writeJson(const char *file, QJsonDocument doc)
{
QFile jsonFile(file);
jsonFile.open(QIODevice::WriteOnly | QIODevice::Text);
auto data = doc.toJson(QJsonDocument::Indented);
qDebug() << QString::fromUtf8(data);
jsonFile.write(data);
jsonFile.close();
}
private
slots:
void test_Through_Simple()
{
QJsonDocument doc = readJson("data/1.9-simple.json");
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9-simple.json");
auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
writeJson("1.9-simple-passthorugh.json", doc2);
QCOMPARE(doc.toJson(), doc2.toJson());
}
void test_Through()
{
QJsonDocument doc = readJson("data/1.9.json");
auto vfile = MojangVersionFormat::versionFileFromJson(doc, "1.9.json");
auto doc2 = MojangVersionFormat::versionFileToJson(vfile);
writeJson("1.9-passthorugh.json", doc2);
QCOMPARE(doc.toJson(), doc2.toJson());
}
};
QTEST_GUILESS_MAIN(MojangVersionFormatTest)
#include "MojangVersionFormat_test.moc"

View File

@@ -1,21 +1,167 @@
#include "OneSixVersionFormat.h"
#include <Json.h>
#include "minecraft/ParseUtils.h"
#include <minecraft/MojangVersionFormat.h>
class MojangVersionFormat
{
friend class OneSixVersionFormat;
protected:
// does not include libraries
static void readVersionProperties(const QJsonObject& in, VersionFile* out);
// does not include libraries
static void writeVersionProperties(const VersionFile* in, QJsonObject& out);
public:
// libraries
static LibraryPtr libraryFromJson(const QJsonObject &libObj, const QString &filename);
static QJsonObject libraryToJson(Library *library);
};
using namespace Json;
static void readString(const QJsonObject &root, const QString &key, QString &variable)
static const int CURRENT_MINIMUM_LAUNCHER_VERSION = 18;
namespace
{
void readString(const QJsonObject &root, const QString &key, QString &variable)
{
if (root.contains(key))
{
variable = requireString(root.value(key));
}
}
void readDownloadInfo(MojangDownloadInfo::Ptr out, const QJsonObject &obj)
{
// optional, not used
readString(obj, "path", out->path);
// required!
out->sha1 = requireString(obj, "sha1");
out->url = ensureString(obj, "url", QString());
out->size = requireInteger(obj, "size");
}
void readAssetIndex(MojangAssetIndexInfo::Ptr out, const QJsonObject &obj)
{
out->totalSize = requireInteger(obj, "totalSize");
out->id = requireString(obj, "id");
// out->known = true;
}
MojangAssetIndexInfo::Ptr assetIndexFromJson(const QJsonObject &obj)
{
auto out = std::make_shared<MojangAssetIndexInfo>();
::readDownloadInfo(out, obj);
::readAssetIndex(out, obj);
return out;
}
QJsonObject assetIndexToJson(MojangAssetIndexInfo::Ptr info)
{
QJsonObject out;
if(!info->path.isNull())
{
out.insert("path", info->path);
}
out.insert("sha1", info->sha1);
out.insert("size", info->size);
out.insert("url", info->url);
out.insert("totalSize", info->totalSize);
out.insert("id", info->id);
return out;
}
MojangDownloadInfo::Ptr downloadInfoFromJson(const QJsonObject &obj)
{
auto out = std::make_shared<MojangDownloadInfo>();
::readDownloadInfo(out, obj);
return out;
}
QJsonObject downloadInfoToJson(MojangDownloadInfo::Ptr info)
{
QJsonObject out;
if(!info->path.isNull())
{
out.insert("path", info->path);
}
out.insert("sha1", info->sha1);
out.insert("size", info->size);
out.insert("url", info->url);
return out;
}
MojangLibraryDownloadInfo::Ptr libDownloadInfoFromJson(const QJsonObject &libObj)
{
auto out = std::make_shared<MojangLibraryDownloadInfo>();
auto dlObj = requireObject(libObj.value("downloads"));
if(dlObj.contains("artifact"))
{
out->artifact = downloadInfoFromJson(requireObject(dlObj, "artifact"));
}
if(dlObj.contains("classifiers"))
{
auto classifiersObj = requireObject(dlObj, "classifiers");
for(auto iter = classifiersObj.begin(); iter != classifiersObj.end(); iter++)
{
auto classifier = iter.key();
auto classifierObj = requireObject(iter.value());
out->classifiers[classifier] = downloadInfoFromJson(classifierObj);
}
}
return out;
}
}
LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const QString &filename)
{
LibraryPtr out = MojangVersionFormat::libraryFromJson(libObj, filename);
LibraryPtr out(new Library());
if (!libObj.contains("name"))
{
throw JSONValidationError(filename + "contains a library that doesn't have a 'name' field");
}
out->m_name = libObj.value("name").toString();
::readString(libObj, "url", out->m_repositoryURL);
if (libObj.contains("extract"))
{
out->m_hasExcludes = true;
auto extractObj = requireObject(libObj.value("extract"));
for (auto excludeVal : requireArray(extractObj.value("exclude")))
{
out->m_extractExcludes.append(requireString(excludeVal));
}
}
if (libObj.contains("natives"))
{
QJsonObject nativesObj = requireObject(libObj.value("natives"));
for (auto it = nativesObj.begin(); it != nativesObj.end(); ++it)
{
if (!it.value().isString())
{
qWarning() << filename << "contains an invalid native (skipping)";
}
OpSys opSys = OpSys_fromString(it.key());
if (opSys != Os_Other)
{
out->m_nativeClassifiers[opSys] = it.value().toString();
}
}
}
if (libObj.contains("rules"))
{
out->applyRules = true;
out->m_rules = rulesFromJsonV4(libObj);
}
if (libObj.contains("downloads"))
{
out->m_mojangDownloads = libDownloadInfoFromJson(libObj);
}
readString(libObj, "MMC-hint", out->m_hint);
readString(libObj, "MMC-absulute_url", out->m_absoluteURL);
readString(libObj, "MMC-absoluteUrl", out->m_absoluteURL);
@@ -24,20 +170,155 @@ LibraryPtr OneSixVersionFormat::libraryFromJson(const QJsonObject &libObj, const
return out;
}
QJsonObject libDownloadInfoToJson(MojangLibraryDownloadInfo::Ptr libinfo)
{
QJsonObject out;
if(libinfo->artifact)
{
out.insert("artifact", downloadInfoToJson(libinfo->artifact));
}
if(libinfo->classifiers.size())
{
QJsonObject classifiersOut;
for(auto iter = libinfo->classifiers.begin(); iter != libinfo->classifiers.end(); iter++)
{
classifiersOut.insert(iter.key(), downloadInfoToJson(iter.value()));
}
out.insert("classifiers", classifiersOut);
}
return out;
}
QJsonObject OneSixVersionFormat::libraryToJson(Library *library)
{
QJsonObject libRoot = MojangVersionFormat::libraryToJson(library);
if (library->m_absoluteURL.size())
QJsonObject libRoot;
libRoot.insert("name", (QString)library->m_name);
if (!library->m_repositoryURL.isEmpty())
{
libRoot.insert("url", library->m_repositoryURL);
}
if (library->isNative())
{
QJsonObject nativeList;
auto iter = library->m_nativeClassifiers.begin();
while (iter != library->m_nativeClassifiers.end())
{
nativeList.insert(OpSys_toString(iter.key()), iter.value());
iter++;
}
libRoot.insert("natives", nativeList);
if (library->m_extractExcludes.size())
{
QJsonArray excludes;
QJsonObject extract;
for (auto exclude : library->m_extractExcludes)
{
excludes.append(exclude);
}
extract.insert("exclude", excludes);
libRoot.insert("extract", extract);
}
}
if (library->m_rules.size())
{
QJsonArray allRules;
for (auto &rule : library->m_rules)
{
QJsonObject ruleObj = rule->toJson();
allRules.append(ruleObj);
}
libRoot.insert("rules", allRules);
}
if(library->m_mojangDownloads)
{
auto downloadsObj = libDownloadInfoToJson(library->m_mojangDownloads);
libRoot.insert("downloads", downloadsObj);
}
// MultiMC extensions
if (library->m_absoluteURL.size()) {
libRoot.insert("MMC-absoluteUrl", library->m_absoluteURL);
if (library->m_hint.size())
}
if (library->m_hint.size()) {
libRoot.insert("MMC-hint", library->m_hint);
if (library->m_filename.size())
}
if (library->m_filename.size()) {
libRoot.insert("MMC-filename", library->m_filename);
if (library->m_displayname.size())
}
if (library->m_displayname.size()) {
libRoot.insert("MMC-displayname", library->m_displayname);
}
return libRoot;
}
void MojangVersionFormat::readVersionProperties(const QJsonObject &in, VersionFile *out)
{
::readString(in, "id", out->minecraftVersion);
::readString(in, "mainClass", out->mainClass);
::readString(in, "minecraftArguments", out->minecraftArguments);
if(out->minecraftArguments.isEmpty())
{
QString processArguments;
::readString(in, "processArguments", processArguments);
QString toCompare = processArguments.toLower();
if (toCompare == "legacy")
{
out->minecraftArguments = " ${auth_player_name} ${auth_session}";
}
else if (toCompare == "username_session")
{
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}";
}
else if (toCompare == "username_session_version")
{
out->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}";
}
else if (!toCompare.isEmpty())
{
out->addProblem(ProblemSeverity::Error, QObject::tr("processArguments is set to unknown value '%1'").arg(processArguments));
}
}
::readString(in, "type", out->type);
::readString(in, "assets", out->assets);
if(in.contains("assetIndex"))
{
out->mojangAssetIndex = assetIndexFromJson(requireObject(in, "assetIndex"));
}
else if (!out->assets.isNull())
{
out->mojangAssetIndex = std::make_shared<MojangAssetIndexInfo>(out->assets);
}
out->releaseTime = timeFromS3Time(in.value("releaseTime").toString(""));
out->updateTime = timeFromS3Time(in.value("time").toString(""));
if (in.contains("minimumLauncherVersion"))
{
out->minimumLauncherVersion = requireInteger(in.value("minimumLauncherVersion"));
if (out->minimumLauncherVersion > CURRENT_MINIMUM_LAUNCHER_VERSION)
{
out->addProblem(
ProblemSeverity::Warning,
QObject::tr("The 'minimumLauncherVersion' value of this version (%1) is higher than supported by MultiMC (%2). It might not work properly!")
.arg(out->minimumLauncherVersion)
.arg(CURRENT_MINIMUM_LAUNCHER_VERSION)
);
}
}
if(in.contains("downloads"))
{
auto downloadsObj = requireObject(in, "downloads");
for(auto iter = downloadsObj.begin(); iter != downloadsObj.end(); iter++)
{
auto classifier = iter.key();
auto classifierObj = requireObject(iter.value());
out->mojangDownloads[classifier] = downloadInfoFromJson(classifierObj);
}
}
}
VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc, const QString &filename, const bool requireOrder)
{
VersionFilePtr out(new VersionFile());
@@ -144,14 +425,14 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
}
}
auto readLibs = [&](const char * which, QList<LibraryPtr> & out)
auto readLibs = [&](const char * which)
{
for (auto libVal : requireArray(root.value(which)))
{
QJsonObject libObj = requireObject(libVal);
// parse the library
auto lib = libraryFromJson(libObj, filename);
out.append(lib);
out->libraries.append(lib);
}
};
bool hasPlusLibs = root.contains("+libraries");
@@ -160,27 +441,31 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
{
out->addProblem(ProblemSeverity::Warning,
QObject::tr("Version file has both '+libraries' and 'libraries'. This is no longer supported."));
readLibs("libraries", out->libraries);
readLibs("+libraries", out->libraries);
readLibs("libraries");
readLibs("+libraries");
}
else if (hasLibs)
{
readLibs("libraries", out->libraries);
readLibs("libraries");
}
else if(hasPlusLibs)
{
readLibs("+libraries", out->libraries);
}
if(root.contains("mavenFiles")) {
readLibs("mavenFiles", out->mavenFiles);
readLibs("+libraries");
}
// if we have mainJar, just use it
if(root.contains("mainJar"))
{
QJsonObject libObj = requireObject(root, "mainJar");
out->mainJar = libraryFromJson(libObj, filename);
auto val = root.value("mainJar");
if(val.isNull())
{
out->removeMainJar = true;
}
else
{
QJsonObject libObj = requireObject(root, "mainJar");
out->mainJar = libraryFromJson(libObj, filename);
}
}
// else reconstruct it from downloads and id ... if that's available
else if(!out->minecraftVersion.isEmpty())
@@ -251,6 +536,40 @@ VersionFilePtr OneSixVersionFormat::versionFileFromJson(const QJsonDocument &doc
return out;
}
void MojangVersionFormat::writeVersionProperties(const VersionFile* in, QJsonObject& out)
{
writeString(out, "id", in->minecraftVersion);
writeString(out, "mainClass", in->mainClass);
writeString(out, "minecraftArguments", in->minecraftArguments);
writeString(out, "type", in->type);
if(!in->releaseTime.isNull())
{
writeString(out, "releaseTime", timeToS3Time(in->releaseTime));
}
if(!in->updateTime.isNull())
{
writeString(out, "time", timeToS3Time(in->updateTime));
}
if(in->minimumLauncherVersion != -1)
{
out.insert("minimumLauncherVersion", in->minimumLauncherVersion);
}
writeString(out, "assets", in->assets);
if(in->mojangAssetIndex && in->mojangAssetIndex->known)
{
out.insert("assetIndex", assetIndexToJson(in->mojangAssetIndex));
}
if(in->mojangDownloads.size())
{
QJsonObject downloadsOut;
for(auto iter = in->mojangDownloads.begin(); iter != in->mojangDownloads.end(); iter++)
{
downloadsOut.insert(iter.key(), downloadInfoToJson(iter.value()));
}
out.insert("downloads", downloadsOut);
}
}
QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch)
{
QJsonObject root;
@@ -268,6 +587,10 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
{
root.insert("mainJar", libraryToJson(patch->mainJar.get()));
}
else if(patch->removeMainJar)
{
root.insert("mainJar", QJsonValue());
}
writeString(root, "appletClass", patch->appletClass);
writeStringList(root, "+tweakers", patch->addTweakers);
writeStringList(root, "+traits", patch->traits.toList());
@@ -280,15 +603,6 @@ QJsonDocument OneSixVersionFormat::versionFileToJson(const VersionFilePtr &patch
}
root.insert("libraries", array);
}
if (!patch->mavenFiles.isEmpty())
{
QJsonArray array;
for (auto value: patch->mavenFiles)
{
array.append(OneSixVersionFormat::libraryToJson(value.get()));
}
root.insert("mavenFiles", array);
}
if (!patch->jarMods.isEmpty())
{
QJsonArray array;

View File

@@ -5,7 +5,7 @@
#include <minecraft/Library.h>
#include <QJsonDocument>
class OneSixVersionFormat
class MULTIMC_LOGIC_EXPORT OneSixVersionFormat
{
public:
// version files / profile patches

View File

@@ -28,7 +28,7 @@ void VersionFile::applyTo(LaunchProfile *profile)
profile->applyMinecraftAssets(mojangAssetIndex);
}
profile->applyMainJar(mainJar);
profile->applyMainJar(mainJar, removeMainJar);
profile->applyMainClass(mainClass);
profile->applyAppletClass(appletClass);
profile->applyMinecraftArguments(minecraftArguments);
@@ -41,10 +41,6 @@ void VersionFile::applyTo(LaunchProfile *profile)
{
profile->applyLibrary(library);
}
for (auto mavenFile : mavenFiles)
{
profile->applyMavenFile(mavenFile);
}
profile->applyProblemSeverity(getProblemSeverity());
}
@@ -57,4 +53,4 @@ void VersionFile::applyTo(LaunchProfile *profile)
throw MinecraftVersionMismatch(uid, dependsOnMinecraftVersion, theirVersion);
}
}
*/
*/

View File

@@ -75,11 +75,9 @@ public: /* data */
/// Mojang: list of libraries to add to the version
QList<LibraryPtr> libraries;
/// MultiMC: list of maven files to put in the libraries folder, but not in classpath
QList<LibraryPtr> mavenFiles;
/// The main jar (Minecraft version library, normally)
LibraryPtr mainJar;
bool removeMainJar = false;
/// MultiMC: list of attached traits of this version file - used to enable features
QSet<QString> traits;

View File

@@ -85,10 +85,6 @@ public:
{
return mods[index];
}
const Mod &at(size_t index) const
{
return mods.at(index);
}
/// Reloads the mod list and returns true if the list changed.
bool update();

View File

@@ -45,8 +45,11 @@ void LibrariesTask::executeTask()
QList<LibraryPtr> libArtifactPool;
libArtifactPool.append(profile->getLibraries());
libArtifactPool.append(profile->getNativeLibraries());
libArtifactPool.append(profile->getMavenFiles());
libArtifactPool.append(profile->getMainJar());
auto mainJar = profile->getMainJar();
if(mainJar)
{
libArtifactPool.append(mainJar);
}
processArtifactPool(libArtifactPool, failedLocalLibraries, inst->getLocalLibraryPath());
QStringList failedLocalJarMods;

View File

@@ -0,0 +1,175 @@
#include "UrlResolvingTask.h"
#include <QtXml>
#include <Json.h>
namespace {
const char * metabase = "https://cursemeta.dries007.net";
}
Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
: m_url(toProcess)
{
}
void Flame::UrlResolvingTask::executeTask()
{
resolveUrl();
}
void Flame::UrlResolvingTask::resolveUrl()
{
setStatus(tr("Resolving URL..."));
setProgress(0, 1);
QUrl actualUrl(m_url);
if(actualUrl.host() != "www.curseforge.com") {
emitFailed(tr("Not a Twitch URL."));
return;
}
m_dljob.reset(new NetJob("URL resolver"));
bool weAreDigging = false;
needle = QString();
if(m_url.startsWith("https://")) {
if(m_url.endsWith("?client=y")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y
m_url.chop(9);
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
}
if(m_url.endsWith("/download")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html...
weAreDigging = true;
needle = m_url;
needle.replace("https://", "twitch://");
needle.replace("/download", "/download-client/");
m_url.append("?client=y");
} else if (m_url.contains("/download/")) {
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
m_url.replace("/download/", "/download-client/");
}
}
else if(m_url.startsWith("twitch://")) {
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
m_url.replace(0, 9, "https://");
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
}
auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
m_dljob->addNetAction(dl);
if(weAreDigging) {
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
} else {
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
}
m_dljob->start();
}
void Flame::UrlResolvingTask::processHTML()
{
QString htmlDoc = QString::fromUtf8(results);
auto index = htmlDoc.indexOf(needle);
if(index < 0) {
emitFailed(tr("Couldn't find the needle in the haystack..."));
return;
}
auto indexStart = index;
int indexEnd = -1;
while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') {
index ++;
if(htmlDoc[index] == '"') {
indexEnd = index;
break;
}
}
if(indexEnd > 0) {
QString found = htmlDoc.mid(indexStart, indexEnd - indexStart);
qDebug() << "Found needle: " << found;
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
m_url = found;
resolveUrl();
return;
}
emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
return;
}
void Flame::UrlResolvingTask::processCCIP()
{
QDomDocument doc;
if (!doc.setContent(results)) {
qDebug() << results;
emitFailed(tr("Resolving failed."));
return;
}
auto packageNode = doc.namedItem("package");
if(!packageNode.isElement()) {
emitFailed(tr("Resolving failed: missing package root element."));
return;
}
auto projectNode = packageNode.namedItem("project");
if(!projectNode.isElement()) {
emitFailed(tr("Resolving failed: missing project element."));
return;
}
auto attribs = projectNode.attributes();
auto projectIdNode = attribs.namedItem("id");
if(!projectIdNode.isAttr()) {
emitFailed(tr("Resolving failed: missing id attribute."));
return;
}
auto fileIdNode = attribs.namedItem("file");
if(!fileIdNode.isAttr()) {
emitFailed(tr("Resolving failed: missing file attribute."));
return;
}
auto projectId = projectIdNode.nodeValue();
auto fileId = fileIdNode.nodeValue();
bool success = true;
m_result.projectId = projectId.toInt(&success);
if(!success) {
emitFailed(tr("Failed to resove projectId as a number."));
return;
}
m_result.fileId = fileId.toInt(&success);
if(!success) {
emitFailed(tr("Failed to resove fileId as a number."));
return;
}
qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
resolveIDs();
}
void Flame::UrlResolvingTask::resolveIDs()
{
setStatus(tr("Resolving mod IDs..."));
m_dljob.reset(new NetJob("Mod id resolver"));
auto projectIdStr = QString::number(m_result.projectId);
auto fileIdStr = QString::number(m_result.fileId);
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
m_dljob->addNetAction(dl);
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
m_dljob->start();
}
void Flame::UrlResolvingTask::processCursemeta()
{
try {
if(m_result.parseFromBytes(results)) {
emitSucceeded();
qDebug() << results;
return;
}
} catch (const JSONValidationError &e) {
qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
qCritical() << e.cause();
qCritical() << "JSON:";
qCritical() << results;
}
emitFailed(tr("Failed to resolve the modpack file."));
}

View File

@@ -0,0 +1,43 @@
#pragma once
#include "tasks/Task.h"
#include "net/NetJob.h"
#include "PackManifest.h"
#include "multimc_logic_export.h"
namespace Flame
{
class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task
{
Q_OBJECT
public:
explicit UrlResolvingTask(const QString &toProcess);
virtual ~UrlResolvingTask() {};
const Flame::File &getResults() const
{
return m_result;
}
protected:
virtual void executeTask() override;
protected slots:
void processCCIP();
void processHTML();
void processCursemeta();
private:
void resolveUrl();
void resolveIDs();
private: /* data */
QString m_url;
QString needle;
Flame::File m_result;
QByteArray results;
NetJobPtr m_dljob;
};
}

View File

@@ -1,36 +1,34 @@
#include "PackFetchTask.h"
#include "PrivatePackManager.h"
#include "FtbPackFetchTask.h"
#include <QDomDocument>
#include "FtbPrivatePackManager.h"
#include "net/URLConstants.h"
namespace LegacyFTB {
void PackFetchTask::fetch()
void FtbPackFetchTask::fetch()
{
publicPacks.clear();
thirdPartyPacks.clear();
NetJob *netJob = new NetJob("LegacyFTB::ModpackFetch");
NetJob *netJob = new NetJob("FtbModpackFetch");
QUrl publicPacksUrl = QUrl(URLConstants::LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
QUrl publicPacksUrl = QUrl(URLConstants::FTB_CDN_BASE_URL + "static/modpacks.xml");
qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
netJob->addNetAction(Net::Download::makeByteArray(publicPacksUrl, &publicModpacksXmlFileData));
QUrl thirdPartyUrl = QUrl(URLConstants::LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml");
QUrl thirdPartyUrl = QUrl(URLConstants::FTB_CDN_BASE_URL + "static/thirdparty.xml");
qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
netJob->addNetAction(Net::Download::makeByteArray(thirdPartyUrl, &thirdPartyModpacksXmlFileData));
QObject::connect(netJob, &NetJob::succeeded, this, &PackFetchTask::fileDownloadFinished);
QObject::connect(netJob, &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
QObject::connect(netJob, &NetJob::succeeded, this, &FtbPackFetchTask::fileDownloadFinished);
QObject::connect(netJob, &NetJob::failed, this, &FtbPackFetchTask::fileDownloadFailed);
jobPtr.reset(netJob);
netJob->start();
}
void PackFetchTask::fetchPrivate(const QStringList & toFetch)
void FtbPackFetchTask::fetchPrivate(const QStringList & toFetch)
{
QString privatePackBaseUrl = URLConstants::LEGACY_FTB_CDN_BASE_URL + "static/%1.xml";
QString privatePackBaseUrl = URLConstants::FTB_CDN_BASE_URL + "static/%1.xml";
for (auto &packCode: toFetch)
{
@@ -40,9 +38,9 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch)
QObject::connect(job, &NetJob::succeeded, this, [this, job, data, packCode]
{
ModpackList packs;
parseAndAddPacks(*data, PackType::Private, packs);
foreach(Modpack currentPack, packs)
FtbModpackList packs;
parseAndAddPacks(*data, FtbPackType::Private, packs);
foreach(FtbModpack currentPack, packs)
{
currentPack.packCode = packCode;
emit privateFileDownloadFinished(currentPack);
@@ -67,18 +65,18 @@ void PackFetchTask::fetchPrivate(const QStringList & toFetch)
}
}
void PackFetchTask::fileDownloadFinished()
void FtbPackFetchTask::fileDownloadFinished()
{
jobPtr.reset();
QStringList failedLists;
if(!parseAndAddPacks(publicModpacksXmlFileData, PackType::Public, publicPacks))
if(!parseAndAddPacks(publicModpacksXmlFileData, FtbPackType::Public, publicPacks))
{
failedLists.append(tr("Public Packs"));
}
if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, PackType::ThirdParty, thirdPartyPacks))
if(!parseAndAddPacks(thirdPartyModpacksXmlFileData, FtbPackType::ThirdParty, thirdPartyPacks))
{
failedLists.append(tr("Third Party Packs"));
}
@@ -93,7 +91,7 @@ void PackFetchTask::fileDownloadFinished()
}
}
bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list)
bool FtbPackFetchTask::parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list)
{
QDomDocument doc;
@@ -114,7 +112,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
{
QDomElement element = nodes.at(i).toElement();
Modpack modpack;
FtbModpack modpack;
modpack.name = element.attribute("name");
modpack.currentVersion = element.attribute("version");
modpack.mcVersion = element.attribute("mcVersion");
@@ -163,10 +161,8 @@ bool PackFetchTask::parseAndAddPacks(QByteArray &data, PackType packType, Modpac
return true;
}
void PackFetchTask::fileDownloadFailed(QString reason)
void FtbPackFetchTask::fileDownloadFailed(QString reason)
{
qWarning() << "Fetching FTBPacks failed:" << reason;
qWarning() << "Fetching FtbPacks failed:" << reason;
emit failed(reason);
}
}

View File

@@ -6,15 +6,13 @@
#include <QObject>
#include "PackHelpers.h"
namespace LegacyFTB {
class MULTIMC_LOGIC_EXPORT PackFetchTask : public QObject {
class MULTIMC_LOGIC_EXPORT FtbPackFetchTask : public QObject {
Q_OBJECT
public:
PackFetchTask() = default;
virtual ~PackFetchTask() = default;
FtbPackFetchTask() = default;
virtual ~FtbPackFetchTask() = default;
void fetch();
void fetchPrivate(const QStringList &toFetch);
@@ -25,20 +23,18 @@ private:
QByteArray publicModpacksXmlFileData;
QByteArray thirdPartyModpacksXmlFileData;
bool parseAndAddPacks(QByteArray &data, PackType packType, ModpackList &list);
ModpackList publicPacks;
ModpackList thirdPartyPacks;
bool parseAndAddPacks(QByteArray &data, FtbPackType packType, FtbModpackList &list);
FtbModpackList publicPacks;
FtbModpackList thirdPartyPacks;
protected slots:
void fileDownloadFinished();
void fileDownloadFailed(QString reason);
signals:
void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
void finished(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
void failed(QString reason);
void privateFileDownloadFinished(Modpack modpack);
void privateFileDownloadFinished(FtbModpack modpack);
void privateFileDownloadFailed(QString reason, QString packCode);
};
}

View File

@@ -1,32 +1,28 @@
#include "PackInstallTask.h"
#include "FtbPackInstallTask.h"
#include "Env.h"
#include "MMCZip.h"
#include "QtConcurrent"
#include "BaseInstance.h"
#include "FileSystem.h"
#include "settings/INISettingsObject.h"
#include "minecraft/MinecraftInstance.h"
#include "minecraft/ComponentList.h"
#include "minecraft/GradleSpecifier.h"
#include "net/URLConstants.h"
#include <QtConcurrent>
namespace LegacyFTB {
PackInstallTask::PackInstallTask(Modpack pack, QString version)
FtbPackInstallTask::FtbPackInstallTask(FtbModpack pack, QString version)
{
m_pack = pack;
m_version = version;
}
void PackInstallTask::executeTask()
void FtbPackInstallTask::executeTask()
{
downloadPack();
}
void PackInstallTask::downloadPack()
void FtbPackInstallTask::downloadPack()
{
setStatus(tr("Downloading zip for %1").arg(m_pack.name));
@@ -36,46 +32,46 @@ void PackInstallTask::downloadPack()
entry->setStale(true);
QString url;
if(m_pack.type == PackType::Private)
if(m_pack.type == FtbPackType::Private)
{
url = QString(URLConstants::LEGACY_FTB_CDN_BASE_URL + "privatepacks/%1").arg(packoffset);
url = QString(URLConstants::FTB_CDN_BASE_URL + "privatepacks/%1").arg(packoffset);
}
else
{
url = QString(URLConstants::LEGACY_FTB_CDN_BASE_URL + "modpacks/%1").arg(packoffset);
url = QString(URLConstants::FTB_CDN_BASE_URL + "modpacks/%1").arg(packoffset);
}
job->addNetAction(Net::Download::makeCached(url, entry));
archivePath = entry->getFullPath();
netJobContainer.reset(job);
connect(job, &NetJob::succeeded, this, &PackInstallTask::onDownloadSucceeded);
connect(job, &NetJob::failed, this, &PackInstallTask::onDownloadFailed);
connect(job, &NetJob::progress, this, &PackInstallTask::onDownloadProgress);
connect(job, &NetJob::succeeded, this, &FtbPackInstallTask::onDownloadSucceeded);
connect(job, &NetJob::failed, this, &FtbPackInstallTask::onDownloadFailed);
connect(job, &NetJob::progress, this, &FtbPackInstallTask::onDownloadProgress);
job->start();
progress(1, 4);
}
void PackInstallTask::onDownloadSucceeded()
void FtbPackInstallTask::onDownloadSucceeded()
{
abortable = false;
unzip();
}
void PackInstallTask::onDownloadFailed(QString reason)
void FtbPackInstallTask::onDownloadFailed(QString reason)
{
abortable = false;
emitFailed(reason);
}
void PackInstallTask::onDownloadProgress(qint64 current, qint64 total)
void FtbPackInstallTask::onDownloadProgress(qint64 current, qint64 total)
{
abortable = true;
progress(current, total * 4);
setStatus(tr("Downloading zip for %1 (%2%)").arg(m_pack.name).arg(current / 10));
}
void PackInstallTask::unzip()
void FtbPackInstallTask::unzip()
{
progress(2, 4);
setStatus(tr("Extracting modpack"));
@@ -89,22 +85,22 @@ void PackInstallTask::unzip()
}
m_extractFuture = QtConcurrent::run(QThreadPool::globalInstance(), MMCZip::extractDir, archivePath, extractDir.absolutePath() + "/unzip");
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &PackInstallTask::onUnzipFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &PackInstallTask::onUnzipCanceled);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::finished, this, &FtbPackInstallTask::onUnzipFinished);
connect(&m_extractFutureWatcher, &QFutureWatcher<QStringList>::canceled, this, &FtbPackInstallTask::onUnzipCanceled);
m_extractFutureWatcher.setFuture(m_extractFuture);
}
void PackInstallTask::onUnzipFinished()
void FtbPackInstallTask::onUnzipFinished()
{
install();
}
void PackInstallTask::onUnzipCanceled()
void FtbPackInstallTask::onUnzipCanceled()
{
emitAborted();
}
void PackInstallTask::install()
void FtbPackInstallTask::install()
{
progress(3, 4);
setStatus(tr("Installing modpack"));
@@ -201,7 +197,7 @@ void PackInstallTask::install()
emitSucceeded();
}
bool PackInstallTask::abort()
bool FtbPackInstallTask::abort()
{
if(abortable)
{
@@ -209,5 +205,3 @@ bool PackInstallTask::abort()
}
return false;
}
}

View File

@@ -6,17 +6,15 @@
#include "meta/Index.h"
#include "meta/Version.h"
#include "meta/VersionList.h"
#include "PackHelpers.h"
#include "modplatform/ftb/PackHelpers.h"
namespace LegacyFTB {
class MULTIMC_LOGIC_EXPORT PackInstallTask : public InstanceTask
class MULTIMC_LOGIC_EXPORT FtbPackInstallTask : public InstanceTask
{
Q_OBJECT
public:
explicit PackInstallTask(Modpack pack, QString version);
virtual ~PackInstallTask(){}
explicit FtbPackInstallTask(FtbModpack pack, QString version);
virtual ~FtbPackInstallTask(){}
bool abort() override;
@@ -45,8 +43,6 @@ private: /* data */
NetJobPtr netJobContainer;
QString archivePath;
Modpack m_pack;
FtbModpack m_pack;
QString m_version;
};
}

View File

@@ -1,12 +1,10 @@
#include "PrivatePackManager.h"
#include "FtbPrivatePackManager.h"
#include <QDebug>
#include "FileSystem.h"
namespace LegacyFTB {
void PrivatePackManager::load()
void FtbPrivatePackManager::load()
{
try
{
@@ -20,7 +18,7 @@ void PrivatePackManager::load()
}
}
void PrivatePackManager::save() const
void FtbPrivatePackManager::save() const
{
if(!dirty)
{
@@ -37,5 +35,3 @@ void PrivatePackManager::save() const
qWarning() << "Failed to write third party FTB pack codes to" << m_filename;
}
}
}

View File

@@ -5,12 +5,10 @@
#include <QFile>
#include "multimc_logic_export.h"
namespace LegacyFTB {
class MULTIMC_LOGIC_EXPORT PrivatePackManager
class MULTIMC_LOGIC_EXPORT FtbPrivatePackManager
{
public:
~PrivatePackManager()
~FtbPrivatePackManager()
{
save();
}
@@ -40,5 +38,3 @@ private:
QString m_filename = "private_packs.txt";
mutable bool dirty = false;
};
}

View File

@@ -5,17 +5,15 @@
#include <QStringList>
#include <QMetaType>
namespace LegacyFTB {
//Header for structs etc...
enum class PackType
enum class FtbPackType
{
Public,
ThirdParty,
Private
};
struct Modpack
struct FtbModpack
{
QString name;
QString description;
@@ -33,13 +31,11 @@ struct Modpack
bool bugged = false;
bool broken = false;
PackType type;
FtbPackType type;
QString packCode;
};
typedef QList<Modpack> ModpackList;
}
//We need it for the proxy model
Q_DECLARE_METATYPE(LegacyFTB::Modpack)
Q_DECLARE_METATYPE(FtbModpack)
typedef QList<FtbModpack> FtbModpackList;

View File

@@ -29,8 +29,7 @@ const QString IMGUR_BASE_URL("https://api.imgur.com/3/");
const QString FMLLIBS_OUR_BASE_URL("https://files.multimc.org/fmllibs/");
const QString FMLLIBS_FORGE_BASE_URL("https://files.minecraftforge.net/fmllibs/");
const QString TRANSLATIONS_BASE_URL("https://files.multimc.org/translations/");
const QString LEGACY_FTB_CDN_BASE_URL("https://dist.creeper.host/FTB2/");
const QString FTB_CDN_BASE_URL("https://ftb.forgecdn.net/FTB2/");
QString getJarPath(QString version);
QString getLegacyJarUrl(QString version);

View File

@@ -33,12 +33,7 @@ struct Language
Language(const QString & _key)
{
key = _key;
if(key == "pt") {
locale = QLocale("pt_PT");
}
else {
locale = QLocale(key);
}
locale = QLocale(key);
updated = (key == defaultLangCode);
}

View File

@@ -185,6 +185,25 @@ slots:
qDebug() << expectedOperations;
QCOMPARE(operations, expectedOperations);
}
void test_OSXPathFixup()
{
QString path, pathOrig;
bool result;
// Proper OSX path
pathOrig = path = "MultiMC.app/Foo/Bar/Baz";
qDebug() << "Proper OSX path: " << path;
result = fixPathForOSX(path);
QCOMPARE(path, QString("Foo/Bar/Baz"));
QCOMPARE(result, true);
// Bad OSX path
pathOrig = path = "translations/klingon.lol";
qDebug() << "Bad OSX path: " << path;
result = fixPathForOSX(path);
QCOMPARE(path, pathOrig);
QCOMPARE(result, false);
}
};
extern "C"

View File

@@ -33,7 +33,13 @@ bool parseVersionInfo(const QByteArray &data, VersionFileList &list, QString &er
QJsonObject fileObj = fileValue.toObject();
QString file_path = fileObj.value("Path").toString();
#ifdef Q_OS_MAC
// On OSX, the paths for the updater need to be fixed.
// basically, anything that isn't in the .app folder is ignored.
// everything else is changed so the code that processes the files actually finds
// them and puts the replacements in the right spots.
fixPathForOSX(file_path);
#endif
VersionFileEntry file{file_path, fileObj.value("Perms").toVariant().toInt(),
FileSourceList(), fileObj.value("MD5").toString(), };
qDebug() << "File" << file.path << "with perms" << file.mode;
@@ -195,4 +201,19 @@ bool processFileLists
}
return true;
}
bool fixPathForOSX(QString &path)
{
if (path.startsWith("MultiMC.app/"))
{
// remove the prefix and add a new, more appropriate one.
path.remove(0, 12);
return true;
}
else
{
qCritical() << "Update path not within .app: " << path;
return false;
}
}
}

View File

@@ -123,5 +123,14 @@ bool MULTIMC_LOGIC_EXPORT processFileLists
OperationList &ops
);
/*!
* This fixes destination paths for OSX - removes 'MultiMC.app' prefix
* The updater runs in MultiMC.app/Contents/MacOs by default
* The destination paths are such as this: MultiMC.app/blah/blah
*
* @return false if the path couldn't be fixed (is invalid)
*/
bool MULTIMC_LOGIC_EXPORT fixPathForOSX(QString &path);
}
Q_DECLARE_METATYPE(GoUpdate::Status)

View File

@@ -125,14 +125,20 @@ SET(MULTIMC_SOURCES
pages/global/ProxyPage.h
pages/global/PasteEEPage.cpp
pages/global/PasteEEPage.h
pages/global/PackagesPage.cpp
pages/global/PackagesPage.h
# GUI - platform pages
pages/modplatform/VanillaPage.cpp
pages/modplatform/VanillaPage.h
pages/modplatform/legacy_ftb/Page.cpp
pages/modplatform/legacy_ftb/Page.h
pages/modplatform/legacy_ftb/ListModel.h
pages/modplatform/legacy_ftb/ListModel.cpp
pages/modplatform/FTBPage.cpp
pages/modplatform/FTBPage.h
pages/modplatform/FtbListModel.h
pages/modplatform/FtbListModel.cpp
pages/modplatform/TwitchPage.cpp
pages/modplatform/TwitchPage.h
pages/modplatform/TechnicPage.cpp
pages/modplatform/TechnicPage.h
pages/modplatform/ImportPage.cpp
pages/modplatform/ImportPage.h
@@ -176,8 +182,6 @@ SET(MULTIMC_SOURCES
widgets/Common.h
widgets/CustomCommands.cpp
widgets/CustomCommands.h
widgets/DropLabel.cpp
widgets/DropLabel.h
widgets/FocusLineEdit.cpp
widgets/FocusLineEdit.h
widgets/IconLabel.cpp
@@ -247,10 +251,13 @@ SET(MULTIMC_UIS
pages/global/MultiMCPage.ui
pages/global/ProxyPage.ui
pages/global/PasteEEPage.ui
pages/global/PackagesPage.ui
# Platform pages
pages/modplatform/VanillaPage.ui
pages/modplatform/legacy_ftb/Page.ui
pages/modplatform/FTBPage.ui
pages/modplatform/TwitchPage.ui
pages/modplatform/TechnicPage.ui
pages/modplatform/ImportPage.ui
# Dialogs
@@ -274,6 +281,7 @@ SET(MULTIMC_UIS
)
set(MULTIMC_QRCS
resources/assets/assets.qrc
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc

View File

@@ -24,7 +24,7 @@ void LaunchController::executeTask()
{
if (!m_instance)
{
emitFailed(tr("No instance specified!"));
emitFailed(tr("No instance specified"));
return;
}
@@ -74,7 +74,7 @@ void LaunchController::login()
// if no account is selected, we bail
if (!account.get())
{
emitFailed(tr("No account selected for launch."));
emitFailed(tr("No account selected for launch"));
return;
}
@@ -83,7 +83,8 @@ void LaunchController::login()
// we loop until the user succeeds in logging in or gives up
bool tryagain = true;
// the failure. the default failure.
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter your password to log in again. <br /> <br /> This could be caused by a password change.");
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter "
"your password to log in again.");
QString failReason = needLoginAgain;
while (tryagain)
@@ -192,7 +193,7 @@ void LaunchController::launchInstance()
if(!m_instance->reloadSettings())
{
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't load the instance profile."));
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't load the instance profile."));
emitFailed(tr("Couldn't load the instance profile."));
return;
}
@@ -232,8 +233,8 @@ void LaunchController::readyForLaunch()
if (!m_profiler->check(&error))
{
m_launcher->abort();
QMessageBox::critical(m_parentWidget, tr("Error!"), tr("Couldn't start profiler: %1").arg(error));
emitFailed("Profiler startup failed!");
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
emitFailed("Profiler startup failed");
return;
}
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
@@ -244,7 +245,7 @@ void LaunchController::readyForLaunch()
msg.setText(tr("The game launch is delayed until you press the "
"button. This is the right time to setup the profiler, as the "
"profiler server is running now.\n\n%1").arg(message));
msg.setWindowTitle(tr("Waiting."));
msg.setWindowTitle(tr("Waiting"));
msg.setIcon(QMessageBox::Information);
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
msg.setModal(true);
@@ -261,7 +262,7 @@ void LaunchController::readyForLaunch()
msg.setModal(true);
msg.exec();
m_launcher->abort();
emitFailed("Profiler startup failed!");
emitFailed("Profiler startup failed");
});
profilerInstance->beginProfiling(m_launcher);
}

View File

@@ -575,10 +575,9 @@ public:
{
MainWindow->setObjectName(QStringLiteral("MainWindow"));
}
MainWindow->resize(800, 600);
MainWindow->resize(694, 563);
MainWindow->setWindowIcon(MMC->getThemedIcon("logo"));
MainWindow->setWindowTitle("MultiMC 5");
MainWindow->setAccessibleName("MultiMC");
createMainToolbar(MainWindow);
@@ -683,10 +682,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow
connect(proxymodel, &InstanceProxyModel::dataChanged, this, &MainWindow::instanceDataChanged);
view->setModel(proxymodel);
view->setSourceOfGroupCollapseStatus([](const QString & groupName)->bool {
return MMC->instances()->isGroupCollapsed(groupName);
});
connect(view, &GroupView::groupStateChanged, MMC->instances().get(), &InstanceList::on_GroupStateChanged);
ui->horizontalLayout->addWidget(view);
}
// The cat background
@@ -1358,7 +1353,7 @@ void MainWindow::on_actionCopyInstance_triggered()
if (!copyInstDlg.exec())
return;
auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.shouldCopySaves(), copyInstDlg.shouldKeepPlaytime());
auto copyTask = new InstanceCopyTask(m_selectedInstance, copyInstDlg.shouldCopySaves());
copyTask->setName(copyInstDlg.instName());
copyTask->setGroup(copyInstDlg.instGroup());
copyTask->setIcon(copyInstDlg.iconKey());

View File

@@ -57,8 +57,6 @@ public:
void checkInstancePathForProblems();
void updatesAllowedChanged(bool allowed);
void droppedURLs(QList<QUrl> urls);
signals:
void isClosing();
@@ -182,6 +180,8 @@ private slots:
*/
void downloadUpdates(GoUpdate::Status status);
void droppedURLs(QList<QUrl> urls);
void konamiTriggered();
void globalSettingsClosed();

View File

@@ -15,6 +15,7 @@
#include "pages/global/ExternalToolsPage.h"
#include "pages/global/AccountListPage.h"
#include "pages/global/PasteEEPage.h"
#include "pages/global/PackagesPage.h"
#include "pages/global/CustomCommandsPage.h"
#include "themes/ITheme.h"
@@ -34,7 +35,6 @@
#include <QNetworkAccessManager>
#include <QTranslator>
#include <QLibraryInfo>
#include <QList>
#include <QStringList>
#include <QDebug>
#include <QStyleFactory>
@@ -157,27 +157,23 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
// --help
parser.addSwitch("help");
parser.addShortOpt("help", 'h');
parser.addDocumentation("help", "Display this help and exit.");
parser.addDocumentation("help", "display this help and exit.");
// --version
parser.addSwitch("version");
parser.addShortOpt("version", 'V');
parser.addDocumentation("version", "Display program version and exit.");
parser.addDocumentation("version", "display program version and exit.");
// --dir
parser.addOption("dir");
parser.addShortOpt("dir", 'd');
parser.addDocumentation("dir", "Use the supplied folder as MultiMC root instead of "
parser.addDocumentation("dir", "use the supplied folder as MultiMC root instead of "
"the binary location (use '.' for current)");
// --launch
parser.addOption("launch");
parser.addShortOpt("launch", 'l');
parser.addDocumentation("launch", "Launch the specified instance (by instance ID)");
parser.addDocumentation("launch", "launch the specified instance (by instance ID)");
// --alive
parser.addSwitch("alive");
parser.addDocumentation("alive", "Write a small '" + liveCheckFile + "' file after MultiMC starts");
// --import
parser.addOption("import");
parser.addShortOpt("import", 'I');
parser.addDocumentation("import", "Import instance from specified zip (local path or URL)");
parser.addDocumentation("alive", "write a small '" + liveCheckFile + "' file after MultiMC starts");
// parse the arguments
try
@@ -212,7 +208,6 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
}
m_instanceIdToLaunch = args["launch"].toString();
m_liveCheck = args["alive"].toBool();
m_zipToImport = args["import"].toUrl();
QString origcwdPath = QDir::currentPath();
QString binPath = applicationDirPath();
@@ -284,20 +279,13 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
connect(m_peerInstance, &LocalPeer::messageReceived, this, &MultiMC::messageReceived);
if(m_peerInstance->isClient())
{
int timeout = 2000;
if(m_instanceIdToLaunch.isEmpty())
{
m_peerInstance->sendMessage("activate", timeout);
if(!m_zipToImport.isEmpty())
{
m_peerInstance->sendMessage("import " + m_zipToImport.toString(), timeout);
}
m_peerInstance->sendMessage("activate", 2000);
}
else
{
m_peerInstance->sendMessage("launch " + m_instanceIdToLaunch, timeout);
m_peerInstance->sendMessage(m_instanceIdToLaunch, 2000);
}
m_status = MultiMC::Succeeded;
return;
@@ -394,7 +382,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
auto payload = appID.toString().toUtf8();
if(check.write(payload) != payload.size())
{
qWarning() << "Could not write into" << liveCheckFile << "!";
qWarning() << "Could not write into" << liveCheckFile;
check.remove();
break;
}
@@ -539,6 +527,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage<LanguagePage>();
m_globalSettingsProvider->addPage<CustomCommandsPage>();
m_globalSettingsProvider->addPage<ProxyPage>();
// m_globalSettingsProvider->addPage<PackagesPage>();
m_globalSettingsProvider->addPage<ExternalToolsPage>();
m_globalSettingsProvider->addPage<AccountListPage>();
m_globalSettingsProvider->addPage<PasteEEPage>();
@@ -546,9 +535,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
qDebug() << "<> Settings loaded.";
}
#ifndef QT_NO_ACCESSIBILITY
QAccessible::installFactory(groupViewAccessibleFactory);
#endif /* !QT_NO_ACCESSIBILITY */
// load translations
{
@@ -613,12 +600,12 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv)
{
auto InstDirSetting = m_settings->getSetting("InstanceDir");
// instance path: check for problems with '!' in instance path and warn the user in the log
// and remember that we have to show him a dialog when the gui starts (if it does so)
// and rememer that we have to show him a dialog when the gui starts (if it does so)
QString instDir = InstDirSetting->get().toString();
qDebug() << "Instance path : " << instDir;
if (FS::checkProblemticPathJava(QDir(instDir)))
{
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems!";
qWarning() << "Your instance path contains \'!\' and this is known to cause java problems";
}
m_instances.reset(new InstanceList(m_settings, instDir, this));
connect(InstDirSetting.get(), &Setting::SettingChanged, m_instances.get(), &InstanceList::on_InstFolderChanged);
@@ -825,11 +812,6 @@ void MultiMC::performMainStartupAction()
showMainWindow(false);
qDebug() << "<> Main window shown.";
}
if(!m_zipToImport.isEmpty())
{
qDebug() << "<> Importing instance from zip:" << m_zipToImport;
m_mainWindow->droppedURLs({ m_zipToImport });
}
}
void MultiMC::showFatalErrorMessage(const QString& title, const QString& content)
@@ -866,40 +848,18 @@ void MultiMC::messageReceived(const QString& message)
qDebug() << "Received message" << message << "while still initializing. It will be ignored.";
return;
}
QStringList args = message.split(' ');
QString command = args.takeFirst();
if(command == "activate")
if(message == "activate")
{
showMainWindow();
}
else if(command == "import")
else
{
if(args.isEmpty())
{
qWarning() << "Received" << command << "message without a zip path/URL.";
return;
}
m_mainWindow->droppedURLs({ QUrl(args.takeFirst()) });
}
else if(command == "launch")
{
if(args.isEmpty())
{
qWarning() << "Received" << command << "message without an instance ID.";
return;
}
auto inst = instances()->getInstanceById(args.takeFirst());
auto inst = instances()->getInstanceById(message);
if(inst)
{
launch(inst, true, nullptr);
}
}
else
{
qWarning() << "Received invalid message" << message;
}
}
void MultiMC::analyticsSettingChanged(const Setting&, QVariant value)
@@ -987,7 +947,7 @@ bool MultiMC::launch(InstancePtr instance, bool online, BaseProfilerFactory *pro
{
if(m_updateRunning)
{
qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed.";
qDebug() << "Cannot launch instances while an update is running.";
}
else if(instance->canLaunch())
{
@@ -1036,7 +996,7 @@ bool MultiMC::kill(InstancePtr instance)
{
if (!instance->isRunning())
{
qWarning() << "Attempted to kill instance" << instance->id() << ", which isn't running.";
qWarning() << "Attempted to kill instance" << instance->id() << "which isn't running.";
return false;
}
auto & extras = m_instanceExtras[instance->id()];

View File

@@ -6,7 +6,6 @@
#include <QFlag>
#include <QIcon>
#include <QDateTime>
#include <QUrl>
#include <updater/GoUpdate.h>
#include <BaseInstance.h>
@@ -222,6 +221,5 @@ private:
public:
QString m_instanceIdToLaunch;
bool m_liveCheck = false;
QUrl m_zipToImport;
std::unique_ptr<QFile> logFile;
};

View File

@@ -23,44 +23,56 @@
#include "HoeDown.h"
namespace {
// Credits
// This is a hack, but I can't think of a better way to do this easily without screwing with QTextDocument...
QString getCreditsHtml(QStringList patrons)
static QString getCreditsHtml(QStringList patrons)
{
QString patronsHeading = QObject::tr("Patrons", "About Credits");
QString output;
QTextStream stream(&output);
stream << "<center>\n";
// TODO: possibly retrieve from git history at build time?
stream << "<h3>" << QObject::tr("MultiMC Developers", "About Credits") << "</h3>\n";
stream << "<p>Andrew Okin &lt;<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>&gt;</p>\n";
stream << "<p>Petr Mrázek &lt;<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>&gt;</p>\n";
stream << "<p>Sky Welch &lt;<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>&gt;</p>\n";
stream << "<p>Jan (02JanDal) &lt;<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>&gt;</p>\n";
stream << "<p>RoboSky &lt;<a href='https://twitter.com/RoboSky_'>@RoboSky_</a>&gt;</p>\n";
stream << "<br />\n";
stream << "<h3>" << QObject::tr("With thanks to", "About Credits") << "</h3>\n";
stream << "<p>Orochimarufan &lt;<a href='mailto:orochimarufan.x3@gmail.com'>orochimarufan.x3@gmail.com</a>&gt;</p>\n";
stream << "<p>TakSuyu &lt;<a href='mailto:taksuyu@gmail.com'>taksuyu@gmail.com</a>&gt;</p>\n";
stream << "<p>Kilobyte &lt;<a href='mailto:stiepen22@gmx.de'>stiepen22@gmx.de</a>&gt;</p>\n";
stream << "<p>Rootbear75 &lt;<a href='https://twitter.com/rootbear75'>@rootbear75</a>&gt;</p>\n";
stream << "<p>Zeker Zhayard &lt;<a href='https://twitter.com/zeker_zhayard'>@Zeker_Zhayard</a>&gt;</p>\n";
stream << "<br />\n";
if(!patrons.isEmpty()) {
stream << "<h3>" << QObject::tr("Patrons", "About Credits") << "</h3>\n";
QString creditsHtml = QObject::tr(
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN' 'http://www.w3.org/TR/REC-html40/strict.dtd'>"
"<html>"
""
"<head>"
"<meta name='qrichtext' content='1' />"
"<style type='text/css'>"
"p { white-space: pre-wrap; margin-top:2px; margin-bottom:2px; }"
"</style>"
"</head>"
""
"<body style=' font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;'>"
""
"<h3>MultiMC Developers</h3>"
"<p>Andrew Okin &lt;<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>&gt;</p>"
"<p>Petr Mrázek &lt;<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>&gt;</p>"
"<p>Sky Welch &lt;<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>&gt;</p>"
"<p>Jan (02JanDal) &lt;<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>&gt;</p>"
"<p>RoboSky &lt;<a href='https://twitter.com/RoboSky_'>@RoboSky_</a>&gt;</p>"
""
"<h3>With thanks to</h3>"
"<p>Orochimarufan &lt;<a href='mailto:orochimarufan.x3@gmail.com'>orochimarufan.x3@gmail.com</a>&gt;</p>"
"<p>TakSuyu &lt;<a href='mailto:taksuyu@gmail.com'>taksuyu@gmail.com</a>&gt;</p>"
"<p>Kilobyte &lt;<a href='mailto:stiepen22@gmx.de'>stiepen22@gmx.de</a>&gt;</p>"
"<p>Rootbear75 &lt;<a href='https://twitter.com/rootbear75'>@rootbear75</a>&gt;</p>"
""
"<h3>Patrons</h3>"
"%1"
""
"</body>"
"</html>");
if (patrons.isEmpty())
return creditsHtml.arg(QObject::tr("<p>Loading...</p>"));
else
{
QString patronsStr;
for (QString patron : patrons)
{
stream << "<p>" << patron << "</p>\n";
patronsStr.append(QString("<p>%1</p>").arg(patron));
}
return creditsHtml.arg(patronsStr);
}
stream << "</center>\n";
return output;
}
QString getLicenseHtml()
static QString getLicenseHtml()
{
HoeDown hoedown;
QFile dataFile(":/documents/COPYING.md");
@@ -69,8 +81,6 @@ QString getLicenseHtml()
return output;
}
}
AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog)
{
ui->setupUi(this);
@@ -99,15 +109,6 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
else
ui->channelLabel->setVisible(false);
ui->redistributionText->setHtml(tr(
"<p>We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.</p>\n"
"<p>Part of the reason for using the Apache license is we don't want people using the &quot;MultiMC&quot; name when redistributing the project. "
"This means people must take the time to go through the source code and remove all references to &quot;MultiMC&quot;, including but not limited to the project "
"icon and the title of windows, (no <b>MultiMC-fork</b> in the title).</p>\n"
"<p>The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. "
"However, it should be abundantly clear that the project is a fork <b>without</b> implying that you have our blessing.</p>"
));
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt);

View File

@@ -212,11 +212,28 @@
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="translationInfo">
<property name="text">
<string extracomment="Hey, Translator, feel free to put credit to you here">No Language file loaded.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licenseTab">
@@ -240,6 +257,13 @@
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string notr="true">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans Mono'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
@@ -253,7 +277,18 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTextEdit" name="redistributionText">
<widget class="QTextEdit" name="textEdit">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;Part of the reason for using the Apache license is we don't want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
@@ -302,11 +337,14 @@
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>creditsText</tabstop>
<tabstop>translationInfo</tabstop>
<tabstop>licenseText</tabstop>
<tabstop>redistributionText</tabstop>
<tabstop>textEdit</tabstop>
<tabstop>aboutQt</tabstop>
<tabstop>closeButton</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../../resources/multimc/multimc.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -53,7 +53,6 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
ui->groupBox->setCurrentIndex(index);
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
ui->copySavesCheckbox->setChecked(m_copySaves);
ui->keepPlaytimeCheckbox->setChecked(m_keepPlaytime);
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -124,21 +123,3 @@ void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
m_copySaves = true;
}
}
bool CopyInstanceDialog::shouldKeepPlaytime() const
{
return m_keepPlaytime;
}
void CopyInstanceDialog::on_keepPlaytimeCheckbox_stateChanged(int state)
{
if(state == Qt::Unchecked)
{
m_keepPlaytime = false;
}
else if(state == Qt::Checked)
{
m_keepPlaytime = true;
}
}

View File

@@ -40,19 +40,16 @@ public:
QString instGroup() const;
QString iconKey() const;
bool shouldCopySaves() const;
bool shouldKeepPlaytime() const;
private
slots:
void on_iconButton_clicked();
void on_instNameTextBox_textChanged(const QString &arg1);
void on_copySavesCheckbox_stateChanged(int state);
void on_keepPlaytimeCheckbox_stateChanged(int state);
private:
Ui::CopyInstanceDialog *ui;
QString InstIconKey;
InstancePtr m_original;
bool m_copySaves = true;
bool m_keepPlaytime = true;
};

View File

@@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>345</width>
<height>323</height>
<height>240</height>
</rect>
</property>
<property name="windowTitle">
@@ -116,13 +116,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="keepPlaytimeCheckbox">
<property name="text">
<string>Keep play time</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
@@ -135,13 +128,6 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>iconButton</tabstop>
<tabstop>instNameTextBox</tabstop>
<tabstop>groupBox</tabstop>
<tabstop>copySavesCheckbox</tabstop>
<tabstop>keepPlaytimeCheckbox</tabstop>
</tabstops>
<resources>
<include location="../../graphics.qrc"/>
</resources>

View File

@@ -30,7 +30,7 @@
<item>
<widget class="QLineEdit" name="userTextBox">
<property name="placeholderText">
<string>Email</string>
<string>Email / Username</string>
</property>
</widget>
</item>

View File

@@ -36,7 +36,7 @@
<item>
<widget class="QLineEdit" name="userTextBox">
<property name="placeholderText">
<string>Email</string>
<string>Email / Username</string>
</property>
</widget>
</item>

View File

@@ -34,8 +34,11 @@
#include "widgets/PageContainer.h"
#include <pages/modplatform/VanillaPage.h>
#include <pages/modplatform/legacy_ftb/Page.h>
#include <pages/modplatform/FTBPage.h>
#include <pages/modplatform/TwitchPage.h>
#include <pages/modplatform/ImportPage.h>
#include <pages/modplatform/TechnicPage.h>
NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString & url, QWidget *parent)
@@ -94,8 +97,14 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
if(!url.isEmpty())
{
QUrl actualUrl(url);
m_container->selectPage("import");
importPage->setUrl(url);
if(actualUrl.host() == "www.curseforge.com") {
m_container->selectPage("twitch");
twitchPage->setUrl(url);
}
else {
m_container->selectPage("import");
importPage->setUrl(url);
}
}
updateDialogState();
@@ -119,11 +128,13 @@ void NewInstanceDialog::accept()
QList<BasePage *> NewInstanceDialog::getPages()
{
importPage = new ImportPage(this);
twitchPage = new TwitchPage(this);
return
{
new VanillaPage(this),
importPage,
new LegacyFTB::Page(this),
twitchPage,
new FTBPage(this)
};
}

View File

@@ -29,6 +29,7 @@ class NewInstanceDialog;
class PageContainer;
class QDialogButtonBox;
class ImportPage;
class TwitchPage;
class NewInstanceDialog : public QDialog, public BasePageProvider
{
@@ -67,6 +68,7 @@ private:
QString InstIconKey;
ImportPage *importPage = nullptr;
TwitchPage *twitchPage = nullptr;
std::unique_ptr<InstanceTask> creationTask;
bool importIcon = false;

View File

@@ -22,7 +22,6 @@ UpdateDialog::UpdateDialog(bool hasUpdate, QWidget *parent) : QDialog(parent), u
ui->btnUpdateNow->setHidden(true);
ui->btnUpdateLater->setText(tr("Close"));
}
ui->changelogBrowser->setHtml(tr("<center><h1>Loading changelog...</h1></center>"));
loadChangelog();
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("UpdateDialogGeometry").toByteArray()));
}

View File

@@ -42,6 +42,13 @@
</item>
<item>
<widget class="QTextBrowser" name="changelogBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:22pt;&quot;&gt;Loading changelog...&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>

View File

@@ -6,8 +6,6 @@
#include <qaccessible.h>
#include <qheaderview.h>
#ifndef QT_NO_ACCESSIBILITY
QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object)
{
QAccessibleInterface *iface = 0;
@@ -774,5 +772,3 @@ QAccessibleInterface *AccessibleGroupViewItem::child(int) const
{
return 0;
}
#endif /* !QT_NO_ACCESSIBILITY */

View File

@@ -1,11 +1,10 @@
#pragma once
#include "GroupView.h"
#include "QtCore/qpointer.h"
#include <QtGui/qaccessible.h>
#include <QAccessibleWidget>
#include <QAbstractItemView>
#ifndef QT_NO_ACCESSIBILITY
#include "GroupView.h"
// #include <QHeaderView>
class QAccessibleTableCell;
@@ -115,4 +114,3 @@ private:
friend class AccessibleGroupView;
};
#endif /* !QT_NO_ACCESSIBILITY */

View File

@@ -93,13 +93,11 @@ void GroupView::currentChanged(const QModelIndex& current, const QModelIndex& pr
{
QAbstractItemView::currentChanged(current, previous);
// TODO: for accessibility support, implement+register a factory, steal QAccessibleTable from Qt and return an instance of it for GroupView.
#ifndef QT_NO_ACCESSIBILITY
if (QAccessible::isActive() && current.isValid()) {
QAccessibleEvent event(this, QAccessible::Focus);
event.setChild(current.row());
QAccessible::updateAccessibility(&event);
}
#endif /* !QT_NO_ACCESSIBILITY */
}
@@ -177,9 +175,6 @@ void GroupView::updateGeometries()
else
{
auto cat = new VisualGroup(groupName, this);
if(fVisibility) {
cat->collapsed = fVisibility(groupName);
}
cats.insert(groupName, cat);
cat->update();
}
@@ -389,25 +384,17 @@ void GroupView::mouseReleaseEvent(QMouseEvent *event)
if (state() == ExpandingState)
{
m_pressedCategory->collapsed = false;
emit groupStateChanged(m_pressedCategory->text, false);
updateGeometries();
viewport()->update();
event->accept();
m_pressedCategory = nullptr;
setState(NoState);
return;
}
else if (state() == CollapsingState)
{
m_pressedCategory->collapsed = true;
emit groupStateChanged(m_pressedCategory->text, true);
updateGeometries();
viewport()->update();
event->accept();
m_pressedCategory = nullptr;
setState(NoState);
return;
}
}
@@ -620,7 +607,8 @@ void GroupView::dropEvent(QDropEvent *event)
const QString categoryText = category->text;
if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex()))
{
model()->setData(model()->index(row, 0), categoryText, GroupViewRoles::GroupRole);
model()->setData(model()->index(row, 0), categoryText,
GroupViewRoles::GroupRole);
event->setDropAction(Qt::MoveAction);
event->accept();
}

View File

@@ -20,7 +20,6 @@
#include <QScrollBar>
#include <QCache>
#include "VisualGroup.h"
#include <functional>
struct GroupViewRoles
{
@@ -42,11 +41,6 @@ public:
void setModel(QAbstractItemModel *model) override;
using visibilityFunction = std::function<bool(const QString &)>;
void setSourceOfGroupCollapseStatus(visibilityFunction f) {
fVisibility = f;
}
/// return geometry rectangle occupied by the specified model item
QRect geometryRect(const QModelIndex &index) const;
/// return visual rectangle occupied by the specified model item
@@ -54,7 +48,8 @@ public:
/// get the model index at the specified visual point
virtual QModelIndex indexAt(const QPoint &point) const override;
QString groupNameAt(const QPoint &point);
void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override;
void setSelection(const QRect &rect,
const QItemSelectionModel::SelectionFlags commands) override;
virtual int horizontalOffset() const override;
virtual int verticalOffset() const override;
@@ -85,7 +80,6 @@ protected slots:
signals:
void droppedURLs(QList<QUrl> urls);
void groupStateChanged(QString group, bool collapsed);
protected:
virtual bool isIndexHidden(const QModelIndex &index) const override;
@@ -109,8 +103,6 @@ private:
friend struct VisualGroup;
QList<VisualGroup *> m_groups;
visibilityFunction fVisibility;
// geometry
int m_leftMargin = 5;
int m_rightMargin = 5;

View File

@@ -43,6 +43,7 @@ int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds);
Q_INIT_RESOURCE(assets);
Q_INIT_RESOURCE(pe_dark);
Q_INIT_RESOURCE(pe_light);

View File

@@ -1,7 +1,7 @@
[Desktop Entry]
Version=1.0
Name=MultiMC
GenericName=Minecraft Launcher
GenericName=MultiMC
Comment=Free, open source launcher and instance manager for Minecraft.
Type=Application
Terminal=false

View File

@@ -1,11 +1,11 @@
Package: multimc
Version: 1.4-1
Version: 1.3-1
Architecture: all
Maintainer: Petr Mrázek <peterix@gmail.com>
Section: games
Priority: optional
Installed-Size: 75
Depends: zenity, desktop-file-utils, qt5-default, wget
Depends: zenity, desktop-file-utils, qt5-default
Recommends: openjdk-8-jre
Homepage: http://multimc.org
Description: A local install wrapper for MultiMC

View File

@@ -103,7 +103,8 @@ void AccountListPage::listChanged()
void AccountListPage::on_actionAdd_triggered()
{
addAccount(tr("Please enter your Minecraft account email and password to add your account."));
addAccount(tr("Please enter your Mojang or Minecraft account username and password to add "
"your account."));
}
void AccountListPage::on_actionRemove_triggered()

View File

@@ -10,6 +10,9 @@
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
@@ -30,6 +33,9 @@
</layout>
</widget>
<widget class="WideBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>RightToolBarArea</enum>
</attribute>

View File

@@ -0,0 +1,224 @@
/* Copyright 2015-2019 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 "PackagesPage.h"
#include "ui_PackagesPage.h"
#include <QDateTime>
#include <QSortFilterProxyModel>
#include <QRegularExpression>
#include "dialogs/ProgressDialog.h"
#include "VersionProxyModel.h"
#include "meta/Index.h"
#include "meta/VersionList.h"
#include "meta/Version.h"
#include "Env.h"
#include "MultiMC.h"
using namespace Meta;
static QString formatRequires(const VersionPtr &version)
{
QStringList lines;
auto & reqs = version->requires();
auto iter = reqs.begin();
while (iter != reqs.end())
{
auto &uid = iter->uid;
auto &version = iter->equalsVersion;
const QString readable = ENV.metadataIndex()->hasUid(uid) ? ENV.metadataIndex()->get(uid)->humanReadable() : uid;
if(!version.isEmpty())
{
lines.append(QString("%1 (%2)").arg(readable, version));
}
else
{
lines.append(QString("%1").arg(readable));
}
iter++;
}
return lines.join('\n');
}
PackagesPage::PackagesPage(QWidget *parent) :
QWidget(parent),
ui(new Ui::PackagesPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_fileProxy = new QSortFilterProxyModel(this);
m_fileProxy->setSortRole(Qt::DisplayRole);
m_fileProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
m_fileProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_fileProxy->setFilterRole(Qt::DisplayRole);
m_fileProxy->setFilterKeyColumn(0);
m_fileProxy->sort(0);
m_fileProxy->setSourceModel(ENV.metadataIndex().get());
ui->indexView->setModel(m_fileProxy);
m_filterProxy = new QSortFilterProxyModel(this);
m_filterProxy->setSortRole(VersionList::SortRole);
m_filterProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_filterProxy->setFilterRole(Qt::DisplayRole);
m_filterProxy->setFilterKeyColumn(0);
m_filterProxy->sort(0, Qt::DescendingOrder);
ui->versionsView->setModel(m_filterProxy);
m_versionProxy = new VersionProxyModel(this);
m_filterProxy->setSourceModel(m_versionProxy);
connect(ui->indexView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PackagesPage::updateCurrentVersionList);
connect(ui->versionsView->selectionModel(), &QItemSelectionModel::currentChanged, this, &PackagesPage::updateVersion);
connect(m_filterProxy, &QSortFilterProxyModel::dataChanged, this, &PackagesPage::versionListDataChanged);
updateCurrentVersionList(QModelIndex());
updateVersion();
}
PackagesPage::~PackagesPage()
{
delete ui;
}
QIcon PackagesPage::icon() const
{
return MMC->getThemedIcon("packages");
}
void PackagesPage::on_refreshIndexBtn_clicked()
{
ENV.metadataIndex()->load(Net::Mode::Online);
}
void PackagesPage::on_refreshFileBtn_clicked()
{
VersionListPtr list = ui->indexView->currentIndex().data(Index::ListPtrRole).value<VersionListPtr>();
if (!list)
{
return;
}
list->load(Net::Mode::Online);
}
void PackagesPage::on_refreshVersionBtn_clicked()
{
VersionPtr version = ui->versionsView->currentIndex().data(VersionList::VersionPtrRole).value<VersionPtr>();
if (!version)
{
return;
}
version->load(Net::Mode::Online);
}
void PackagesPage::on_fileSearchEdit_textChanged(const QString &search)
{
if (search.isEmpty())
{
m_fileProxy->setFilterFixedString(QString());
}
else
{
QStringList parts = search.split(' ');
std::transform(parts.begin(), parts.end(), parts.begin(), &QRegularExpression::escape);
m_fileProxy->setFilterRegExp(".*" + parts.join(".*") + ".*");
}
}
void PackagesPage::on_versionSearchEdit_textChanged(const QString &search)
{
if (search.isEmpty())
{
m_filterProxy->setFilterFixedString(QString());
}
else
{
QStringList parts = search.split(' ');
std::transform(parts.begin(), parts.end(), parts.begin(), &QRegularExpression::escape);
m_filterProxy->setFilterRegExp(".*" + parts.join(".*") + ".*");
}
}
void PackagesPage::updateCurrentVersionList(const QModelIndex &index)
{
if (index.isValid())
{
VersionListPtr list = index.data(Index::ListPtrRole).value<VersionListPtr>();
ui->versionsBox->setEnabled(true);
ui->refreshFileBtn->setEnabled(true);
ui->fileUidLabel->setEnabled(true);
ui->fileUid->setText(list->uid());
ui->fileNameLabel->setEnabled(true);
ui->fileName->setText(list->name());
m_versionProxy->setSourceModel(list.get());
ui->refreshFileBtn->setText(tr("Refresh %1").arg(list->humanReadable()));
list->load(Net::Mode::Offline);
}
else
{
ui->versionsBox->setEnabled(false);
ui->refreshFileBtn->setEnabled(false);
ui->fileUidLabel->setEnabled(false);
ui->fileUid->clear();
ui->fileNameLabel->setEnabled(false);
ui->fileName->clear();
m_versionProxy->setSourceModel(nullptr);
ui->refreshFileBtn->setText(tr("Refresh"));
}
}
void PackagesPage::versionListDataChanged(const QModelIndex &tl, const QModelIndex &br)
{
if (QItemSelection(tl, br).contains(ui->versionsView->currentIndex()))
{
updateVersion();
}
}
void PackagesPage::updateVersion()
{
VersionPtr version = std::dynamic_pointer_cast<Version>(
ui->versionsView->currentIndex().data(VersionList::VersionPointerRole).value<BaseVersionPtr>());
if (version)
{
ui->refreshVersionBtn->setEnabled(true);
ui->versionVersionLabel->setEnabled(true);
ui->versionVersion->setText(version->version());
ui->versionTimeLabel->setEnabled(true);
ui->versionTime->setText(version->time().toString("yyyy-MM-dd HH:mm"));
ui->versionTypeLabel->setEnabled(true);
ui->versionType->setText(version->type());
ui->versionRequiresLabel->setEnabled(true);
ui->versionRequires->setText(formatRequires(version));
ui->refreshVersionBtn->setText(tr("Refresh %1").arg(version->version()));
}
else
{
ui->refreshVersionBtn->setEnabled(false);
ui->versionVersionLabel->setEnabled(false);
ui->versionVersion->clear();
ui->versionTimeLabel->setEnabled(false);
ui->versionTime->clear();
ui->versionTypeLabel->setEnabled(false);
ui->versionType->clear();
ui->versionRequiresLabel->setEnabled(false);
ui->versionRequires->clear();
ui->refreshVersionBtn->setText(tr("Refresh"));
}
}
void PackagesPage::openedImpl()
{
ENV.metadataIndex()->load(Net::Mode::Offline);
}

View File

@@ -0,0 +1,57 @@
/* Copyright 2015-2019 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 <QWidget>
#include "pages/BasePage.h"
namespace Ui {
class PackagesPage;
}
class QSortFilterProxyModel;
class VersionProxyModel;
class PackagesPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit PackagesPage(QWidget *parent = 0);
~PackagesPage();
QString id() const override { return "packages-global"; }
QString displayName() const override { return tr("Packages"); }
QIcon icon() const override;
void openedImpl() override;
private slots:
void on_refreshIndexBtn_clicked();
void on_refreshFileBtn_clicked();
void on_refreshVersionBtn_clicked();
void on_fileSearchEdit_textChanged(const QString &search);
void on_versionSearchEdit_textChanged(const QString &search);
void updateCurrentVersionList(const QModelIndex &index);
void versionListDataChanged(const QModelIndex &tl, const QModelIndex &br);
private:
Ui::PackagesPage *ui;
QSortFilterProxyModel *m_fileProxy;
QSortFilterProxyModel *m_filterProxy;
VersionProxyModel *m_versionProxy;
void updateVersion();
};

View File

@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PackagesPage</class>
<widget class="QWidget" name="PackagesPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>636</width>
<height>621</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<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="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QGroupBox" name="versionsBox">
<property name="title">
<string>Versions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLineEdit" name="versionSearchEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="versionsView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="refreshVersionBtn">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="versionVersionLabel">
<property name="text">
<string>Version:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="versionVersion">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="versionTimeLabel">
<property name="text">
<string>Time:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="versionTime">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="versionTypeLabel">
<property name="text">
<string>Type:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="versionType">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="versionRequiresLabel">
<property name="text">
<string>Dependencies:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="versionRequires">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="versionListsBox">
<property name="title">
<string>Resources</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="fileSearchEdit">
<property name="placeholderText">
<string>Search...</string>
</property>
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QTreeView" name="indexView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QPushButton" name="refreshFileBtn">
<property name="text">
<string>Refresh</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="fileUidLabel">
<property name="text">
<string>UID:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="fileUid">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="fileNameLabel">
<property name="text">
<string>Name:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="fileName">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QPushButton" name="refreshIndexBtn">
<property name="text">
<string>Refresh Index</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -51,26 +51,6 @@ public:
}
protected:
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override {
ModFolderModel *model = qobject_cast<ModFolderModel *>(sourceModel());
if(!model) {
return false;
}
const auto &mod = model->at(source_row);
if(mod.name().contains(filterRegExp())) {
return true;
}
if(mod.description().contains(filterRegExp())) {
return true;
}
for(auto & author: mod.authors()) {
if (author.contains(filterRegExp())) {
return true;
}
}
return false;
}
bool lessThan(const QModelIndex & source_left, const QModelIndex & source_right) const override
{
ModFolderModel *model = qobject_cast<ModFolderModel *>(sourceModel());

View File

@@ -10,6 +10,9 @@
<height>501</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">

View File

@@ -10,6 +10,9 @@
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">

View File

@@ -10,6 +10,9 @@
<height>879</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">

View File

@@ -43,7 +43,6 @@
#include "icons/IconList.h"
#include "Exception.h"
#include "Version.h"
#include "DesktopServices.h"
#include <meta/Index.h>
#include <meta/VersionList.h>
@@ -206,7 +205,7 @@ void VersionPage::updateVersionControls()
bool newCraft = controlsEnabled && (minecraftVersion >= Version("1.14"));
bool oldCraft = controlsEnabled && (minecraftVersion <= Version("1.12.2"));
ui->actionInstall_Fabric->setEnabled(newCraft);
ui->actionInstall_Forge->setEnabled(true);
ui->actionInstall_Forge->setEnabled(oldCraft);
ui->actionInstall_LiteLoader->setEnabled(oldCraft);
ui->actionReload->setEnabled(true);
updateButtons();
@@ -380,7 +379,7 @@ void VersionPage::on_actionChange_version_triggered()
m_container->refreshContainer();
}
void VersionPage::on_actionDownload_All_triggered()
void VersionPage::on_actionDownload_triggered()
{
if (!MMC->accounts()->anyAccountIsValid())
{
@@ -508,16 +507,6 @@ void VersionPage::on_actionInstall_LiteLoader_triggered()
}
}
void VersionPage::on_actionLibrariesFolder_triggered()
{
DesktopServices::openDirectory(m_inst->getLocalLibraryPath(), true);
}
void VersionPage::on_actionMinecraftFolder_triggered()
{
DesktopServices::openDirectory(m_inst->gameRoot(), true);
}
void VersionPage::versionCurrent(const QModelIndex &current, const QModelIndex &previous)
{
currentIdx = current.row();

View File

@@ -64,10 +64,7 @@ private slots:
void on_actionEdit_triggered();
void on_actionInstall_mods_triggered();
void on_actionCustomize_triggered();
void on_actionDownload_All_triggered();
void on_actionMinecraftFolder_triggered();
void on_actionLibrariesFolder_triggered();
void on_actionDownload_triggered();
void updateVersionControls();

View File

@@ -10,6 +10,9 @@
<height>1091</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
@@ -95,10 +98,6 @@
<addaction name="actionAdd_to_Minecraft_jar"/>
<addaction name="actionReplace_Minecraft_jar"/>
<addaction name="actionAdd_Empty"/>
<addaction name="separator"/>
<addaction name="actionMinecraftFolder"/>
<addaction name="actionLibrariesFolder"/>
<addaction name="separator"/>
<addaction name="actionReload"/>
<addaction name="actionDownload_All"/>
</widget>
@@ -227,22 +226,6 @@
<string>Download the files needed to launch the instance now.</string>
</property>
</action>
<action name="actionMinecraftFolder">
<property name="text">
<string>Open .minecraft</string>
</property>
<property name="toolTip">
<string>Open the instance's .minecraft folder.</string>
</property>
</action>
<action name="actionLibrariesFolder">
<property name="text">
<string>Open libraries</string>
</property>
<property name="toolTip">
<string>Open the instance's local libraries folder.</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View File

@@ -10,6 +10,9 @@
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">

View File

@@ -1,29 +1,27 @@
#include "Page.h"
#include "ui_Page.h"
#include "FTBPage.h"
#include "ui_FTBPage.h"
#include <QInputDialog>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/NewInstanceDialog.h"
#include "modplatform/legacy_ftb/PackFetchTask.h"
#include "modplatform/legacy_ftb/PackInstallTask.h"
#include "modplatform/legacy_ftb/PrivatePackManager.h"
#include "ListModel.h"
#include "modplatform/ftb/FtbPackFetchTask.h"
#include "modplatform/ftb/FtbPackInstallTask.h"
#include "modplatform/ftb/FtbPrivatePackManager.h"
#include "FtbListModel.h"
namespace LegacyFTB {
Page::Page(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), dialog(dialog), ui(new Ui::Page)
FTBPage::FTBPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), dialog(dialog), ui(new Ui::FTBPage)
{
ftbFetchTask.reset(new PackFetchTask());
ftbPrivatePacks.reset(new PrivatePackManager());
ftbFetchTask.reset(new FtbPackFetchTask());
ftbPrivatePacks.reset(new FtbPrivatePackManager());
ui->setupUi(this);
{
publicFilterModel = new FilterModel(this);
publicListModel = new ListModel(this);
publicFilterModel = new FtbFilterModel(this);
publicListModel = new FtbListModel(this);
publicFilterModel->setSourceModel(publicListModel);
ui->publicPackList->setModel(publicFilterModel);
@@ -41,8 +39,8 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent)
}
{
thirdPartyFilterModel = new FilterModel(this);
thirdPartyModel = new ListModel(this);
thirdPartyFilterModel = new FtbFilterModel(this);
thirdPartyModel = new FtbListModel(this);
thirdPartyFilterModel->setSourceModel(thirdPartyModel);
ui->thirdPartyPackList->setModel(thirdPartyFilterModel);
@@ -55,8 +53,8 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent)
}
{
privateFilterModel = new FilterModel(this);
privateListModel = new ListModel(this);
privateFilterModel = new FtbFilterModel(this);
privateListModel = new FtbListModel(this);
privateFilterModel->setSourceModel(privateListModel);
ui->privatePackList->setModel(privateFilterModel);
@@ -71,17 +69,17 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent)
ui->versionSelectionBox->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->versionSelectionBox->view()->parentWidget()->setMaximumHeight(300);
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &Page::onSortingSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &Page::onVersionSelectionItemChanged);
connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FTBPage::onSortingSelectionChanged);
connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FTBPage::onVersionSelectionItemChanged);
connect(ui->publicPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPublicPackSelectionChanged);
connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onThirdPartyPackSelectionChanged);
connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &Page::onPrivatePackSelectionChanged);
connect(ui->publicPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onPublicPackSelectionChanged);
connect(ui->thirdPartyPackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onThirdPartyPackSelectionChanged);
connect(ui->privatePackList->selectionModel(), &QItemSelectionModel::currentChanged, this, &FTBPage::onPrivatePackSelectionChanged);
connect(ui->addPackBtn, &QPushButton::pressed, this, &Page::onAddPackClicked);
connect(ui->removePackBtn, &QPushButton::pressed, this, &Page::onRemovePackClicked);
connect(ui->addPackBtn, &QPushButton::pressed, this, &FTBPage::onAddPackClicked);
connect(ui->removePackBtn, &QPushButton::pressed, this, &FTBPage::onRemovePackClicked);
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &Page::onTabChanged);
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &FTBPage::onTabChanged);
// ui->modpackInfo->setOpenExternalLinks(true);
@@ -92,25 +90,25 @@ Page::Page(NewInstanceDialog* dialog, QWidget *parent)
onTabChanged(ui->tabWidget->currentIndex());
}
Page::~Page()
FTBPage::~FTBPage()
{
delete ui;
}
bool Page::shouldDisplay() const
bool FTBPage::shouldDisplay() const
{
return true;
}
void Page::openedImpl()
void FTBPage::openedImpl()
{
if(!initialized)
{
connect(ftbFetchTask.get(), &PackFetchTask::finished, this, &Page::ftbPackDataDownloadSuccessfully);
connect(ftbFetchTask.get(), &PackFetchTask::failed, this, &Page::ftbPackDataDownloadFailed);
connect(ftbFetchTask.get(), &FtbPackFetchTask::finished, this, &FTBPage::ftbPackDataDownloadSuccessfully);
connect(ftbFetchTask.get(), &FtbPackFetchTask::failed, this, &FTBPage::ftbPackDataDownloadFailed);
connect(ftbFetchTask.get(), &PackFetchTask::privateFileDownloadFinished, this, &Page::ftbPrivatePackDataDownloadSuccessfully);
connect(ftbFetchTask.get(), &PackFetchTask::privateFileDownloadFailed, this, &Page::ftbPrivatePackDataDownloadFailed);
connect(ftbFetchTask.get(), &FtbPackFetchTask::privateFileDownloadFinished, this, &FTBPage::ftbPrivatePackDataDownloadSuccessfully);
connect(ftbFetchTask.get(), &FtbPackFetchTask::privateFileDownloadFailed, this, &FTBPage::ftbPrivatePackDataDownloadFailed);
ftbFetchTask->fetch();
ftbPrivatePacks->load();
@@ -120,13 +118,13 @@ void Page::openedImpl()
suggestCurrent();
}
void Page::suggestCurrent()
void FTBPage::suggestCurrent()
{
if(isOpened)
{
if(!selected.broken)
{
dialog->setSuggestedPack(selected.name, new PackInstallTask(selected, selectedVersion));
dialog->setSuggestedPack(selected.name, new FtbPackInstallTask(selected, selectedVersion));
QString editedLogoName;
if(selected.logo.toLower().startsWith("ftb"))
{
@@ -139,21 +137,21 @@ void Page::suggestCurrent()
editedLogoName = editedLogoName.left(editedLogoName.lastIndexOf(".png"));
if(selected.type == PackType::Public)
if(selected.type == FtbPackType::Public)
{
publicListModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo, editedLogoName);
});
}
else if (selected.type == PackType::ThirdParty)
else if (selected.type == FtbPackType::ThirdParty)
{
thirdPartyModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
dialog->setSuggestedIconFromFile(logo, editedLogoName);
});
}
else if (selected.type == PackType::Private)
else if (selected.type == FtbPackType::Private)
{
privateListModel->getLogo(selected.logo, [this, editedLogoName](QString logo)
{
@@ -168,23 +166,23 @@ void Page::suggestCurrent()
}
}
void Page::ftbPackDataDownloadSuccessfully(ModpackList publicPacks, ModpackList thirdPartyPacks)
void FTBPage::ftbPackDataDownloadSuccessfully(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks)
{
publicListModel->fill(publicPacks);
thirdPartyModel->fill(thirdPartyPacks);
}
void Page::ftbPackDataDownloadFailed(QString reason)
void FTBPage::ftbPackDataDownloadFailed(QString reason)
{
//TODO: Display the error
}
void Page::ftbPrivatePackDataDownloadSuccessfully(Modpack pack)
void FTBPage::ftbPrivatePackDataDownloadSuccessfully(FtbModpack pack)
{
privateListModel->addPack(pack);
}
void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
void FTBPage::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
{
auto reply = QMessageBox::question(
this,
@@ -197,40 +195,40 @@ void Page::ftbPrivatePackDataDownloadFailed(QString reason, QString packCode)
}
}
void Page::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
void FTBPage::onPublicPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
Modpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value<Modpack>();
FtbModpack selectedPack = publicFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
onPackSelectionChanged(&selectedPack);
}
void Page::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev)
void FTBPage::onThirdPartyPackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
Modpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value<Modpack>();
FtbModpack selectedPack = thirdPartyFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
onPackSelectionChanged(&selectedPack);
}
void Page::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev)
void FTBPage::onPrivatePackSelectionChanged(QModelIndex now, QModelIndex prev)
{
if(!now.isValid())
{
onPackSelectionChanged();
return;
}
Modpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value<Modpack>();
FtbModpack selectedPack = privateFilterModel->data(now, Qt::UserRole).value<FtbModpack>();
onPackSelectionChanged(&selectedPack);
}
void Page::onPackSelectionChanged(Modpack* pack)
void FTBPage::onPackSelectionChanged(FtbModpack* pack)
{
ui->versionSelectionBox->clear();
if(pack)
@@ -268,7 +266,7 @@ void Page::onPackSelectionChanged(Modpack* pack)
suggestCurrent();
}
void Page::onVersionSelectionItemChanged(QString data)
void FTBPage::onVersionSelectionItemChanged(QString data)
{
if(data.isNull() || data.isEmpty())
{
@@ -280,15 +278,15 @@ void Page::onVersionSelectionItemChanged(QString data)
suggestCurrent();
}
void Page::onSortingSelectionChanged(QString data)
void FTBPage::onSortingSelectionChanged(QString data)
{
FilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data);
FtbFilterModel::Sorting toSet = publicFilterModel->getAvailableSortings().value(data);
publicFilterModel->setSorting(toSet);
thirdPartyFilterModel->setSorting(toSet);
privateFilterModel->setSorting(toSet);
}
void Page::onTabChanged(int tab)
void FTBPage::onTabChanged(int tab)
{
if(tab == 1)
{
@@ -313,7 +311,7 @@ void Page::onTabChanged(int tab)
QModelIndex idx = currentList->currentIndex();
if(idx.isValid())
{
auto pack = currentModel->data(idx, Qt::UserRole).value<Modpack>();
auto pack = currentModel->data(idx, Qt::UserRole).value<FtbModpack>();
onPackSelectionChanged(&pack);
}
else
@@ -322,7 +320,7 @@ void Page::onTabChanged(int tab)
}
}
void Page::onAddPackClicked()
void FTBPage::onAddPackClicked()
{
bool ok;
QString text = QInputDialog::getText(
@@ -340,7 +338,7 @@ void Page::onAddPackClicked()
}
}
void Page::onRemovePackClicked()
void FTBPage::onRemovePackClicked()
{
auto index = ui->privatePackList->currentIndex();
if(!index.isValid())
@@ -348,7 +346,7 @@ void Page::onRemovePackClicked()
return;
}
auto row = index.row();
Modpack pack = privateListModel->at(row);
FtbModpack pack = privateListModel->at(row);
auto answer = QMessageBox::question(
this,
tr("Remove pack"),
@@ -364,5 +362,3 @@ void Page::onRemovePackClicked()
privateListModel->remove(row);
onPackSelectionChanged();
}
}

View File

@@ -22,32 +22,29 @@
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
#include "modplatform/legacy_ftb/PackHelpers.h"
#include "modplatform/legacy_ftb/PackFetchTask.h"
#include "modplatform/ftb/PackHelpers.h"
#include "modplatform/ftb/FtbPackFetchTask.h"
#include "QObjectPtr.h"
class NewInstanceDialog;
namespace LegacyFTB {
namespace Ui
{
class Page;
class FTBPage;
}
class ListModel;
class FilterModel;
class PrivatePackListModel;
class PrivatePackFilterModel;
class PrivatePackManager;
class FtbListModel;
class FtbFilterModel;
class NewInstanceDialog;
class FtbPrivatePackListModel;
class FtbPrivatePackFilterModel;
class FtbPrivatePackManager;
class Page : public QWidget, public BasePage
class FTBPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit Page(NewInstanceDialog * dialog, QWidget *parent = 0);
virtual ~Page();
explicit FTBPage(NewInstanceDialog * dialog, QWidget *parent = 0);
virtual ~FTBPage();
QString displayName() const override
{
return tr("FTB Legacy");
@@ -58,7 +55,7 @@ public:
}
QString id() const override
{
return "legacy_ftb";
return "ftb";
}
QString helpPage() const override
{
@@ -69,13 +66,13 @@ public:
private:
void suggestCurrent();
void onPackSelectionChanged(Modpack *pack = nullptr);
void onPackSelectionChanged(FtbModpack *pack = nullptr);
private slots:
void ftbPackDataDownloadSuccessfully(ModpackList publicPacks, ModpackList thirdPartyPacks);
void ftbPackDataDownloadSuccessfully(FtbModpackList publicPacks, FtbModpackList thirdPartyPacks);
void ftbPackDataDownloadFailed(QString reason);
void ftbPrivatePackDataDownloadSuccessfully(Modpack pack);
void ftbPrivatePackDataDownloadSuccessfully(FtbModpack pack);
void ftbPrivatePackDataDownloadFailed(QString reason, QString packCode);
void onSortingSelectionChanged(QString data);
@@ -91,29 +88,27 @@ private slots:
void onRemovePackClicked();
private:
FilterModel* currentModel = nullptr;
FtbFilterModel* currentModel = nullptr;
QTreeView* currentList = nullptr;
QTextBrowser* currentModpackInfo = nullptr;
bool initialized = false;
Modpack selected;
FtbModpack selected;
QString selectedVersion;
ListModel* publicListModel = nullptr;
FilterModel* publicFilterModel = nullptr;
FtbListModel* publicListModel = nullptr;
FtbFilterModel* publicFilterModel = nullptr;
ListModel *thirdPartyModel = nullptr;
FilterModel *thirdPartyFilterModel = nullptr;
FtbListModel *thirdPartyModel = nullptr;
FtbFilterModel *thirdPartyFilterModel = nullptr;
ListModel *privateListModel = nullptr;
FilterModel *privateFilterModel = nullptr;
FtbListModel *privateListModel = nullptr;
FtbFilterModel *privateFilterModel = nullptr;
unique_qobject_ptr<PackFetchTask> ftbFetchTask;
std::unique_ptr<PrivatePackManager> ftbPrivatePacks;
unique_qobject_ptr<FtbPackFetchTask> ftbFetchTask;
std::unique_ptr<FtbPrivatePackManager> ftbPrivatePacks;
NewInstanceDialog* dialog = nullptr;
Ui::Page *ui = nullptr;
Ui::FTBPage *ui = nullptr;
};
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LegacyFTB::Page</class>
<widget class="QWidget" name="LegacyFTB::Page">
<class>FTBPage</class>
<widget class="QWidget" name="FTBPage">
<property name="geometry">
<rect>
<x>0</x>

View File

@@ -1,4 +1,4 @@
#include "ListModel.h"
#include "FtbListModel.h"
#include "MultiMC.h"
#include <MMCStrings.h>
@@ -12,19 +12,17 @@
#include "net/URLConstants.h"
namespace LegacyFTB {
FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent)
FtbFilterModel::FtbFilterModel(QObject *parent) : QSortFilterProxyModel(parent)
{
currentSorting = Sorting::ByGameVersion;
sortings.insert(tr("Sort by name"), Sorting::ByName);
sortings.insert(tr("Sort by game version"), Sorting::ByGameVersion);
}
bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
bool FtbFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<Modpack>();
Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<Modpack>();
FtbModpack leftPack = sourceModel()->data(left, Qt::UserRole).value<FtbModpack>();
FtbModpack rightPack = sourceModel()->data(right, Qt::UserRole).value<FtbModpack>();
if(currentSorting == Sorting::ByGameVersion) {
Version lv(leftPack.mcVersion);
@@ -40,66 +38,66 @@ bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) co
return true;
}
bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
bool FtbFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
return true;
}
const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings()
const QMap<QString, FtbFilterModel::Sorting> FtbFilterModel::getAvailableSortings()
{
return sortings;
}
QString FilterModel::translateCurrentSorting()
QString FtbFilterModel::translateCurrentSorting()
{
return sortings.key(currentSorting);
}
void FilterModel::setSorting(Sorting s)
void FtbFilterModel::setSorting(Sorting s)
{
currentSorting = s;
invalidate();
}
FilterModel::Sorting FilterModel::getCurrentSorting()
FtbFilterModel::Sorting FtbFilterModel::getCurrentSorting()
{
return currentSorting;
}
ListModel::ListModel(QObject *parent) : QAbstractListModel(parent)
FtbListModel::FtbListModel(QObject *parent) : QAbstractListModel(parent)
{
}
ListModel::~ListModel()
FtbListModel::~FtbListModel()
{
}
QString ListModel::translatePackType(PackType type) const
QString FtbListModel::translatePackType(FtbPackType type) const
{
switch(type)
{
case PackType::Public:
case FtbPackType::Public:
return tr("Public Modpack");
case PackType::ThirdParty:
case FtbPackType::ThirdParty:
return tr("Third Party Modpack");
case PackType::Private:
case FtbPackType::Private:
return tr("Private Modpack");
}
qWarning() << "Unknown FTB modpack type:" << int(type);
return QString();
}
int ListModel::rowCount(const QModelIndex &parent) const
int FtbListModel::rowCount(const QModelIndex &parent) const
{
return modpacks.size();
}
int ListModel::columnCount(const QModelIndex &parent) const
int FtbListModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QVariant ListModel::data(const QModelIndex &index, int role) const
QVariant FtbListModel::data(const QModelIndex &index, int role) const
{
int pos = index.row();
if(pos >= modpacks.size() || pos < 0 || !index.isValid())
@@ -107,7 +105,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
return QString("INVALID INDEX %1").arg(pos);
}
Modpack pack = modpacks.at(pos);
FtbModpack pack = modpacks.at(pos);
if(role == Qt::DisplayRole)
{
return pack.name + "\n" + translatePackType(pack.type);
@@ -131,7 +129,7 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
return (m_logoMap.value(pack.logo));
}
QIcon icon = MMC->getThemedIcon("screenshot-placeholder");
((ListModel *)this)->requestLogo(pack.logo);
((FtbListModel *)this)->requestLogo(pack.logo);
return icon;
}
else if(role == Qt::TextColorRole)
@@ -158,33 +156,33 @@ QVariant ListModel::data(const QModelIndex &index, int role) const
return QVariant();
}
void ListModel::fill(ModpackList modpacks)
void FtbListModel::fill(FtbModpackList modpacks)
{
beginResetModel();
this->modpacks = modpacks;
endResetModel();
}
void ListModel::addPack(Modpack modpack)
void FtbListModel::addPack(FtbModpack modpack)
{
beginResetModel();
this->modpacks.append(modpack);
endResetModel();
}
void ListModel::clear()
void FtbListModel::clear()
{
beginResetModel();
modpacks.clear();
endResetModel();
}
Modpack ListModel::at(int row)
FtbModpack FtbListModel::at(int row)
{
return modpacks.at(row);
}
void ListModel::remove(int row)
void FtbListModel::remove(int row)
{
if(row < 0 || row >= modpacks.size())
{
@@ -196,20 +194,20 @@ void ListModel::remove(int row)
endRemoveRows();
}
void ListModel::logoLoaded(QString logo, QIcon out)
void FtbListModel::logoLoaded(QString logo, QIcon out)
{
m_loadingLogos.removeAll(logo);
m_logoMap.insert(logo, out);
emit dataChanged(createIndex(0, 0), createIndex(1, 0));
}
void ListModel::logoFailed(QString logo)
void FtbListModel::logoFailed(QString logo)
{
m_failedLogos.append(logo);
m_loadingLogos.removeAll(logo);
}
void ListModel::requestLogo(QString file)
void FtbListModel::requestLogo(QString file)
{
if(m_loadingLogos.contains(file) || m_failedLogos.contains(file))
{
@@ -218,7 +216,7 @@ void ListModel::requestLogo(QString file)
MetaEntryPtr entry = ENV.metacache()->resolveEntry("FTBPacks", QString("logos/%1").arg(file.section(".", 0, 0)));
NetJob *job = new NetJob(QString("FTB Icon Download for %1").arg(file));
job->addNetAction(Net::Download::makeCached(QUrl(QString(URLConstants::LEGACY_FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
job->addNetAction(Net::Download::makeCached(QUrl(QString(URLConstants::FTB_CDN_BASE_URL + "static/%1").arg(file)), entry));
auto fullPath = entry->getFullPath();
QObject::connect(job, &NetJob::finished, this, [this, file, fullPath]
@@ -240,7 +238,7 @@ void ListModel::requestLogo(QString file)
m_loadingLogos.append(file);
}
void ListModel::getLogo(const QString &logo, LogoCallback callback)
void FtbListModel::getLogo(const QString &logo, LogoCallback callback)
{
if(m_logoMap.contains(logo))
{
@@ -252,9 +250,7 @@ void ListModel::getLogo(const QString &logo, LogoCallback callback)
}
}
Qt::ItemFlags ListModel::flags(const QModelIndex &index) const
Qt::ItemFlags FtbListModel::flags(const QModelIndex &index) const
{
return QAbstractListModel::flags(index);
}
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <modplatform/legacy_ftb/PackHelpers.h>
#include <modplatform/ftb/PackHelpers.h>
#include <RWStorage.h>
#include <QAbstractListModel>
@@ -11,16 +11,14 @@
#include <functional>
namespace LegacyFTB {
typedef QMap<QString, QIcon> FTBLogoMap;
typedef QMap<QString, QIcon> FtbLogoMap;
typedef std::function<void(QString)> LogoCallback;
class FilterModel : public QSortFilterProxyModel
class FtbFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
FilterModel(QObject* parent = Q_NULLPTR);
FtbFilterModel(QObject* parent = Q_NULLPTR);
enum Sorting {
ByName,
ByGameVersion
@@ -40,18 +38,18 @@ private:
};
class ListModel : public QAbstractListModel
class FtbListModel : public QAbstractListModel
{
Q_OBJECT
private:
ModpackList modpacks;
FtbModpackList modpacks;
QStringList m_failedLogos;
QStringList m_loadingLogos;
FTBLogoMap m_logoMap;
FtbLogoMap m_logoMap;
QMap<QString, LogoCallback> waitingCallbacks;
void requestLogo(QString file);
QString translatePackType(PackType type) const;
QString translatePackType(FtbPackType type) const;
private slots:
@@ -59,20 +57,18 @@ private slots:
void logoLoaded(QString logo, QIcon out);
public:
ListModel(QObject *parent);
~ListModel();
FtbListModel(QObject *parent);
~FtbListModel();
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
void fill(ModpackList modpacks);
void addPack(Modpack modpack);
void fill(FtbModpackList modpacks);
void addPack(FtbModpack modpack);
void clear();
void remove(int row);
Modpack at(int row);
FtbModpack at(int row);
void getLogo(const QString &logo, LogoCallback callback);
};
}

View File

@@ -0,0 +1,26 @@
#include "TechnicPage.h"
#include "ui_TechnicPage.h"
#include "MultiMC.h"
#include "dialogs/NewInstanceDialog.h"
TechnicPage::TechnicPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TechnicPage), dialog(dialog)
{
ui->setupUi(this);
}
TechnicPage::~TechnicPage()
{
delete ui;
}
bool TechnicPage::shouldDisplay() const
{
return true;
}
void TechnicPage::openedImpl()
{
dialog->setSuggestedPack();
}

View File

@@ -0,0 +1,61 @@
/* Copyright 2013-2019 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 <QWidget>
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
namespace Ui
{
class TechnicPage;
}
class NewInstanceDialog;
class TechnicPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit TechnicPage(NewInstanceDialog* dialog, QWidget *parent = 0);
virtual ~TechnicPage();
virtual QString displayName() const override
{
return tr("Technic");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("technic");
}
virtual QString id() const override
{
return "technic";
}
virtual QString helpPage() const override
{
return "Technic-platform";
}
virtual bool shouldDisplay() const override;
void openedImpl() override;
private:
Ui::TechnicPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
};

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TechnicPage</class>
<widget class="QWidget" name="TechnicPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>405</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<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>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#ffc000</string>
</property>
<property name="text">
<string notr="true">UNDER</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true"/>
</property>
<property name="pixmap">
<pixmap resource="../../resources/assets/assets.qrc">:/assets/underconstruction</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">color:#7ca32b</string>
</property>
<property name="text">
<string notr="true">CONSTRUCTION</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources>
<include location="../../resources/assets/assets.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,60 @@
#include "TwitchPage.h"
#include "ui_TwitchPage.h"
#include "MultiMC.h"
#include "dialogs/NewInstanceDialog.h"
#include <InstanceImportTask.h>
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
{
ui->setupUi(this);
connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck);
}
TwitchPage::~TwitchPage()
{
delete ui;
}
bool TwitchPage::shouldDisplay() const
{
return true;
}
void TwitchPage::openedImpl()
{
dialog->setSuggestedPack();
}
void TwitchPage::triggerCheck(bool)
{
if(m_modIdResolver) {
return;
}
auto task = new Flame::UrlResolvingTask(ui->lineEdit->text());
connect(task, &Task::finished, this, &TwitchPage::checkDone);
m_modIdResolver.reset(task);
task->start();
}
void TwitchPage::setUrl(const QString& url)
{
ui->lineEdit->setText(url);
triggerCheck(true);
}
void TwitchPage::checkDone()
{
auto result = m_modIdResolver->getResults();
auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
if(result.resolved && result.type == Flame::File::Type::Modpack) {
ui->twitchLabel->setText(formatted);
QFileInfo fi(result.fileName);
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
} else {
ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
dialog->setSuggestedPack();
}
m_modIdResolver.reset();
}

View File

@@ -0,0 +1,69 @@
/* Copyright 2013-2019 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 <QWidget>
#include "pages/BasePage.h"
#include <MultiMC.h>
#include "tasks/Task.h"
#include "modplatform/flame/UrlResolvingTask.h"
namespace Ui
{
class TwitchPage;
}
class NewInstanceDialog;
class TwitchPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0);
virtual ~TwitchPage();
virtual QString displayName() const override
{
return tr("Twitch URL");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("twitch");
}
virtual QString id() const override
{
return "twitch";
}
virtual QString helpPage() const override
{
return "Twitch-platform";
}
virtual bool shouldDisplay() const override;
void openedImpl() override;
void setUrl(const QString & url);
private slots:
void triggerCheck(bool checked);
void checkDone();
private:
Ui::TwitchPage *ui = nullptr;
NewInstanceDialog* dialog = nullptr;
shared_qobject_ptr<Flame::UrlResolvingTask> m_modIdResolver;
};

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TwitchPage</class>
<widget class="QWidget" name="TwitchPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>405</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QLineEdit" name="lineEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Twitch URL:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QLabel" name="twitchLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>40</pointsize>
</font>
</property>
<property name="pixmap">
<pixmap resource="../../resources/assets/assets.qrc">:/assets/deadglitch</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="checkButton">
<property name="text">
<string>Check</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lineEdit</tabstop>
<tabstop>checkButton</tabstop>
</tabstops>
<resources>
<include location="../../resources/assets/assets.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,7 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/assets">
<file alias="underconstruction">underconstruction.png</file>
<file alias="deadglitch">deadglitch.svg</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,66 @@
<?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"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
class="tw-svg__asset tw-svg__asset--deadglitch tw-svg__asset--inherit"
width="92px"
height="96px"
version="1.1"
viewBox="0 0 30 30"
x="0px"
y="0px"
id="svg8"
sodipodi:docname="deadglitch.svg"
inkscape:version="0.92.2 2405546, 2018-03-11">
<metadata
id="metadata14">
<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>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1353"
inkscape:window-height="828"
id="namedview10"
showgrid="false"
inkscape:zoom="4.9166667"
inkscape:cx="44.285787"
inkscape:cy="52.833458"
inkscape:window-x="2958"
inkscape:window-y="702"
inkscape:window-maximized="0"
inkscape:current-layer="svg8" />
<g
id="g6"
style="fill:#898395;fill-opacity:1">
<path
d="M26,17.4589613 L26,3 L4,3 L4,22.0601057 L10.0032868,22.0601057 L10.0032868,26 L14.0004537,22.0601057 L21.3322933,22.0601057 L26,17.4589613 L26,17.4589613 Z M21.0896458,26.0850335 L15.1583403,26.0850335 L11.2051771,30 L7.24798611,30 L7.24798611,26.0850335 L0,26.0850335 L0,5.21746493 L1.97773958,0 L29,0 L29,18.2620736 L21.0896458,26.0850335 L21.0896458,26.0850335 Z"
id="path2"
style="fill:#898395;fill-opacity:1" />
<path
d="M20.8587626,12.1710126 L22.4052753,13.7175252 L23.7175252,12.4052753 L22.1710126,10.8587626 L23.7175252,9.31224999 L22.4052753,8 L20.8587626,9.54651264 L19.31225,8 L18,9.31224999 L19.5465126,10.8587626 L18,12.4052753 L19.31225,13.7175252 L20.8587626,12.1710126 Z M11.8587626,12.1710126 L13.4052753,13.7175252 L14.7175252,12.4052753 L13.1710126,10.8587626 L14.7175252,9.31224999 L13.4052753,8 L11.8587626,9.54651264 L10.31225,8 L9,9.31224999 L10.5465126,10.8587626 L9,12.4052753 L10.31225,13.7175252 L11.8587626,12.1710126 Z"
id="path4"
style="fill:#898395;fill-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -310,6 +310,5 @@
<file>50x50/instances/enderman.png</file>
<file>scalable/instances/fox.svg</file>
<file>scalable/instances/bee.svg</file>
</qresource>
</RCC>

View File

@@ -1,159 +0,0 @@
<?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"
version="1.1"
viewBox="0 0 25.399999 25.4"
height="96"
width="96">
<g transform="translate(-33.928467,-255.46043)">
<g transform="rotate(-9.9635201,-96.932986,622.95265)">
<path style="fill:#f1f2e0;fill-opacity:1;fill-rule:evenodd;stroke:#999999;stroke-width:0.28753757px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 101.98199,286.42583 -1.1502,0.57512 1.14992,0.5755 2.30058,1.14996 1.15019,-0.57514 -2.30058,-1.14995 z m 2.3003,2.30058 -1.1502,0.57512 1.15002,0.57493 1.15019,-0.57513 z m -1.8e-4,1.15005 -1.1502,0.57512 1.15057,0.57502 1.15019,-0.57512 z m 3.7e-4,1.15014 -1.15019,0.57514 -1.1502,0.57512 1.15001,0.57493 1.1502,-0.57513 1.15019,-0.57512 z m -2.30039,1.15026 -1.15001,-0.57494 -1.150566,-0.57502 -1.150193,0.57512 1.150565,0.57502 1.150014,0.57494 z m -2.300576,-1.14996 1.150196,-0.57513 -1.150014,-0.57493 -1.150097,0.57456 z m -1.149915,-0.5755 1.150193,-0.57512 -1.150012,-0.57492 -1.150194,0.57512 z m 1.81e-4,-1.15004 1.150194,-0.57513 -1.150567,-0.57503 -1.150193,0.57512 z m -3.73e-4,-1.15016 1.150194,-0.57512 1.150189,-0.57513 -1.150008,-0.57492 -1.150193,0.57512 -1.150194,0.57512 z m 1.150567,0.57503 1.149916,0.57548 1.15001,0.57494 1.15019,-0.57512 -1.15001,-0.57494 -1.15047,-0.57558 z" />
</g>
<g style="fill:none;stroke:#999999" transform="matrix(1.0012918,0.26829532,-0.26829532,1.0012918,86.112205,-31.978257)">
<path style="stroke:#999999;stroke-width:1.03661346px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 85.501953 51.128906 C 82.473901 51.748417 79.445938 52.368362 76.417969 52.988281 L 79.886719 56.064453 C 82.914656 55.444381 85.942682 54.824742 88.970703 54.205078 L 85.501953 51.128906 z "
transform="matrix(0.24654113,-0.06606049,0.06606049,0.24654113,23.141685,280.86706)" />
<path style="stroke:#999999;stroke-width:1.03661346px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 79.636719 40.972656 L 75.095703 41.902344 C 78.563735 44.978675 82.032556 48.054115 85.501953 51.128906 L 90.042969 50.201172 L 79.636719 40.972656 z "
transform="matrix(0.24654113,-0.06606049,0.06606049,0.24654113,23.141685,280.86706)" />
</g>
<path style="fill:#fed668" d="m 41.865965,262.07502 -4.233333,2.11667 7.408333,3.70417 4.233334,-2.11667 z" />
<path style="fill:#0a0707" d="m 50.332633,272.39378 v 1.32291 l 1.058333,0.52917 v -1.32292 z" />
<path style="fill:#422117" d="m 50.332633,273.7167 v 1.32291 l 1.058333,0.52917 v -1.32292 z" />
<path style="fill:#0a0707" d="m 48.215966,273.45211 v 1.32291 l 1.058333,0.52917 v -1.32292 z" />
<path style="fill:#422117" d="m 48.215966,274.77503 v 1.32291 l 1.058333,0.52917 v -1.32292 z" />
<path style="fill:#422117" d="m 45.040966,275.3042 v 1.32291 l 1.058333,0.52917 v -1.32292 z" />
<path style="fill:#78621d" d="m 45.040965,277.15626 4.233334,-2.11665 v -9.26042 l -4.233334,2.11667 z" />
<path style="fill:#1d0c08" d="m 50.332632,265.25002 -1.058333,0.52917 v 9.26042 l 1.058333,-0.52917 z" />
<path style="fill:#1d0c08" d="m 52.449299,264.19169 -1.058333,0.52917 v 9.26042 l 1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 38.690965,263.66252 1.058334,0.52917 1.058333,-0.52917 1.058333,0.52917 2.116667,-1.05834 -2.116667,-1.05833 z" />
<path style="fill:#fed668" d="m 42.924299,261.54586 7.408334,3.70417 1.058333,-0.52917 -7.408334,-3.70417 z" />
<path style="fill:#e4ae3b" d="m 40.807633,262.60419 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#fed668" d="m 45.040966,260.48752 7.408333,3.70417 1.058333,-0.52917 -7.408333,-3.70417 z" />
<path style="fill:#5f3225" d="m 42.924299,261.54585 -1.058334,0.52917 7.408333,3.70417 1.058334,-0.52917 z" />
<path style="fill:#e4ae3b" d="m 42.9243,261.54585 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 45.040965,260.48752 -1.058333,0.52917 7.408333,3.70416 1.058333,-0.52916 z" />
<path style="fill:#e4ae3b" d="m 45.040966,260.48752 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 46.099299,259.95835 7.408333,3.70417 2.116666,-1.05833 -7.408333,-3.70417 z" />
<path style="fill:#552e22" d="m 47.157633,259.42919 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 43.982633,262.07502 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 46.099299,261.01669 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 40.807633,264.72086 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#e4ae3b" d="m 45.040967,262.60419 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#552e22" d="m 49.2743,260.48753 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 48.215966,262.07502 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 41.865966,266.30836 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#552e22" d="m 50.332633,262.07502 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 46.099299,265.25002 -1.058334,0.52917 1.058334,0.52916 -2.116667,1.05834 1.058333,0.52916 3.175,-1.5875 z" />
<path style="fill:#edc343" d="m 48.215967,264.19169 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 50.332633,263.13336 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#e4ae3b" d="m 47.157633,265.77919 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#e4ae3b" d="m 49.2743,264.72085 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#e4ae3b" d="m 51.390966,263.66252 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#edc343" d="m 38.690966,264.72086 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#ac8d2e" d="m 37.632633,273.45211 7.408333,3.70415 v -9.2604 l -7.408333,-3.70417 z" />
<path style="fill:#12121a" d="m 37.632633,268.16044 2.116666,1.05834 v 3.96875 l -2.116666,-1.05834 z" />
<path style="fill:#78621d" d="m 50.332633,265.25002 v 9.26041 l 1.058333,-0.52916 v -9.26042 z" />
<path style="fill:#78621d" d="m 52.449299,264.19169 v 9.26041 l 1.058333,-0.52916 v -9.26042 z" />
<path style="fill:#27120b" d="m 49.274299,265.77919 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#09090e" d="m 45.040966,271.86461 1.058333,-0.52917 v 3.96875 l -1.058333,0.52917 -2.116667,-1.05834 v -3.96875 z" />
<path style="fill:#150704" d="m 49.2743,271.07085 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#050303" d="m 49.2743,273.71669 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#150704" d="m 51.390966,268.68961 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#050303" d="m 51.390966,271.33544 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#2c140d" d="m 51.390966,264.72086 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#1d0c08" d="m 55.624299,262.60419 -2.116666,1.05833 v 9.26042 l 2.116666,-1.05833 z" />
<path style="fill:#150704" d="m 53.507633,268.95419 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#050303" d="m 53.507633,270.2771 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#2c140d" d="m 53.507633,263.66252 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#27120b" d="m 54.565966,263.13335 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#150704" d="m 54.565966,269.74794 v 2.64583 l 1.058333,-0.52916 v -2.64584 z" />
<path style="fill:#27120b" d="m 54.565966,267.10211 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#633f19" d="m 45.040965,277.15626 3.175,-1.58749 v -1.32291 l -3.175,1.5875 z" />
<path style="fill:#55300a" d="m 48.215966,274.24586 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#694520" d="m 48.215966,272.92294 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#735619" d="m 48.215966,271.60002 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#735619" d="m 47.157633,273.45211 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#131016" d="m 45.040966,271.86461 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#131016" d="m 45.040966,274.51044 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#694520" d="m 45.040966,270.54169 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#735619" d="m 46.0993,270.01252 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#816c31" d="m 45.040966,267.89586 v 1.32291 l 1.058333,-0.52916 v 1.32291 l 1.058334,-0.52916 v -1.32292 l 2.116666,-1.05833 v -1.32292 z" />
<path style="fill:#49250c" d="m 50.332633,273.18752 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#633f19" d="m 50.332633,271.86461 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#735619" d="m 50.332633,269.21878 v 2.64582 l 1.058333,-0.52916 v -2.64583 z" />
<path style="fill:#816c31" d="m 50.332633,265.25002 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#49250c" d="m 52.449299,270.80627 10e-7,2.64583 1.058333,-0.52916 -10e-7,-2.64584 z" />
<path style="fill:#55300a" d="m 52.4493,269.48336 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#694520" d="m 52.4493,268.16044 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#755719" d="m 52.4493,266.83752 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#816c31" d="m 52.4493,264.19169 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#b89b49" d="m 37.632632,265.51461 2.116667,1.05833 v -1.32292 l -2.116667,-1.05833 z" />
<path style="fill:#b89b49" d="m 40.807633,267.10211 2.116667,1.05833 v -1.32292 l -2.116667,-1.05833 z" />
<path style="fill:#b89b49" d="m 43.982633,270.01253 1.058333,0.52916 v -2.64584 l -1.058333,-0.52916 z" />
<path style="fill:#966531" d="m 43.982632,271.33545 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#a57d28" d="m 42.924299,270.80627 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#b89b49" d="m 41.865965,270.27712 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#966531" d="m 43.982632,271.33545 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#a57d28" d="m 39.749299,273.18753 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#a57d28" d="m 41.865965,274.24587 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#a57d28" d="m 40.807633,275.03961 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#8e5c28" d="m 43.982633,276.62711 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#966531" d="m 41.865966,275.56877 2.116667,1.05833 v -1.32292 l -2.116667,-1.05833 z" />
<path style="fill:#966531" d="m 38.690966,273.98128 2.116667,1.05833 v -1.32292 l -2.116667,-1.05833 z" />
<path style="fill:#8e5c28" d="m 37.632632,273.45212 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#956531" d="m 37.632633,268.16044 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#b89b49" d="m 39.749299,267.89587 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 38.690966,267.36669 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 42.924299,269.48337 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#589197" d="m 42.924299,272.12919 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#589197" d="m 38.690966,270.01252 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#12121a" d="m 43.982633,272.65836 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#12121a" d="m 42.924299,273.45211 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#12121a" d="m 43.982633,275.30419 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 43.982633,273.98127 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 42.924299,274.77503 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 38.690966,272.65836 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#1f1c25" d="m 37.632633,270.80627 1.058334,0.52916 v -1.32292 l -1.058334,-0.52916 z" />
<path style="fill:#131016" d="m 38.690966,267.10211 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#131016" d="m 36.574299,266.83752 v 1.32291 l 2.116667,-1.05832 v -1.32292 z" />
<path style="fill:#131016" d="m 41.865966,268.68961 v 1.32291 l 1.058333,-0.52916 v -1.32292 z" />
<path style="fill:#131016" d="m 39.749299,268.42502 v 1.32291 l 2.116667,-1.05832 v -1.32292 z" />
<g transform="matrix(1.0012918,0.26829532,-0.26829532,1.0012918,86.112205,-31.978257)">
<path style="fill:#f1f2e0" d="m 38.073986,286.23688 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 37.015652,286.76606 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 37.015652,287.82438 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 38.073986,288.35355 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 40.190652,286.23689 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 42.307319,286.23688 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 42.307319,287.29521 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 43.365653,287.82437 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 43.365653,288.88271 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 41.248986,288.88271 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 40.190653,288.35354 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 45.482319,288.88271 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 46.540653,288.35355 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 46.540653,287.29521 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 44.423986,286.23688 2.116667,1.05833 1.058333,-0.52917 -2.116667,-1.05833 z" />
</g>
<g transform="matrix(1.0703659,-0.18803179,0.18803179,1.0703659,-63.348962,-38.123102)">
<path style="fill:#f1f2e0" d="m 48.392735,288.88271 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 47.334402,289.41187 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#5f3225" d="m 45.217735,289.41188 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 44.159402,288.8827 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 43.101069,286.23687 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 43.101068,285.17854 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 46.276068,284.64938 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 45.217735,286.23688 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 44.159402,284.64937 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 43.101068,287.2952 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 44.159402,287.82438 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 48.392735,286.76604 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 48.392735,287.82438 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f1f2e0" d="m 46.276068,286.76604 1.058333,0.52917 1.058333,-0.52917 -1.058333,-0.52917 z" />
<path style="fill:#f7fdfd" d="m 47.334402,285.17854 2.116667,1.05833 1.058333,-0.52917 -2.116667,-1.05833 z" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,41 +0,0 @@
#include "DropLabel.h"
#include <QMimeData>
#include <QDropEvent>
DropLabel::DropLabel(QWidget *parent) : QLabel(parent)
{
setAcceptDrops(true);
}
void DropLabel::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void DropLabel::dragMoveEvent(QDragMoveEvent *event)
{
event->acceptProposedAction();
}
void DropLabel::dragLeaveEvent(QDragLeaveEvent *event)
{
event->accept();
}
void DropLabel::dropEvent(QDropEvent *event)
{
const QMimeData *mimeData = event->mimeData();
if (!mimeData)
{
return;
}
if (mimeData->hasUrls()) {
auto urls = mimeData->urls();
emit droppedURLs(urls);
}
event->acceptProposedAction();
}

View File

@@ -1,20 +0,0 @@
#pragma once
#include <QLabel>
class DropLabel : public QLabel
{
Q_OBJECT
public:
explicit DropLabel(QWidget *parent = nullptr);
signals:
void droppedURLs(QList<QUrl> urls);
protected:
void dropEvent(QDropEvent *event) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dragLeaveEvent(QDragLeaveEvent *event) override;
};

Some files were not shown because too many files have changed in this diff Show More