mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-13 20:22:13 +00:00
Compare commits
325 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6a5241e12 | ||
|
|
cf616efb5d | ||
|
|
0902fd5bec | ||
|
|
de48f102bd | ||
|
|
0f3d88cb14 | ||
|
|
8fda1595f7 | ||
|
|
e9c8ca02ba | ||
|
|
95203df9ca | ||
|
|
27732d66b4 | ||
|
|
b3f717c582 | ||
|
|
cba22f7ee0 | ||
|
|
3d8f6ac640 | ||
|
|
605a334057 | ||
|
|
5fa36f67b3 | ||
|
|
0cd8ded4d4 | ||
|
|
9d724e0fe4 | ||
|
|
a9dfe6d7ec | ||
|
|
7de007502c | ||
|
|
e955b1c6f9 | ||
|
|
c8f468057e | ||
|
|
6ded1168b0 | ||
|
|
adecc53df8 | ||
|
|
c9465e9e86 | ||
|
|
f14c1888a9 | ||
|
|
ca60784a44 | ||
|
|
565dab24b5 | ||
|
|
eea347b82c | ||
|
|
0b60e50af8 | ||
|
|
0959aeb046 | ||
|
|
89edc3e15e | ||
|
|
f67ca674c4 | ||
|
|
3ed5b1570b | ||
|
|
4674aad125 | ||
|
|
bf1632e4ed | ||
|
|
b286b93281 | ||
|
|
632c087483 | ||
|
|
318a945d74 | ||
|
|
b6d7ffab47 | ||
|
|
04b36a3e55 | ||
|
|
3f2152c14d | ||
|
|
8a6c64ef62 | ||
|
|
6e42d51283 | ||
|
|
86830967b6 | ||
|
|
1657313105 | ||
|
|
a00fb1e8da | ||
|
|
50d18a06d5 | ||
|
|
3cd2b898e5 | ||
|
|
17d4947b30 | ||
|
|
dd7b6642a3 | ||
|
|
eb246c4fa7 | ||
|
|
6ef38d0873 | ||
|
|
482ad250a4 | ||
|
|
f9169654c5 | ||
|
|
e58e2643ca | ||
|
|
42e305bb9d | ||
|
|
8594cc8f6c | ||
|
|
ad9d082f57 | ||
|
|
52ff6a4140 | ||
|
|
1ef6ec4178 | ||
|
|
72bc860983 | ||
|
|
29b00eab31 | ||
|
|
a3c95d9bcc | ||
|
|
fbc29b6a06 | ||
|
|
e1e1d99102 | ||
|
|
7cb76788bd | ||
|
|
20eb5ac3cc | ||
|
|
5f4a364955 | ||
|
|
ff9f9dd629 | ||
|
|
5f7a48a35e | ||
|
|
1f677a3325 | ||
|
|
902dc50c87 | ||
|
|
a19aca0648 | ||
|
|
c069147f67 | ||
|
|
64ca8be101 | ||
|
|
f521a925be | ||
|
|
9617326cf8 | ||
|
|
b60fbf87ab | ||
|
|
0e6bc97bf3 | ||
|
|
4a24ea6c38 | ||
|
|
a01b1707de | ||
|
|
4901985db6 | ||
|
|
1705832feb | ||
|
|
4623c1b34f | ||
|
|
39d3739442 | ||
|
|
26b485d82f | ||
|
|
919dea0de6 | ||
|
|
c33e5ca62e | ||
|
|
00a945d84b | ||
|
|
55e4cb6fb5 | ||
|
|
4f452d5815 | ||
|
|
a74f3b553a | ||
|
|
ad9b16bd3d | ||
|
|
42a85def60 | ||
|
|
e95619fa67 | ||
|
|
e5b4dee1c0 | ||
|
|
de2eb3fc54 | ||
|
|
fcc5bc2ce0 | ||
|
|
d11f10ea1e | ||
|
|
73fc9c79cf | ||
|
|
5328cc7bbe | ||
|
|
b77f4eb144 | ||
|
|
df89183c10 | ||
|
|
d18b97ae3d | ||
|
|
7fd56a30bd | ||
|
|
b2c803a378 | ||
|
|
ffff2cd324 | ||
|
|
f1dc456802 | ||
|
|
44f21406e9 | ||
|
|
91faaa5b59 | ||
|
|
96ee20072f | ||
|
|
0cc682c629 | ||
|
|
737169d1d3 | ||
|
|
cc1de6a649 | ||
|
|
6e964acde5 | ||
|
|
bb6894893d | ||
|
|
97ad7d287c | ||
|
|
47bc7e5ee3 | ||
|
|
a4779d32b6 | ||
|
|
6b76af116e | ||
|
|
211a72e144 | ||
|
|
011ea84530 | ||
|
|
d66f2500a6 | ||
|
|
29cdc9364b | ||
|
|
28ad9befdc | ||
|
|
80d146866c | ||
|
|
5a344a2933 | ||
|
|
053b938beb | ||
|
|
7c24bcc834 | ||
|
|
7dfd6aa051 | ||
|
|
00f4f533aa | ||
|
|
3133bb3ea0 | ||
|
|
acff155624 | ||
|
|
9d4e840a6e | ||
|
|
cb5cfe7242 | ||
|
|
b1cddb4600 | ||
|
|
55e21737dd | ||
|
|
da33fa4090 | ||
|
|
a8811a27f7 | ||
|
|
226c1bdae5 | ||
|
|
49dc9695f5 | ||
|
|
5e33da258c | ||
|
|
f7c97efcf3 | ||
|
|
e3d2e5fd74 | ||
|
|
4a77524b05 | ||
|
|
a354e8bfae | ||
|
|
4883d15262 | ||
|
|
f54705e1c5 | ||
|
|
43881b9cdb | ||
|
|
7146724607 | ||
|
|
0b56b5efaf | ||
|
|
4e8be668cb | ||
|
|
8d0ff99089 | ||
|
|
549198031d | ||
|
|
16d378687c | ||
|
|
dd2d8f48fa | ||
|
|
e4ecc31e07 | ||
|
|
9c87bc6c4b | ||
|
|
f26b7dedad | ||
|
|
f5273ae2b1 | ||
|
|
1dc34269bd | ||
|
|
c608841f77 | ||
|
|
2e64d0308c | ||
|
|
c88c639b8e | ||
|
|
616c372690 | ||
|
|
994972bf5d | ||
|
|
82b35b5445 | ||
|
|
7ceb2cacb1 | ||
|
|
8219dbf612 | ||
|
|
3b236483df | ||
|
|
c0e58fbfb2 | ||
|
|
6f6d912d07 | ||
|
|
efa8e26a3f | ||
|
|
5cf599673d | ||
|
|
65e30aa118 | ||
|
|
a59dbdcb38 | ||
|
|
725e6a36bb | ||
|
|
93ee6f9010 | ||
|
|
5a0e7877b0 | ||
|
|
2f0275c194 | ||
|
|
1f218bb8cd | ||
|
|
ba401922e1 | ||
|
|
1f6a484cb2 | ||
|
|
18f532b0d7 | ||
|
|
0d30a2655f | ||
|
|
9022042360 | ||
|
|
583786757a | ||
|
|
af33b96684 | ||
|
|
aa41b891f0 | ||
|
|
53069205fa | ||
|
|
6d9819cccf | ||
|
|
f8df07c327 | ||
|
|
573d4c8050 | ||
|
|
c84c51860d | ||
|
|
6206a241ea | ||
|
|
7839c4ecc0 | ||
|
|
b82eb5873e | ||
|
|
a17caba2c9 | ||
|
|
946d49675c | ||
|
|
ac2f64f337 | ||
|
|
ddedd077b6 | ||
|
|
2cd9b06476 | ||
|
|
eb0ed082d8 | ||
|
|
cdd35910c3 | ||
|
|
ece826bdbc | ||
|
|
b2bf50a6d7 | ||
|
|
790402bdce | ||
|
|
983a40698c | ||
|
|
866d7029af | ||
|
|
1936bd181f | ||
|
|
8637cce433 | ||
|
|
4a9e213238 | ||
|
|
179451d591 | ||
|
|
3fb7a0faf0 | ||
|
|
b4b6091372 | ||
|
|
556d8f0ec1 | ||
|
|
986141b503 | ||
|
|
176783c8ca | ||
|
|
f9ea3dbfde | ||
|
|
ffbc5bb62c | ||
|
|
966f9d1206 | ||
|
|
0f7b38c76b | ||
|
|
7d5787025a | ||
|
|
156bc8f276 | ||
|
|
d7113de3bd | ||
|
|
c39d26f445 | ||
|
|
2831ca94f8 | ||
|
|
0a592ab99b | ||
|
|
d166b48072 | ||
|
|
13ac46bc18 | ||
|
|
a1a06cc89f | ||
|
|
c46c508fc6 | ||
|
|
b182f12c20 | ||
|
|
222d3c0dc5 | ||
|
|
3a3c9ac951 | ||
|
|
130a790807 | ||
|
|
5eb9ef5d56 | ||
|
|
e9ed4b29bc | ||
|
|
58741d2df4 | ||
|
|
53f61bea71 | ||
|
|
498bb48338 | ||
|
|
2fe033c4a0 | ||
|
|
d8413fa5ec | ||
|
|
e6ab57b8b1 | ||
|
|
e71295d760 | ||
|
|
50c441a773 | ||
|
|
e9186d6d2c | ||
|
|
208209e4a7 | ||
|
|
3fabb11f4c | ||
|
|
8650aa81f0 | ||
|
|
ecc80bd763 | ||
|
|
188d0d5886 | ||
|
|
7b96d74d3b | ||
|
|
d85e820a07 | ||
|
|
555cbe00ce | ||
|
|
4744ea07a8 | ||
|
|
85ff1657fd | ||
|
|
afd1778fd7 | ||
|
|
54a9ee5eb0 | ||
|
|
f552366e03 | ||
|
|
b589a12d17 | ||
|
|
fca4441229 | ||
|
|
398167e8b0 | ||
|
|
500581d095 | ||
|
|
c51090dcff | ||
|
|
a774b3d248 | ||
|
|
498520446f | ||
|
|
d52079e734 | ||
|
|
43a39a3bfb | ||
|
|
c00946c855 | ||
|
|
088ad07a80 | ||
|
|
8e286c2b5c | ||
|
|
9ddf2aec31 | ||
|
|
3c189a6553 | ||
|
|
86b6cdfcb3 | ||
|
|
e6201f9ff7 | ||
|
|
52c9cd5497 | ||
|
|
0e22e4b399 | ||
|
|
48b587e7b6 | ||
|
|
7d74c9bc25 | ||
|
|
0b1e0221f0 | ||
|
|
06b5fa5dcc | ||
|
|
c51a993ff7 | ||
|
|
b767d4b134 | ||
|
|
b4fcbab513 | ||
|
|
7ace682260 | ||
|
|
5650a28fc6 | ||
|
|
87b12a63a1 | ||
|
|
d7564a51ce | ||
|
|
9bf4d08770 | ||
|
|
8dfc1028ee | ||
|
|
3629f9b999 | ||
|
|
a14eeab2d6 | ||
|
|
fb83299e59 | ||
|
|
3202b972f8 | ||
|
|
28cb66e85c | ||
|
|
7773e77150 | ||
|
|
5fe0ed7e8f | ||
|
|
012b007e56 | ||
|
|
28c35ea0db | ||
|
|
267f18ca81 | ||
|
|
0a312d3b08 | ||
|
|
093143cfef | ||
|
|
fcb8612c10 | ||
|
|
ddb961fad5 | ||
|
|
7d76fd57e9 | ||
|
|
6961525faa | ||
|
|
8950953d91 | ||
|
|
7f6b344b49 | ||
|
|
9bbbb05060 | ||
|
|
0885668292 | ||
|
|
0f1a3329e4 | ||
|
|
998a14c95d | ||
|
|
3b97e3c363 | ||
|
|
c47933d95c | ||
|
|
8cfd0881ac | ||
|
|
e6be883d14 | ||
|
|
1e1b2342f4 | ||
|
|
4662fbd298 | ||
|
|
0109220678 | ||
|
|
f8d835cd22 | ||
|
|
53db8edb85 | ||
|
|
acbbdf319a | ||
|
|
c71808446b | ||
|
|
525f508d94 | ||
|
|
ccbf341dc8 |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,17 +1,25 @@
|
||||
Thumbs.db
|
||||
.kdev4
|
||||
MultiMC5.kdev4
|
||||
MultiMC.pro.user
|
||||
CMakeLists.txt.user
|
||||
.user
|
||||
.directory
|
||||
build
|
||||
resources/CMakeFiles
|
||||
resources/MultiMCLauncher.jar
|
||||
*~
|
||||
*.swp
|
||||
html/
|
||||
|
||||
# Project Files
|
||||
MultiMC5.kdev4
|
||||
MultiMC.pro.user
|
||||
CMakeLists.txt.user
|
||||
CMakeLists.txt.user.*
|
||||
/.project
|
||||
/.settings
|
||||
|
||||
# Build dirs
|
||||
build
|
||||
/build-*
|
||||
|
||||
# Ctags File
|
||||
tags
|
||||
|
||||
|
||||
29
.travis.yml
Normal file
29
.travis.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
cache: apt
|
||||
before_install:
|
||||
- sudo apt-add-repository -y ppa:beineri/opt-qt521
|
||||
- sudo apt-add-repository -y ppa:kalakris/cmake
|
||||
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo apt-get update -qq
|
||||
install:
|
||||
- sudo apt-get install -y -qq cmake qt52base qt52svg qt52tools qt52x11extras
|
||||
- sudo apt-get install -y -qq g++-4.8
|
||||
- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake ..
|
||||
script:
|
||||
- make -j4
|
||||
after_script:
|
||||
- make test
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- "irc.esper.net#MultiMC"
|
||||
template:
|
||||
- "%{build_number} (%{branch} - %{commit} : %{author}): %{message} (%{build_url})"
|
||||
email: false
|
||||
2
BUILD.md
2
BUILD.md
@@ -56,7 +56,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
|
||||
## Dependencies
|
||||
* Qt 5.1.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Windows")
|
||||
* OpenSSL (http://slproweb.com/products/Win32OpenSSL.html) ("Win32 OpenSSL v1.0.1e Light")
|
||||
* OpenSSL (http://slproweb.com/products/Win32OpenSSL.html) ("Win32 OpenSSL \<version\> Light")
|
||||
- Microsoft Visual C++ 2008 Redist. is required for this, there's a link on the OpenSSL download page above next to the main download.
|
||||
* CMake (http://www.cmake.org/cmake/resources/software.html) ("Windows (Win32 Installer)")
|
||||
* A copy of the MultiMC source (clone it with git)
|
||||
|
||||
63
BuildConfig.cpp.in
Normal file
63
BuildConfig.cpp.in
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "BuildConfig.h"
|
||||
|
||||
Config BuildConfig;
|
||||
|
||||
Config::Config()
|
||||
{
|
||||
// Version information
|
||||
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
|
||||
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
|
||||
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
|
||||
VERSION_BUILD = @MultiMC_VERSION_BUILD@;
|
||||
VERSION_TYPE = "@MultiMC_VERSION_TYPE@";
|
||||
|
||||
if(VERSION_TYPE == "Release")
|
||||
versionTypeEnum = Release;
|
||||
else if(VERSION_TYPE == "ReleaseCandidate")
|
||||
versionTypeEnum = ReleaseCandidate;
|
||||
else if(VERSION_TYPE == "Development")
|
||||
versionTypeEnum = Development;
|
||||
else
|
||||
versionTypeEnum = Custom;
|
||||
VERSION_CHANNEL = "@MultiMC_VERSION_CHANNEL@";
|
||||
BUILD_PLATFORM = "@MultiMC_BUILD_PLATFORM@";
|
||||
CHANLIST_URL = "@MultiMC_CHANLIST_URL@";
|
||||
NOTIFICATION_URL = "@MultiMC_NOTIFICATION_URL@";
|
||||
FULL_VERSION_STR = "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@";
|
||||
|
||||
UPDATER_DRY_RUN = @MultiMC_UPDATER_DRY_RUN_value@;
|
||||
UPDATER_FORCE_LOCAL = @MultiMC_UPDATER_FORCE_LOCAL_value@;
|
||||
|
||||
GIT_COMMIT = "@MultiMC_GIT_COMMIT@";
|
||||
VERSION_STR = "@MultiMC_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
|
||||
}
|
||||
|
||||
QString Config::versionTypeName() const
|
||||
{
|
||||
switch (versionTypeEnum)
|
||||
{
|
||||
case Release:
|
||||
return "Stable Release";
|
||||
case ReleaseCandidate:
|
||||
return "Release Candidate";
|
||||
case Development:
|
||||
return "Development";
|
||||
case Custom:
|
||||
default:
|
||||
return "Custom";
|
||||
}
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
{
|
||||
QString vstr = QString("%1.%2").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR));
|
||||
|
||||
if (VERSION_HOTFIX > 0) vstr += "." + QString::number(VERSION_HOTFIX);
|
||||
|
||||
// If the build is a development build or release candidate, add that info to the end.
|
||||
if (versionTypeEnum == Development) vstr += "-dev" + QString::number(VERSION_BUILD);
|
||||
else if (versionTypeEnum == ReleaseCandidate) vstr += "-rc" + QString::number(VERSION_BUILD);
|
||||
|
||||
return vstr;
|
||||
}
|
||||
86
BuildConfig.h
Normal file
86
BuildConfig.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#include <QString>
|
||||
|
||||
/**
|
||||
* \brief The Config class holds all the build-time information passed from the build system.
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
Config();
|
||||
/// The major version number.
|
||||
int VERSION_MAJOR;
|
||||
/// The minor version number.
|
||||
int VERSION_MINOR;
|
||||
/// The hotfix number.
|
||||
int VERSION_HOTFIX;
|
||||
/// The build number.
|
||||
int VERSION_BUILD;
|
||||
/// The build type, as specified at build time.
|
||||
QString VERSION_TYPE;
|
||||
|
||||
/// The build type, transformed.
|
||||
enum Type
|
||||
{
|
||||
/// Version type for stable release builds.
|
||||
Release,
|
||||
|
||||
/// Version type for release candidates.
|
||||
ReleaseCandidate,
|
||||
|
||||
/// Version type for development builds.
|
||||
Development,
|
||||
|
||||
/// Version type for custom builds. This is the default when no version type is specified.
|
||||
Custom
|
||||
} versionTypeEnum;
|
||||
|
||||
/**
|
||||
* The version channel
|
||||
* This is used by the updater to determine what channel the current version came from.
|
||||
*/
|
||||
QString VERSION_CHANNEL;
|
||||
|
||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
/// URL for the updater's channel
|
||||
QString CHANLIST_URL;
|
||||
|
||||
/// URL for notifications
|
||||
QString NOTIFICATION_URL;
|
||||
|
||||
/// Used for matching notifications
|
||||
QString FULL_VERSION_STR;
|
||||
|
||||
/// enabled for updater dry run
|
||||
bool UPDATER_DRY_RUN;
|
||||
|
||||
/// enabled for updater dry run
|
||||
bool UPDATER_FORCE_LOCAL;
|
||||
|
||||
/// The commit hash of this build
|
||||
QString GIT_COMMIT;
|
||||
|
||||
/// This is printed on start to standard output
|
||||
QString VERSION_STR;
|
||||
|
||||
/**
|
||||
* This is used to fetch the news RSS feed.
|
||||
* It defaults in CMakeLists.txt to "http://multimc.org/rss.xml"
|
||||
*/
|
||||
QString NEWS_RSS_URL;
|
||||
|
||||
/**
|
||||
* \brief Converts the Version to a string.
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
*/
|
||||
QString printableVersionString() const;
|
||||
|
||||
/**
|
||||
* returns a string representation of the version channel type, suitable for printing.
|
||||
*/
|
||||
QString versionTypeName() const;
|
||||
};
|
||||
|
||||
extern Config BuildConfig;
|
||||
1115
CMakeLists.txt
1115
CMakeLists.txt
File diff suppressed because it is too large
Load Diff
25
MMCError.h
Normal file
25
MMCError.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include <QString>
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
class MMCError : public std::exception
|
||||
{
|
||||
public:
|
||||
MMCError(QString cause)
|
||||
{
|
||||
exceptionCause = cause;
|
||||
QLOG_ERROR() << "Exception: " + cause;
|
||||
};
|
||||
virtual ~MMCError() noexcept {}
|
||||
virtual const char *what() const noexcept
|
||||
{
|
||||
return exceptionCause.toLocal8Bit();
|
||||
};
|
||||
virtual QString cause() const
|
||||
{
|
||||
return exceptionCause;
|
||||
}
|
||||
private:
|
||||
QString exceptionCause;
|
||||
};
|
||||
230
MultiMC.cpp
230
MultiMC.cpp
@@ -1,5 +1,6 @@
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
@@ -17,17 +18,25 @@
|
||||
#include "logic/lists/LwjglVersionList.h"
|
||||
#include "logic/lists/MinecraftVersionList.h"
|
||||
#include "logic/lists/ForgeVersionList.h"
|
||||
#include "logic/lists/LiteLoaderVersionList.h"
|
||||
|
||||
#include "logic/news/NewsChecker.h"
|
||||
|
||||
#include "logic/status/StatusChecker.h"
|
||||
|
||||
#include "logic/InstanceLauncher.h"
|
||||
#include "logic/net/HttpMetaCache.h"
|
||||
#include "logic/net/URLConstants.h"
|
||||
|
||||
#include "logic/JavaUtils.h"
|
||||
|
||||
#include "logic/updater/UpdateChecker.h"
|
||||
#include "logic/updater/NotificationChecker.h"
|
||||
|
||||
#include "logic/tools/JProfiler.h"
|
||||
#include "logic/tools/JVisualVM.h"
|
||||
#include "logic/tools/MCEditTool.h"
|
||||
|
||||
#include "pathutils.h"
|
||||
#include "cmdutils.h"
|
||||
#include <inisettingsobject.h>
|
||||
@@ -35,17 +44,19 @@
|
||||
#include "logger/QsLog.h"
|
||||
#include <logger/QsLogDest.h>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
static const int APPDATA_BUFFER_SIZE = 1024;
|
||||
#endif
|
||||
|
||||
using namespace Util::Commandline;
|
||||
|
||||
MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
: QApplication(argc, argv), m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD,
|
||||
VERSION_CHANNEL, VERSION_BUILD_TYPE}
|
||||
: QApplication(argc, argv)
|
||||
{
|
||||
setOrganizationName("MultiMC");
|
||||
setApplicationName("MultiMC5");
|
||||
|
||||
initTranslations();
|
||||
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
@@ -100,8 +111,8 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
// display version and exit
|
||||
if (args["version"].toBool())
|
||||
{
|
||||
std::cout << "Version " << VERSION_STR << std::endl;
|
||||
std::cout << "Git " << GIT_COMMIT << std::endl;
|
||||
std::cout << "Version " << BuildConfig.VERSION_STR.toStdString() << std::endl;
|
||||
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
||||
m_status = MultiMC::Succeeded;
|
||||
return;
|
||||
}
|
||||
@@ -154,8 +165,8 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
initLogger();
|
||||
|
||||
QLOG_INFO() << "MultiMC 5, (c) 2013 MultiMC Contributors";
|
||||
QLOG_INFO() << "Version : " << VERSION_STR;
|
||||
QLOG_INFO() << "Git commit : " << GIT_COMMIT;
|
||||
QLOG_INFO() << "Version : " << BuildConfig.VERSION_STR;
|
||||
QLOG_INFO() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
if (adjustedBy.size())
|
||||
{
|
||||
QLOG_INFO() << "Work dir before adjustment : " << origcwdPath;
|
||||
@@ -172,6 +183,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
// load settings
|
||||
initGlobalSettings();
|
||||
|
||||
// load translations
|
||||
initTranslations();
|
||||
|
||||
// initialize the updater
|
||||
m_updateChecker.reset(new UpdateChecker());
|
||||
|
||||
@@ -179,7 +193,10 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
m_notificationChecker.reset(new NotificationChecker());
|
||||
|
||||
// initialize the news checker
|
||||
m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL));
|
||||
m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL));
|
||||
|
||||
// initialize the status checker
|
||||
m_statusChecker.reset(new StatusChecker());
|
||||
|
||||
// and instances
|
||||
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
||||
@@ -198,56 +215,27 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
|
||||
// init the http meta cache
|
||||
initHttpMetaCache();
|
||||
|
||||
// set up a basic autodetected proxy (system default)
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
|
||||
QLOG_INFO() << "Detecting system proxy settings...";
|
||||
auto proxies = QNetworkProxyFactory::systemProxyForQuery();
|
||||
if (proxies.size() == 1 && proxies[0].type() == QNetworkProxy::NoProxy)
|
||||
{
|
||||
QLOG_INFO() << "No proxy found.";
|
||||
}
|
||||
else
|
||||
for (auto proxy : proxies)
|
||||
{
|
||||
QString proxyDesc;
|
||||
if (proxy.type() == QNetworkProxy::NoProxy)
|
||||
{
|
||||
QLOG_INFO() << "Using no proxy is an option!";
|
||||
continue;
|
||||
}
|
||||
switch (proxy.type())
|
||||
{
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
proxyDesc = "Default proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
proxyDesc = "Socks5 proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpProxy:
|
||||
proxyDesc = "HTTP proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
proxyDesc = "HTTP caching: ";
|
||||
break;
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
proxyDesc = "FTP caching: ";
|
||||
break;
|
||||
default:
|
||||
proxyDesc = "DERP proxy: ";
|
||||
break;
|
||||
}
|
||||
proxyDesc += QString("%3@%1:%2 pass %4")
|
||||
.arg(proxy.hostName())
|
||||
.arg(proxy.port())
|
||||
.arg(proxy.user())
|
||||
.arg(proxy.password());
|
||||
QLOG_INFO() << proxyDesc;
|
||||
}
|
||||
|
||||
// create the global network manager
|
||||
m_qnam.reset(new QNetworkAccessManager(this));
|
||||
|
||||
// init proxy settings
|
||||
updateProxySettings();
|
||||
|
||||
m_profilers.insert("jprofiler",
|
||||
std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
|
||||
m_profilers.insert("jvisualvm",
|
||||
std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
|
||||
for (auto profiler : m_profilers.values())
|
||||
{
|
||||
profiler->registerSettings(m_settings.get());
|
||||
}
|
||||
m_tools.insert("mcedit",
|
||||
std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
|
||||
for (auto tool : m_tools.values())
|
||||
{
|
||||
tool->registerSettings(m_settings.get());
|
||||
}
|
||||
|
||||
// launch instance, if that's what should be done
|
||||
// WARNING: disabled until further notice
|
||||
/*
|
||||
@@ -278,18 +266,20 @@ MultiMC::~MultiMC()
|
||||
|
||||
void MultiMC::initTranslations()
|
||||
{
|
||||
QLocale locale(m_settings->get("Language").toString());
|
||||
QLocale::setDefault(locale);
|
||||
QLOG_INFO() << "Your language is" << locale.bcp47Name();
|
||||
m_qt_translator.reset(new QTranslator());
|
||||
if (m_qt_translator->load("qt_" + QLocale::system().name(),
|
||||
if (m_qt_translator->load("qt_" + locale.bcp47Name(),
|
||||
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
{
|
||||
std::cout << "Loading Qt Language File for "
|
||||
<< QLocale::system().name().toLocal8Bit().constData() << "...";
|
||||
QLOG_DEBUG() << "Loading Qt Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_qt_translator.get()))
|
||||
{
|
||||
std::cout << " failed.";
|
||||
QLOG_ERROR() << "Loading Qt Language File failed.";
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -297,17 +287,15 @@ void MultiMC::initTranslations()
|
||||
}
|
||||
|
||||
m_mmc_translator.reset(new QTranslator());
|
||||
if (m_mmc_translator->load("mmc_" + QLocale::system().name(),
|
||||
QDir("translations").absolutePath()))
|
||||
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), MMC->root() + "/translations"))
|
||||
{
|
||||
std::cout << "Loading MMC Language File for "
|
||||
<< QLocale::system().name().toLocal8Bit().constData() << "...";
|
||||
QLOG_DEBUG() << "Loading MMC Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_mmc_translator.get()))
|
||||
{
|
||||
std::cout << " failed.";
|
||||
QLOG_ERROR() << "Loading MMC Language File failed.";
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -345,9 +333,9 @@ void MultiMC::initGlobalSettings()
|
||||
{
|
||||
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
||||
// Updates
|
||||
m_settings->registerSetting("UpdateChannel", version().channel);
|
||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||
m_settings->registerSetting("AutoUpdate", true);
|
||||
|
||||
|
||||
// Notifications
|
||||
m_settings->registerSetting("ShownNotifications", QString());
|
||||
|
||||
@@ -356,7 +344,16 @@ void MultiMC::initGlobalSettings()
|
||||
#ifdef Q_OS_LINUX
|
||||
QString ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
||||
#elif defined(Q_OS_WIN32)
|
||||
QString ftbDefault = PathCombine(QDir::homePath(), "AppData/Roaming/ftblauncher");
|
||||
wchar_t buf[APPDATA_BUFFER_SIZE];
|
||||
QString ftbDefault;
|
||||
if(!GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE))
|
||||
{
|
||||
QLOG_FATAL() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken. If you aren't on windows, how the **** are you running the windows build????";
|
||||
}
|
||||
else
|
||||
{
|
||||
ftbDefault = PathCombine(QString::fromWCharArray(buf), "ftblauncher");
|
||||
}
|
||||
#elif defined(Q_OS_MAC)
|
||||
QString ftbDefault =
|
||||
PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
||||
@@ -410,9 +407,14 @@ void MultiMC::initGlobalSettings()
|
||||
// Editors
|
||||
m_settings->registerSetting("JsonEditor", QString());
|
||||
|
||||
// Language
|
||||
m_settings->registerSetting("Language", QLocale(QLocale::system().language()).bcp47Name());
|
||||
|
||||
// Console
|
||||
m_settings->registerSetting("ShowConsole", true);
|
||||
m_settings->registerSetting("RaiseConsole", true);
|
||||
m_settings->registerSetting("AutoCloseConsole", true);
|
||||
m_settings->registerSetting("LogPrePostOutput", true);
|
||||
|
||||
// Console Colors
|
||||
// m_settings->registerSetting("SysMessageColor", QColor(Qt::blue));
|
||||
@@ -424,6 +426,13 @@ void MultiMC::initGlobalSettings()
|
||||
m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
|
||||
m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
|
||||
|
||||
// Proxy Settings
|
||||
m_settings->registerSetting("ProxyType", "Default");
|
||||
m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1");
|
||||
m_settings->registerSetting("ProxyPort", 8080);
|
||||
m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, "");
|
||||
m_settings->registerSetting({"ProxyPass", "ProxyPassword"}, "");
|
||||
|
||||
// Memory
|
||||
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
|
||||
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
|
||||
@@ -462,11 +471,81 @@ void MultiMC::initHttpMetaCache()
|
||||
m_metacache->addBase("versions", QDir("versions").absolutePath());
|
||||
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
|
||||
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
|
||||
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
|
||||
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
|
||||
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
|
||||
m_metacache->addBase("root", QDir(root()).absolutePath());
|
||||
m_metacache->Load();
|
||||
}
|
||||
|
||||
void MultiMC::updateProxySettings()
|
||||
{
|
||||
QString proxyTypeStr = settings()->get("ProxyType").toString();
|
||||
|
||||
// Get the proxy settings from the settings object.
|
||||
QString addr = settings()->get("ProxyAddr").toString();
|
||||
int port = settings()->get("ProxyPort").value<qint16>();
|
||||
QString user = settings()->get("ProxyUser").toString();
|
||||
QString pass = settings()->get("ProxyPass").toString();
|
||||
|
||||
// Set the application proxy settings.
|
||||
if (proxyTypeStr == "SOCKS5")
|
||||
{
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, pass));
|
||||
}
|
||||
else if (proxyTypeStr == "HTTP")
|
||||
{
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, pass));
|
||||
}
|
||||
else if (proxyTypeStr == "None")
|
||||
{
|
||||
// If we have no proxy set, set no proxy and return.
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have "Default" selected, set Qt to use the system proxy settings.
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
}
|
||||
|
||||
QLOG_INFO() << "Detecting proxy settings...";
|
||||
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
|
||||
if (m_qnam.get()) m_qnam->setProxy(proxy);
|
||||
QString proxyDesc;
|
||||
if (proxy.type() == QNetworkProxy::NoProxy)
|
||||
{
|
||||
QLOG_INFO() << "Using no proxy is an option!";
|
||||
return;
|
||||
}
|
||||
switch (proxy.type())
|
||||
{
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
proxyDesc = "Default proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
proxyDesc = "Socks5 proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpProxy:
|
||||
proxyDesc = "HTTP proxy: ";
|
||||
break;
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
proxyDesc = "HTTP caching: ";
|
||||
break;
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
proxyDesc = "FTP caching: ";
|
||||
break;
|
||||
default:
|
||||
proxyDesc = "DERP proxy: ";
|
||||
break;
|
||||
}
|
||||
proxyDesc += QString("%3@%1:%2 pass %4")
|
||||
.arg(proxy.hostName())
|
||||
.arg(proxy.port())
|
||||
.arg(proxy.user())
|
||||
.arg(proxy.password());
|
||||
QLOG_INFO() << proxyDesc;
|
||||
}
|
||||
|
||||
std::shared_ptr<IconList> MultiMC::icons()
|
||||
{
|
||||
if (!m_icons)
|
||||
@@ -494,6 +573,15 @@ std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
|
||||
return m_forgelist;
|
||||
}
|
||||
|
||||
std::shared_ptr<LiteLoaderVersionList> MultiMC::liteloaderlist()
|
||||
{
|
||||
if (!m_liteloaderlist)
|
||||
{
|
||||
m_liteloaderlist.reset(new LiteLoaderVersionList());
|
||||
}
|
||||
return m_liteloaderlist;
|
||||
}
|
||||
|
||||
std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
|
||||
{
|
||||
if (!m_minecraftlist)
|
||||
|
||||
37
MultiMC.h
37
MultiMC.h
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
#include <QApplication>
|
||||
#include "MultiMCVersion.h"
|
||||
#include <memory>
|
||||
#include "logger/QsLog.h"
|
||||
#include "logger/QsLogDest.h"
|
||||
@@ -17,10 +15,14 @@ class MojangAccountList;
|
||||
class IconList;
|
||||
class QNetworkAccessManager;
|
||||
class ForgeVersionList;
|
||||
class LiteLoaderVersionList;
|
||||
class JavaVersionList;
|
||||
class UpdateChecker;
|
||||
class NotificationChecker;
|
||||
class NewsChecker;
|
||||
class StatusChecker;
|
||||
class BaseProfilerFactory;
|
||||
class BaseDetachedToolFactory;
|
||||
|
||||
#if defined(MMC)
|
||||
#undef MMC
|
||||
@@ -83,11 +85,6 @@ public:
|
||||
return m_status;
|
||||
}
|
||||
|
||||
MultiMCVersion version()
|
||||
{
|
||||
return m_version;
|
||||
}
|
||||
|
||||
std::shared_ptr<QNetworkAccessManager> qnam()
|
||||
{
|
||||
return m_qnam;
|
||||
@@ -113,16 +110,37 @@ public:
|
||||
return m_newsChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<StatusChecker> statusChecker()
|
||||
{
|
||||
return m_statusChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<LWJGLVersionList> lwjgllist();
|
||||
|
||||
std::shared_ptr<ForgeVersionList> forgelist();
|
||||
|
||||
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
|
||||
|
||||
std::shared_ptr<MinecraftVersionList> minecraftlist();
|
||||
|
||||
std::shared_ptr<JavaVersionList> javalist();
|
||||
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers()
|
||||
{
|
||||
return m_profilers;
|
||||
}
|
||||
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> tools()
|
||||
{
|
||||
return m_tools;
|
||||
}
|
||||
|
||||
void installUpdates(const QString updateFilesDir, UpdateFlags flags = None);
|
||||
|
||||
/*!
|
||||
* Updates the application proxy settings from the settings object.
|
||||
*/
|
||||
void updateProxySettings();
|
||||
|
||||
/*!
|
||||
* Opens a json file using either a system default editor, or, if note empty, the editor
|
||||
* specified in the settings
|
||||
@@ -178,14 +196,18 @@ private:
|
||||
std::shared_ptr<UpdateChecker> m_updateChecker;
|
||||
std::shared_ptr<NotificationChecker> m_notificationChecker;
|
||||
std::shared_ptr<NewsChecker> m_newsChecker;
|
||||
std::shared_ptr<StatusChecker> m_statusChecker;
|
||||
std::shared_ptr<MojangAccountList> m_accounts;
|
||||
std::shared_ptr<IconList> m_icons;
|
||||
std::shared_ptr<QNetworkAccessManager> m_qnam;
|
||||
std::shared_ptr<HttpMetaCache> m_metacache;
|
||||
std::shared_ptr<LWJGLVersionList> m_lwjgllist;
|
||||
std::shared_ptr<ForgeVersionList> m_forgelist;
|
||||
std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist;
|
||||
std::shared_ptr<MinecraftVersionList> m_minecraftlist;
|
||||
std::shared_ptr<JavaVersionList> m_javalist;
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
|
||||
QsLogging::DestinationPtr m_fileDestination;
|
||||
QsLogging::DestinationPtr m_debugDestination;
|
||||
|
||||
@@ -198,5 +220,4 @@ private:
|
||||
QString origcwdPath;
|
||||
|
||||
Status m_status = MultiMC::Failed;
|
||||
MultiMCVersion m_version;
|
||||
};
|
||||
|
||||
BIN
MultiMC.icns
BIN
MultiMC.icns
Binary file not shown.
@@ -1,75 +0,0 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
/*!
|
||||
* \brief The Version class represents a MultiMC version number.
|
||||
*/
|
||||
struct MultiMCVersion
|
||||
{
|
||||
/*!
|
||||
* \brief Converts the Version to a string.
|
||||
* \return The version number in string format (major.minor.revision.build).
|
||||
*/
|
||||
QString toString() const
|
||||
{
|
||||
QString vstr = QString("5.%1.%2").arg(
|
||||
QString::number(major),
|
||||
QString::number(minor));
|
||||
|
||||
if (build >= 0) vstr += "." + QString::number(build);
|
||||
if (!channel.isEmpty()) vstr += "-" + channel;
|
||||
if (!buildType.isEmpty()) vstr += "-" + buildType;
|
||||
|
||||
return vstr;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief The major version number.
|
||||
* This is no longer going to always be 5 for MultiMC 5. Doing so is useless.
|
||||
* Instead, we'll be starting major off at 1 and incrementing it with every major feature.
|
||||
*/
|
||||
int major;
|
||||
|
||||
/*!
|
||||
* \brief The minor version number.
|
||||
* This number is incremented for major features and bug fixes.
|
||||
*/
|
||||
int minor;
|
||||
|
||||
/*!
|
||||
* \brief The build number.
|
||||
* This number is automatically set by Buildbot it is set to the build number of the buildbot
|
||||
* build that this build came from.
|
||||
* If this build didn't come from buildbot and no build number was given to CMake, this will default
|
||||
* to -1, causing it to not show in this version's string representation.
|
||||
*/
|
||||
int build;
|
||||
|
||||
/*!
|
||||
* \brief This build's channel.
|
||||
*/
|
||||
QString channel;
|
||||
|
||||
/*!
|
||||
* \brief The build type.
|
||||
* This indicates the type of build that this is. For example, lin64 or custombuild.
|
||||
*/
|
||||
QString buildType;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
MultiMC 5
|
||||
MultiMC 5 [](https://travis-ci.org/MultiMC/MultiMC5)
|
||||
=========
|
||||
|
||||
MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping.
|
||||
|
||||
78
changelog.yaml
Normal file
78
changelog.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# This is MultiMC's changelog. It is formatted in YAML.
|
||||
#
|
||||
# Each key below represents a release version name. Each release key has several string entries under it, each containing information about a single change. Each of these entries may contain Markdown for formatting.
|
||||
#
|
||||
|
||||
0.0:
|
||||
- Initial release.
|
||||
0.1:
|
||||
- Reworked the version numbering system to support our [new Git workflow](http://nvie.com/posts/a-successful-git-branching-model/).
|
||||
- Added a tray icon for the console window.
|
||||
- Fixed instances getting deselected after FTB instances are loaded (or whenever the model is reset).
|
||||
- Implemented proxy settings.
|
||||
- Fixed sorting of Java installations in the Java list.
|
||||
- Jar files are now distributed separately, rather than being extracted from the binary at runtime.
|
||||
- Added additional information to the about dialog.
|
||||
0.1.1:
|
||||
- Hotfix - Changed the issue tracker URL to [GitHub issues](https://github.com/MultiMC/MultiMC5/issues).
|
||||
0.2:
|
||||
- Java memory settings have MB added to the number to make the units obvious.
|
||||
- Complete rework of the launcher part. No more sensitive information in the process arguments.
|
||||
- Cached downloads now do not destroy files on failure.
|
||||
- Mojang service status is now on the MultiMC status bar.
|
||||
- Java checker is no longer needed/used on instance launch.
|
||||
- Support for private FTB packs.
|
||||
- Fixed instance ID issues related to copying FTB packs without changing the instance name.
|
||||
- Forge versions are better sorted (build numbers above 999 were sorted wrong).
|
||||
- Fixed crash related to the MultiMC update channel picker in offline mode.
|
||||
- Started using icon themes for the application icons, fixing many OSX graphical glitches.
|
||||
- Icon sources have been located, along with icon licenses.
|
||||
- Update to the German translation.
|
||||
0.2.1:
|
||||
- Hotfix - move the native library extraction into the onesix launcher part.
|
||||
0.3:
|
||||
- Improved instance view
|
||||
- Overhauled 1.6+ version loading
|
||||
- Added a patch system for instance modification
|
||||
- There is no longer a single custom.json file that overrides version.json
|
||||
- Instead there are now "patch" files in <instance>/patches/, one for each main tweaker (forge, liteloader etc.)
|
||||
- These patches are applied after version.json in a customisable order,
|
||||
- A list of these files is shown in the left most tab in the Edit Mods dialog, where a list of libraries was shown before.
|
||||
- custom.json can still be used for overriding everything.
|
||||
- Offline mode can be used even when online
|
||||
- Show an "empty" message in version selector dialogs
|
||||
- Fix FTB paths on windows
|
||||
- Tooling support
|
||||
- JProfiler
|
||||
- JVisualVM
|
||||
- MCEdit
|
||||
- Don't assume forge in FTB instances and allow other libraries (liteloader, mcpatcher, etc.) in FTB instances
|
||||
- Screenshot uploading/managing
|
||||
- Instance badges
|
||||
- Some pre/post command stuff (remove the timeout, variable substitution)
|
||||
- Fix logging when the system language is not en_US
|
||||
- Setting PermGen to 64 will now omit the java parameter because it is the default
|
||||
- Fix encoding of escape sequences (tabs and newlines) in config files
|
||||
0.3.1:
|
||||
- Fix copying of FTB instances (instance type is changed properly now)
|
||||
- Customizing FTB pack versions will remove the FTB pack patch file
|
||||
0.3.2:
|
||||
- Fix issues with libraries not getting replaced properly (fixes instance startup for new instances)
|
||||
- Fix april fools
|
||||
0.3.3:
|
||||
- Tweak context menu to prevent accidental clicks
|
||||
- Fix adding icons to custom icon directories
|
||||
- Added a Patreon button to the toolbar
|
||||
- Minecraft authentication tasks now provide better error reports
|
||||
0.3.4:
|
||||
- Show a list of Patreon patrons in credits section of the about dialog
|
||||
- Make the console window raise itself after minecraft closes
|
||||
- Add Control/Command+q shortcut to quit from the main window
|
||||
- Add french translation
|
||||
- Download and cache FML libs for legacy versions
|
||||
- Update the OS X icon
|
||||
- Fix FTB libraries not being used properly
|
||||
0.3.5
|
||||
- More versions are now selectable when changing instance versions
|
||||
- Fix for Forge/FML changing its mcmod.info metadata format
|
||||
786
cmake/BundleUtilities.cmake
Normal file
786
cmake/BundleUtilities.cmake
Normal file
@@ -0,0 +1,786 @@
|
||||
# - Functions to help assemble a standalone bundle application.
|
||||
# A collection of CMake utility functions useful for dealing with .app
|
||||
# bundles on the Mac and bundle-like directories on any OS.
|
||||
#
|
||||
# The following functions are provided by this module:
|
||||
# fixup_bundle
|
||||
# copy_and_fixup_bundle
|
||||
# verify_app
|
||||
# get_bundle_main_executable
|
||||
# get_dotapp_dir
|
||||
# get_bundle_and_executable
|
||||
# get_bundle_all_executables
|
||||
# get_item_key
|
||||
# clear_bundle_keys
|
||||
# set_bundle_key_values
|
||||
# get_bundle_keys
|
||||
# copy_resolved_item_into_bundle
|
||||
# copy_resolved_framework_into_bundle
|
||||
# fixup_bundle_item
|
||||
# verify_bundle_prerequisites
|
||||
# verify_bundle_symlinks
|
||||
# Requires CMake 2.6 or greater because it uses function, break and
|
||||
# PARENT_SCOPE. Also depends on GetPrerequisites.cmake.
|
||||
#
|
||||
# FIXUP_BUNDLE(<app> <libs> <dirs>)
|
||||
# Fix up a bundle in-place and make it standalone, such that it can be
|
||||
# drag-n-drop copied to another machine and run on that machine as long as all
|
||||
# of the system libraries are compatible.
|
||||
#
|
||||
# If you pass plugins to fixup_bundle as the libs parameter, you should install
|
||||
# them or copy them into the bundle before calling fixup_bundle. The "libs"
|
||||
# parameter is a list of libraries that must be fixed up, but that cannot be
|
||||
# determined by otool output analysis. (i.e., plugins)
|
||||
#
|
||||
# Gather all the keys for all the executables and libraries in a bundle, and
|
||||
# then, for each key, copy each prerequisite into the bundle. Then fix each one
|
||||
# up according to its own list of prerequisites.
|
||||
#
|
||||
# Then clear all the keys and call verify_app on the final bundle to ensure
|
||||
# that it is truly standalone.
|
||||
#
|
||||
# COPY_AND_FIXUP_BUNDLE(<src> <dst> <libs> <dirs>)
|
||||
# Makes a copy of the bundle <src> at location <dst> and then fixes up the
|
||||
# new copied bundle in-place at <dst>...
|
||||
#
|
||||
# VERIFY_APP(<app>)
|
||||
# Verifies that an application <app> appears valid based on running analysis
|
||||
# tools on it. Calls "message(FATAL_ERROR" if the application is not verified.
|
||||
#
|
||||
# GET_BUNDLE_MAIN_EXECUTABLE(<bundle> <result_var>)
|
||||
# The result will be the full path name of the bundle's main executable file
|
||||
# or an "error:" prefixed string if it could not be determined.
|
||||
#
|
||||
# GET_DOTAPP_DIR(<exe> <dotapp_dir_var>)
|
||||
# Returns the nearest parent dir whose name ends with ".app" given the full
|
||||
# path to an executable. If there is no such parent dir, then simply return
|
||||
# the dir containing the executable.
|
||||
#
|
||||
# The returned directory may or may not exist.
|
||||
#
|
||||
# GET_BUNDLE_AND_EXECUTABLE(<app> <bundle_var> <executable_var> <valid_var>)
|
||||
# Takes either a ".app" directory name or the name of an executable
|
||||
# nested inside a ".app" directory and returns the path to the ".app"
|
||||
# directory in <bundle_var> and the path to its main executable in
|
||||
# <executable_var>
|
||||
#
|
||||
# GET_BUNDLE_ALL_EXECUTABLES(<bundle> <exes_var>)
|
||||
# Scans the given bundle recursively for all executable files and accumulates
|
||||
# them into a variable.
|
||||
#
|
||||
# GET_ITEM_KEY(<item> <key_var>)
|
||||
# Given a file (item) name, generate a key that should be unique considering
|
||||
# the set of libraries that need copying or fixing up to make a bundle
|
||||
# standalone. This is essentially the file name including extension with "."
|
||||
# replaced by "_"
|
||||
#
|
||||
# This key is used as a prefix for CMake variables so that we can associate a
|
||||
# set of variables with a given item based on its key.
|
||||
#
|
||||
# CLEAR_BUNDLE_KEYS(<keys_var>)
|
||||
# Loop over the list of keys, clearing all the variables associated with each
|
||||
# key. After the loop, clear the list of keys itself.
|
||||
#
|
||||
# Caller of get_bundle_keys should call clear_bundle_keys when done with list
|
||||
# of keys.
|
||||
#
|
||||
# SET_BUNDLE_KEY_VALUES(<keys_var> <context> <item> <exepath> <dirs>
|
||||
# <copyflag>)
|
||||
# Add a key to the list (if necessary) for the given item. If added,
|
||||
# also set all the variables associated with that key.
|
||||
#
|
||||
# GET_BUNDLE_KEYS(<app> <libs> <dirs> <keys_var>)
|
||||
# Loop over all the executable and library files within the bundle (and given
|
||||
# as extra <libs>) and accumulate a list of keys representing them. Set
|
||||
# values associated with each key such that we can loop over all of them and
|
||||
# copy prerequisite libs into the bundle and then do appropriate
|
||||
# install_name_tool fixups.
|
||||
#
|
||||
# COPY_RESOLVED_ITEM_INTO_BUNDLE(<resolved_item> <resolved_embedded_item>)
|
||||
# Copy a resolved item into the bundle if necessary. Copy is not necessary if
|
||||
# the resolved_item is "the same as" the resolved_embedded_item.
|
||||
#
|
||||
# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE(<resolved_item> <resolved_embedded_item>)
|
||||
# Copy a resolved framework into the bundle if necessary. Copy is not necessary
|
||||
# if the resolved_item is "the same as" the resolved_embedded_item.
|
||||
#
|
||||
# By default, BU_COPY_FULL_FRAMEWORK_CONTENTS is not set. If you want full
|
||||
# frameworks embedded in your bundles, set BU_COPY_FULL_FRAMEWORK_CONTENTS to
|
||||
# ON before calling fixup_bundle. By default,
|
||||
# COPY_RESOLVED_FRAMEWORK_INTO_BUNDLE copies the framework dylib itself plus
|
||||
# the framework Resources directory.
|
||||
#
|
||||
# FIXUP_BUNDLE_ITEM(<resolved_embedded_item> <exepath> <dirs>)
|
||||
# Get the direct/non-system prerequisites of the resolved embedded item. For
|
||||
# each prerequisite, change the way it is referenced to the value of the
|
||||
# _EMBEDDED_ITEM keyed variable for that prerequisite. (Most likely changing to
|
||||
# an "@executable_path" style reference.)
|
||||
#
|
||||
# This function requires that the resolved_embedded_item be "inside" the bundle
|
||||
# already. In other words, if you pass plugins to fixup_bundle as the libs
|
||||
# parameter, you should install them or copy them into the bundle before
|
||||
# calling fixup_bundle. The "libs" parameter is a list of libraries that must
|
||||
# be fixed up, but that cannot be determined by otool output analysis. (i.e.,
|
||||
# plugins)
|
||||
#
|
||||
# Also, change the id of the item being fixed up to its own _EMBEDDED_ITEM
|
||||
# value.
|
||||
#
|
||||
# Accumulate changes in a local variable and make *one* call to
|
||||
# install_name_tool at the end of the function with all the changes at once.
|
||||
#
|
||||
# If the BU_CHMOD_BUNDLE_ITEMS variable is set then bundle items will be
|
||||
# marked writable before install_name_tool tries to change them.
|
||||
#
|
||||
# VERIFY_BUNDLE_PREREQUISITES(<bundle> <result_var> <info_var>)
|
||||
# Verifies that the sum of all prerequisites of all files inside the bundle
|
||||
# are contained within the bundle or are "system" libraries, presumed to exist
|
||||
# everywhere.
|
||||
#
|
||||
# VERIFY_BUNDLE_SYMLINKS(<bundle> <result_var> <info_var>)
|
||||
# Verifies that any symlinks found in the bundle point to other files that are
|
||||
# already also in the bundle... Anything that points to an external file causes
|
||||
# this function to fail the verification.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2008-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
# The functions defined in this file depend on the get_prerequisites function
|
||||
# (and possibly others) found in:
|
||||
#
|
||||
get_filename_component(BundleUtilities_cmake_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
|
||||
include("${BundleUtilities_cmake_dir}/GetPrerequisites.cmake")
|
||||
|
||||
|
||||
function(get_bundle_main_executable bundle result_var)
|
||||
set(result "error: '${bundle}/Contents/Info.plist' file does not exist")
|
||||
|
||||
if(EXISTS "${bundle}/Contents/Info.plist")
|
||||
set(result "error: no CFBundleExecutable in '${bundle}/Contents/Info.plist' file")
|
||||
set(line_is_main_executable 0)
|
||||
set(bundle_executable "")
|
||||
|
||||
# Read Info.plist as a list of lines:
|
||||
#
|
||||
set(eol_char "E")
|
||||
file(READ "${bundle}/Contents/Info.plist" info_plist)
|
||||
string(REGEX REPLACE ";" "\\\\;" info_plist "${info_plist}")
|
||||
string(REGEX REPLACE "\n" "${eol_char};" info_plist "${info_plist}")
|
||||
|
||||
# Scan the lines for "<key>CFBundleExecutable</key>" - the line after that
|
||||
# is the name of the main executable.
|
||||
#
|
||||
foreach(line ${info_plist})
|
||||
if(line_is_main_executable)
|
||||
string(REGEX REPLACE "^.*<string>(.*)</string>.*$" "\\1" bundle_executable "${line}")
|
||||
break()
|
||||
endif()
|
||||
|
||||
if(line MATCHES "^.*<key>CFBundleExecutable</key>.*$")
|
||||
set(line_is_main_executable 1)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT "${bundle_executable}" STREQUAL "")
|
||||
if(EXISTS "${bundle}/Contents/MacOS/${bundle_executable}")
|
||||
set(result "${bundle}/Contents/MacOS/${bundle_executable}")
|
||||
else()
|
||||
|
||||
# Ultimate goal:
|
||||
# If not in "Contents/MacOS" then scan the bundle for matching files. If
|
||||
# there is only one executable file that matches, then use it, otherwise
|
||||
# it's an error...
|
||||
#
|
||||
#file(GLOB_RECURSE file_list "${bundle}/${bundle_executable}")
|
||||
|
||||
# But for now, pragmatically, it's an error. Expect the main executable
|
||||
# for the bundle to be in Contents/MacOS, it's an error if it's not:
|
||||
#
|
||||
set(result "error: '${bundle}/Contents/MacOS/${bundle_executable}' does not exist")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
#
|
||||
# More inclusive technique... (This one would work on Windows and Linux
|
||||
# too, if a developer followed the typical Mac bundle naming convention...)
|
||||
#
|
||||
# If there is no Info.plist file, try to find an executable with the same
|
||||
# base name as the .app directory:
|
||||
#
|
||||
endif()
|
||||
|
||||
set(${result_var} "${result}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_dotapp_dir exe dotapp_dir_var)
|
||||
set(s "${exe}")
|
||||
|
||||
if(s MATCHES "^.*/.*\\.app/.*$")
|
||||
# If there is a ".app" parent directory,
|
||||
# ascend until we hit it:
|
||||
# (typical of a Mac bundle executable)
|
||||
#
|
||||
set(done 0)
|
||||
while(NOT ${done})
|
||||
get_filename_component(snamewe "${s}" NAME_WE)
|
||||
get_filename_component(sname "${s}" NAME)
|
||||
get_filename_component(sdir "${s}" PATH)
|
||||
set(s "${sdir}")
|
||||
if(sname MATCHES "\\.app$")
|
||||
set(done 1)
|
||||
set(dotapp_dir "${sdir}/${sname}")
|
||||
endif()
|
||||
endwhile()
|
||||
else()
|
||||
# Otherwise use a directory containing the exe
|
||||
# (typical of a non-bundle executable on Mac, Windows or Linux)
|
||||
#
|
||||
is_file_executable("${s}" is_executable)
|
||||
if(is_executable)
|
||||
get_filename_component(sdir "${s}" PATH)
|
||||
set(dotapp_dir "${sdir}")
|
||||
else()
|
||||
set(dotapp_dir "${s}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
set(${dotapp_dir_var} "${dotapp_dir}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_bundle_and_executable app bundle_var executable_var valid_var)
|
||||
set(valid 0)
|
||||
|
||||
if(EXISTS "${app}")
|
||||
# Is it a directory ending in .app?
|
||||
if(IS_DIRECTORY "${app}")
|
||||
if(app MATCHES "\\.app$")
|
||||
get_bundle_main_executable("${app}" executable)
|
||||
if(EXISTS "${app}" AND EXISTS "${executable}")
|
||||
set(${bundle_var} "${app}" PARENT_SCOPE)
|
||||
set(${executable_var} "${executable}" PARENT_SCOPE)
|
||||
set(valid 1)
|
||||
#message(STATUS "info: handled .app directory case...")
|
||||
else()
|
||||
message(STATUS "warning: *NOT* handled - .app directory case...")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "warning: *NOT* handled - directory but not .app case...")
|
||||
endif()
|
||||
else()
|
||||
# Is it an executable file?
|
||||
is_file_executable("${app}" is_executable)
|
||||
if(is_executable)
|
||||
get_dotapp_dir("${app}" dotapp_dir)
|
||||
if(EXISTS "${dotapp_dir}")
|
||||
set(${bundle_var} "${dotapp_dir}" PARENT_SCOPE)
|
||||
set(${executable_var} "${app}" PARENT_SCOPE)
|
||||
set(valid 1)
|
||||
#message(STATUS "info: handled executable file in .app dir case...")
|
||||
else()
|
||||
get_filename_component(app_dir "${app}" PATH)
|
||||
set(${bundle_var} "${app_dir}" PARENT_SCOPE)
|
||||
set(${executable_var} "${app}" PARENT_SCOPE)
|
||||
set(valid 1)
|
||||
#message(STATUS "info: handled executable file in any dir case...")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "warning: *NOT* handled - not .app dir, not executable file...")
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "warning: *NOT* handled - directory/file does not exist...")
|
||||
endif()
|
||||
|
||||
if(NOT valid)
|
||||
set(${bundle_var} "error: not a bundle" PARENT_SCOPE)
|
||||
set(${executable_var} "error: not a bundle" PARENT_SCOPE)
|
||||
endif()
|
||||
|
||||
set(${valid_var} ${valid} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_bundle_all_executables bundle exes_var)
|
||||
set(exes "")
|
||||
|
||||
file(GLOB_RECURSE file_list "${bundle}/*")
|
||||
foreach(f ${file_list})
|
||||
is_file_executable("${f}" is_executable)
|
||||
if(is_executable)
|
||||
set(exes ${exes} "${f}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(${exes_var} "${exes}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_item_key item key_var)
|
||||
get_filename_component(item_name "${item}" NAME)
|
||||
if(WIN32)
|
||||
string(TOLOWER "${item_name}" item_name)
|
||||
endif()
|
||||
string(REGEX REPLACE "\\." "_" ${key_var} "${item_name}")
|
||||
set(${key_var} ${${key_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(clear_bundle_keys keys_var)
|
||||
foreach(key ${${keys_var}})
|
||||
set(${key}_ITEM PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_ITEM PARENT_SCOPE)
|
||||
set(${key}_DEFAULT_EMBEDDED_PATH PARENT_SCOPE)
|
||||
set(${key}_EMBEDDED_ITEM PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG PARENT_SCOPE)
|
||||
endforeach()
|
||||
set(${keys_var} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(set_bundle_key_values keys_var context item exepath dirs copyflag)
|
||||
get_filename_component(item_name "${item}" NAME)
|
||||
|
||||
get_item_key("${item}" key)
|
||||
|
||||
list(LENGTH ${keys_var} length_before)
|
||||
gp_append_unique(${keys_var} "${key}")
|
||||
list(LENGTH ${keys_var} length_after)
|
||||
|
||||
if(NOT length_before EQUAL length_after)
|
||||
gp_resolve_item("${context}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
||||
|
||||
gp_item_default_embedded_path("${item}" default_embedded_path)
|
||||
|
||||
if(item MATCHES "[^/]+\\.framework/")
|
||||
# For frameworks, construct the name under the embedded path from the
|
||||
# opening "${item_name}.framework/" to the closing "/${item_name}":
|
||||
#
|
||||
string(REGEX REPLACE "^.*(${item_name}.framework/.*/?${item_name}).*$" "${default_embedded_path}/\\1" embedded_item "${item}")
|
||||
else()
|
||||
# For other items, just use the same name as the original, but in the
|
||||
# embedded path:
|
||||
#
|
||||
set(embedded_item "${default_embedded_path}/${item_name}")
|
||||
endif()
|
||||
|
||||
# Replace @executable_path and resolve ".." references:
|
||||
#
|
||||
string(REPLACE "@executable_path" "${exepath}" resolved_embedded_item "${embedded_item}")
|
||||
get_filename_component(resolved_embedded_item "${resolved_embedded_item}" ABSOLUTE)
|
||||
|
||||
# *But* -- if we are not copying, then force resolved_embedded_item to be
|
||||
# the same as resolved_item. In the case of multiple executables in the
|
||||
# original bundle, using the default_embedded_path results in looking for
|
||||
# the resolved executable next to the main bundle executable. This is here
|
||||
# so that exes in the other sibling directories (like "bin") get fixed up
|
||||
# properly...
|
||||
#
|
||||
if(NOT copyflag)
|
||||
set(resolved_embedded_item "${resolved_item}")
|
||||
endif()
|
||||
|
||||
set(${keys_var} ${${keys_var}} PARENT_SCOPE)
|
||||
set(${key}_ITEM "${item}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_ITEM "${resolved_item}" PARENT_SCOPE)
|
||||
set(${key}_DEFAULT_EMBEDDED_PATH "${default_embedded_path}" PARENT_SCOPE)
|
||||
set(${key}_EMBEDDED_ITEM "${embedded_item}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${resolved_embedded_item}" PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG "${copyflag}" PARENT_SCOPE)
|
||||
else()
|
||||
#message("warning: item key '${key}' already in the list, subsequent references assumed identical to first")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_bundle_keys app libs dirs keys_var)
|
||||
set(${keys_var} PARENT_SCOPE)
|
||||
|
||||
get_bundle_and_executable("${app}" bundle executable valid)
|
||||
if(valid)
|
||||
# Always use the exepath of the main bundle executable for @executable_path
|
||||
# replacements:
|
||||
#
|
||||
get_filename_component(exepath "${executable}" PATH)
|
||||
|
||||
# But do fixups on all executables in the bundle:
|
||||
#
|
||||
get_bundle_all_executables("${bundle}" exes)
|
||||
|
||||
# For each extra lib, accumulate a key as well and then also accumulate
|
||||
# any of its prerequisites. (Extra libs are typically dynamically loaded
|
||||
# plugins: libraries that are prerequisites for full runtime functionality
|
||||
# but that do not show up in otool -L output...)
|
||||
#
|
||||
foreach(lib ${libs})
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${lib}" "${exepath}" "${dirs}" 0)
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${lib}" prereqs 1 1 "${exepath}" "${dirs}")
|
||||
foreach(pr ${prereqs})
|
||||
set_bundle_key_values(${keys_var} "${lib}" "${pr}" "${exepath}" "${dirs}" 1)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# For each executable found in the bundle, accumulate keys as we go.
|
||||
# The list of keys should be complete when all prerequisites of all
|
||||
# binaries in the bundle have been analyzed.
|
||||
#
|
||||
foreach(exe ${exes})
|
||||
# Add the exe itself to the keys:
|
||||
#
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${exe}" "${exepath}" "${dirs}" 0)
|
||||
|
||||
# Add each prerequisite to the keys:
|
||||
#
|
||||
set(prereqs "")
|
||||
get_prerequisites("${exe}" prereqs 1 1 "${exepath}" "${dirs}")
|
||||
foreach(pr ${prereqs})
|
||||
set_bundle_key_values(${keys_var} "${exe}" "${pr}" "${exepath}" "${dirs}" 1)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# Propagate values to caller's scope:
|
||||
#
|
||||
set(${keys_var} ${${keys_var}} PARENT_SCOPE)
|
||||
foreach(key ${${keys_var}})
|
||||
set(${key}_ITEM "${${key}_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_ITEM "${${key}_RESOLVED_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_DEFAULT_EMBEDDED_PATH "${${key}_DEFAULT_EMBEDDED_PATH}" PARENT_SCOPE)
|
||||
set(${key}_EMBEDDED_ITEM "${${key}_EMBEDDED_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_RESOLVED_EMBEDDED_ITEM "${${key}_RESOLVED_EMBEDDED_ITEM}" PARENT_SCOPE)
|
||||
set(${key}_COPYFLAG "${${key}_COPYFLAG}" PARENT_SCOPE)
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(copy_resolved_item_into_bundle resolved_item resolved_embedded_item)
|
||||
if(WIN32)
|
||||
# ignore case on Windows
|
||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
||||
else()
|
||||
set(resolved_item_compare "${resolved_item}")
|
||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
||||
message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
|
||||
else()
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
function(copy_resolved_framework_into_bundle resolved_item resolved_embedded_item)
|
||||
if(WIN32)
|
||||
# ignore case on Windows
|
||||
string(TOLOWER "${resolved_item}" resolved_item_compare)
|
||||
string(TOLOWER "${resolved_embedded_item}" resolved_embedded_item_compare)
|
||||
else()
|
||||
set(resolved_item_compare "${resolved_item}")
|
||||
set(resolved_embedded_item_compare "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
if("${resolved_item_compare}" STREQUAL "${resolved_embedded_item_compare}")
|
||||
message(STATUS "warning: resolved_item == resolved_embedded_item - not copying...")
|
||||
else()
|
||||
if(BU_COPY_FULL_FRAMEWORK_CONTENTS)
|
||||
# Full Framework (everything):
|
||||
get_filename_component(resolved_dir "${resolved_item}" PATH)
|
||||
get_filename_component(resolved_dir "${resolved_dir}/../.." ABSOLUTE)
|
||||
get_filename_component(resolved_embedded_dir "${resolved_embedded_item}" PATH)
|
||||
get_filename_component(resolved_embedded_dir "${resolved_embedded_dir}/../.." ABSOLUTE)
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_dir}' '${resolved_embedded_dir}'")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_dir}" "${resolved_embedded_dir}")
|
||||
else()
|
||||
# Framework lib itself:
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy ${resolved_item} ${resolved_embedded_item}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy "${resolved_item}" "${resolved_embedded_item}")
|
||||
|
||||
# Plus Resources, if they exist:
|
||||
string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_resources "${resolved_item}")
|
||||
string(REGEX REPLACE "^(.*)/[^/]+/[^/]+/[^/]+$" "\\1/Resources" resolved_embedded_resources "${resolved_embedded_item}")
|
||||
if(EXISTS "${resolved_resources}")
|
||||
#message(STATUS "copying COMMAND ${CMAKE_COMMAND} -E copy_directory '${resolved_resources}' '${resolved_embedded_resources}'")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${resolved_resources}" "${resolved_embedded_resources}")
|
||||
endif()
|
||||
endif()
|
||||
if(UNIX AND NOT APPLE)
|
||||
file(RPATH_REMOVE FILE "${resolved_embedded_item}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
|
||||
function(fixup_bundle_item resolved_embedded_item exepath dirs)
|
||||
# This item's key is "ikey":
|
||||
#
|
||||
get_item_key("${resolved_embedded_item}" ikey)
|
||||
|
||||
# Ensure the item is "inside the .app bundle" -- it should not be fixed up if
|
||||
# it is not in the .app bundle... Otherwise, we'll modify files in the build
|
||||
# tree, or in other varied locations around the file system, with our call to
|
||||
# install_name_tool. Make sure that doesn't happen here:
|
||||
#
|
||||
get_dotapp_dir("${exepath}" exe_dotapp_dir)
|
||||
string(LENGTH "${exe_dotapp_dir}/" exe_dotapp_dir_length)
|
||||
string(LENGTH "${resolved_embedded_item}" resolved_embedded_item_length)
|
||||
set(path_too_short 0)
|
||||
set(is_embedded 0)
|
||||
if(${resolved_embedded_item_length} LESS ${exe_dotapp_dir_length})
|
||||
set(path_too_short 1)
|
||||
endif()
|
||||
if(NOT path_too_short)
|
||||
string(SUBSTRING "${resolved_embedded_item}" 0 ${exe_dotapp_dir_length} item_substring)
|
||||
if("${exe_dotapp_dir}/" STREQUAL "${item_substring}")
|
||||
set(is_embedded 1)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT is_embedded)
|
||||
message(" exe_dotapp_dir/='${exe_dotapp_dir}/'")
|
||||
message(" item_substring='${item_substring}'")
|
||||
message(" resolved_embedded_item='${resolved_embedded_item}'")
|
||||
message("")
|
||||
message("Install or copy the item into the bundle before calling fixup_bundle.")
|
||||
message("Or maybe there's a typo or incorrect path in one of the args to fixup_bundle?")
|
||||
message("")
|
||||
message(FATAL_ERROR "cannot fixup an item that is not in the bundle...")
|
||||
endif()
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${resolved_embedded_item}" prereqs 1 0 "${exepath}" "${dirs}")
|
||||
|
||||
set(changes "")
|
||||
|
||||
foreach(pr ${prereqs})
|
||||
# Each referenced item's key is "rkey" in the loop:
|
||||
#
|
||||
get_item_key("${pr}" rkey)
|
||||
|
||||
if(NOT "${${rkey}_EMBEDDED_ITEM}" STREQUAL "")
|
||||
set(changes ${changes} "-change" "${pr}" "${${rkey}_EMBEDDED_ITEM}")
|
||||
else()
|
||||
message("warning: unexpected reference to '${pr}'")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(BU_CHMOD_BUNDLE_ITEMS)
|
||||
execute_process(COMMAND chmod u+w "${resolved_embedded_item}")
|
||||
endif()
|
||||
|
||||
# Change this item's id and all of its references in one call
|
||||
# to install_name_tool:
|
||||
#
|
||||
execute_process(COMMAND install_name_tool
|
||||
${changes} -id "${${ikey}_EMBEDDED_ITEM}" "${resolved_embedded_item}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(fixup_bundle app libs dirs)
|
||||
message(STATUS "fixup_bundle")
|
||||
message(STATUS " app='${app}'")
|
||||
message(STATUS " libs='${libs}'")
|
||||
message(STATUS " dirs='${dirs}'")
|
||||
|
||||
get_bundle_and_executable("${app}" bundle executable valid)
|
||||
if(valid)
|
||||
get_filename_component(exepath "${executable}" PATH)
|
||||
|
||||
message(STATUS "fixup_bundle: preparing...")
|
||||
get_bundle_keys("${app}" "${libs}" "${dirs}" keys)
|
||||
|
||||
message(STATUS "fixup_bundle: copying...")
|
||||
list(LENGTH keys n)
|
||||
math(EXPR n ${n}*2)
|
||||
|
||||
set(i 0)
|
||||
foreach(key ${keys})
|
||||
math(EXPR i ${i}+1)
|
||||
if(${${key}_COPYFLAG})
|
||||
message(STATUS "${i}/${n}: copying '${${key}_RESOLVED_ITEM}'")
|
||||
else()
|
||||
message(STATUS "${i}/${n}: *NOT* copying '${${key}_RESOLVED_ITEM}'")
|
||||
endif()
|
||||
|
||||
set(show_status 0)
|
||||
if(show_status)
|
||||
message(STATUS "key='${key}'")
|
||||
message(STATUS "item='${${key}_ITEM}'")
|
||||
message(STATUS "resolved_item='${${key}_RESOLVED_ITEM}'")
|
||||
message(STATUS "default_embedded_path='${${key}_DEFAULT_EMBEDDED_PATH}'")
|
||||
message(STATUS "embedded_item='${${key}_EMBEDDED_ITEM}'")
|
||||
message(STATUS "resolved_embedded_item='${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
||||
message(STATUS "copyflag='${${key}_COPYFLAG}'")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(${${key}_COPYFLAG})
|
||||
set(item "${${key}_ITEM}")
|
||||
if(item MATCHES "[^/]+\\.framework/")
|
||||
copy_resolved_framework_into_bundle("${${key}_RESOLVED_ITEM}"
|
||||
"${${key}_RESOLVED_EMBEDDED_ITEM}")
|
||||
else()
|
||||
copy_resolved_item_into_bundle("${${key}_RESOLVED_ITEM}"
|
||||
"${${key}_RESOLVED_EMBEDDED_ITEM}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "fixup_bundle: fixing...")
|
||||
foreach(key ${keys})
|
||||
math(EXPR i ${i}+1)
|
||||
if(APPLE)
|
||||
message(STATUS "${i}/${n}: fixing up '${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
||||
fixup_bundle_item("${${key}_RESOLVED_EMBEDDED_ITEM}" "${exepath}" "${dirs}")
|
||||
else()
|
||||
message(STATUS "${i}/${n}: fix-up not required on this platform '${${key}_RESOLVED_EMBEDDED_ITEM}'")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
message(STATUS "fixup_bundle: cleaning up...")
|
||||
clear_bundle_keys(keys)
|
||||
|
||||
message(STATUS "fixup_bundle: verifying...")
|
||||
verify_app("${app}")
|
||||
else()
|
||||
message(SEND_ERROR "error: fixup_bundle: not a valid bundle")
|
||||
endif()
|
||||
|
||||
message(STATUS "fixup_bundle: done")
|
||||
endfunction()
|
||||
|
||||
|
||||
function(copy_and_fixup_bundle src dst libs dirs)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory "${src}" "${dst}")
|
||||
fixup_bundle("${dst}" "${libs}" "${dirs}")
|
||||
endfunction()
|
||||
|
||||
|
||||
function(verify_bundle_prerequisites bundle result_var info_var)
|
||||
set(result 1)
|
||||
set(info "")
|
||||
set(count 0)
|
||||
|
||||
get_bundle_main_executable("${bundle}" main_bundle_exe)
|
||||
|
||||
file(GLOB_RECURSE file_list "${bundle}/*")
|
||||
foreach(f ${file_list})
|
||||
is_file_executable("${f}" is_executable)
|
||||
if(is_executable)
|
||||
get_filename_component(exepath "${f}" PATH)
|
||||
math(EXPR count "${count} + 1")
|
||||
|
||||
message(STATUS "executable file ${count}: ${f}")
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${f}" prereqs 1 1 "${exepath}" "")
|
||||
|
||||
# On the Mac,
|
||||
# "embedded" and "system" prerequisites are fine... anything else means
|
||||
# the bundle's prerequisites are not verified (i.e., the bundle is not
|
||||
# really "standalone")
|
||||
#
|
||||
# On Windows (and others? Linux/Unix/...?)
|
||||
# "local" and "system" prereqs are fine...
|
||||
#
|
||||
set(external_prereqs "")
|
||||
|
||||
foreach(p ${prereqs})
|
||||
set(p_type "")
|
||||
gp_file_type("${f}" "${p}" p_type)
|
||||
|
||||
if(APPLE)
|
||||
if(NOT "${p_type}" STREQUAL "embedded" AND NOT "${p_type}" STREQUAL "system")
|
||||
set(external_prereqs ${external_prereqs} "${p}")
|
||||
endif()
|
||||
else()
|
||||
if(NOT "${p_type}" STREQUAL "local" AND NOT "${p_type}" STREQUAL "system")
|
||||
set(external_prereqs ${external_prereqs} "${p}")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(external_prereqs)
|
||||
# Found non-system/somehow-unacceptable prerequisites:
|
||||
set(result 0)
|
||||
set(info ${info} "external prerequisites found:\nf='${f}'\nexternal_prereqs='${external_prereqs}'\n")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(result)
|
||||
set(info "Verified ${count} executable files in '${bundle}'")
|
||||
endif()
|
||||
|
||||
set(${result_var} "${result}" PARENT_SCOPE)
|
||||
set(${info_var} "${info}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(verify_bundle_symlinks bundle result_var info_var)
|
||||
set(result 1)
|
||||
set(info "")
|
||||
set(count 0)
|
||||
|
||||
# TODO: implement this function for real...
|
||||
# Right now, it is just a stub that verifies unconditionally...
|
||||
|
||||
set(${result_var} "${result}" PARENT_SCOPE)
|
||||
set(${info_var} "${info}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(verify_app app)
|
||||
set(verified 0)
|
||||
set(info "")
|
||||
|
||||
get_bundle_and_executable("${app}" bundle executable valid)
|
||||
|
||||
message(STATUS "===========================================================================")
|
||||
message(STATUS "Analyzing app='${app}'")
|
||||
message(STATUS "bundle='${bundle}'")
|
||||
message(STATUS "executable='${executable}'")
|
||||
message(STATUS "valid='${valid}'")
|
||||
|
||||
# Verify that the bundle does not have any "external" prerequisites:
|
||||
#
|
||||
verify_bundle_prerequisites("${bundle}" verified info)
|
||||
message(STATUS "verified='${verified}'")
|
||||
message(STATUS "info='${info}'")
|
||||
message(STATUS "")
|
||||
|
||||
if(verified)
|
||||
# Verify that the bundle does not have any symlinks to external files:
|
||||
#
|
||||
verify_bundle_symlinks("${bundle}" verified info)
|
||||
message(STATUS "verified='${verified}'")
|
||||
message(STATUS "info='${info}'")
|
||||
message(STATUS "")
|
||||
endif()
|
||||
|
||||
if(NOT verified)
|
||||
message(FATAL_ERROR "error: verify_app failed")
|
||||
endif()
|
||||
endfunction()
|
||||
13
cmake/Coverage.cmake
Normal file
13
cmake/Coverage.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
if(__COVERAGE_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__COVERAGE_CMAKE__ TRUE)
|
||||
|
||||
if(MultiMC_CODE_COVERAGE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 --coverage")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 --coverage")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 --coverage")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 --coverage")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
|
||||
set(CMAKE_BUILD_TYPE "Debug")
|
||||
endif(MultiMC_CODE_COVERAGE)
|
||||
35
cmake/Coverity.cmake
Normal file
35
cmake/Coverity.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
if(__COVERITY_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__COVERITY_CMAKE__ TRUE)
|
||||
|
||||
include(GitFunctions)
|
||||
|
||||
git_run(COMMAND config --get user.email DEFAULT "" OUTPUT_VAR GIT_EMAIL)
|
||||
git_run(COMMAND describe DEFAULT "" OUTPUT_VAR GIT_VERSION)
|
||||
|
||||
set(MultiMC_COVERITY_TOKEN "" CACHE STRING "Coverity access token")
|
||||
set(MultiMC_COVERITY_EMAIL "${GIT_EMAIL}" CACHE STRING "Coverity email")
|
||||
|
||||
set(MultiMC_COVERITY_TOOLS_DIR "${CMAKE_BINARY_DIR}/coverity_tools" CACHE PATH "Path to the coverity tools")
|
||||
|
||||
find_program(CURL_EXECUTABLE NAMES curl PATHS /usr/bin)
|
||||
|
||||
if(NOT CURL_EXECUTABLE STREQUAL "" AND NOT MultiMC_COVERITY_TOKEN STREQUAL "" AND NOT MultiMC_COVERITY_EMAIL STREQUAL "")
|
||||
add_custom_target(coverity_configure
|
||||
COMMAND ${MultiMC_COVERITY_TOOLS_DIR}/bin/cov-configure --comptype gcc --compiler ${CMAKE_C_COMPILER}
|
||||
)
|
||||
add_custom_target(coverity_create_tarball
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Cleaning..." && ${CMAKE_MAKE_PROGRAM} clean
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Building..." && ${MultiMC_COVERITY_TOOLS_DIR}/bin/cov-build --dir cov-int ${CMAKE_MAKE_PROGRAM} -j3
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Creating tarball..." && ${CMAKE_COMMAND} -E tar cfz multimc_coverity.tgz cov-int/
|
||||
COMMENT "Creating coverity build..."
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
add_custom_target(coverity_upload
|
||||
COMMAND ${CURL_EXECUTABLE} --form project=02JanDal/MultiMC5 --form token=${MultiMC_COVERITY_TOKEN} --form email=${MultiMC_COVERITY_EMAIL} --form file=@multimc_coverity.tgz --form version=${MultiMC_GIT_COMMIT} --form description=${GIT_VERSION} http://scan5.coverity.com/cgi-bin/upload.py
|
||||
DEPENDS coverity_create_tarball
|
||||
COMMENT "Uploading to coverity..."
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
895
cmake/GetPrerequisites.cmake
Normal file
895
cmake/GetPrerequisites.cmake
Normal file
@@ -0,0 +1,895 @@
|
||||
# - Functions to analyze and list executable file prerequisites.
|
||||
# This module provides functions to list the .dll, .dylib or .so
|
||||
# files that an executable or shared library file depends on. (Its
|
||||
# prerequisites.)
|
||||
#
|
||||
# It uses various tools to obtain the list of required shared library files:
|
||||
# dumpbin (Windows)
|
||||
# objdump (MinGW on Windows)
|
||||
# ldd (Linux/Unix)
|
||||
# otool (Mac OSX)
|
||||
# The following functions are provided by this module:
|
||||
# get_prerequisites
|
||||
# list_prerequisites
|
||||
# list_prerequisites_by_glob
|
||||
# gp_append_unique
|
||||
# is_file_executable
|
||||
# gp_item_default_embedded_path
|
||||
# (projects can override with gp_item_default_embedded_path_override)
|
||||
# gp_resolve_item
|
||||
# (projects can override with gp_resolve_item_override)
|
||||
# gp_resolved_file_type
|
||||
# (projects can override with gp_resolved_file_type_override)
|
||||
# gp_file_type
|
||||
# Requires CMake 2.6 or greater because it uses function, break, return and
|
||||
# PARENT_SCOPE.
|
||||
#
|
||||
# GET_PREREQUISITES(<target> <prerequisites_var> <exclude_system> <recurse>
|
||||
# <exepath> <dirs>)
|
||||
# Get the list of shared library files required by <target>. The list in
|
||||
# the variable named <prerequisites_var> should be empty on first entry to
|
||||
# this function. On exit, <prerequisites_var> will contain the list of
|
||||
# required shared library files.
|
||||
#
|
||||
# <target> is the full path to an executable file. <prerequisites_var> is the
|
||||
# name of a CMake variable to contain the results. <exclude_system> must be 0
|
||||
# or 1 indicating whether to include or exclude "system" prerequisites. If
|
||||
# <recurse> is set to 1 all prerequisites will be found recursively, if set to
|
||||
# 0 only direct prerequisites are listed. <exepath> is the path to the top
|
||||
# level executable used for @executable_path replacment on the Mac. <dirs> is
|
||||
# a list of paths where libraries might be found: these paths are searched
|
||||
# first when a target without any path info is given. Then standard system
|
||||
# locations are also searched: PATH, Framework locations, /usr/lib...
|
||||
#
|
||||
# LIST_PREREQUISITES(<target> [<recurse> [<exclude_system> [<verbose>]]])
|
||||
# Print a message listing the prerequisites of <target>.
|
||||
#
|
||||
# <target> is the name of a shared library or executable target or the full
|
||||
# path to a shared library or executable file. If <recurse> is set to 1 all
|
||||
# prerequisites will be found recursively, if set to 0 only direct
|
||||
# prerequisites are listed. <exclude_system> must be 0 or 1 indicating whether
|
||||
# to include or exclude "system" prerequisites. With <verbose> set to 0 only
|
||||
# the full path names of the prerequisites are printed, set to 1 extra
|
||||
# informatin will be displayed.
|
||||
#
|
||||
# LIST_PREREQUISITES_BY_GLOB(<glob_arg> <glob_exp>)
|
||||
# Print the prerequisites of shared library and executable files matching a
|
||||
# globbing pattern. <glob_arg> is GLOB or GLOB_RECURSE and <glob_exp> is a
|
||||
# globbing expression used with "file(GLOB" or "file(GLOB_RECURSE" to retrieve
|
||||
# a list of matching files. If a matching file is executable, its prerequisites
|
||||
# are listed.
|
||||
#
|
||||
# Any additional (optional) arguments provided are passed along as the
|
||||
# optional arguments to the list_prerequisites calls.
|
||||
#
|
||||
# GP_APPEND_UNIQUE(<list_var> <value>)
|
||||
# Append <value> to the list variable <list_var> only if the value is not
|
||||
# already in the list.
|
||||
#
|
||||
# IS_FILE_EXECUTABLE(<file> <result_var>)
|
||||
# Return 1 in <result_var> if <file> is a binary executable, 0 otherwise.
|
||||
#
|
||||
# GP_ITEM_DEFAULT_EMBEDDED_PATH(<item> <default_embedded_path_var>)
|
||||
# Return the path that others should refer to the item by when the item
|
||||
# is embedded inside a bundle.
|
||||
#
|
||||
# Override on a per-project basis by providing a project-specific
|
||||
# gp_item_default_embedded_path_override function.
|
||||
#
|
||||
# GP_RESOLVE_ITEM(<context> <item> <exepath> <dirs> <resolved_item_var>)
|
||||
# Resolve an item into an existing full path file.
|
||||
#
|
||||
# Override on a per-project basis by providing a project-specific
|
||||
# gp_resolve_item_override function.
|
||||
#
|
||||
# GP_RESOLVED_FILE_TYPE(<original_file> <file> <exepath> <dirs> <type_var>)
|
||||
# Return the type of <file> with respect to <original_file>. String
|
||||
# describing type of prerequisite is returned in variable named <type_var>.
|
||||
#
|
||||
# Use <exepath> and <dirs> if necessary to resolve non-absolute <file>
|
||||
# values -- but only for non-embedded items.
|
||||
#
|
||||
# Possible types are:
|
||||
# system
|
||||
# local
|
||||
# embedded
|
||||
# other
|
||||
# Override on a per-project basis by providing a project-specific
|
||||
# gp_resolved_file_type_override function.
|
||||
#
|
||||
# GP_FILE_TYPE(<original_file> <file> <type_var>)
|
||||
# Return the type of <file> with respect to <original_file>. String
|
||||
# describing type of prerequisite is returned in variable named <type_var>.
|
||||
#
|
||||
# Possible types are:
|
||||
# system
|
||||
# local
|
||||
# embedded
|
||||
# other
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2008-2009 Kitware, Inc.
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
# see accompanying file Copyright.txt for details.
|
||||
#
|
||||
# This software is distributed WITHOUT ANY WARRANTY; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the License for more information.
|
||||
#=============================================================================
|
||||
# (To distribute this file outside of CMake, substitute the full
|
||||
# License text for the above reference.)
|
||||
|
||||
function(gp_append_unique list_var value)
|
||||
set(contains 0)
|
||||
|
||||
foreach(item ${${list_var}})
|
||||
if("${item}" STREQUAL "${value}")
|
||||
set(contains 1)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT contains)
|
||||
set(${list_var} ${${list_var}} "${value}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(is_file_executable file result_var)
|
||||
#
|
||||
# A file is not executable until proven otherwise:
|
||||
#
|
||||
set(${result_var} 0 PARENT_SCOPE)
|
||||
|
||||
get_filename_component(file_full "${file}" ABSOLUTE)
|
||||
string(TOLOWER "${file_full}" file_full_lower)
|
||||
|
||||
# If file name ends in .exe on Windows, *assume* executable:
|
||||
#
|
||||
if(WIN32 AND NOT UNIX)
|
||||
if("${file_full_lower}" MATCHES "\\.exe$")
|
||||
set(${result_var} 1 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
# A clause could be added here that uses output or return value of dumpbin
|
||||
# to determine ${result_var}. In 99%+? practical cases, the exe name
|
||||
# match will be sufficient...
|
||||
#
|
||||
endif()
|
||||
|
||||
# Use the information returned from the Unix shell command "file" to
|
||||
# determine if ${file_full} should be considered an executable file...
|
||||
#
|
||||
# If the file command's output contains "executable" and does *not* contain
|
||||
# "text" then it is likely an executable suitable for prerequisite analysis
|
||||
# via the get_prerequisites macro.
|
||||
#
|
||||
if(UNIX)
|
||||
if(NOT file_cmd)
|
||||
find_program(file_cmd "file")
|
||||
mark_as_advanced(file_cmd)
|
||||
endif()
|
||||
|
||||
if(file_cmd)
|
||||
execute_process(COMMAND "${file_cmd}" "${file_full}"
|
||||
OUTPUT_VARIABLE file_ov
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# Replace the name of the file in the output with a placeholder token
|
||||
# (the string " _file_full_ ") so that just in case the path name of
|
||||
# the file contains the word "text" or "executable" we are not fooled
|
||||
# into thinking "the wrong thing" because the file name matches the
|
||||
# other 'file' command output we are looking for...
|
||||
#
|
||||
string(REPLACE "${file_full}" " _file_full_ " file_ov "${file_ov}")
|
||||
string(TOLOWER "${file_ov}" file_ov)
|
||||
|
||||
#message(STATUS "file_ov='${file_ov}'")
|
||||
if("${file_ov}" MATCHES "executable")
|
||||
#message(STATUS "executable!")
|
||||
if("${file_ov}" MATCHES "text")
|
||||
#message(STATUS "but text, so *not* a binary executable!")
|
||||
else()
|
||||
set(${result_var} 1 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Also detect position independent executables on Linux,
|
||||
# where "file" gives "shared object ... (uses shared libraries)"
|
||||
if("${file_ov}" MATCHES "shared object.*\(uses shared libs\)")
|
||||
set(${result_var} 1 PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "warning: No 'file' command, skipping execute_process...")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(gp_item_default_embedded_path item default_embedded_path_var)
|
||||
|
||||
# On Windows and Linux, "embed" prerequisites in the same directory
|
||||
# as the executable by default:
|
||||
#
|
||||
set(path "@executable_path")
|
||||
set(overridden 0)
|
||||
|
||||
# On the Mac, relative to the executable depending on the type
|
||||
# of the thing we are embedding:
|
||||
#
|
||||
if(APPLE)
|
||||
#
|
||||
# The assumption here is that all executables in the bundle will be
|
||||
# in same-level-directories inside the bundle. The parent directory
|
||||
# of an executable inside the bundle should be MacOS or a sibling of
|
||||
# MacOS and all embedded paths returned from here will begin with
|
||||
# "@executable_path/../" and will work from all executables in all
|
||||
# such same-level-directories inside the bundle.
|
||||
#
|
||||
|
||||
# By default, embed things right next to the main bundle executable:
|
||||
#
|
||||
set(path "@executable_path/../../Contents/MacOS")
|
||||
|
||||
# Embed .dylibs right next to the main bundle executable:
|
||||
#
|
||||
if(item MATCHES "\\.dylib$")
|
||||
set(path "@executable_path/../MacOS")
|
||||
set(overridden 1)
|
||||
endif()
|
||||
|
||||
# Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS):
|
||||
#
|
||||
if(NOT overridden)
|
||||
if(item MATCHES "[^/]+\\.framework/")
|
||||
set(path "@executable_path/../Frameworks")
|
||||
set(overridden 1)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Provide a hook so that projects can override the default embedded location
|
||||
# of any given library by whatever logic they choose:
|
||||
#
|
||||
if(COMMAND gp_item_default_embedded_path_override)
|
||||
gp_item_default_embedded_path_override("${item}" path)
|
||||
endif()
|
||||
|
||||
set(${default_embedded_path_var} "${path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(gp_resolve_item context item exepath dirs resolved_item_var)
|
||||
set(resolved 0)
|
||||
set(resolved_item "${item}")
|
||||
|
||||
# Is it already resolved?
|
||||
#
|
||||
if(IS_ABSOLUTE "${resolved_item}" AND EXISTS "${resolved_item}")
|
||||
set(resolved 1)
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
if(item MATCHES "@executable_path")
|
||||
#
|
||||
# @executable_path references are assumed relative to exepath
|
||||
#
|
||||
string(REPLACE "@executable_path" "${exepath}" ri "${item}")
|
||||
get_filename_component(ri "${ri}" ABSOLUTE)
|
||||
|
||||
if(EXISTS "${ri}")
|
||||
#message(STATUS "info: embedded item exists (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
else()
|
||||
message(STATUS "warning: embedded item does not exist '${ri}'")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
if(item MATCHES "@loader_path")
|
||||
#
|
||||
# @loader_path references are assumed relative to the
|
||||
# PATH of the given "context" (presumably another library)
|
||||
#
|
||||
get_filename_component(contextpath "${context}" PATH)
|
||||
string(REPLACE "@loader_path" "${contextpath}" ri "${item}")
|
||||
get_filename_component(ri "${ri}" ABSOLUTE)
|
||||
|
||||
if(EXISTS "${ri}")
|
||||
#message(STATUS "info: embedded item exists (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
else()
|
||||
message(STATUS "warning: embedded item does not exist '${ri}'")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
if(item MATCHES "@rpath")
|
||||
#
|
||||
# @rpath references are relative to the paths built into the binaries with -rpath
|
||||
# We handle this case like we do for other Unixes
|
||||
#
|
||||
string(REPLACE "@rpath/" "" norpath_item "${item}")
|
||||
|
||||
set(ri "ri-NOTFOUND")
|
||||
find_file(ri "${norpath_item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
|
||||
if(ri)
|
||||
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
set(ri "ri-NOTFOUND")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
set(ri "ri-NOTFOUND")
|
||||
find_file(ri "${item}" ${exepath} ${dirs} NO_DEFAULT_PATH)
|
||||
find_file(ri "${item}" ${exepath} ${dirs} /usr/lib)
|
||||
if(ri)
|
||||
#message(STATUS "info: 'find_file' in exepath/dirs (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
set(ri "ri-NOTFOUND")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
if(item MATCHES "[^/]+\\.framework/")
|
||||
set(fw "fw-NOTFOUND")
|
||||
find_file(fw "${item}"
|
||||
"~/Library/Frameworks"
|
||||
"/Library/Frameworks"
|
||||
"/System/Library/Frameworks"
|
||||
)
|
||||
if(fw)
|
||||
#message(STATUS "info: 'find_file' found framework (${fw})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${fw}")
|
||||
set(fw "fw-NOTFOUND")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Using find_program on Windows will find dll files that are in the PATH.
|
||||
# (Converting simple file names into full path names if found.)
|
||||
#
|
||||
if(WIN32 AND NOT UNIX)
|
||||
if(NOT resolved)
|
||||
set(ri "ri-NOTFOUND")
|
||||
find_program(ri "${item}" PATHS "${exepath};${dirs}" NO_DEFAULT_PATH)
|
||||
find_program(ri "${item}" PATHS "${exepath};${dirs}")
|
||||
if(ri)
|
||||
#message(STATUS "info: 'find_program' in exepath/dirs (${ri})")
|
||||
set(resolved 1)
|
||||
set(resolved_item "${ri}")
|
||||
set(ri "ri-NOTFOUND")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Provide a hook so that projects can override item resolution
|
||||
# by whatever logic they choose:
|
||||
#
|
||||
if(COMMAND gp_resolve_item_override)
|
||||
gp_resolve_item_override("${context}" "${item}" "${exepath}" "${dirs}" resolved_item resolved)
|
||||
endif()
|
||||
|
||||
if(NOT resolved)
|
||||
message(STATUS "
|
||||
warning: cannot resolve item '${item}'
|
||||
|
||||
possible problems:
|
||||
need more directories?
|
||||
need to use InstallRequiredSystemLibraries?
|
||||
run in install tree instead of build tree?
|
||||
")
|
||||
# message(STATUS "
|
||||
#******************************************************************************
|
||||
#warning: cannot resolve item '${item}'
|
||||
#
|
||||
# possible problems:
|
||||
# need more directories?
|
||||
# need to use InstallRequiredSystemLibraries?
|
||||
# run in install tree instead of build tree?
|
||||
#
|
||||
# context='${context}'
|
||||
# item='${item}'
|
||||
# exepath='${exepath}'
|
||||
# dirs='${dirs}'
|
||||
# resolved_item_var='${resolved_item_var}'
|
||||
#******************************************************************************
|
||||
#")
|
||||
endif()
|
||||
|
||||
set(${resolved_item_var} "${resolved_item}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(gp_resolved_file_type original_file file exepath dirs type_var)
|
||||
#message(STATUS "**")
|
||||
|
||||
if(NOT IS_ABSOLUTE "${original_file}")
|
||||
message(STATUS "warning: gp_resolved_file_type expects absolute full path for first arg original_file")
|
||||
endif()
|
||||
|
||||
set(is_embedded 0)
|
||||
set(is_local 0)
|
||||
set(is_system 0)
|
||||
|
||||
set(resolved_file "${file}")
|
||||
|
||||
if("${file}" MATCHES "^@(executable|loader)_path")
|
||||
set(is_embedded 1)
|
||||
endif()
|
||||
|
||||
if(NOT is_embedded)
|
||||
if(NOT IS_ABSOLUTE "${file}")
|
||||
gp_resolve_item("${original_file}" "${file}" "${exepath}" "${dirs}" resolved_file)
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${original_file}" original_lower)
|
||||
string(TOLOWER "${resolved_file}" lower)
|
||||
|
||||
if(UNIX)
|
||||
if(resolved_file MATCHES "^(/lib/|/lib32/|/lib64/|/usr/lib/|/usr/lib32/|/usr/lib64/|/usr/X11R6/|/usr/bin/)")
|
||||
set(is_system 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(resolved_file MATCHES "^(/System/Library/|/usr/lib/)")
|
||||
set(is_system 1)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
string(TOLOWER "$ENV{SystemRoot}" sysroot)
|
||||
string(REGEX REPLACE "\\\\" "/" sysroot "${sysroot}")
|
||||
|
||||
string(TOLOWER "$ENV{windir}" windir)
|
||||
string(REGEX REPLACE "\\\\" "/" windir "${windir}")
|
||||
|
||||
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
|
||||
set(is_system 1)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
# if cygwin, we can get the properly formed windows paths from cygpath
|
||||
find_program(CYGPATH_EXECUTABLE cygpath)
|
||||
|
||||
if(CYGPATH_EXECUTABLE)
|
||||
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -W
|
||||
OUTPUT_VARIABLE env_windir
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
execute_process(COMMAND ${CYGPATH_EXECUTABLE} -S
|
||||
OUTPUT_VARIABLE env_sysdir
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
string(TOLOWER "${env_windir}" windir)
|
||||
string(TOLOWER "${env_sysdir}" sysroot)
|
||||
|
||||
if(lower MATCHES "^(${sysroot}/sys(tem|wow)|${windir}/sys(tem|wow)|(.*/)*msvc[^/]+dll)")
|
||||
set(is_system 1)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT is_system)
|
||||
get_filename_component(original_path "${original_lower}" PATH)
|
||||
get_filename_component(path "${lower}" PATH)
|
||||
if("${original_path}" STREQUAL "${path}")
|
||||
set(is_local 1)
|
||||
else()
|
||||
string(LENGTH "${original_path}/" original_length)
|
||||
string(LENGTH "${lower}" path_length)
|
||||
if(${path_length} GREATER ${original_length})
|
||||
string(SUBSTRING "${lower}" 0 ${original_length} path)
|
||||
if("${original_path}/" STREQUAL "${path}")
|
||||
set(is_embedded 1)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Return type string based on computed booleans:
|
||||
#
|
||||
set(type "other")
|
||||
|
||||
if(is_system)
|
||||
set(type "system")
|
||||
elseif(is_embedded)
|
||||
set(type "embedded")
|
||||
elseif(is_local)
|
||||
set(type "local")
|
||||
endif()
|
||||
|
||||
#message(STATUS "gp_resolved_file_type: '${file}' '${resolved_file}'")
|
||||
#message(STATUS " type: '${type}'")
|
||||
|
||||
if(NOT is_embedded)
|
||||
if(NOT IS_ABSOLUTE "${resolved_file}")
|
||||
if(lower MATCHES "^msvc[^/]+dll" AND is_system)
|
||||
message(STATUS "info: non-absolute msvc file '${file}' returning type '${type}'")
|
||||
else()
|
||||
message(STATUS "warning: gp_resolved_file_type non-absolute file '${file}' returning type '${type}' -- possibly incorrect")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Provide a hook so that projects can override the decision on whether a
|
||||
# library belongs to the system or not by whatever logic they choose:
|
||||
#
|
||||
if(COMMAND gp_resolved_file_type_override)
|
||||
gp_resolved_file_type_override("${resolved_file}" type)
|
||||
endif()
|
||||
|
||||
set(${type_var} "${type}" PARENT_SCOPE)
|
||||
|
||||
#message(STATUS "**")
|
||||
endfunction()
|
||||
|
||||
|
||||
function(gp_file_type original_file file type_var)
|
||||
if(NOT IS_ABSOLUTE "${original_file}")
|
||||
message(STATUS "warning: gp_file_type expects absolute full path for first arg original_file")
|
||||
endif()
|
||||
|
||||
get_filename_component(exepath "${original_file}" PATH)
|
||||
|
||||
set(type "")
|
||||
gp_resolved_file_type("${original_file}" "${file}" "${exepath}" "" type)
|
||||
|
||||
set(${type_var} "${type}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(get_prerequisites target prerequisites_var exclude_system recurse exepath dirs)
|
||||
set(verbose 0)
|
||||
set(eol_char "E")
|
||||
|
||||
if(NOT IS_ABSOLUTE "${target}")
|
||||
message("warning: target '${target}' is not absolute...")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${target}")
|
||||
message("warning: target '${target}' does not exist...")
|
||||
endif()
|
||||
|
||||
set(gp_cmd_paths ${gp_cmd_paths}
|
||||
"C:/Program Files/Microsoft Visual Studio 9.0/VC/bin"
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin"
|
||||
"C:/Program Files/Microsoft Visual Studio 8/VC/BIN"
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN"
|
||||
"C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN"
|
||||
"C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN"
|
||||
"/usr/local/bin"
|
||||
"/usr/bin"
|
||||
)
|
||||
|
||||
# <setup-gp_tool-vars>
|
||||
#
|
||||
# Try to choose the right tool by default. Caller can set gp_tool prior to
|
||||
# calling this function to force using a different tool.
|
||||
#
|
||||
if("${gp_tool}" STREQUAL "")
|
||||
set(gp_tool "ldd")
|
||||
|
||||
if(APPLE)
|
||||
set(gp_tool "otool")
|
||||
endif()
|
||||
|
||||
if(WIN32 AND NOT UNIX) # This is how to check for cygwin, har!
|
||||
find_program(gp_dumpbin "dumpbin" PATHS ${gp_cmd_paths})
|
||||
if(gp_dumpbin)
|
||||
set(gp_tool "dumpbin")
|
||||
else() # Try harder. Maybe we're on MinGW
|
||||
set(gp_tool "objdump")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_program(gp_cmd ${gp_tool} PATHS ${gp_cmd_paths})
|
||||
|
||||
if(NOT gp_cmd)
|
||||
message(FATAL_ERROR "FATAL ERROR: could not find '${gp_tool}' - cannot analyze prerequisites!")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(gp_tool_known 0)
|
||||
|
||||
if("${gp_tool}" STREQUAL "ldd")
|
||||
set(gp_cmd_args "")
|
||||
set(gp_regex "^[\t ]*[^\t ]+ => ([^\t\(]+) .*${eol_char}$")
|
||||
set(gp_regex_error "not found${eol_char}$")
|
||||
set(gp_regex_fallback "^[\t ]*([^\t ]+) => ([^\t ]+).*${eol_char}$")
|
||||
set(gp_regex_cmp_count 1)
|
||||
set(gp_tool_known 1)
|
||||
endif()
|
||||
|
||||
if("${gp_tool}" STREQUAL "otool")
|
||||
set(gp_cmd_args "-L")
|
||||
set(gp_regex "^\t([^\t]+) \\(compatibility version ([0-9]+.[0-9]+.[0-9]+), current version ([0-9]+.[0-9]+.[0-9]+)\\)${eol_char}$")
|
||||
set(gp_regex_error "")
|
||||
set(gp_regex_fallback "")
|
||||
set(gp_regex_cmp_count 3)
|
||||
set(gp_tool_known 1)
|
||||
endif()
|
||||
|
||||
if("${gp_tool}" STREQUAL "dumpbin")
|
||||
set(gp_cmd_args "/dependents")
|
||||
set(gp_regex "^ ([^ ].*[Dd][Ll][Ll])${eol_char}$")
|
||||
set(gp_regex_error "")
|
||||
set(gp_regex_fallback "")
|
||||
set(gp_regex_cmp_count 1)
|
||||
set(gp_tool_known 1)
|
||||
endif()
|
||||
|
||||
if("${gp_tool}" STREQUAL "objdump")
|
||||
set(gp_cmd_args "-p")
|
||||
set(gp_regex "^\t*DLL Name: (.*\\.[Dd][Ll][Ll])${eol_char}$")
|
||||
set(gp_regex_error "")
|
||||
set(gp_regex_fallback "")
|
||||
set(gp_regex_cmp_count 1)
|
||||
set(gp_tool_known 1)
|
||||
endif()
|
||||
|
||||
if(NOT gp_tool_known)
|
||||
message(STATUS "warning: gp_tool='${gp_tool}' is an unknown tool...")
|
||||
message(STATUS "CMake function get_prerequisites needs more code to handle '${gp_tool}'")
|
||||
message(STATUS "Valid gp_tool values are dumpbin, ldd, objdump and otool.")
|
||||
return()
|
||||
endif()
|
||||
|
||||
|
||||
if("${gp_tool}" STREQUAL "dumpbin")
|
||||
# When running dumpbin, it also needs the "Common7/IDE" directory in the
|
||||
# PATH. It will already be in the PATH if being run from a Visual Studio
|
||||
# command prompt. Add it to the PATH here in case we are running from a
|
||||
# different command prompt.
|
||||
#
|
||||
get_filename_component(gp_cmd_dir "${gp_cmd}" PATH)
|
||||
get_filename_component(gp_cmd_dlls_dir "${gp_cmd_dir}/../../Common7/IDE" ABSOLUTE)
|
||||
# Use cmake paths as a user may have a PATH element ending with a backslash.
|
||||
# This will escape the list delimiter and create havoc!
|
||||
if(EXISTS "${gp_cmd_dlls_dir}")
|
||||
# only add to the path if it is not already in the path
|
||||
set(gp_found_cmd_dlls_dir 0)
|
||||
file(TO_CMAKE_PATH "$ENV{PATH}" env_path)
|
||||
foreach(gp_env_path_element ${env_path})
|
||||
if("${gp_env_path_element}" STREQUAL "${gp_cmd_dlls_dir}")
|
||||
set(gp_found_cmd_dlls_dir 1)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT gp_found_cmd_dlls_dir)
|
||||
file(TO_NATIVE_PATH "${gp_cmd_dlls_dir}" gp_cmd_dlls_dir)
|
||||
set(ENV{PATH} "$ENV{PATH};${gp_cmd_dlls_dir}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
#
|
||||
# </setup-gp_tool-vars>
|
||||
|
||||
if("${gp_tool}" STREQUAL "ldd")
|
||||
set(old_ld_env "$ENV{LD_LIBRARY_PATH}")
|
||||
foreach(dir ${exepath} ${dirs})
|
||||
set(ENV{LD_LIBRARY_PATH} "${dir}:$ENV{LD_LIBRARY_PATH}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
# Track new prerequisites at each new level of recursion. Start with an
|
||||
# empty list at each level:
|
||||
#
|
||||
set(unseen_prereqs)
|
||||
|
||||
# Run gp_cmd on the target:
|
||||
#
|
||||
execute_process(
|
||||
COMMAND ${gp_cmd} ${gp_cmd_args} ${target}
|
||||
OUTPUT_VARIABLE gp_cmd_ov
|
||||
)
|
||||
|
||||
if("${gp_tool}" STREQUAL "ldd")
|
||||
set(ENV{LD_LIBRARY_PATH} "${old_ld_env}")
|
||||
endif()
|
||||
|
||||
if(verbose)
|
||||
message(STATUS "<RawOutput cmd='${gp_cmd} ${gp_cmd_args} ${target}'>")
|
||||
message(STATUS "gp_cmd_ov='${gp_cmd_ov}'")
|
||||
message(STATUS "</RawOutput>")
|
||||
endif()
|
||||
|
||||
get_filename_component(target_dir "${target}" PATH)
|
||||
|
||||
# Convert to a list of lines:
|
||||
#
|
||||
string(REGEX REPLACE ";" "\\\\;" candidates "${gp_cmd_ov}")
|
||||
string(REGEX REPLACE "\n" "${eol_char};" candidates "${candidates}")
|
||||
|
||||
# check for install id and remove it from list, since otool -L can include a
|
||||
# reference to itself
|
||||
set(gp_install_id)
|
||||
if("${gp_tool}" STREQUAL "otool")
|
||||
execute_process(
|
||||
COMMAND otool -D ${target}
|
||||
OUTPUT_VARIABLE gp_install_id_ov
|
||||
)
|
||||
# second line is install name
|
||||
string(REGEX REPLACE ".*:\n" "" gp_install_id "${gp_install_id_ov}")
|
||||
if(gp_install_id)
|
||||
# trim
|
||||
string(REGEX MATCH "[^\n ].*[^\n ]" gp_install_id "${gp_install_id}")
|
||||
#message("INSTALL ID is \"${gp_install_id}\"")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Analyze each line for file names that match the regular expression:
|
||||
#
|
||||
foreach(candidate ${candidates})
|
||||
if("${candidate}" MATCHES "${gp_regex}")
|
||||
|
||||
# Extract information from each candidate:
|
||||
if(gp_regex_error AND "${candidate}" MATCHES "${gp_regex_error}")
|
||||
string(REGEX REPLACE "${gp_regex_fallback}" "\\1" raw_item "${candidate}")
|
||||
else()
|
||||
string(REGEX REPLACE "${gp_regex}" "\\1" raw_item "${candidate}")
|
||||
endif()
|
||||
|
||||
if(gp_regex_cmp_count GREATER 1)
|
||||
string(REGEX REPLACE "${gp_regex}" "\\2" raw_compat_version "${candidate}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" compat_major_version "${raw_compat_version}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" compat_minor_version "${raw_compat_version}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" compat_patch_version "${raw_compat_version}")
|
||||
endif()
|
||||
|
||||
if(gp_regex_cmp_count GREATER 2)
|
||||
string(REGEX REPLACE "${gp_regex}" "\\3" raw_current_version "${candidate}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\1" current_major_version "${raw_current_version}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\2" current_minor_version "${raw_current_version}")
|
||||
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$" "\\3" current_patch_version "${raw_current_version}")
|
||||
endif()
|
||||
|
||||
# Use the raw_item as the list entries returned by this function. Use the
|
||||
# gp_resolve_item function to resolve it to an actual full path file if
|
||||
# necessary.
|
||||
#
|
||||
set(item "${raw_item}")
|
||||
|
||||
# Add each item unless it is excluded:
|
||||
#
|
||||
set(add_item 1)
|
||||
|
||||
if("${item}" STREQUAL "${gp_install_id}")
|
||||
set(add_item 0)
|
||||
endif()
|
||||
|
||||
if(add_item AND ${exclude_system})
|
||||
set(type "")
|
||||
gp_resolved_file_type("${target}" "${item}" "${exepath}" "${dirs}" type)
|
||||
|
||||
if("${type}" STREQUAL "system")
|
||||
set(add_item 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(add_item)
|
||||
list(LENGTH ${prerequisites_var} list_length_before_append)
|
||||
gp_append_unique(${prerequisites_var} "${item}")
|
||||
list(LENGTH ${prerequisites_var} list_length_after_append)
|
||||
|
||||
if(${recurse})
|
||||
# If item was really added, this is the first time we have seen it.
|
||||
# Add it to unseen_prereqs so that we can recursively add *its*
|
||||
# prerequisites...
|
||||
#
|
||||
# But first: resolve its name to an absolute full path name such
|
||||
# that the analysis tools can simply accept it as input.
|
||||
#
|
||||
if(NOT list_length_before_append EQUAL list_length_after_append)
|
||||
gp_resolve_item("${target}" "${item}" "${exepath}" "${dirs}" resolved_item)
|
||||
set(unseen_prereqs ${unseen_prereqs} "${resolved_item}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
if(verbose)
|
||||
message(STATUS "ignoring non-matching line: '${candidate}'")
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
list(LENGTH ${prerequisites_var} prerequisites_var_length)
|
||||
if(prerequisites_var_length GREATER 0)
|
||||
list(SORT ${prerequisites_var})
|
||||
endif()
|
||||
if(${recurse})
|
||||
set(more_inputs ${unseen_prereqs})
|
||||
foreach(input ${more_inputs})
|
||||
get_prerequisites("${input}" ${prerequisites_var} ${exclude_system} ${recurse} "${exepath}" "${dirs}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(${prerequisites_var} ${${prerequisites_var}} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
function(list_prerequisites target)
|
||||
if("${ARGV1}" STREQUAL "")
|
||||
set(all 1)
|
||||
else()
|
||||
set(all "${ARGV1}")
|
||||
endif()
|
||||
|
||||
if("${ARGV2}" STREQUAL "")
|
||||
set(exclude_system 0)
|
||||
else()
|
||||
set(exclude_system "${ARGV2}")
|
||||
endif()
|
||||
|
||||
if("${ARGV3}" STREQUAL "")
|
||||
set(verbose 0)
|
||||
else()
|
||||
set(verbose "${ARGV3}")
|
||||
endif()
|
||||
|
||||
set(count 0)
|
||||
set(count_str "")
|
||||
set(print_count "${verbose}")
|
||||
set(print_prerequisite_type "${verbose}")
|
||||
set(print_target "${verbose}")
|
||||
set(type_str "")
|
||||
|
||||
get_filename_component(exepath "${target}" PATH)
|
||||
|
||||
set(prereqs "")
|
||||
get_prerequisites("${target}" prereqs ${exclude_system} ${all} "${exepath}" "")
|
||||
|
||||
if(print_target)
|
||||
message(STATUS "File '${target}' depends on:")
|
||||
endif()
|
||||
|
||||
foreach(d ${prereqs})
|
||||
math(EXPR count "${count} + 1")
|
||||
|
||||
if(print_count)
|
||||
set(count_str "${count}. ")
|
||||
endif()
|
||||
|
||||
if(print_prerequisite_type)
|
||||
gp_file_type("${target}" "${d}" type)
|
||||
set(type_str " (${type})")
|
||||
endif()
|
||||
|
||||
message(STATUS "${count_str}${d}${type_str}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
|
||||
function(list_prerequisites_by_glob glob_arg glob_exp)
|
||||
message(STATUS "=============================================================================")
|
||||
message(STATUS "List prerequisites of executables matching ${glob_arg} '${glob_exp}'")
|
||||
message(STATUS "")
|
||||
file(${glob_arg} file_list ${glob_exp})
|
||||
foreach(f ${file_list})
|
||||
is_file_executable("${f}" is_f_executable)
|
||||
if(is_f_executable)
|
||||
message(STATUS "=============================================================================")
|
||||
list_prerequisites("${f}" ${ARGN})
|
||||
message(STATUS "")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
37
cmake/GitFunctions.cmake
Normal file
37
cmake/GitFunctions.cmake
Normal file
@@ -0,0 +1,37 @@
|
||||
if(__GITFUNCTIONS_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__GITFUNCTIONS_CMAKE__ TRUE)
|
||||
|
||||
find_package(Git QUIET)
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
if(GIT_FOUND)
|
||||
function(git_run)
|
||||
set(oneValueArgs OUTPUT_VAR DEFAULT)
|
||||
set(multiValueArgs COMMAND)
|
||||
cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} ${GIT_RUN_COMMAND}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_RESULTVAR
|
||||
OUTPUT_VARIABLE GIT_OUTVAR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(GIT_RESULTVAR EQUAL 0)
|
||||
set(${GIT_RUN_OUTPUT_VAR} "${GIT_OUTVAR}" PARENT_SCOPE)
|
||||
else()
|
||||
set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT})
|
||||
message(STATUS "Failed to run Git: ${GIT_OUTVAR}")
|
||||
endif()
|
||||
endfunction()
|
||||
else()
|
||||
function(git_run)
|
||||
set(oneValueArgs OUTPUT_VAR DEFAULT)
|
||||
set(multiValueArgs COMMAND)
|
||||
cmake_parse_arguments(GIT_RUN "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
set(${GIT_RUN_OUTPUT_VAR} ${GIT_RUN_DEFAULT})
|
||||
endfunction(git_run)
|
||||
endif()
|
||||
14
cmake/QMakeQuery.cmake
Normal file
14
cmake/QMakeQuery.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
if(__QMAKEQUERY_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__QMAKEQUERY_CMAKE__ TRUE)
|
||||
|
||||
get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
|
||||
|
||||
function(QUERY_QMAKE VAR RESULT)
|
||||
exec_program(${QMAKE_EXECUTABLE} ARGS "-query ${VAR}" RETURN_VALUE return_code OUTPUT_VARIABLE output )
|
||||
if(NOT return_code)
|
||||
file(TO_CMAKE_PATH "${output}" output)
|
||||
set(${RESULT} ${output} PARENT_SCOPE)
|
||||
endif(NOT return_code)
|
||||
endfunction(QUERY_QMAKE)
|
||||
13
cmake/UseCXX11.cmake
Normal file
13
cmake/UseCXX11.cmake
Normal file
@@ -0,0 +1,13 @@
|
||||
if(__USECXX11_CMAKE__)
|
||||
return()
|
||||
endif()
|
||||
set(__USECXX11_CMAKE__ TRUE)
|
||||
|
||||
if(APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++ -std=c++11")
|
||||
elseif(UNIX)
|
||||
# assume GCC, add C++0x/C++11 stuff
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
elseif(MINGW)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
|
||||
endif()
|
||||
36
config.h.in
36
config.h.in
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Minor and major version, used to communicate changes to users.
|
||||
#define VERSION_MAJOR @MultiMC_VERSION_MAJOR@
|
||||
#define VERSION_MINOR @MultiMC_VERSION_MINOR@
|
||||
|
||||
// Build number, channel, and type -- number and channel are used by the updater, type is purely visual
|
||||
#define VERSION_BUILD @MultiMC_VERSION_BUILD@
|
||||
#define VERSION_CHANNEL "@MultiMC_VERSION_CHANNEL@"
|
||||
#define VERSION_BUILD_TYPE "@MultiMC_VERSION_BUILD_TYPE@"
|
||||
|
||||
// URL for the updater's channel
|
||||
#define CHANLIST_URL "@MultiMC_CHANLIST_URL@"
|
||||
|
||||
// URL for notifications
|
||||
#define NOTIFICATION_URL "@MultiMC_NOTIFICATION_URL@"
|
||||
|
||||
// Used for matching notifications
|
||||
#define FULL_VERSION_STR "@MultiMC_VERSION_MAJOR@.@MultiMC_VERSION_MINOR@.@MultiMC_VERSION_BUILD@"
|
||||
|
||||
// enabled for updater dry run
|
||||
#cmakedefine MultiMC_UPDATER_DRY_RUN
|
||||
|
||||
// enabled for updater dry run
|
||||
#cmakedefine MultiMC_UPDATER_FORCE_LOCAL
|
||||
|
||||
// The commit hash of this build
|
||||
#define GIT_COMMIT "@MultiMC_GIT_COMMIT@"
|
||||
|
||||
// This is printed on start to standard output
|
||||
#define VERSION_STR "@MultiMC_VERSION_STRING@"
|
||||
|
||||
// This is used to fetch the news RSS feed.
|
||||
// It defaults in CMakeLists.txt to "http://multimc.org/rss.xml"
|
||||
#define NEWS_RSS_URL "@MultiMC_NEWS_RSS_URL@"
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.8.9)
|
||||
|
||||
message(STATUS "Running install script...")
|
||||
|
||||
SET(Qt5_DIR @Qt5_DIR@)
|
||||
|
||||
IF(WIN32)
|
||||
SET(LIB_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
|
||||
ELSE()
|
||||
SET(LIB_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}/lib)
|
||||
ENDIF()
|
||||
|
||||
INCLUDE(GetPrerequisites)
|
||||
GET_PREREQUISITES(@BINARY_LOCATION@ MULTIMC_PREREQS 1 1 "" "")
|
||||
|
||||
message(STATUS "Prerequisites: ${MULTIMC_PREREQS}")
|
||||
|
||||
FOREACH(PREREQ ${MULTIMC_PREREQS})
|
||||
GET_FILENAME_COMPONENT(PREREQ_NAME "${PREREQ}" NAME)
|
||||
GET_FILENAME_COMPONENT(PREREQ_ACTUAL "${PREREQ}" REALPATH)
|
||||
IF(WIN32)
|
||||
SET(PREREQ_ACTUAL "${Qt5_DIR}/bin/${PREREQ}")
|
||||
ENDIF()
|
||||
|
||||
message(STATUS "Adding install prerequisite: ${PREREQ_NAME}")
|
||||
|
||||
FILE(INSTALL
|
||||
DESTINATION "${LIB_INSTALL_PREFIX}"
|
||||
TYPE PROGRAM
|
||||
RENAME "${PREREQ_NAME}"
|
||||
FILES "${PREREQ_ACTUAL}"
|
||||
)
|
||||
ENDFOREACH()
|
||||
@@ -8,7 +8,7 @@ find_package(Qt5Core REQUIRED)
|
||||
# Include Qt headers.
|
||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
||||
|
||||
SET(CLASSPARSER_HEADERS
|
||||
set(CLASSPARSER_HEADERS
|
||||
include/classparser_config.h
|
||||
|
||||
# Public headers
|
||||
@@ -23,13 +23,13 @@ src/javaendian.h
|
||||
src/membuffer.h
|
||||
)
|
||||
|
||||
SET(CLASSPARSER_SOURCES
|
||||
set(CLASSPARSER_SOURCES
|
||||
src/javautils.cpp
|
||||
src/annotations.cpp
|
||||
)
|
||||
|
||||
# Set the include dir path.
|
||||
SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
set(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
|
||||
# Include self.
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
project(libGroupView)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
# Find Qt
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
|
||||
# Include Qt headers.
|
||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
||||
|
||||
SET(LIBGROUPVIEW_HEADERS
|
||||
include/groupview_config.h
|
||||
|
||||
# Public headers
|
||||
include/categorizedsortfilterproxymodel.h
|
||||
include/categorizedview.h
|
||||
include/categorydrawer.h
|
||||
|
||||
# Private headers
|
||||
src/categorizedsortfilterproxymodel_p.h
|
||||
src/categorizedview_p.h
|
||||
)
|
||||
|
||||
SET(LIBGROUPVIEW_SOURCES
|
||||
src/categorizedsortfilterproxymodel.cpp
|
||||
src/categorizedview.cpp
|
||||
src/categorydrawer.cpp
|
||||
)
|
||||
|
||||
# Set the include dir path.
|
||||
SET(LIBGROUPVIEW_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
|
||||
# Include self.
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||
include_directories(${CMAKE_BINARY_DIR}/include)
|
||||
|
||||
# Static link!
|
||||
ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC)
|
||||
|
||||
add_definitions(-DLIBGROUPVIEW_LIBRARY)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
add_library(libGroupView STATIC ${LIBGROUPVIEW_SOURCES} ${LIBGROUPVIEW_HEADERS})
|
||||
qt5_use_modules(libGroupView Core Widgets)
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
||||
#define KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <groupview_config.h>
|
||||
|
||||
class QItemSelection;
|
||||
|
||||
|
||||
/**
|
||||
* This class lets you categorize a view. It is meant to be used along with
|
||||
* KCategorizedView class.
|
||||
*
|
||||
* In general terms all you need to do is to reimplement subSortLessThan() and
|
||||
* compareCategories() methods. In order to make categorization work, you need
|
||||
* to also call setCategorizedModel() class to enable it, since the categorization
|
||||
* is disabled by default.
|
||||
*
|
||||
* @see KCategorizedView
|
||||
*
|
||||
* @author Rafael Fernández López <ereslibre@kde.org>
|
||||
*/
|
||||
class LIBGROUPVIEW_EXPORT KCategorizedSortFilterProxyModel
|
||||
: public QSortFilterProxyModel
|
||||
{
|
||||
public:
|
||||
enum AdditionalRoles
|
||||
{
|
||||
// Note: use printf "0x%08X\n" $(($RANDOM*$RANDOM))
|
||||
// to define additional roles.
|
||||
CategoryDisplayRole = 0x17CE990A, ///< This role is used for asking the category to a given index
|
||||
|
||||
CategorySortRole = 0x27857E60 ///< This role is used for sorting categories. You can return a
|
||||
///< string or a long long value. Strings will be sorted alphabetically
|
||||
///< while long long will be sorted by their value. Please note that this
|
||||
///< value won't be shown on the view, is only for sorting purposes. What will
|
||||
///< be shown as "Category" on the view will be asked with the role
|
||||
///< CategoryDisplayRole.
|
||||
};
|
||||
|
||||
KCategorizedSortFilterProxyModel ( QObject *parent = 0 );
|
||||
virtual ~KCategorizedSortFilterProxyModel();
|
||||
|
||||
/**
|
||||
* Overridden from QSortFilterProxyModel. Sorts the source model using
|
||||
* @p column for the given @p order.
|
||||
*/
|
||||
virtual void sort ( int column, Qt::SortOrder order = Qt::AscendingOrder );
|
||||
|
||||
/**
|
||||
* @return whether the model is categorized or not. Disabled by default.
|
||||
*/
|
||||
bool isCategorizedModel() const;
|
||||
|
||||
/**
|
||||
* Enables or disables the categorization feature.
|
||||
*
|
||||
* @param categorizedModel whether to enable or disable the categorization feature.
|
||||
*/
|
||||
void setCategorizedModel ( bool categorizedModel );
|
||||
|
||||
/**
|
||||
* @return the column being used for sorting.
|
||||
*/
|
||||
int sortColumn() const;
|
||||
|
||||
/**
|
||||
* @return the sort order being used for sorting.
|
||||
*/
|
||||
Qt::SortOrder sortOrder() const;
|
||||
|
||||
/**
|
||||
* Set if the sorting using CategorySortRole will use a natural comparison
|
||||
* in the case that strings were returned. If enabled, QString::localeAwareCompare
|
||||
* will be used for sorting.
|
||||
*
|
||||
* @param sortCategoriesByNaturalComparison whether to sort using a natural comparison or not.
|
||||
*/
|
||||
void setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison );
|
||||
|
||||
/**
|
||||
* @return whether it is being used a natural comparison for sorting. Enabled by default.
|
||||
*/
|
||||
bool sortCategoriesByNaturalComparison() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Overridden from QSortFilterProxyModel. If you are subclassing
|
||||
* KCategorizedSortFilterProxyModel, you will probably not need to reimplement this
|
||||
* method.
|
||||
*
|
||||
* It calls compareCategories() to sort by category. If the both items are in the
|
||||
* same category (i.e. compareCategories returns 0), then subSortLessThan is called.
|
||||
*
|
||||
* @return Returns true if the item @p left is less than the item @p right when sorting.
|
||||
*
|
||||
* @warning You usually won't need to reimplement this method when subclassing
|
||||
* from KCategorizedSortFilterProxyModel.
|
||||
*/
|
||||
virtual bool lessThan ( const QModelIndex &left, const QModelIndex &right ) const;
|
||||
|
||||
/**
|
||||
* This method has a similar purpose as lessThan() has on QSortFilterProxyModel.
|
||||
* It is used for sorting items that are in the same category.
|
||||
*
|
||||
* @return Returns true if the item @p left is less than the item @p right when sorting.
|
||||
*/
|
||||
virtual bool subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const;
|
||||
|
||||
/**
|
||||
* This method compares the category of the @p left index with the category
|
||||
* of the @p right index.
|
||||
*
|
||||
* Internally and if not reimplemented, this method will ask for @p left and
|
||||
* @p right models for role CategorySortRole. In order to correctly sort
|
||||
* categories, the data() metod of the model should return a qlonglong (or numeric) value, or
|
||||
* a QString object. QString objects will be sorted with QString::localeAwareCompare if
|
||||
* sortCategoriesByNaturalComparison() is true.
|
||||
*
|
||||
* @note Please have present that:
|
||||
* QString(QChar(QChar::ObjectReplacementCharacter)) >
|
||||
* QString(QChar(QChar::ReplacementCharacter)) >
|
||||
* [ all possible strings ] >
|
||||
* QString();
|
||||
*
|
||||
* This means that QString() will be sorted the first one, while
|
||||
* QString(QChar(QChar::ObjectReplacementCharacter)) and
|
||||
* QString(QChar(QChar::ReplacementCharacter)) will be sorted in last
|
||||
* position.
|
||||
*
|
||||
* @warning Please note that data() method of the model should return always
|
||||
* information of the same type. If you return a QString for an index,
|
||||
* you should return always QStrings for all indexes for role CategorySortRole
|
||||
* in order to correctly sort categories. You can't mix by returning
|
||||
* a QString for one index, and a qlonglong for other.
|
||||
*
|
||||
* @note If you need a more complex layout, you will have to reimplement this
|
||||
* method.
|
||||
*
|
||||
* @return A negative value if the category of @p left should be placed before the
|
||||
* category of @p right. 0 if @p left and @p right are on the same category, and
|
||||
* a positive value if the category of @p left should be placed after the
|
||||
* category of @p right.
|
||||
*/
|
||||
virtual int compareCategories ( const QModelIndex &left, const QModelIndex &right ) const;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
|
||||
#endif // KCATEGORIZEDSORTFILTERPROXYMODEL_H
|
||||
@@ -1,332 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KCATEGORIZEDVIEW_H
|
||||
#define KCATEGORIZEDVIEW_H
|
||||
|
||||
#include <QListView>
|
||||
|
||||
#include <groupview_config.h>
|
||||
|
||||
class KCategoryDrawer;
|
||||
|
||||
/**
|
||||
* @short Item view for listing items in a categorized fashion optionally
|
||||
*
|
||||
* KCategorizedView basically has the same functionality as QListView, only that it also lets you
|
||||
* layout items in a way that they are categorized visually.
|
||||
*
|
||||
* For it to work you will need to set a KCategorizedSortFilterProxyModel and a KCategoryDrawer
|
||||
* with methods setModel() and setCategoryDrawer() respectively. Also, the model will need to be
|
||||
* flagged as categorized with KCategorizedSortFilterProxyModel::setCategorizedModel(true).
|
||||
*
|
||||
* The way it works (if categorization enabled):
|
||||
*
|
||||
* - When sorting, it does more things than QListView does. It will ask the model for the
|
||||
* special role CategorySortRole (@see KCategorizedSortFilterProxyModel). This can return
|
||||
* a QString or an int in order to tell the view the order of categories. In this sense, for
|
||||
* instance, if we are sorting by name ascending, "A" would be before than "B". If we are
|
||||
* sorting by size ascending, 512 bytes would be before 1024 bytes. This way categories are
|
||||
* also sorted.
|
||||
*
|
||||
* - When the view has to paint, it will ask the model with the role CategoryDisplayRole
|
||||
* (@see KCategorizedSortFilterProxyModel). It will for instance return "F" for "foo.pdf" if
|
||||
* we are sorting by name ascending, or "Small" if a certain item has 100 bytes, for example.
|
||||
*
|
||||
* For drawing categories, KCategoryDrawer will be used. You can inherit this class to do your own
|
||||
* drawing.
|
||||
*
|
||||
* @note All examples cited before talk about filesystems and such, but have present that this
|
||||
* is a completely generic class, and it can be used for whatever your purpose is. For
|
||||
* instance when talking about animals, you can separate them by "Mammal" and "Oviparous". In
|
||||
* this very case, for example, the CategorySortRole and the CategoryDisplayRole could be the
|
||||
* same ("Mammal" and "Oviparous").
|
||||
*
|
||||
* @note There is a really performance boost if CategorySortRole returns an int instead of a QString.
|
||||
* Have present that this role is asked (n * log n) times when sorting and compared. Comparing
|
||||
* ints is always faster than comparing strings, whithout mattering how fast the string
|
||||
* comparison is. Consider thinking of a way of returning ints instead of QStrings if your
|
||||
* model can contain a high number of items.
|
||||
*
|
||||
* @warning Note that for really drawing items in blocks you will need some things to be done:
|
||||
* - The model set to this view has to be (or inherit if you want to do special stuff
|
||||
* in it) KCategorizedSortFilterProxyModel.
|
||||
* - This model needs to be set setCategorizedModel to true.
|
||||
* - Set a category drawer by calling setCategoryDrawer.
|
||||
*
|
||||
* @see KCategorizedSortFilterProxyModel, KCategoryDrawer
|
||||
*
|
||||
* @author Rafael Fernández López <ereslibre@kde.org>
|
||||
*/
|
||||
class LIBGROUPVIEW_EXPORT KCategorizedView
|
||||
: public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY ( int categorySpacing READ categorySpacing WRITE setCategorySpacing )
|
||||
Q_PROPERTY ( bool alternatingBlockColors READ alternatingBlockColors WRITE setAlternatingBlockColors )
|
||||
Q_PROPERTY ( bool collapsibleBlocks READ collapsibleBlocks WRITE setCollapsibleBlocks )
|
||||
|
||||
public:
|
||||
KCategorizedView ( QWidget *parent = 0 );
|
||||
|
||||
~KCategorizedView();
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void setModel ( QAbstractItemModel *model );
|
||||
|
||||
/**
|
||||
* Calls to setGridSizeOwn().
|
||||
*/
|
||||
void setGridSize ( const QSize &size );
|
||||
|
||||
/**
|
||||
* @warning note that setGridSize is not virtual in the base class (QListView), so if you are
|
||||
* calling to this method, make sure you have a KCategorizedView pointer around. This
|
||||
* means that something like:
|
||||
* @code
|
||||
* QListView *lv = new KCategorizedView();
|
||||
* lv->setGridSize(mySize);
|
||||
* @endcode
|
||||
*
|
||||
* will not call to the expected setGridSize method. Instead do something like this:
|
||||
*
|
||||
* @code
|
||||
* QListView *lv;
|
||||
* ...
|
||||
* KCategorizedView *cv = qobject_cast<KCategorizedView*>(lv);
|
||||
* if (cv) {
|
||||
* cv->setGridSizeOwn(mySize);
|
||||
* } else {
|
||||
* lv->setGridSize(mySize);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @note this method will call to QListView::setGridSize among other operations.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setGridSizeOwn ( const QSize &size );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual QRect visualRect ( const QModelIndex &index ) const;
|
||||
|
||||
/**
|
||||
* Returns the current category drawer.
|
||||
*/
|
||||
KCategoryDrawer *categoryDrawer() const;
|
||||
|
||||
/**
|
||||
* The category drawer that will be used for drawing categories.
|
||||
*/
|
||||
void setCategoryDrawer ( KCategoryDrawer *categoryDrawer );
|
||||
|
||||
/**
|
||||
* @return Category spacing. The spacing between categories.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
int categorySpacing() const;
|
||||
|
||||
/**
|
||||
* Stablishes the category spacing. This is the spacing between categories.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setCategorySpacing ( int categorySpacing );
|
||||
|
||||
/**
|
||||
* @return Whether blocks should be drawn with alternating colors.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
bool alternatingBlockColors() const;
|
||||
|
||||
/**
|
||||
* Sets whether blocks should be drawn with alternating colors.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setAlternatingBlockColors ( bool enable );
|
||||
|
||||
/**
|
||||
* @return Whether blocks can be collapsed or not.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
bool collapsibleBlocks() const;
|
||||
|
||||
/**
|
||||
* Sets whether blocks can be collapsed or not.
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setCollapsibleBlocks ( bool enable );
|
||||
|
||||
/**
|
||||
* @return Block of indexes that are into @p category.
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
QModelIndexList block ( const QString &category );
|
||||
|
||||
/**
|
||||
* @return Block of indexes that are represented by @p representative.
|
||||
*
|
||||
* @since 4.5
|
||||
*/
|
||||
QModelIndexList block ( const QModelIndex &representative );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual QModelIndex indexAt ( const QPoint &point ) const;
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void reset();
|
||||
|
||||
/**
|
||||
* Signify that all item delegates size hints return the same fixed size
|
||||
*/
|
||||
void setUniformItemWidths(bool enable);
|
||||
|
||||
/**
|
||||
* Do all item delegate size hints return the same fixed size?
|
||||
*/
|
||||
bool uniformItemWidths() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void paintEvent ( QPaintEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void resizeEvent ( QResizeEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void setSelection ( const QRect &rect,
|
||||
QItemSelectionModel::SelectionFlags flags );
|
||||
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void mouseMoveEvent ( QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void mousePressEvent ( QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void mouseReleaseEvent ( QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QWidget.
|
||||
*/
|
||||
virtual void leaveEvent ( QEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void startDrag ( Qt::DropActions supportedActions );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dragMoveEvent ( QDragMoveEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dragEnterEvent ( QDragEnterEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dragLeaveEvent ( QDragLeaveEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dropEvent ( QDropEvent *event );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual QModelIndex moveCursor ( CursorAction cursorAction,
|
||||
Qt::KeyboardModifiers modifiers );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void rowsAboutToBeRemoved ( const QModelIndex &parent,
|
||||
int start,
|
||||
int end );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void updateGeometries();
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void currentChanged ( const QModelIndex ¤t,
|
||||
const QModelIndex &previous );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void dataChanged ( const QModelIndex &topLeft,
|
||||
const QModelIndex &bottomRight );
|
||||
|
||||
/**
|
||||
* Reimplemented from QAbstractItemView.
|
||||
*/
|
||||
virtual void rowsInserted ( const QModelIndex &parent,
|
||||
int start,
|
||||
int end );
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* @internal
|
||||
* Reposition items as needed.
|
||||
*/
|
||||
virtual void slotLayoutChanged();
|
||||
virtual void slotCollapseOrExpandClicked ( QModelIndex );
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
#endif // KCATEGORIZEDVIEW_H
|
||||
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KCATEGORYDRAWER_H
|
||||
#define KCATEGORYDRAWER_H
|
||||
|
||||
#include <groupview_config.h>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
||||
class QPainter;
|
||||
class QModelIndex;
|
||||
class QStyleOption;
|
||||
class KCategorizedView;
|
||||
|
||||
|
||||
/**
|
||||
* @since 4.5
|
||||
*/
|
||||
class LIBGROUPVIEW_EXPORT KCategoryDrawer
|
||||
: public QObject
|
||||
{
|
||||
friend class KCategorizedView;
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
public:
|
||||
KCategoryDrawer ( KCategorizedView *view );
|
||||
virtual ~KCategoryDrawer();
|
||||
|
||||
/**
|
||||
* @return The view this category drawer is associated with.
|
||||
*/
|
||||
KCategorizedView *view() const;
|
||||
|
||||
/**
|
||||
* This method purpose is to draw a category represented by the given
|
||||
* @param index with the given @param sortRole sorting role
|
||||
*
|
||||
* @note This method will be called one time per category, always with the
|
||||
* first element in that category
|
||||
*/
|
||||
virtual void drawCategory ( const QModelIndex &index,
|
||||
int sortRole,
|
||||
const QStyleOption &option,
|
||||
QPainter *painter ) const;
|
||||
|
||||
/**
|
||||
* @return The category height for the category representated by index @p index with
|
||||
* style options @p option.
|
||||
*/
|
||||
virtual int categoryHeight ( const QModelIndex &index, const QStyleOption &option ) const;
|
||||
|
||||
//TODO KDE5: make virtual as leftMargin
|
||||
/**
|
||||
* @note 0 by default
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
int leftMargin() const;
|
||||
|
||||
/**
|
||||
* @note call to this method on the KCategoryDrawer constructor to set the left margin
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setLeftMargin ( int leftMargin );
|
||||
|
||||
//TODO KDE5: make virtual as rightMargin
|
||||
/**
|
||||
* @note 0 by default
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
int rightMargin() const;
|
||||
|
||||
/**
|
||||
* @note call to this method on the KCategoryDrawer constructor to set the right margin
|
||||
*
|
||||
* @since 4.4
|
||||
*/
|
||||
void setRightMargin ( int rightMargin );
|
||||
|
||||
KCategoryDrawer &operator= ( const KCategoryDrawer &cd );
|
||||
protected:
|
||||
/**
|
||||
* Method called when the mouse button has been pressed.
|
||||
*
|
||||
* @param index The representative index of the block of items.
|
||||
* @param blockRect The rect occupied by the block of items.
|
||||
* @param event The mouse event.
|
||||
*
|
||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
||||
* your code.
|
||||
*/
|
||||
virtual void mouseButtonPressed ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Method called when the mouse button has been released.
|
||||
*
|
||||
* @param index The representative index of the block of items.
|
||||
* @param blockRect The rect occupied by the block of items.
|
||||
* @param event The mouse event.
|
||||
*
|
||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
||||
* your code.
|
||||
*/
|
||||
virtual void mouseButtonReleased ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Method called when the mouse has been moved.
|
||||
*
|
||||
* @param index The representative index of the block of items.
|
||||
* @param blockRect The rect occupied by the block of items.
|
||||
* @param event The mouse event.
|
||||
*/
|
||||
virtual void mouseMoved ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Method called when the mouse button has been double clicked.
|
||||
*
|
||||
* @param index The representative index of the block of items.
|
||||
* @param blockRect The rect occupied by the block of items.
|
||||
* @param event The mouse event.
|
||||
*
|
||||
* @warning You explicitly have to determine whether the event has been accepted or not. You
|
||||
* have to call event->accept() or event->ignore() at all possible case branches in
|
||||
* your code.
|
||||
*/
|
||||
virtual void mouseButtonDoubleClicked ( const QModelIndex &index, const QRect &blockRect, QMouseEvent *event );
|
||||
|
||||
/**
|
||||
* Method called when the mouse button has left this block.
|
||||
*
|
||||
* @param index The representative index of the block of items.
|
||||
* @param blockRect The rect occupied by the block of items.
|
||||
*/
|
||||
virtual void mouseLeft ( const QModelIndex &index, const QRect &blockRect );
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* This signal becomes emitted when collapse or expand has been clicked.
|
||||
*/
|
||||
void collapseOrExpandClicked ( const QModelIndex &index );
|
||||
|
||||
/**
|
||||
* Emit this signal on your subclass implementation to notify that something happened. Usually
|
||||
* this will be triggered when you have received an event, and its position matched some "hot spot".
|
||||
*
|
||||
* You give this action the integer you want, and having connected this signal to your code,
|
||||
* the connected slot can perform the needed changes (view, model, selection model, delegate...)
|
||||
*/
|
||||
void actionRequested ( int action, const QModelIndex &index );
|
||||
};
|
||||
|
||||
#endif // KCATEGORYDRAWER_H
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "categorizedsortfilterproxymodel.h"
|
||||
#include "categorizedsortfilterproxymodel_p.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include <QItemSelection>
|
||||
#include <QStringList>
|
||||
#include <QSize>
|
||||
|
||||
KCategorizedSortFilterProxyModel::KCategorizedSortFilterProxyModel ( QObject *parent )
|
||||
: QSortFilterProxyModel ( parent )
|
||||
, d ( new Private() )
|
||||
{
|
||||
}
|
||||
|
||||
KCategorizedSortFilterProxyModel::~KCategorizedSortFilterProxyModel()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void KCategorizedSortFilterProxyModel::sort ( int column, Qt::SortOrder order )
|
||||
{
|
||||
d->sortColumn = column;
|
||||
d->sortOrder = order;
|
||||
|
||||
QSortFilterProxyModel::sort ( column, order );
|
||||
}
|
||||
|
||||
bool KCategorizedSortFilterProxyModel::isCategorizedModel() const
|
||||
{
|
||||
return d->categorizedModel;
|
||||
}
|
||||
|
||||
void KCategorizedSortFilterProxyModel::setCategorizedModel ( bool categorizedModel )
|
||||
{
|
||||
if ( categorizedModel == d->categorizedModel )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->categorizedModel = categorizedModel;
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
int KCategorizedSortFilterProxyModel::sortColumn() const
|
||||
{
|
||||
return d->sortColumn;
|
||||
}
|
||||
|
||||
Qt::SortOrder KCategorizedSortFilterProxyModel::sortOrder() const
|
||||
{
|
||||
return d->sortOrder;
|
||||
}
|
||||
|
||||
void KCategorizedSortFilterProxyModel::setSortCategoriesByNaturalComparison ( bool sortCategoriesByNaturalComparison )
|
||||
{
|
||||
if ( sortCategoriesByNaturalComparison == d->sortCategoriesByNaturalComparison )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
d->sortCategoriesByNaturalComparison = sortCategoriesByNaturalComparison;
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
bool KCategorizedSortFilterProxyModel::sortCategoriesByNaturalComparison() const
|
||||
{
|
||||
return d->sortCategoriesByNaturalComparison;
|
||||
}
|
||||
|
||||
bool KCategorizedSortFilterProxyModel::lessThan ( const QModelIndex &left, const QModelIndex &right ) const
|
||||
{
|
||||
if ( d->categorizedModel )
|
||||
{
|
||||
int compare = compareCategories ( left, right );
|
||||
|
||||
if ( compare > 0 ) // left is greater than right
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ( compare < 0 ) // left is less than right
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return subSortLessThan ( left, right );
|
||||
}
|
||||
|
||||
bool KCategorizedSortFilterProxyModel::subSortLessThan ( const QModelIndex &left, const QModelIndex &right ) const
|
||||
{
|
||||
return QSortFilterProxyModel::lessThan ( left, right );
|
||||
}
|
||||
|
||||
int KCategorizedSortFilterProxyModel::compareCategories ( const QModelIndex &left, const QModelIndex &right ) const
|
||||
{
|
||||
QVariant l = ( left.model() ? left.model()->data ( left, CategorySortRole ) : QVariant() );
|
||||
QVariant r = ( right.model() ? right.model()->data ( right, CategorySortRole ) : QVariant() );
|
||||
|
||||
Q_ASSERT ( l.isValid() );
|
||||
Q_ASSERT ( r.isValid() );
|
||||
Q_ASSERT ( l.type() == r.type() );
|
||||
|
||||
if ( l.type() == QVariant::String )
|
||||
{
|
||||
QString lstr = l.toString();
|
||||
QString rstr = r.toString();
|
||||
|
||||
/*
|
||||
if ( d->sortCategoriesByNaturalComparison )
|
||||
{
|
||||
return KStringHandler::naturalCompare ( lstr, rstr );
|
||||
}
|
||||
else
|
||||
{
|
||||
*/
|
||||
if ( lstr < rstr )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( lstr > rstr )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
//}
|
||||
}
|
||||
|
||||
qlonglong lint = l.toLongLong();
|
||||
qlonglong rint = r.toLongLong();
|
||||
|
||||
if ( lint < rint )
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( lint > rint )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
|
||||
* Copyright (C) 2007 John Tapsell <tapsell@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KCATEGORIZEDSORTFILTERPROXYMODEL_P_H
|
||||
#define KCATEGORIZEDSORTFILTERPROXYMODEL_P_H
|
||||
|
||||
class KCategorizedSortFilterProxyModel;
|
||||
|
||||
class KCategorizedSortFilterProxyModel::Private
|
||||
{
|
||||
public:
|
||||
Private()
|
||||
: sortColumn ( 0 )
|
||||
, sortOrder ( Qt::AscendingOrder )
|
||||
, categorizedModel ( false )
|
||||
, sortCategoriesByNaturalComparison ( true )
|
||||
{
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
}
|
||||
|
||||
int sortColumn;
|
||||
Qt::SortOrder sortOrder;
|
||||
bool categorizedModel;
|
||||
bool sortCategoriesByNaturalComparison;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef KCATEGORIZEDVIEW_P_H
|
||||
#define KCATEGORIZEDVIEW_P_H
|
||||
|
||||
class KCategorizedSortFilterProxyModel;
|
||||
class KCategoryDrawer;
|
||||
class KCategoryDrawerV2;
|
||||
class KCategoryDrawerV3;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class KCategorizedView::Private
|
||||
{
|
||||
public:
|
||||
struct Block;
|
||||
struct Item;
|
||||
|
||||
Private(KCategorizedView *q);
|
||||
~Private();
|
||||
|
||||
/**
|
||||
* @return whether this view has all required elements to be categorized.
|
||||
*/
|
||||
bool isCategorized() const;
|
||||
|
||||
/**
|
||||
* @return the block rect for the representative @p representative.
|
||||
*/
|
||||
QStyleOptionViewItemV4 blockRect(const QModelIndex &representative);
|
||||
|
||||
/**
|
||||
* Returns the first and last element that intersects with rect.
|
||||
*
|
||||
* @note see that here we cannot take out items between first and last (as we could
|
||||
* do with the rubberband).
|
||||
*
|
||||
* Complexity: O(log(n)) where n is model()->rowCount().
|
||||
*/
|
||||
QPair<QModelIndex, QModelIndex> intersectingIndexesWithRect(const QRect &rect) const;
|
||||
|
||||
/**
|
||||
* Returns the position of the block of @p category.
|
||||
*
|
||||
* Complexity: O(n) where n is the number of different categories when the block has been
|
||||
* marked as in quarantine. O(1) the rest of the times (the vast majority).
|
||||
*/
|
||||
QPoint blockPosition(const QString &category);
|
||||
|
||||
/**
|
||||
* Returns the height of the block determined by @p category.
|
||||
*/
|
||||
int blockHeight(const QString &category);
|
||||
|
||||
/**
|
||||
* Returns the actual viewport width.
|
||||
*/
|
||||
int viewportWidth() const;
|
||||
|
||||
/**
|
||||
* Marks all elements as in quarantine.
|
||||
*
|
||||
* Complexity: O(n) where n is model()->rowCount().
|
||||
*
|
||||
* @warning this is an expensive operation
|
||||
*/
|
||||
void regenerateAllElements();
|
||||
|
||||
/**
|
||||
* Update internal information, and keep sync with the real information that the model contains.
|
||||
*/
|
||||
void rowsInserted(const QModelIndex &parent, int start, int end);
|
||||
|
||||
/**
|
||||
* Returns @p rect in viewport terms, taking in count horizontal and vertical offsets.
|
||||
*/
|
||||
QRect mapToViewport(const QRect &rect) const;
|
||||
|
||||
/**
|
||||
* Returns @p rect in absolute terms, converted from viewport position.
|
||||
*/
|
||||
QRect mapFromViewport(const QRect &rect) const;
|
||||
|
||||
/**
|
||||
* Returns the height of the highest element in last row. This is only applicable if there is
|
||||
* no grid set and uniformItemSizes is false.
|
||||
*
|
||||
* @param block in which block are we searching. Necessary to stop the search if we hit the
|
||||
* first item in this block.
|
||||
*/
|
||||
int highestElementInLastRow(const Block &block) const;
|
||||
|
||||
/**
|
||||
* Returns whether the view has a valid grid size.
|
||||
*/
|
||||
bool hasGrid() const;
|
||||
|
||||
/**
|
||||
* Returns the category for the given index.
|
||||
*/
|
||||
QString categoryForIndex(const QModelIndex &index) const;
|
||||
|
||||
/**
|
||||
* Updates the visual rect for item when flow is LeftToRight.
|
||||
*/
|
||||
void leftToRightVisualRect(const QModelIndex &index, Item &item,
|
||||
const Block &block, const QPoint &blockPos) const;
|
||||
|
||||
/**
|
||||
* Updates the visual rect for item when flow is TopToBottom.
|
||||
* @note we only support viewMode == ListMode in this case.
|
||||
*/
|
||||
void topToBottomVisualRect(const QModelIndex &index, Item &item,
|
||||
const Block &block, const QPoint &blockPos) const;
|
||||
|
||||
/**
|
||||
* Called when expand or collapse has been clicked on the category drawer.
|
||||
*/
|
||||
void _k_slotCollapseOrExpandClicked(QModelIndex);
|
||||
|
||||
KCategorizedView *q = nullptr;
|
||||
KCategorizedSortFilterProxyModel *proxyModel = nullptr;
|
||||
KCategoryDrawer *categoryDrawer = nullptr;
|
||||
int categorySpacing = 5;
|
||||
bool alternatingBlockColors = false;
|
||||
bool collapsibleBlocks = false;
|
||||
bool constantItemWidth = false;
|
||||
|
||||
// FIXME: this is some really weird logic. Investigate.
|
||||
Block *hoveredBlock;
|
||||
QString hoveredCategory;
|
||||
QModelIndex hoveredIndex;
|
||||
|
||||
QPoint pressedPosition;
|
||||
QRect rubberBandRect;
|
||||
|
||||
QHash<QString, Block> blocks;
|
||||
};
|
||||
|
||||
#endif // KCATEGORIZEDVIEW_P_H
|
||||
|
||||
@@ -1,231 +0,0 @@
|
||||
/**
|
||||
* This file is part of the KDE project
|
||||
* Copyright (C) 2007, 2009 Rafael Fernández López <ereslibre@kde.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public License
|
||||
* along with this library; see the file COPYING.LIB. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "categorydrawer.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QStyleOption>
|
||||
#include <QApplication>
|
||||
|
||||
#include <categorizedview.h>
|
||||
#include <categorizedsortfilterproxymodel.h>
|
||||
|
||||
#define HORIZONTAL_HINT 3
|
||||
|
||||
class KCategoryDrawer::Private
|
||||
{
|
||||
public:
|
||||
Private(KCategorizedView *view)
|
||||
: view(view)
|
||||
, leftMargin(0)
|
||||
, rightMargin(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Private()
|
||||
{
|
||||
}
|
||||
KCategorizedView *view;
|
||||
int leftMargin;
|
||||
int rightMargin;
|
||||
};
|
||||
|
||||
KCategoryDrawer::KCategoryDrawer(KCategorizedView *view)
|
||||
: QObject(view)
|
||||
, d(new Private(view))
|
||||
{
|
||||
setLeftMargin(2);
|
||||
setRightMargin(2);
|
||||
}
|
||||
|
||||
KCategoryDrawer::~KCategoryDrawer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
void KCategoryDrawer::drawCategory(const QModelIndex &index,
|
||||
int /*sortRole*/,
|
||||
const QStyleOption &option,
|
||||
QPainter *painter) const
|
||||
{
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
|
||||
const QRect optRect = option.rect;
|
||||
QFont font(QApplication::font());
|
||||
font.setBold(true);
|
||||
const QFontMetrics fontMetrics = QFontMetrics(font);
|
||||
|
||||
QColor outlineColor = option.palette.text().color();
|
||||
outlineColor.setAlphaF(0.35);
|
||||
|
||||
//BEGIN: top left corner
|
||||
{
|
||||
painter->save();
|
||||
painter->setPen(outlineColor);
|
||||
const QPointF topLeft(optRect.topLeft());
|
||||
QRectF arc(topLeft, QSizeF(4, 4));
|
||||
arc.translate(0.5, 0.5);
|
||||
painter->drawArc(arc, 1440, 1440);
|
||||
painter->restore();
|
||||
}
|
||||
//END: top left corner
|
||||
|
||||
//BEGIN: left vertical line
|
||||
{
|
||||
QPoint start(optRect.topLeft());
|
||||
start.ry() += 3;
|
||||
QPoint verticalGradBottom(optRect.topLeft());
|
||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||
QLinearGradient gradient(start, verticalGradBottom);
|
||||
gradient.setColorAt(0, outlineColor);
|
||||
gradient.setColorAt(1, Qt::transparent);
|
||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||
}
|
||||
//END: left vertical line
|
||||
|
||||
//BEGIN: horizontal line
|
||||
{
|
||||
QPoint start(optRect.topLeft());
|
||||
start.rx() += 3;
|
||||
QPoint horizontalGradTop(optRect.topLeft());
|
||||
horizontalGradTop.rx() += optRect.width() - 6;
|
||||
painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
|
||||
}
|
||||
//END: horizontal line
|
||||
|
||||
//BEGIN: top right corner
|
||||
{
|
||||
painter->save();
|
||||
painter->setPen(outlineColor);
|
||||
QPointF topRight(optRect.topRight());
|
||||
topRight.rx() -= 4;
|
||||
QRectF arc(topRight, QSizeF(4, 4));
|
||||
arc.translate(0.5, 0.5);
|
||||
painter->drawArc(arc, 0, 1440);
|
||||
painter->restore();
|
||||
}
|
||||
//END: top right corner
|
||||
|
||||
//BEGIN: right vertical line
|
||||
{
|
||||
QPoint start(optRect.topRight());
|
||||
start.ry() += 3;
|
||||
QPoint verticalGradBottom(optRect.topRight());
|
||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||
QLinearGradient gradient(start, verticalGradBottom);
|
||||
gradient.setColorAt(0, outlineColor);
|
||||
gradient.setColorAt(1, Qt::transparent);
|
||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||
}
|
||||
//END: right vertical line
|
||||
|
||||
//BEGIN: text
|
||||
{
|
||||
QRect textRect(option.rect);
|
||||
textRect.setTop(textRect.top() + 7);
|
||||
textRect.setLeft(textRect.left() + 7);
|
||||
textRect.setHeight(fontMetrics.height());
|
||||
textRect.setRight(textRect.right() - 7);
|
||||
|
||||
painter->save();
|
||||
painter->setFont(font);
|
||||
QColor penColor(option.palette.text().color());
|
||||
penColor.setAlphaF(0.6);
|
||||
painter->setPen(penColor);
|
||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category);
|
||||
painter->restore();
|
||||
}
|
||||
//END: text
|
||||
}
|
||||
|
||||
int KCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &option) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
Q_UNUSED(option)
|
||||
|
||||
QFont font(QApplication::font());
|
||||
font.setBold(true);
|
||||
QFontMetrics fontMetrics(font);
|
||||
|
||||
const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */
|
||||
+ 11 /* top and bottom separation */;
|
||||
return height;
|
||||
}
|
||||
|
||||
int KCategoryDrawer::leftMargin() const
|
||||
{
|
||||
return d->leftMargin;
|
||||
}
|
||||
|
||||
void KCategoryDrawer::setLeftMargin(int leftMargin)
|
||||
{
|
||||
d->leftMargin = leftMargin;
|
||||
}
|
||||
|
||||
int KCategoryDrawer::rightMargin() const
|
||||
{
|
||||
return d->rightMargin;
|
||||
}
|
||||
|
||||
void KCategoryDrawer::setRightMargin(int rightMargin)
|
||||
{
|
||||
d->rightMargin = rightMargin;
|
||||
}
|
||||
|
||||
KCategoryDrawer &KCategoryDrawer::operator=(const KCategoryDrawer &cd)
|
||||
{
|
||||
d->leftMargin = cd.d->leftMargin;
|
||||
d->rightMargin = cd.d->rightMargin;
|
||||
d->view = cd.d->view;
|
||||
return *this;
|
||||
}
|
||||
|
||||
KCategorizedView *KCategoryDrawer::view() const
|
||||
{
|
||||
return d->view;
|
||||
}
|
||||
|
||||
void KCategoryDrawer::mouseButtonPressed(const QModelIndex&, const QRect&, QMouseEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void KCategoryDrawer::mouseButtonReleased(const QModelIndex&, const QRect&, QMouseEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void KCategoryDrawer::mouseMoved(const QModelIndex&, const QRect&, QMouseEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void KCategoryDrawer::mouseButtonDoubleClicked(const QModelIndex&, const QRect&, QMouseEvent *event)
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void KCategoryDrawer::mouseLeft(const QModelIndex&, const QRect&)
|
||||
{
|
||||
}
|
||||
|
||||
#include "categorydrawer.moc"
|
||||
6
depends/javacheck/.gitignore
vendored
Normal file
6
depends/javacheck/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
.classpath
|
||||
.idea
|
||||
.project
|
||||
@@ -5,10 +5,11 @@ find_package(Java 1.6 REQUIRED COMPONENTS Development)
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_JAR_ENTRY_POINT JavaCheck)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked)
|
||||
#set(CMAKE_JAVA_TARGET_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/../../resources")
|
||||
|
||||
set(SRC
|
||||
JavaCheck.java
|
||||
JavaCheck.java
|
||||
)
|
||||
|
||||
add_jar(JavaCheck ${SRC})
|
||||
add_jar(JavaCheck ${SRC})
|
||||
|
||||
install_jar(JavaCheck "${BINARY_DEST_DIR}/jars")
|
||||
|
||||
6
depends/launcher/.gitignore
vendored
Normal file
6
depends/launcher/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
.idea
|
||||
*.iml
|
||||
out
|
||||
.classpath
|
||||
.idea
|
||||
.project
|
||||
@@ -3,19 +3,33 @@ project(launcher Java)
|
||||
find_package(Java 1.6 REQUIRED COMPONENTS Development)
|
||||
|
||||
include(UseJava)
|
||||
set(CMAKE_JAVA_JAR_ENTRY_POINT MultiMCLauncher)
|
||||
set(CMAKE_JAVA_JAR_ENTRY_POINT org.multimc.EntryPoint)
|
||||
set(CMAKE_JAVA_COMPILE_FLAGS -target 1.6 -source 1.6 -Xlint:deprecation -Xlint:unchecked)
|
||||
#set(CMAKE_JAVA_TARGET_OUTPUT_DIR "${PROJECT_SOURCE_DIR}/../../resources")
|
||||
|
||||
set(SRC
|
||||
MultiMCLauncher.java
|
||||
org/simplericity/macify/eawt/Application.java
|
||||
org/simplericity/macify/eawt/ApplicationAdapter.java
|
||||
org/simplericity/macify/eawt/ApplicationEvent.java
|
||||
org/simplericity/macify/eawt/ApplicationListener.java
|
||||
org/simplericity/macify/eawt/DefaultApplication.java
|
||||
net/minecraft/Launcher.java
|
||||
MCFrame.java
|
||||
)
|
||||
# OSX things
|
||||
org/simplericity/macify/eawt/Application.java
|
||||
org/simplericity/macify/eawt/ApplicationAdapter.java
|
||||
org/simplericity/macify/eawt/ApplicationEvent.java
|
||||
org/simplericity/macify/eawt/ApplicationListener.java
|
||||
org/simplericity/macify/eawt/DefaultApplication.java
|
||||
|
||||
add_jar(MultiMCLauncher ${SRC})
|
||||
# legacy applet wrapper thing.
|
||||
# The launcher has to be there for silly FML/Forge relauncher.
|
||||
net/minecraft/Launcher.java
|
||||
org/multimc/legacy/LegacyLauncher.java
|
||||
org/multimc/legacy/LegacyFrame.java
|
||||
|
||||
# onesix launcher
|
||||
org/multimc/onesix/OneSixLauncher.java
|
||||
|
||||
# generic launcher
|
||||
org/multimc/EntryPoint.java
|
||||
org/multimc/Launcher.java
|
||||
org/multimc/ParseException.java
|
||||
org/multimc/Utils.java
|
||||
org/multimc/IconLoader.java
|
||||
)
|
||||
add_jar(NewLaunch ${SRC})
|
||||
|
||||
install_jar(NewLaunch "${BINARY_DEST_DIR}/jars")
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
|
||||
import java.applet.Applet;
|
||||
import java.awt.Dimension;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipException;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import org.simplericity.macify.eawt.Application;
|
||||
import org.simplericity.macify.eawt.DefaultApplication;
|
||||
|
||||
public class MultiMCLauncher
|
||||
{
|
||||
/**
|
||||
* @param args
|
||||
* The arguments you want to launch Minecraft with. New path,
|
||||
* Username, Session ID.
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (args.length < 3)
|
||||
{
|
||||
System.out.println("Not enough arguments.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// Set the OSX application icon first, if we are on OSX.
|
||||
Application application = new DefaultApplication();
|
||||
if(application.isMac())
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedImage image = ImageIO.read(new File("icon.png"));
|
||||
application.setApplicationIconImage(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
String userName = args[0];
|
||||
String sessionId = args[1];
|
||||
String windowtitle = args[2];
|
||||
String windowParams = args[3];
|
||||
String lwjgl = args[4];
|
||||
String cwd = System.getProperty("user.dir");
|
||||
|
||||
Dimension winSize = new Dimension(854, 480);
|
||||
boolean maximize = false;
|
||||
boolean compatMode = false;
|
||||
|
||||
|
||||
String[] dimStrings = windowParams.split("x");
|
||||
|
||||
if (windowParams.equalsIgnoreCase("compatmode"))
|
||||
{
|
||||
compatMode = true;
|
||||
}
|
||||
else if (windowParams.equalsIgnoreCase("max"))
|
||||
{
|
||||
maximize = true;
|
||||
}
|
||||
else if (dimStrings.length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
winSize = new Dimension(Integer.parseInt(dimStrings[0]),
|
||||
Integer.parseInt(dimStrings[1]));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
System.out.println("Invalid Window size argument, " +
|
||||
"using default.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Invalid Window size argument, " +
|
||||
"using default.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File binDir = new File(cwd, "bin");
|
||||
File lwjglDir;
|
||||
if(lwjgl.equalsIgnoreCase("Mojang"))
|
||||
lwjglDir = binDir;
|
||||
else
|
||||
lwjglDir = new File(lwjgl);
|
||||
|
||||
System.out.println("Loading jars...");
|
||||
String[] lwjglJars = new String[] {
|
||||
"lwjgl.jar", "lwjgl_util.jar", "jinput.jar"
|
||||
};
|
||||
|
||||
URL[] urls = new URL[4];
|
||||
try
|
||||
{
|
||||
File f = new File(binDir, "minecraft.jar");
|
||||
urls[0] = f.toURI().toURL();
|
||||
System.out.println("Loading URL: " + urls[0].toString());
|
||||
|
||||
for (int i = 1; i < urls.length; i++)
|
||||
{
|
||||
File jar = new File(lwjglDir, lwjglJars[i-1]);
|
||||
urls[i] = jar.toURI().toURL();
|
||||
System.out.println("Loading URL: " + urls[i].toString());
|
||||
}
|
||||
}
|
||||
catch (MalformedURLException e)
|
||||
{
|
||||
System.err.println("MalformedURLException, " + e.toString());
|
||||
System.exit(5);
|
||||
}
|
||||
|
||||
System.out.println("Loading natives...");
|
||||
String nativesDir = new File(lwjglDir, "natives").toString();
|
||||
|
||||
System.setProperty("org.lwjgl.librarypath", nativesDir);
|
||||
System.setProperty("net.java.games.input.librarypath", nativesDir);
|
||||
|
||||
URLClassLoader cl =
|
||||
new URLClassLoader(urls, MultiMCLauncher.class.getClassLoader());
|
||||
|
||||
// Get the Minecraft Class.
|
||||
Class<?> mc = null;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass("net.minecraft.client.Minecraft");
|
||||
|
||||
Field f = getMCPathField(mc);
|
||||
|
||||
if (f == null)
|
||||
{
|
||||
System.err.println("Could not find Minecraft path field. Launch failed.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
f.setAccessible(true);
|
||||
f.set(null, new File(cwd));
|
||||
// And set it.
|
||||
System.out.println("Fixed Minecraft Path: Field was " + f.toString());
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
System.err.println("Can't find main class. Searching...");
|
||||
|
||||
// Look for any class that looks like the main class.
|
||||
File mcJar = new File(new File(cwd, "bin"), "minecraft.jar");
|
||||
ZipFile zip = null;
|
||||
try
|
||||
{
|
||||
zip = new ZipFile(mcJar);
|
||||
} catch (ZipException e1)
|
||||
{
|
||||
e1.printStackTrace();
|
||||
System.err.println("Search failed.");
|
||||
System.exit(-1);
|
||||
} catch (IOException e1)
|
||||
{
|
||||
e1.printStackTrace();
|
||||
System.err.println("Search failed.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
ArrayList<String> classes = new ArrayList<String>();
|
||||
|
||||
while (entries.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = entries.nextElement();
|
||||
if (entry.getName().endsWith(".class"))
|
||||
{
|
||||
String entryName = entry.getName().substring(0, entry.getName().lastIndexOf('.'));
|
||||
entryName = entryName.replace('/', '.');
|
||||
System.out.println("Found class: " + entryName);
|
||||
classes.add(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
for (String clsName : classes)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> cls = cl.loadClass(clsName);
|
||||
if (!Runnable.class.isAssignableFrom(cls))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found class implementing runnable: " +
|
||||
cls.getName());
|
||||
}
|
||||
|
||||
if (getMCPathField(cls) == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found class implementing runnable " +
|
||||
"with mcpath field: " + cls.getName());
|
||||
}
|
||||
|
||||
mc = cls;
|
||||
break;
|
||||
}
|
||||
catch (ClassNotFoundException e1)
|
||||
{
|
||||
// Ignore
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mc == null)
|
||||
{
|
||||
System.err.println("Failed to find Minecraft main class.");
|
||||
System.exit(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Found main class: " + mc.getName());
|
||||
}
|
||||
}
|
||||
|
||||
System.setProperty("minecraft.applet.TargetDirectory", cwd);
|
||||
|
||||
String[] mcArgs = new String[2];
|
||||
mcArgs[0] = userName;
|
||||
mcArgs[1] = sessionId;
|
||||
|
||||
if (compatMode)
|
||||
{
|
||||
System.out.println("Launching in compatibility mode...");
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.out.println("Launching with applet wrapper...");
|
||||
try
|
||||
{
|
||||
Class<?> MCAppletClass = cl.loadClass(
|
||||
"net.minecraft.client.MinecraftApplet");
|
||||
Applet mcappl = (Applet) MCAppletClass.newInstance();
|
||||
MCFrame mcWindow = new MCFrame(windowtitle);
|
||||
mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
|
||||
} catch (InstantiationException e)
|
||||
{
|
||||
System.out.println("Applet wrapper failed! Falling back " +
|
||||
"to compatibility mode.");
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
} catch (IllegalArgumentException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
} catch (IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
} catch (InvocationTargetException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(3);
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(3);
|
||||
} catch (SecurityException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
System.exit(4);
|
||||
}
|
||||
}
|
||||
|
||||
public static Field getMCPathField(Class<?> mc)
|
||||
{
|
||||
Field[] fields = mc.getDeclaredFields();
|
||||
|
||||
for (int i = 0; i < fields.length; i++)
|
||||
{
|
||||
Field f = fields[i];
|
||||
if (f.getType() != File.class)
|
||||
{
|
||||
// Has to be File
|
||||
continue;
|
||||
}
|
||||
if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC))
|
||||
{
|
||||
// And Private Static.
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package net.minecraft;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class Launcher extends Applet implements AppletStub
|
||||
|
||||
this.setLayout(new BorderLayout());
|
||||
this.add(applet, "Center");
|
||||
this.wrappedApplet = applet;
|
||||
this.wrappedApplet = applet;
|
||||
this.documentBase = documentBase;
|
||||
}
|
||||
|
||||
@@ -46,17 +46,17 @@ public class Launcher extends Applet implements AppletStub
|
||||
{
|
||||
params.put(name, value);
|
||||
}
|
||||
|
||||
|
||||
public void replace(Applet applet)
|
||||
{
|
||||
this.wrappedApplet = applet;
|
||||
|
||||
|
||||
applet.setStub(this);
|
||||
applet.setSize(getWidth(), getHeight());
|
||||
|
||||
|
||||
this.setLayout(new BorderLayout());
|
||||
this.add(applet, "Center");
|
||||
|
||||
|
||||
applet.init();
|
||||
active = true;
|
||||
applet.start();
|
||||
@@ -99,7 +99,7 @@ public class Launcher extends Applet implements AppletStub
|
||||
{
|
||||
wrappedApplet.resize(d);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
@@ -127,7 +127,7 @@ public class Launcher extends Applet implements AppletStub
|
||||
{
|
||||
wrappedApplet.destroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URL getCodeBase() {
|
||||
return wrappedApplet.getCodeBase();
|
||||
|
||||
173
depends/launcher/org/multimc/EntryPoint.java
Normal file
173
depends/launcher/org/multimc/EntryPoint.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package org.multimc;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import org.multimc.legacy.LegacyLauncher;
|
||||
import org.multimc.onesix.OneSixLauncher;
|
||||
import org.simplericity.macify.eawt.Application;
|
||||
import org.simplericity.macify.eawt.DefaultApplication;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class EntryPoint
|
||||
{
|
||||
private enum Action
|
||||
{
|
||||
Proceed,
|
||||
Launch,
|
||||
Abort
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// Set the OSX application icon first, if we are on OSX.
|
||||
Application application = new DefaultApplication();
|
||||
if(application.isMac())
|
||||
{
|
||||
try
|
||||
{
|
||||
BufferedImage image = ImageIO.read(new File("icon.png"));
|
||||
application.setApplicationIconImage(image);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
EntryPoint listener = new EntryPoint();
|
||||
int retCode = listener.listen();
|
||||
if (retCode != 0)
|
||||
{
|
||||
System.out.println("Exiting with " + retCode);
|
||||
System.exit(retCode);
|
||||
}
|
||||
}
|
||||
|
||||
private Action parseLine(String inData) throws ParseException
|
||||
{
|
||||
String[] pair = inData.split(" ", 2);
|
||||
|
||||
if(pair.length == 1)
|
||||
{
|
||||
String command = pair[0];
|
||||
if (pair[0].equals("launch"))
|
||||
return Action.Launch;
|
||||
|
||||
else if (pair[0].equals("abort"))
|
||||
return Action.Abort;
|
||||
|
||||
else throw new ParseException();
|
||||
}
|
||||
|
||||
if(pair.length != 2)
|
||||
throw new ParseException();
|
||||
|
||||
String command = pair[0];
|
||||
String param = pair[1];
|
||||
|
||||
if(command.equals("launcher"))
|
||||
{
|
||||
if(param.equals("legacy"))
|
||||
{
|
||||
m_launcher = new LegacyLauncher();
|
||||
Utils.log("Using legacy launcher.");
|
||||
Utils.log();
|
||||
return Action.Proceed;
|
||||
}
|
||||
if(param.equals("onesix"))
|
||||
{
|
||||
m_launcher = new OneSixLauncher();
|
||||
Utils.log("Using onesix launcher.");
|
||||
Utils.log();
|
||||
return Action.Proceed;
|
||||
}
|
||||
else
|
||||
throw new ParseException();
|
||||
}
|
||||
|
||||
m_params.add(command, param);
|
||||
//System.out.println(command + " : " + param);
|
||||
return Action.Proceed;
|
||||
}
|
||||
|
||||
public int listen()
|
||||
{
|
||||
BufferedReader buffer;
|
||||
try
|
||||
{
|
||||
buffer = new BufferedReader(new InputStreamReader(System.in, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e)
|
||||
{
|
||||
System.err.println("For some reason, your java does not support UTF-8. Consider living in the current century.");
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
boolean isListening = true;
|
||||
boolean isAborted = false;
|
||||
// Main loop
|
||||
while (isListening)
|
||||
{
|
||||
String inData;
|
||||
try
|
||||
{
|
||||
// Read from the pipe one line at a time
|
||||
inData = buffer.readLine();
|
||||
if (inData != null)
|
||||
{
|
||||
Action a = parseLine(inData);
|
||||
if(a == Action.Abort)
|
||||
{
|
||||
isListening = false;
|
||||
isAborted = true;
|
||||
}
|
||||
if(a == Action.Launch)
|
||||
{
|
||||
isListening = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
System.err.println("Launcher ABORT due to IO exception:");
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
System.err.println("Launcher ABORT due to PARSE exception:");
|
||||
e.printStackTrace();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(isAborted)
|
||||
{
|
||||
System.err.println("Launch aborted by MultiMC.");
|
||||
return 1;
|
||||
}
|
||||
if(m_launcher != null)
|
||||
{
|
||||
return m_launcher.launch(m_params);
|
||||
}
|
||||
System.err.println("No valid launcher implementation specified.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
private ParamBucket m_params = new ParamBucket();
|
||||
private org.multimc.Launcher m_launcher;
|
||||
}
|
||||
132
depends/launcher/org/multimc/IconLoader.java
Normal file
132
depends/launcher/org/multimc/IconLoader.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package org.multimc;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/*****************************************************************************
|
||||
* A convenience class for loading icons from images.
|
||||
*
|
||||
* Icons loaded from this class are formatted to fit within the required
|
||||
* dimension (16x16, 32x32, or 128x128). If the source image is larger than the
|
||||
* target dimension, it is shrunk down to the minimum size that will fit. If it
|
||||
* is smaller, then it is only scaled up if the new scale can be a per-pixel
|
||||
* linear scale (i.e., x2, x3, x4, etc). In both cases, the image's width/height
|
||||
* ratio is kept the same as the source image.
|
||||
*
|
||||
* @author Chris Molini
|
||||
*****************************************************************************/
|
||||
public class IconLoader
|
||||
{
|
||||
/*************************************************************************
|
||||
* Loads an icon in ByteBuffer form.
|
||||
*
|
||||
* @param filepath
|
||||
* The location of the Image to use as an icon.
|
||||
*
|
||||
* @return An array of ByteBuffers containing the pixel data for the icon in
|
||||
* various sizes (as recommended by the OS).
|
||||
*************************************************************************/
|
||||
public static ByteBuffer[] load(String filepath)
|
||||
{
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read ( new File( filepath ) );
|
||||
} catch ( IOException e ) {
|
||||
e.printStackTrace();
|
||||
return new ByteBuffer[0];
|
||||
}
|
||||
ByteBuffer[] buffers;
|
||||
buffers = new ByteBuffer[1];
|
||||
buffers[0] = loadInstance(image, 128);
|
||||
return buffers;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Copies the supplied image into a square icon at the indicated size.
|
||||
*
|
||||
* @param image
|
||||
* The image to place onto the icon.
|
||||
* @param dimension
|
||||
* The desired size of the icon.
|
||||
*
|
||||
* @return A ByteBuffer of pixel data at the indicated size.
|
||||
*************************************************************************/
|
||||
private static ByteBuffer loadInstance(BufferedImage image, int dimension)
|
||||
{
|
||||
BufferedImage scaledIcon = new BufferedImage(dimension, dimension,
|
||||
BufferedImage.TYPE_INT_ARGB_PRE);
|
||||
Graphics2D g = scaledIcon.createGraphics();
|
||||
double ratio = getIconRatio(image, scaledIcon);
|
||||
double width = image.getWidth() * ratio;
|
||||
double height = image.getHeight() * ratio;
|
||||
g.drawImage(image, (int) ((scaledIcon.getWidth() - width) / 2),
|
||||
(int) ((scaledIcon.getHeight() - height) / 2), (int) (width),
|
||||
(int) (height), null);
|
||||
g.dispose();
|
||||
|
||||
return convertToByteBuffer(scaledIcon);
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Gets the width/height ratio of the icon. This is meant to simplify
|
||||
* scaling the icon to a new dimension.
|
||||
*
|
||||
* @param src
|
||||
* The base image that will be placed onto the icon.
|
||||
* @param icon
|
||||
* The icon that will have the image placed on it.
|
||||
*
|
||||
* @return The amount to scale the source image to fit it onto the icon
|
||||
* appropriately.
|
||||
*************************************************************************/
|
||||
private static double getIconRatio(BufferedImage src, BufferedImage icon)
|
||||
{
|
||||
double ratio = 1;
|
||||
if (src.getWidth() > icon.getWidth())
|
||||
ratio = (double) (icon.getWidth()) / src.getWidth();
|
||||
else
|
||||
ratio = (int) (icon.getWidth() / src.getWidth());
|
||||
if (src.getHeight() > icon.getHeight())
|
||||
{
|
||||
double r2 = (double) (icon.getHeight()) / src.getHeight();
|
||||
if (r2 < ratio)
|
||||
ratio = r2;
|
||||
}
|
||||
else
|
||||
{
|
||||
double r2 = (int) (icon.getHeight() / src.getHeight());
|
||||
if (r2 < ratio)
|
||||
ratio = r2;
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
|
||||
/*************************************************************************
|
||||
* Converts a BufferedImage into a ByteBuffer of pixel data.
|
||||
*
|
||||
* @param image
|
||||
* The image to convert.
|
||||
*
|
||||
* @return A ByteBuffer that contains the pixel data of the supplied image.
|
||||
*************************************************************************/
|
||||
public static ByteBuffer convertToByteBuffer(BufferedImage image)
|
||||
{
|
||||
byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
|
||||
int counter = 0;
|
||||
for (int i = 0; i < image.getHeight(); i++)
|
||||
for (int j = 0; j < image.getWidth(); j++)
|
||||
{
|
||||
int colorSpace = image.getRGB(j, i);
|
||||
buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
|
||||
buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
|
||||
buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
|
||||
buffer[counter + 3] = (byte) (colorSpace >> 24);
|
||||
counter += 4;
|
||||
}
|
||||
return ByteBuffer.wrap(buffer);
|
||||
}
|
||||
}
|
||||
22
depends/launcher/org/multimc/Launcher.java
Normal file
22
depends/launcher/org/multimc/Launcher.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
public interface Launcher
|
||||
{
|
||||
abstract int launch(ParamBucket params);
|
||||
}
|
||||
21
depends/launcher/org/multimc/NotFoundException.java
Normal file
21
depends/launcher/org/multimc/NotFoundException.java
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
public class NotFoundException extends Exception
|
||||
{
|
||||
}
|
||||
86
depends/launcher/org/multimc/ParamBucket.java
Normal file
86
depends/launcher/org/multimc/ParamBucket.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ParamBucket
|
||||
{
|
||||
public void add(String key, String value)
|
||||
{
|
||||
List<String> coll = null;
|
||||
if(!m_params.containsKey(key))
|
||||
{
|
||||
coll = new ArrayList<String>();
|
||||
m_params.put(key, coll);
|
||||
}
|
||||
else
|
||||
{
|
||||
coll = m_params.get(key);
|
||||
}
|
||||
coll.add(value);
|
||||
}
|
||||
|
||||
public List<String> all(String key) throws NotFoundException
|
||||
{
|
||||
if(!m_params.containsKey(key))
|
||||
throw new NotFoundException();
|
||||
return m_params.get(key);
|
||||
}
|
||||
|
||||
public List<String> allSafe(String key, List<String> def)
|
||||
{
|
||||
if(!m_params.containsKey(key) || m_params.get(key).size() < 1)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
return m_params.get(key);
|
||||
}
|
||||
|
||||
public List<String> allSafe(String key)
|
||||
{
|
||||
return allSafe(key, new ArrayList<String>());
|
||||
}
|
||||
|
||||
public String first(String key) throws NotFoundException
|
||||
{
|
||||
List<String> list = all(key);
|
||||
if(list.size() < 1)
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
public String firstSafe(String key, String def)
|
||||
{
|
||||
if(!m_params.containsKey(key) || m_params.get(key).size() < 1)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
return m_params.get(key).get(0);
|
||||
}
|
||||
|
||||
public String firstSafe(String key)
|
||||
{
|
||||
return firstSafe(key, "");
|
||||
}
|
||||
|
||||
private HashMap<String, List<String>> m_params = new HashMap<String, List<String>>();
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/*
|
||||
* Copyright 2012-2014 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -13,16 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
package org.multimc;
|
||||
|
||||
#include <QtCore/QtGlobal>
|
||||
public class ParseException extends java.lang.Exception
|
||||
{
|
||||
|
||||
#ifdef LIBGROUPVIEW_STATIC
|
||||
#define LIBGROUPVIEW_EXPORT
|
||||
#else
|
||||
#ifdef LIBGROUPVIEW_LIBRARY
|
||||
#define LIBGROUPVIEW_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define LIBGROUPVIEW_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
242
depends/launcher/org/multimc/Utils.java
Normal file
242
depends/launcher/org/multimc/Utils.java
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public class Utils
|
||||
{
|
||||
/**
|
||||
* Combine two parts of a path.
|
||||
*
|
||||
* @param path1
|
||||
* @param path2
|
||||
* @return the paths, combined
|
||||
*/
|
||||
public static String combine(String path1, String path2)
|
||||
{
|
||||
File file1 = new File(path1);
|
||||
File file2 = new File(file1, path2);
|
||||
return file2.getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Join a list of strings into a string using a separator!
|
||||
*
|
||||
* @param strings the string list to join
|
||||
* @param separator the glue
|
||||
* @return the result.
|
||||
*/
|
||||
public static String join(List<String> strings, String separator)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String sep = "";
|
||||
for (String s : strings)
|
||||
{
|
||||
sb.append(sep).append(s);
|
||||
sep = separator;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified library to the classpath
|
||||
*
|
||||
* @param s the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void addToClassPath(String s) throws Exception
|
||||
{
|
||||
File f = new File(s);
|
||||
URL u = f.toURI().toURL();
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||
Class urlClass = URLClassLoader.class;
|
||||
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
|
||||
method.setAccessible(true);
|
||||
method.invoke(urlClassLoader, new Object[]{u});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds many libraries to the classpath
|
||||
*
|
||||
* @param jars the paths to add
|
||||
*/
|
||||
public static boolean addToClassPath(List<String> jars)
|
||||
{
|
||||
boolean pure = true;
|
||||
// initialize the class path
|
||||
for (String jar : jars)
|
||||
{
|
||||
try
|
||||
{
|
||||
Utils.addToClassPath(jar);
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Unable to load: " + jar);
|
||||
e.printStackTrace(System.err);
|
||||
pure = false;
|
||||
}
|
||||
}
|
||||
return pure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified path to the java library path
|
||||
*
|
||||
* @param pathToAdd the path to add
|
||||
* @throws Exception
|
||||
*/
|
||||
@Deprecated
|
||||
public static void addLibraryPath(String pathToAdd) throws Exception
|
||||
{
|
||||
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
|
||||
usrPathsField.setAccessible(true);
|
||||
|
||||
//get array of paths
|
||||
final String[] paths = (String[]) usrPathsField.get(null);
|
||||
|
||||
//check if the path to add is already present
|
||||
for (String path : paths)
|
||||
{
|
||||
if (path.equals(pathToAdd))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//add the new path
|
||||
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
|
||||
newPaths[newPaths.length - 1] = pathToAdd;
|
||||
usrPathsField.set(null, newPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a field that looks like a Minecraft base folder in a supplied class
|
||||
*
|
||||
* @param mc the class to scan
|
||||
*/
|
||||
public static Field getMCPathField(Class<?> mc)
|
||||
{
|
||||
Field[] fields = mc.getDeclaredFields();
|
||||
|
||||
for (Field f : fields)
|
||||
{
|
||||
if (f.getType() != File.class)
|
||||
{
|
||||
// Has to be File
|
||||
continue;
|
||||
}
|
||||
if (f.getModifiers() != (Modifier.PRIVATE + Modifier.STATIC))
|
||||
{
|
||||
// And Private Static.
|
||||
continue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log to the MultiMC console
|
||||
*
|
||||
* @param message A String containing the message
|
||||
* @param level A String containing the level name. See MinecraftProcess::getLevel()
|
||||
*/
|
||||
public static void log(String message, String level)
|
||||
{
|
||||
// Kinda dirty
|
||||
String tag = "!![" + level + "]!";
|
||||
System.out.println(tag + message.replace("\n", "\n" + tag));
|
||||
}
|
||||
|
||||
public static void log(String message)
|
||||
{
|
||||
log(message, "MultiMC");
|
||||
}
|
||||
|
||||
public static void log()
|
||||
{
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes bytes from in to out. Closes both streams no matter what.
|
||||
* @param in the input stream
|
||||
* @param out the output stream
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void copyStream(InputStream in, OutputStream out) throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
|
||||
while((len = in.read(buffer)) >= 0)
|
||||
out.write(buffer, 0, len);
|
||||
} finally
|
||||
{
|
||||
in.close();
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzip zip file 'source' into the folder 'targetFolder'
|
||||
* @param source
|
||||
* @param targetFolder
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void unzip(File source, File targetFolder) throws IOException
|
||||
{
|
||||
ZipFile zip = new ZipFile(source);
|
||||
try
|
||||
{
|
||||
Enumeration entries = zip.entries();
|
||||
|
||||
while (entries.hasMoreElements())
|
||||
{
|
||||
ZipEntry entry = (ZipEntry) entries.nextElement();
|
||||
|
||||
File targetFile = new File(targetFolder, entry.getName());
|
||||
if (targetFile.getParentFile() != null)
|
||||
{
|
||||
targetFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (entry.isDirectory())
|
||||
continue;
|
||||
|
||||
copyStream(zip.getInputStream(entry), new BufferedOutputStream(new FileOutputStream(targetFile)));
|
||||
}
|
||||
} finally
|
||||
{
|
||||
zip.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
//
|
||||
// Copyright 2012 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.
|
||||
//
|
||||
package org.multimc.legacy;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import net.minecraft.Launcher;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.applet.Applet;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Frame;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
public class MCFrame extends Frame implements WindowListener
|
||||
public class LegacyFrame extends Frame implements WindowListener
|
||||
{
|
||||
private Launcher appletWrap = null;
|
||||
public MCFrame ( String title )
|
||||
public LegacyFrame(String title)
|
||||
{
|
||||
super ( title );
|
||||
BufferedImage image = null;
|
||||
BufferedImage image;
|
||||
try {
|
||||
image = ImageIO.read ( new File ( "icon.png" ) );
|
||||
setIconImage ( image );
|
||||
@@ -47,14 +46,14 @@ public class MCFrame extends Frame implements WindowListener
|
||||
public void start ( Applet mcApplet, String user, String session, Dimension winSize, boolean maximize )
|
||||
{
|
||||
try {
|
||||
appletWrap = new Launcher ( mcApplet, new URL ( "http://www.minecraft.net/game" ) );
|
||||
appletWrap = new Launcher( mcApplet, new URL ( "http://www.minecraft.net/game" ) );
|
||||
} catch ( MalformedURLException ignored ) {}
|
||||
|
||||
appletWrap.setParameter ( "username", user );
|
||||
appletWrap.setParameter ( "sessionid", session );
|
||||
appletWrap.setParameter ( "stand-alone", "true" ); // Show the quit button.
|
||||
mcApplet.setStub ( appletWrap );
|
||||
|
||||
appletWrap.setParameter ( "demo", "false" );
|
||||
appletWrap.setParameter("fullscreen", "false");
|
||||
mcApplet.setStub(appletWrap);
|
||||
this.add ( appletWrap );
|
||||
appletWrap.setPreferredSize ( winSize );
|
||||
this.pack();
|
||||
@@ -63,7 +62,6 @@ public class MCFrame extends Frame implements WindowListener
|
||||
if ( maximize ) {
|
||||
this.setExtendedState ( MAXIMIZED_BOTH );
|
||||
}
|
||||
|
||||
validate();
|
||||
appletWrap.init();
|
||||
appletWrap.start();
|
||||
178
depends/launcher/org/multimc/legacy/LegacyLauncher.java
Normal file
178
depends/launcher/org/multimc/legacy/LegacyLauncher.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package org.multimc.legacy;/*
|
||||
* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
import org.multimc.Launcher;
|
||||
import org.multimc.NotFoundException;
|
||||
import org.multimc.ParamBucket;
|
||||
import org.multimc.Utils;
|
||||
|
||||
import java.applet.Applet;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
public class LegacyLauncher implements Launcher
|
||||
{
|
||||
@Override
|
||||
public int launch(ParamBucket params)
|
||||
{
|
||||
String userName, sessionId, windowTitle, windowParams, lwjgl;
|
||||
String mainClass = "net.minecraft.client.Minecraft";
|
||||
try
|
||||
{
|
||||
userName = params.first("userName");
|
||||
sessionId = params.first("sessionId");
|
||||
windowTitle = params.first("windowTitle");
|
||||
windowParams = params.first("windowParams");
|
||||
lwjgl = params.first("lwjgl");
|
||||
} catch (NotFoundException e)
|
||||
{
|
||||
System.err.println("Not enough arguments.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
String cwd = System.getProperty("user.dir");
|
||||
Dimension winSize = new Dimension(854, 480);
|
||||
boolean maximize = false;
|
||||
|
||||
String[] dimStrings = windowParams.split("x");
|
||||
|
||||
if (windowParams.equalsIgnoreCase("max"))
|
||||
{
|
||||
maximize = true;
|
||||
}
|
||||
else if (dimStrings.length == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
winSize = new Dimension(Integer.parseInt(dimStrings[0]), Integer.parseInt(dimStrings[1]));
|
||||
} catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
File binDir = new File(cwd, "bin");
|
||||
File lwjglDir;
|
||||
if (lwjgl.equalsIgnoreCase("Mojang"))
|
||||
{
|
||||
lwjglDir = binDir;
|
||||
}
|
||||
else
|
||||
{
|
||||
lwjglDir = new File(lwjgl);
|
||||
}
|
||||
|
||||
URL[] classpath;
|
||||
{
|
||||
try
|
||||
{
|
||||
classpath = new URL[]
|
||||
{
|
||||
new File(binDir, "minecraft.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "lwjgl.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "lwjgl_util.jar").toURI().toURL(),
|
||||
new File(lwjglDir, "jinput.jar").toURI().toURL(),
|
||||
};
|
||||
} catch (MalformedURLException e)
|
||||
{
|
||||
System.err.println("Class path entry is badly formed:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
String nativesDir = new File(lwjglDir, "natives").toString();
|
||||
|
||||
System.setProperty("org.lwjgl.librarypath", nativesDir);
|
||||
System.setProperty("net.java.games.input.librarypath", nativesDir);
|
||||
|
||||
// print the pretty things
|
||||
{
|
||||
Utils.log("Main Class:");
|
||||
Utils.log(" " + mainClass);
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Class Path:");
|
||||
for (URL s : classpath)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Native Path:");
|
||||
Utils.log(" " + nativesDir);
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
URLClassLoader cl = new URLClassLoader(classpath, LegacyLauncher.class.getClassLoader());
|
||||
|
||||
// Get the Minecraft Class and set the base folder
|
||||
Class<?> mc;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass(mainClass);
|
||||
|
||||
Field f = Utils.getMCPathField(mc);
|
||||
|
||||
if (f == null)
|
||||
{
|
||||
System.err.println("Could not find Minecraft path field. Launch failed.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
f.setAccessible(true);
|
||||
f.set(null, new File(cwd));
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Could not set base folder. Failed to find/access Minecraft main class:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
System.setProperty("minecraft.applet.TargetDirectory", cwd);
|
||||
|
||||
String[] mcArgs = new String[2];
|
||||
mcArgs[0] = userName;
|
||||
mcArgs[1] = sessionId;
|
||||
|
||||
Utils.log("Launching with applet wrapper...");
|
||||
try
|
||||
{
|
||||
Class<?> MCAppletClass = cl.loadClass("net.minecraft.client.MinecraftApplet");
|
||||
Applet mcappl = (Applet) MCAppletClass.newInstance();
|
||||
LegacyFrame mcWindow = new LegacyFrame(windowTitle);
|
||||
mcWindow.start(mcappl, userName, sessionId, winSize, maximize);
|
||||
} catch (Exception e)
|
||||
{
|
||||
Utils.log("Applet wrapper failed:", "Error");
|
||||
e.printStackTrace(System.err);
|
||||
Utils.log();
|
||||
Utils.log("Falling back to compatibility mode.");
|
||||
try
|
||||
{
|
||||
mc.getMethod("main", String[].class).invoke(null, (Object) mcArgs);
|
||||
} catch (Exception e1)
|
||||
{
|
||||
Utils.log("Failed to invoke the Minecraft main class:", "Fatal");
|
||||
e1.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
223
depends/launcher/org/multimc/onesix/OneSixLauncher.java
Normal file
223
depends/launcher/org/multimc/onesix/OneSixLauncher.java
Normal file
@@ -0,0 +1,223 @@
|
||||
/* Copyright 2012-2014 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.
|
||||
*/
|
||||
|
||||
package org.multimc.onesix;
|
||||
|
||||
import org.multimc.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OneSixLauncher implements Launcher
|
||||
{
|
||||
@Override
|
||||
public int launch(ParamBucket params)
|
||||
{
|
||||
// get and process the launch script params
|
||||
List<String> libraries;
|
||||
List<String> extlibs;
|
||||
List<String> mcparams;
|
||||
List<String> mods;
|
||||
String mainClass;
|
||||
String natives;
|
||||
final String windowTitle;
|
||||
String windowParams;
|
||||
try
|
||||
{
|
||||
libraries = params.all("cp");
|
||||
extlibs = params.all("ext");
|
||||
mcparams = params.all("param");
|
||||
mainClass = params.first("mainClass");
|
||||
mods = params.allSafe("mods", new ArrayList<String>());
|
||||
natives = params.first("natives");
|
||||
|
||||
windowTitle = params.first("windowTitle");
|
||||
// windowParams = params.first("windowParams");
|
||||
} catch (NotFoundException e)
|
||||
{
|
||||
System.err.println("Not enough arguments.");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
List<String> allJars = new ArrayList<String>();
|
||||
allJars.addAll(mods);
|
||||
allJars.addAll(libraries);
|
||||
|
||||
if(!Utils.addToClassPath(allJars))
|
||||
{
|
||||
System.err.println("Halting launch due to previous errors.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// print the pretty things
|
||||
{
|
||||
Utils.log("Main Class:");
|
||||
Utils.log(" " + mainClass);
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Native path:");
|
||||
Utils.log(" " + natives);
|
||||
Utils.log();
|
||||
|
||||
Utils.log("Libraries:");
|
||||
for (String s : libraries)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
if(mods.size() > 0)
|
||||
{
|
||||
Utils.log("Class Path Mods:");
|
||||
for (String s : mods)
|
||||
{
|
||||
Utils.log(" " + s);
|
||||
}
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
Utils.log("Params:");
|
||||
Utils.log(" " + mcparams.toString());
|
||||
Utils.log();
|
||||
}
|
||||
|
||||
// set up the natives path(s).
|
||||
Utils.log("Preparing native libraries...");
|
||||
String property = System.getProperty("os.arch");
|
||||
boolean is_64 = property.equalsIgnoreCase("x86_64") || property.equalsIgnoreCase("amd64");
|
||||
for(String extlib: extlibs)
|
||||
{
|
||||
try
|
||||
{
|
||||
String cleanlib = extlib.replace("${arch}", is_64 ? "64" : "32");
|
||||
File cleanlibf = new File(cleanlib);
|
||||
Utils.log("Extracting " + cleanlibf.getName());
|
||||
Utils.unzip(cleanlibf, new File(natives));
|
||||
} catch (IOException e)
|
||||
{
|
||||
System.err.println("Failed to extract native library:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
Utils.log();
|
||||
|
||||
System.setProperty("java.library.path", natives);
|
||||
Field fieldSysPath;
|
||||
try
|
||||
{
|
||||
fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
|
||||
fieldSysPath.setAccessible( true );
|
||||
fieldSysPath.set( null, null );
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Failed to set the native library path:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the Minecraft Class.
|
||||
final ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
Class<?> mc;
|
||||
try
|
||||
{
|
||||
mc = cl.loadClass(mainClass);
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
System.err.println("Failed to find Minecraft main class:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get the main method.
|
||||
Method meth;
|
||||
try
|
||||
{
|
||||
meth = mc.getMethod("main", String[].class);
|
||||
} catch (NoSuchMethodException e)
|
||||
{
|
||||
System.err.println("Failed to acquire the main method:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// FIXME: works only on linux, we need a better solution
|
||||
/*
|
||||
final java.nio.ByteBuffer[] icons = IconLoader.load("icon.png");
|
||||
new Thread() {
|
||||
public void run() {
|
||||
ClassLoader cl = ClassLoader.getSystemClassLoader();
|
||||
try
|
||||
{
|
||||
Class<?> Display;
|
||||
Method isCreated;
|
||||
Method setTitle;
|
||||
Method setIcon;
|
||||
|
||||
Display = cl.loadClass("org.lwjgl.opengl.Display");
|
||||
isCreated = Display.getMethod("isCreated");
|
||||
setTitle = Display.getMethod("setTitle", String.class);
|
||||
setIcon = Display.getMethod("setIcon", java.nio.ByteBuffer[].class);
|
||||
|
||||
// set the window title? Maybe?
|
||||
while(!(Boolean) isCreated.invoke(null))
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(150);
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
// Give it a bit more time ;)
|
||||
Thread.sleep(150);
|
||||
// set the title
|
||||
setTitle.invoke(null,windowTitle);
|
||||
// only set icon when there's actually something to set...
|
||||
if(icons.length > 0)
|
||||
{
|
||||
setIcon.invoke(null,(Object)icons);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.err.println("Couldn't set window icon or title.");
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
.start();
|
||||
*/
|
||||
// start Minecraft
|
||||
String[] paramsArray = mcparams.toArray(new String[mcparams.size()]); // init params accordingly
|
||||
try
|
||||
{
|
||||
meth.invoke(null, (Object) paramsArray); // static method doesn't have an instance
|
||||
} catch (Exception e)
|
||||
{
|
||||
System.err.println("Failed to start Minecraft:");
|
||||
e.printStackTrace(System.err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,59 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
IF(WIN32)
|
||||
if(WIN32)
|
||||
# In Qt 5.1+ we have our own main() function, don't autolink to qtmain on Windows
|
||||
cmake_policy(SET CMP0020 OLD)
|
||||
ENDIF()
|
||||
endif()
|
||||
|
||||
project(unpack200)
|
||||
|
||||
# Find ZLIB for quazip
|
||||
# Use system zlib on unix and Qt ZLIB on Windows
|
||||
IF(UNIX)
|
||||
if(UNIX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
ELSE(UNIX)
|
||||
get_filename_component (ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
|
||||
SET(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
|
||||
SET(ZLIB_LIBRARIES "")
|
||||
IF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
MESSAGE("Please specify a valid zlib include dir")
|
||||
ENDIF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
ENDIF(UNIX)
|
||||
else(UNIX)
|
||||
get_filename_component(ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
|
||||
set(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
|
||||
set(ZLIB_LIBRARIES "")
|
||||
if(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
message("Please specify a valid zlib include dir")
|
||||
endif(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
endif(UNIX)
|
||||
|
||||
SET(PACK200_SRC
|
||||
include/unpack200.h
|
||||
src/bands.cpp
|
||||
src/bands.h
|
||||
src/bytes.cpp
|
||||
src/bytes.h
|
||||
src/coding.cpp
|
||||
src/coding.h
|
||||
src/constants.h
|
||||
src/defines.h
|
||||
src/unpack200.cpp
|
||||
src/unpack.cpp
|
||||
src/unpack.h
|
||||
src/utils.cpp
|
||||
src/utils.h
|
||||
src/zip.cpp
|
||||
src/zip.h
|
||||
set(PACK200_SRC
|
||||
include/unpack200.h
|
||||
src/bands.cpp
|
||||
src/bands.h
|
||||
src/bytes.cpp
|
||||
src/bytes.h
|
||||
src/coding.cpp
|
||||
src/coding.h
|
||||
src/constants.h
|
||||
src/defines.h
|
||||
src/unpack200.cpp
|
||||
src/unpack.cpp
|
||||
src/unpack.h
|
||||
src/utils.cpp
|
||||
src/utils.h
|
||||
src/zip.cpp
|
||||
src/zip.h
|
||||
)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
SET(PACK200_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
set(PACK200_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
include_directories(
|
||||
include
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
add_library(unpack200 STATIC ${PACK200_SRC})
|
||||
|
||||
IF(UNIX)
|
||||
if(UNIX)
|
||||
target_link_libraries(unpack200 ${ZLIB_LIBRARIES})
|
||||
ELSE()
|
||||
else()
|
||||
# zlib is part of Qt on windows. use it.
|
||||
QT5_USE_MODULES(unpack200 Core)
|
||||
ENDIF()
|
||||
qt5_use_modules(unpack200 Core)
|
||||
endif()
|
||||
|
||||
add_executable(anti200 anti200.cpp)
|
||||
target_link_libraries(anti200 unpack200)
|
||||
|
||||
@@ -2,16 +2,16 @@ project(quazip)
|
||||
|
||||
# Find ZLIB for quazip
|
||||
# Use system zlib on unix and Qt ZLIB on Windows
|
||||
IF(UNIX)
|
||||
if(UNIX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
ELSE(UNIX)
|
||||
get_filename_component (ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
|
||||
SET(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
|
||||
SET(ZLIB_LIBRARIES "")
|
||||
IF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
MESSAGE("Please specify a valid zlib include dir")
|
||||
ENDIF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
ENDIF(UNIX)
|
||||
else(UNIX)
|
||||
get_filename_component(ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE)
|
||||
set(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt")
|
||||
set(ZLIB_LIBRARIES "")
|
||||
if(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
message("Please specify a valid zlib include dir")
|
||||
endif(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h")
|
||||
endif(UNIX)
|
||||
|
||||
# set all include directories for in and out of source builds
|
||||
include_directories(
|
||||
@@ -20,23 +20,12 @@ include_directories(
|
||||
${ZLIB_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# include with QT_USE selected library parts
|
||||
# INCLUDE(${QT_USE_FILE})
|
||||
|
||||
file(GLOB SRCS "*.c" "*.cpp")
|
||||
file(GLOB PUBLIC_HEADERS "*.h")
|
||||
|
||||
# Static link!
|
||||
ADD_DEFINITIONS(-DQUAZIP_STATIC)
|
||||
|
||||
#qt5_wrap_cpp(MOC_SRCS ${PUBLIC_HEADERS})
|
||||
#set(SRCS ${SRCS} ${MOC_SRCS})
|
||||
|
||||
#set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
add_definitions(-DQUAZIP_STATIC)
|
||||
|
||||
add_library(quazip STATIC ${SRCS})
|
||||
QT5_USE_MODULES(quazip Core)
|
||||
qt5_use_modules(quazip Core)
|
||||
target_link_libraries(quazip ${ZLIB_LIBRARIES})
|
||||
|
||||
#install(FILES ${PUBLIC_HEADERS} DESTINATION include/quazip)
|
||||
#install(TARGETS quazip LIBRARY DESTINATION ${LIB_DESTINATION} ARCHIVE DESTINATION ${LIB_DESTINATION} RUNTIME DESTINATION ${LIB_DESTINATION})
|
||||
|
||||
@@ -6,42 +6,36 @@ find_package(Qt5Core REQUIRED)
|
||||
# Include Qt headers.
|
||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
||||
|
||||
include(UseCXX11)
|
||||
include(Coverage)
|
||||
|
||||
SET(LIBSETTINGS_SOURCES
|
||||
libsettings_config.h
|
||||
set(LIBSETTINGS_SOURCES
|
||||
libsettings_config.h
|
||||
|
||||
inifile.h
|
||||
inifile.cpp
|
||||
inifile.h
|
||||
inifile.cpp
|
||||
|
||||
settingsobject.h
|
||||
settingsobject.cpp
|
||||
inisettingsobject.h
|
||||
inisettingsobject.cpp
|
||||
settingsobject.h
|
||||
settingsobject.cpp
|
||||
inisettingsobject.h
|
||||
inisettingsobject.cpp
|
||||
|
||||
setting.h
|
||||
setting.cpp
|
||||
overridesetting.h
|
||||
overridesetting.cpp
|
||||
setting.h
|
||||
setting.cpp
|
||||
overridesetting.h
|
||||
overridesetting.cpp
|
||||
)
|
||||
|
||||
# Set the include dir path.
|
||||
SET(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
|
||||
set(LIBSETTINGS_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE)
|
||||
|
||||
# Static link!
|
||||
ADD_DEFINITIONS(-DLIBSETTINGS_STATIC)
|
||||
add_definitions(-DLIBSETTINGS_STATIC)
|
||||
|
||||
add_definitions(-DLIBSETTINGS_LIBRARY)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
IF(MultiMC_CODE_COVERAGE)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 --coverage")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
|
||||
ENDIF(MultiMC_CODE_COVERAGE)
|
||||
|
||||
add_library(libSettings STATIC ${LIBSETTINGS_SOURCES})
|
||||
qt5_use_modules(libSettings Core)
|
||||
target_link_libraries(libSettings)
|
||||
|
||||
@@ -25,17 +25,49 @@ INIFile::INIFile()
|
||||
|
||||
QString INIFile::unescape(QString orig)
|
||||
{
|
||||
orig.replace("\\n", "\n");
|
||||
orig.replace("\\t", "\t");
|
||||
orig.replace("\\\\", "\\");
|
||||
return orig;
|
||||
QString out;
|
||||
QChar prev = 0;
|
||||
for(auto c: orig)
|
||||
{
|
||||
if(prev == '\\')
|
||||
{
|
||||
if(c == 'n')
|
||||
out += '\n';
|
||||
else if (c == 't')
|
||||
out += '\t';
|
||||
else
|
||||
out += c;
|
||||
prev = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(c == '\\')
|
||||
{
|
||||
prev = c;
|
||||
continue;
|
||||
}
|
||||
out += c;
|
||||
prev = 0;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
QString INIFile::escape(QString orig)
|
||||
{
|
||||
orig.replace("\\", "\\\\");
|
||||
orig.replace("\n", "\\n");
|
||||
orig.replace("\t", "\\t");
|
||||
return orig;
|
||||
QString out;
|
||||
for(auto c: orig)
|
||||
{
|
||||
if(c == '\n')
|
||||
out += "\\n";
|
||||
else if (c == '\t')
|
||||
out += "\\t";
|
||||
else if(c == '\\')
|
||||
out += "\\\\";
|
||||
else
|
||||
out += c;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool INIFile::saveFile(QString fileName)
|
||||
|
||||
@@ -33,6 +33,6 @@ public:
|
||||
|
||||
QVariant get(QString key, QVariant def) const;
|
||||
void set(QString key, QVariant val);
|
||||
QString unescape(QString orig);
|
||||
QString escape(QString orig);
|
||||
static QString unescape(QString orig);
|
||||
static QString escape(QString orig);
|
||||
};
|
||||
|
||||
@@ -28,6 +28,11 @@ void INISettingsObject::setFilePath(const QString &filePath)
|
||||
m_filePath = filePath;
|
||||
}
|
||||
|
||||
bool INISettingsObject::reload()
|
||||
{
|
||||
return m_ini.loadFile(m_filePath) && SettingsObject::reload();
|
||||
}
|
||||
|
||||
void INISettingsObject::changeSetting(const Setting &setting, QVariant value)
|
||||
{
|
||||
if (contains(setting.id()))
|
||||
|
||||
@@ -47,6 +47,8 @@ public:
|
||||
*/
|
||||
virtual void setFilePath(const QString &filePath);
|
||||
|
||||
bool reload() override;
|
||||
|
||||
protected
|
||||
slots:
|
||||
virtual void changeSetting(const Setting &setting, QVariant value);
|
||||
|
||||
@@ -126,6 +126,15 @@ bool SettingsObject::contains(const QString &id)
|
||||
return m_settings.contains(id);
|
||||
}
|
||||
|
||||
bool SettingsObject::reload()
|
||||
{
|
||||
for (auto setting : m_settings.values())
|
||||
{
|
||||
setting->set(setting->get());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SettingsObject::connectSignals(const Setting &setting)
|
||||
{
|
||||
connect(&setting, SIGNAL(settingChanged(const Setting &, QVariant)),
|
||||
|
||||
@@ -113,6 +113,12 @@ public:
|
||||
*/
|
||||
bool contains(const QString &id);
|
||||
|
||||
/*!
|
||||
* \brief Reloads the settings and emit signals for changed settings
|
||||
* \return True if reloading was successful
|
||||
*/
|
||||
virtual bool reload();
|
||||
|
||||
signals:
|
||||
/*!
|
||||
* \brief Signal emitted when one of this SettingsObject object's settings changes.
|
||||
|
||||
@@ -1,61 +1,42 @@
|
||||
project(libUtil)
|
||||
|
||||
######## Set compiler flags ########
|
||||
IF(APPLE)
|
||||
# assume clang 4.1.0+, add C++0x/C++11 stuff
|
||||
message(STATUS "Using APPLE CMAKE_CXX_FLAGS")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
ELSEIF(UNIX)
|
||||
# assume GCC, add C++0x/C++11 stuff
|
||||
MESSAGE(STATUS "Using UNIX CMAKE_CXX_FLAGS")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
ELSEIF(MINGW)
|
||||
MESSAGE(STATUS "Using MINGW CMAKE_CXX_FLAGS")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
|
||||
ENDIF()
|
||||
|
||||
include(UseCXX11)
|
||||
include(Coverage)
|
||||
|
||||
# Find Qt
|
||||
find_package(Qt5Core REQUIRED)
|
||||
|
||||
# Include Qt headers.
|
||||
include_directories(${Qt5Base_INCLUDE_DIRS})
|
||||
# include_directories(${Qt5Network_INCLUDE_DIRS})
|
||||
|
||||
SET(LIBUTIL_SOURCES
|
||||
include/libutil_config.h
|
||||
set(LIBUTIL_SOURCES
|
||||
include/libutil_config.h
|
||||
|
||||
include/pathutils.h
|
||||
src/pathutils.cpp
|
||||
include/pathutils.h
|
||||
src/pathutils.cpp
|
||||
|
||||
include/osutils.h
|
||||
include/osutils.h
|
||||
|
||||
include/userutils.h
|
||||
src/userutils.cpp
|
||||
include/userutils.h
|
||||
src/userutils.cpp
|
||||
|
||||
include/cmdutils.h
|
||||
src/cmdutils.cpp
|
||||
include/cmdutils.h
|
||||
src/cmdutils.cpp
|
||||
|
||||
include/modutils.h
|
||||
src/modutils.cpp
|
||||
)
|
||||
|
||||
# Set the include dir path.
|
||||
SET(LIBUTIL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
set(LIBUTIL_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
|
||||
# Static link!
|
||||
ADD_DEFINITIONS(-DLIBUTIL_STATIC)
|
||||
add_definitions(-DLIBUTIL_STATIC)
|
||||
|
||||
add_definitions(-DLIBUTIL_LIBRARY)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
IF(MultiMC_CODE_COVERAGE)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 --coverage")
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -g -O0 --coverage")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O0 --coverage")
|
||||
ENDIF(MultiMC_CODE_COVERAGE)
|
||||
|
||||
add_library(libUtil STATIC ${LIBUTIL_SOURCES})
|
||||
# qt5_use_modules(libUtil Core Network)
|
||||
qt5_use_modules(libUtil Core)
|
||||
target_link_libraries(libUtil)
|
||||
|
||||
32
depends/util/include/modutils.h
Normal file
32
depends/util/include/modutils.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include "libutil_config.h"
|
||||
|
||||
class QUrl;
|
||||
|
||||
namespace Util
|
||||
{
|
||||
struct Version
|
||||
{
|
||||
Version(const QString &str);
|
||||
|
||||
bool operator<(const Version &other) const;
|
||||
bool operator<=(const Version &other) const;
|
||||
bool operator>(const Version &other) const;
|
||||
bool operator==(const Version &other) const;
|
||||
bool operator!=(const Version &other) const;
|
||||
|
||||
QString toString() const
|
||||
{
|
||||
return m_string;
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_string;
|
||||
};
|
||||
|
||||
LIBUTIL_EXPORT QUrl expandQMURL(const QString &in);
|
||||
LIBUTIL_EXPORT bool versionIsInInterval(const QString &version, const QString &interval);
|
||||
}
|
||||
|
||||
216
depends/util/src/modutils.cpp
Normal file
216
depends/util/src/modutils.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "include/modutils.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
Util::Version::Version(const QString &str) : m_string(str)
|
||||
{
|
||||
}
|
||||
|
||||
bool Util::Version::operator<(const Version &other) const
|
||||
{
|
||||
QStringList parts1 = m_string.split('.');
|
||||
QStringList parts2 = other.m_string.split('.');
|
||||
|
||||
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||
{
|
||||
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||
bool ok1 = false;
|
||||
bool ok2 = false;
|
||||
int int1 = part1.toInt(&ok1);
|
||||
int int2 = part2.toInt(&ok2);
|
||||
if (ok1 && ok2)
|
||||
{
|
||||
if (int1 == int2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return int1 < int2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (part1 == part2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return part1 < part2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool Util::Version::operator<=(const Util::Version &other) const
|
||||
{
|
||||
return *this < other || *this == other;
|
||||
}
|
||||
bool Util::Version::operator>(const Version &other) const
|
||||
{
|
||||
QStringList parts1 = m_string.split('.');
|
||||
QStringList parts2 = other.m_string.split('.');
|
||||
|
||||
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||
{
|
||||
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||
bool ok1 = false;
|
||||
bool ok2 = false;
|
||||
int int1 = part1.toInt(&ok1);
|
||||
int int2 = part2.toInt(&ok2);
|
||||
if (ok1 && ok2)
|
||||
{
|
||||
if (int1 == int2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return int1 > int2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (part1 == part2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return part1 > part2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool Util::Version::operator==(const Version &other) const
|
||||
{
|
||||
QStringList parts1 = m_string.split('.');
|
||||
QStringList parts2 = other.m_string.split('.');
|
||||
|
||||
while (!parts1.isEmpty() && !parts2.isEmpty())
|
||||
{
|
||||
QString part1 = parts1.isEmpty() ? "0" : parts1.takeFirst();
|
||||
QString part2 = parts2.isEmpty() ? "0" : parts2.takeFirst();
|
||||
bool ok1 = false;
|
||||
bool ok2 = false;
|
||||
int int1 = part1.toInt(&ok1);
|
||||
int int2 = part2.toInt(&ok2);
|
||||
if (ok1 && ok2)
|
||||
{
|
||||
if (int1 == int2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (part1 == part2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool Util::Version::operator!=(const Version &other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
QUrl Util::expandQMURL(const QString &in)
|
||||
{
|
||||
QUrl inUrl(in);
|
||||
if (inUrl.scheme() == "github")
|
||||
{
|
||||
// needed because QUrl makes the host all lower cases
|
||||
const QString repo = in.mid(in.indexOf(inUrl.host(), 0, Qt::CaseInsensitive), inUrl.host().size());
|
||||
QUrl out;
|
||||
out.setScheme("https");
|
||||
out.setHost("raw.github.com");
|
||||
out.setPath(QString("/%1/%2/%3%4")
|
||||
.arg(inUrl.userInfo(), repo,
|
||||
inUrl.fragment().isEmpty() ? "master" : inUrl.fragment(), inUrl.path()));
|
||||
return out;
|
||||
}
|
||||
else if (inUrl.scheme() == "mcf")
|
||||
{
|
||||
QUrl out;
|
||||
out.setScheme("http");
|
||||
out.setHost("www.minecraftforum.net");
|
||||
out.setPath(QString("/topic/%1-").arg(inUrl.path()));
|
||||
return out;
|
||||
}
|
||||
else
|
||||
{
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
bool Util::versionIsInInterval(const QString &version, const QString &interval)
|
||||
{
|
||||
if (interval.isEmpty() || version == interval)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Interval notation is used
|
||||
QRegularExpression exp(
|
||||
"(?<start>[\\[\\]\\(\\)])(?<bottom>.*?)(,(?<top>.*?))?(?<end>[\\[\\]\\(\\)])");
|
||||
QRegularExpressionMatch match = exp.match(interval);
|
||||
if (match.hasMatch())
|
||||
{
|
||||
const QChar start = match.captured("start").at(0);
|
||||
const QChar end = match.captured("end").at(0);
|
||||
const QString bottom = match.captured("bottom");
|
||||
const QString top = match.captured("top");
|
||||
|
||||
// check if in range (bottom)
|
||||
if (!bottom.isEmpty())
|
||||
{
|
||||
if ((start == '[') && !(version >= bottom))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((start == '(') && !(version > bottom))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if in range (top)
|
||||
if (!top.isEmpty())
|
||||
{
|
||||
if ((end == ']') && !(version <= top))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if ((end == ')') && !(version < top))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,22 +8,22 @@ option(XZ_BUILD_MINIDEC "Build a tiny utility that decompresses xz streams" OFF)
|
||||
set(CMAKE_C_FLAGS "-std=c99")
|
||||
|
||||
include_directories(include)
|
||||
SET(XZ_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
set(XZ_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE)
|
||||
|
||||
# See include/xz.h for manual feature configuration
|
||||
# tweak this list and xz.h to fit your needs
|
||||
|
||||
set(XZ_SOURCES
|
||||
include/xz.h
|
||||
src/xz_config.h
|
||||
src/xz_crc32.c
|
||||
src/xz_crc64.c
|
||||
src/xz_dec_lzma2.c
|
||||
src/xz_dec_stream.c
|
||||
src/xz_lzma2.h
|
||||
src/xz_private.h
|
||||
src/xz_stream.h
|
||||
# src/xz_dec_bcj.c
|
||||
include/xz.h
|
||||
src/xz_config.h
|
||||
src/xz_crc32.c
|
||||
src/xz_crc64.c
|
||||
src/xz_dec_lzma2.c
|
||||
src/xz_dec_stream.c
|
||||
src/xz_lzma2.h
|
||||
src/xz_private.h
|
||||
src/xz_stream.h
|
||||
# src/xz_dec_bcj.c
|
||||
)
|
||||
# TODO: look into what would be needed for plain old lzma
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/java">
|
||||
<file alias="launcher.jar">@MMC_BIN@/depends/launcher/MultiMCLauncher.jar</file>
|
||||
<file alias="checker.jar">@MMC_BIN@/depends/javacheck/JavaCheck.jar</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
51
graphics.qrc
51
graphics.qrc
@@ -1,51 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/icons/toolbar">
|
||||
<file alias="about">resources/icons/toolbar/about.png</file>
|
||||
<file alias="bug">resources/icons/toolbar/ReportBug.png</file>
|
||||
<file alias="centralmods">resources/icons/toolbar/centralmods.png</file>
|
||||
<file alias="checkupdate">resources/icons/toolbar/checkupdate.png</file>
|
||||
<file alias="help">resources/icons/toolbar/help.png</file>
|
||||
<file alias="new">resources/icons/toolbar/new.png</file>
|
||||
<file alias="copy">resources/icons/toolbar/InstCopy.png</file>
|
||||
<file alias="news">resources/icons/toolbar/NewsIcon.png</file>
|
||||
<file alias="refresh">resources/icons/toolbar/refresh.png</file>
|
||||
<file alias="settings">resources/icons/toolbar/settings.png</file>
|
||||
<file alias="viewfolder">resources/icons/toolbar/viewfolder.png</file>
|
||||
<file alias="cat">resources/icons/toolbar/Cat.png</file>
|
||||
<file alias="noaccount">resources/icons/toolbar/NoAccount.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons/instances">
|
||||
<file alias="brick">resources/icons/instances/brick.png</file>
|
||||
<file alias="chicken">resources/icons/instances/chicken128.png</file>
|
||||
<file alias="creeper">resources/icons/instances/creeper128.png</file>
|
||||
<file alias="derp">resources/icons/instances/derp.png</file>
|
||||
<file alias="diamond">resources/icons/instances/diamond.png</file>
|
||||
<file alias="dirt">resources/icons/instances/dirt.png</file>
|
||||
<file alias="enderman">resources/icons/instances/enderman.png</file>
|
||||
<file alias="enderpearl">resources/icons/instances/enderpearl128.png</file>
|
||||
<file alias="ftb-glow">resources/icons/instances/ftb_glow128.png</file>
|
||||
<file alias="ftb-logo">resources/icons/instances/ftb_logo128.png</file>
|
||||
<file alias="gear">resources/icons/instances/gear128.png</file>
|
||||
<file alias="gold">resources/icons/instances/gold.png</file>
|
||||
<file alias="grass">resources/icons/instances/grass.png</file>
|
||||
<file alias="herobrine">resources/icons/instances/herobrine128.png</file>
|
||||
<file alias="infinity">resources/icons/instances/infinity128.png</file>
|
||||
<file alias="iron">resources/icons/instances/iron.png</file>
|
||||
<file alias="magitech">resources/icons/instances/magitech128.png</file>
|
||||
<file alias="meat">resources/icons/instances/meat128.png</file>
|
||||
<file alias="netherstar">resources/icons/instances/netherstar128.png</file>
|
||||
<file alias="planks">resources/icons/instances/planks.png</file>
|
||||
<file alias="skeleton">resources/icons/instances/skeleton128.png</file>
|
||||
<file alias="squarecreeper">resources/icons/instances/squarecreeper128.png</file>
|
||||
<file alias="steve">resources/icons/instances/steve128.png</file>
|
||||
<file alias="stone">resources/icons/instances/stone.png</file>
|
||||
<file alias="tnt">resources/icons/instances/tnt.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="/icons/multimc">
|
||||
<file alias="scalable/apps/multimc.svg">resources/icons/multimc.svg</file>
|
||||
<file alias="index.theme">resources/XdgIcon.theme</file>
|
||||
</qresource>
|
||||
<qresource prefix="/backgrounds">
|
||||
<file alias="kitteh">resources/catbgrnd2.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -19,12 +19,14 @@
|
||||
|
||||
#include <QScrollBar>
|
||||
#include <QMessageBox>
|
||||
#include <QSystemTrayIcon>
|
||||
|
||||
#include <gui/Platform.h>
|
||||
#include <gui/dialogs/CustomMessageBox.h>
|
||||
#include <gui/dialogs/ProgressDialog.h>
|
||||
|
||||
#include "logic/net/PasteUpload.h"
|
||||
#include "logic/icons/IconList.h"
|
||||
|
||||
ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
|
||||
: QMainWindow(parent), ui(new Ui::ConsoleWindow), proc(mcproc)
|
||||
@@ -35,14 +37,31 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
|
||||
SLOT(write(QString, MessageLevel::Enum)));
|
||||
connect(mcproc, SIGNAL(ended(BaseInstance *, int, QProcess::ExitStatus)), this,
|
||||
SLOT(onEnded(BaseInstance *, int, QProcess::ExitStatus)));
|
||||
connect(mcproc, SIGNAL(prelaunch_failed(BaseInstance*,int,QProcess::ExitStatus)), this,
|
||||
connect(mcproc, SIGNAL(prelaunch_failed(BaseInstance *, int, QProcess::ExitStatus)), this,
|
||||
SLOT(onEnded(BaseInstance *, int, QProcess::ExitStatus)));
|
||||
connect(mcproc, SIGNAL(launch_failed(BaseInstance*)), this,
|
||||
SLOT(onLaunchFailed(BaseInstance*)));
|
||||
connect(mcproc, SIGNAL(launch_failed(BaseInstance *)), this,
|
||||
SLOT(onLaunchFailed(BaseInstance *)));
|
||||
|
||||
restoreState(QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowState").toByteArray()));
|
||||
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowGeometry").toByteArray()));
|
||||
connect(ui->btnScreenshots, &QPushButton::clicked, this, &ConsoleWindow::uploadScreenshots);
|
||||
|
||||
restoreState(
|
||||
QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowState").toByteArray()));
|
||||
restoreGeometry(
|
||||
QByteArray::fromBase64(MMC->settings()->get("ConsoleWindowGeometry").toByteArray()));
|
||||
|
||||
QString iconKey = proc->instance()->iconKey();
|
||||
QString name = proc->instance()->name();
|
||||
auto icon = MMC->icons()->getIcon(iconKey);
|
||||
setWindowIcon(icon);
|
||||
m_trayIcon = new QSystemTrayIcon(icon, this);
|
||||
// TODO add screenshot upload as a menu item in the tray icon
|
||||
QString consoleTitle = tr("Console window for ") + name;
|
||||
m_trayIcon->setToolTip(consoleTitle);
|
||||
setWindowTitle(consoleTitle);
|
||||
|
||||
connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
|
||||
m_trayIcon->show();
|
||||
if (mcproc->instance()->settings().get("ShowConsole").toBool())
|
||||
{
|
||||
show();
|
||||
@@ -55,14 +74,29 @@ ConsoleWindow::~ConsoleWindow()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ConsoleWindow::writeColor(QString text, const char *color)
|
||||
void ConsoleWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
switch (reason)
|
||||
{
|
||||
case QSystemTrayIcon::Trigger:
|
||||
{
|
||||
toggleConsole();
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWindow::writeColor(QString text, const char *color, const char * background)
|
||||
{
|
||||
// append a paragraph
|
||||
QString newtext;
|
||||
newtext += "<span style=\"";
|
||||
{
|
||||
if(color)
|
||||
if (color)
|
||||
newtext += QString("color:") + color + ";";
|
||||
if (background)
|
||||
newtext += QString("background-color:") + background + ";";
|
||||
newtext += "font-family: monospace;";
|
||||
}
|
||||
newtext += "\">";
|
||||
@@ -76,16 +110,17 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
|
||||
QScrollBar *bar = ui->text->verticalScrollBar();
|
||||
int max_bar = bar->maximum();
|
||||
int val_bar = bar->value();
|
||||
if(m_scroll_active)
|
||||
if(isVisible())
|
||||
{
|
||||
if(m_last_scroll_value > val_bar)
|
||||
m_scroll_active = false;
|
||||
if (m_scroll_active)
|
||||
{
|
||||
m_scroll_active = (max_bar - val_bar) <= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scroll_active = val_bar == max_bar;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_scroll_active = val_bar == max_bar;
|
||||
}
|
||||
|
||||
if (data.endsWith('\n'))
|
||||
data = data.left(data.length() - 1);
|
||||
QStringList paragraphs = data.split('\n');
|
||||
@@ -97,28 +132,34 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode)
|
||||
QListIterator<QString> iter(paragraphs);
|
||||
if (mode == MessageLevel::MultiMC)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "blue");
|
||||
writeColor(iter.next(), "blue", 0);
|
||||
else if (mode == MessageLevel::Error)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "red");
|
||||
writeColor(iter.next(), "red", 0);
|
||||
else if (mode == MessageLevel::Warning)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "orange");
|
||||
writeColor(iter.next(), "orange", 0);
|
||||
else if (mode == MessageLevel::Fatal)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "pink");
|
||||
writeColor(iter.next(), "red", "black");
|
||||
else if (mode == MessageLevel::Debug)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "green");
|
||||
writeColor(iter.next(), "green", 0);
|
||||
else if (mode == MessageLevel::PrePost)
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next(), "grey", 0);
|
||||
// TODO: implement other MessageLevels
|
||||
else
|
||||
while (iter.hasNext())
|
||||
writeColor(iter.next());
|
||||
if(m_scroll_active)
|
||||
writeColor(iter.next(), 0, 0);
|
||||
if(isVisible())
|
||||
{
|
||||
bar->setValue(bar->maximum());
|
||||
if (m_scroll_active)
|
||||
{
|
||||
bar->setValue(bar->maximum());
|
||||
}
|
||||
m_last_scroll_value = bar->value();
|
||||
}
|
||||
m_last_scroll_value = bar->value();
|
||||
}
|
||||
|
||||
void ConsoleWindow::clear()
|
||||
@@ -133,23 +174,56 @@ void ConsoleWindow::on_closeButton_clicked()
|
||||
|
||||
void ConsoleWindow::setMayClose(bool mayclose)
|
||||
{
|
||||
m_mayclose = mayclose;
|
||||
if (mayclose)
|
||||
ui->closeButton->setEnabled(true);
|
||||
if(mayclose)
|
||||
ui->closeButton->setText(tr("Close"));
|
||||
else
|
||||
ui->closeButton->setEnabled(false);
|
||||
ui->closeButton->setText(tr("Hide"));
|
||||
m_mayclose = mayclose;
|
||||
}
|
||||
|
||||
void ConsoleWindow::toggleConsole()
|
||||
{
|
||||
QScrollBar *bar = ui->text->verticalScrollBar();
|
||||
if (isVisible())
|
||||
{
|
||||
if(!isActiveWindow())
|
||||
{
|
||||
activateWindow();
|
||||
return;
|
||||
}
|
||||
int max_bar = bar->maximum();
|
||||
int val_bar = m_last_scroll_value = bar->value();
|
||||
m_scroll_active = (max_bar - val_bar) <= 1;
|
||||
hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
show();
|
||||
isTopLevel();
|
||||
if (m_scroll_active)
|
||||
{
|
||||
bar->setValue(bar->maximum());
|
||||
}
|
||||
else
|
||||
{
|
||||
bar->setValue(m_last_scroll_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (!m_mayclose)
|
||||
event->ignore();
|
||||
{
|
||||
toggleConsole();
|
||||
}
|
||||
else
|
||||
{
|
||||
MMC->settings()->set("ConsoleWindowState", saveState().toBase64());
|
||||
MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
|
||||
|
||||
emit isClosing();
|
||||
m_trayIcon->hide();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -170,20 +244,34 @@ void ConsoleWindow::on_btnKillMinecraft_clicked()
|
||||
|
||||
void ConsoleWindow::onEnded(BaseInstance *instance, int code, QProcess::ExitStatus status)
|
||||
{
|
||||
bool peacefulExit = code == 0 && status != QProcess::CrashExit;
|
||||
ui->btnKillMinecraft->setEnabled(false);
|
||||
|
||||
setMayClose(true);
|
||||
|
||||
if (instance->settings().get("AutoCloseConsole").toBool())
|
||||
{
|
||||
if (code == 0 && status != QProcess::CrashExit)
|
||||
if (peacefulExit)
|
||||
{
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(!isVisible())
|
||||
/*
|
||||
if(!peacefulExit)
|
||||
{
|
||||
m_trayIcon->showMessage(tr("Oh no!"), tr("Minecraft crashed!"), QSystemTrayIcon::Critical);
|
||||
}
|
||||
*/
|
||||
if (!isVisible())
|
||||
show();
|
||||
|
||||
// Raise Window
|
||||
if (MMC->settings()->get("RaiseConsole").toBool())
|
||||
{
|
||||
raise();
|
||||
activateWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWindow::onLaunchFailed(BaseInstance *instance)
|
||||
@@ -192,7 +280,7 @@ void ConsoleWindow::onLaunchFailed(BaseInstance *instance)
|
||||
|
||||
setMayClose(true);
|
||||
|
||||
if(!isVisible())
|
||||
if (!isVisible())
|
||||
show();
|
||||
}
|
||||
|
||||
@@ -200,10 +288,11 @@ void ConsoleWindow::on_btnPaste_clicked()
|
||||
{
|
||||
auto text = ui->text->toPlainText();
|
||||
ProgressDialog dialog(this);
|
||||
PasteUpload* paste=new PasteUpload(this, text);
|
||||
PasteUpload *paste = new PasteUpload(this, text);
|
||||
dialog.exec(paste);
|
||||
if(!paste->successful())
|
||||
if (!paste->successful())
|
||||
{
|
||||
CustomMessageBox::selectable(this, "Upload failed", paste->failReason(), QMessageBox::Critical)->exec();
|
||||
CustomMessageBox::selectable(this, "Upload failed", paste->failReason(),
|
||||
QMessageBox::Critical)->exec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
#include "logic/MinecraftProcess.h"
|
||||
|
||||
namespace Ui
|
||||
@@ -46,10 +47,11 @@ private:
|
||||
* this will only insert a single paragraph.
|
||||
* \n are ignored. a real \n is always appended.
|
||||
*/
|
||||
void writeColor(QString data, const char *color = nullptr);
|
||||
void writeColor(QString text, const char *color, const char *background);
|
||||
|
||||
signals:
|
||||
void isClosing();
|
||||
void uploadScreenshots();
|
||||
|
||||
public
|
||||
slots:
|
||||
@@ -77,7 +79,8 @@ slots:
|
||||
// failures)
|
||||
|
||||
void on_btnPaste_clicked();
|
||||
|
||||
void iconActivated(QSystemTrayIcon::ActivationReason);
|
||||
void toggleConsole();
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
|
||||
@@ -87,4 +90,6 @@ private:
|
||||
bool m_mayclose = true;
|
||||
int m_last_scroll_value = 0;
|
||||
bool m_scroll_active = true;
|
||||
QSystemTrayIcon *m_trayIcon = nullptr;
|
||||
int m_saved_offset = 0;
|
||||
};
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnScreenshots">
|
||||
<property name="text">
|
||||
<string>Manage Screenshots</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,20 +17,20 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#include "logic/lists/InstanceList.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
|
||||
#include "logic/auth/MojangAccount.h"
|
||||
#include <logic/net/NetJob.h>
|
||||
|
||||
class QToolButton;
|
||||
class LabeledToolButton;
|
||||
class QLabel;
|
||||
class InstanceProxyModel;
|
||||
class KCategorizedView;
|
||||
class KCategoryDrawer;
|
||||
class MinecraftProcess;
|
||||
class ConsoleWindow;
|
||||
class BaseProfilerFactory;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -85,6 +85,8 @@ slots:
|
||||
|
||||
void on_actionReportBug_triggered();
|
||||
|
||||
void on_actionPatreon_triggered();
|
||||
|
||||
void on_actionMoreNews_triggered();
|
||||
|
||||
void newsButtonClicked();
|
||||
@@ -95,6 +97,8 @@ slots:
|
||||
|
||||
void on_actionLaunchInstance_triggered();
|
||||
|
||||
void on_actionLaunchInstanceOffline_triggered();
|
||||
|
||||
void on_actionDeleteInstance_triggered();
|
||||
|
||||
void on_actionRenameInstance_triggered();
|
||||
@@ -111,25 +115,18 @@ slots:
|
||||
* Launches the currently selected instance with the default account.
|
||||
* If no default account is selected, prompts the user to pick an account.
|
||||
*/
|
||||
void doLaunch();
|
||||
|
||||
/*!
|
||||
* Opens an input dialog, allowing the user to input their password and refresh its access token.
|
||||
* This function will execute the proper Yggdrasil task to refresh the access token.
|
||||
* Returns true if successful. False if the user cancelled.
|
||||
*/
|
||||
bool loginWithPassword(MojangAccountPtr account, const QString& errorMsg="");
|
||||
void doLaunch(bool online = true, BaseProfilerFactory *profiler = 0);
|
||||
|
||||
/*!
|
||||
* Launches the given instance with the given account.
|
||||
* This function assumes that the given account has a valid, usable access token.
|
||||
*/
|
||||
void launchInstance(BaseInstance* instance, MojangAccountPtr account);
|
||||
void launchInstance(InstancePtr instance, AuthSessionPtr session, BaseProfilerFactory *profiler = 0);
|
||||
|
||||
/*!
|
||||
* Prepares the given instance for launch with the given account.
|
||||
*/
|
||||
void updateInstance(BaseInstance* instance, MojangAccountPtr account);
|
||||
void updateInstance(InstancePtr instance, AuthSessionPtr account, BaseProfilerFactory *profiler = 0);
|
||||
|
||||
void onGameUpdateError(QString error);
|
||||
|
||||
@@ -145,8 +142,13 @@ slots:
|
||||
// called when an icon is changed in the icon model.
|
||||
void iconUpdated(QString);
|
||||
|
||||
void showInstanceContextMenu(const QPoint&);
|
||||
void showInstanceContextMenu(const QPoint &);
|
||||
|
||||
void on_actionScreenshots_triggered();
|
||||
|
||||
void updateToolsMenu();
|
||||
|
||||
void skinJobFinished();
|
||||
public
|
||||
slots:
|
||||
void instanceActivated(QModelIndex);
|
||||
@@ -168,36 +170,48 @@ slots:
|
||||
void repopulateAccountsMenu();
|
||||
|
||||
void updateNewsLabel();
|
||||
|
||||
|
||||
void updateStatusUI();
|
||||
|
||||
void updateStatusFailedUI();
|
||||
|
||||
void reloadStatus();
|
||||
|
||||
/*!
|
||||
* Runs the DownloadUpdateTask and installs updates.
|
||||
*/
|
||||
void downloadUpdates(QString repo, int versionId, bool installOnExit=false);
|
||||
void downloadUpdates(QString repo, int versionId, bool installOnExit = false);
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev);
|
||||
void setCatBackground(bool enabled);
|
||||
void updateInstanceToolIcon(QString new_icon);
|
||||
|
||||
void setSelectedInstanceById(const QString &id);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
KCategoryDrawer *drawer;
|
||||
KCategorizedView *view;
|
||||
class GroupView *view;
|
||||
InstanceProxyModel *proxymodel;
|
||||
NetJobPtr skin_download_job;
|
||||
MinecraftProcess *proc;
|
||||
ConsoleWindow *console;
|
||||
LabeledToolButton *renameButton;
|
||||
QToolButton *changeIconButton;
|
||||
QToolButton* newsLabel;
|
||||
QToolButton *newsLabel;
|
||||
|
||||
BaseInstance *m_selectedInstance;
|
||||
InstancePtr m_selectedInstance;
|
||||
QString m_currentInstIcon;
|
||||
|
||||
Task *m_versionLoadTask;
|
||||
|
||||
QLabel *m_statusLeft;
|
||||
QLabel *m_statusRight;
|
||||
QToolButton *m_statusRefresh;
|
||||
|
||||
QMenu *accountMenu;
|
||||
QToolButton *accountMenuButton;
|
||||
QAction *manageAccountsAction;
|
||||
|
||||
QTimer statusTimer;
|
||||
};
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>688</width>
|
||||
<height>460</height>
|
||||
<width>694</width>
|
||||
<height>563</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MultiMC 5</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<iconset resource="../resources/multimc/multimc.qrc">
|
||||
<normaloff>:/icons/multimc/scalable/apps/multimc.svg</normaloff>:/icons/multimc/scalable/apps/multimc.svg</iconset>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralWidget">
|
||||
@@ -74,6 +74,7 @@
|
||||
<addaction name="actionReportBug"/>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionPatreon"/>
|
||||
<addaction name="actionCAT"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
@@ -107,10 +108,13 @@
|
||||
</attribute>
|
||||
<addaction name="actionChangeInstIcon"/>
|
||||
<addaction name="actionLaunchInstance"/>
|
||||
<addaction name="actionLaunchInstanceOffline"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEditInstNotes"/>
|
||||
<addaction name="actionChangeInstGroup"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionScreenshots"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionInstanceSettings"/>
|
||||
<addaction name="actionChangeInstMCVersion"/>
|
||||
<addaction name="actionChangeInstLWJGLVersion"/>
|
||||
@@ -152,8 +156,9 @@
|
||||
</widget>
|
||||
<action name="actionAddInstance">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset>
|
||||
<iconset theme="new">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add Instance</string>
|
||||
@@ -167,8 +172,9 @@
|
||||
</action>
|
||||
<action name="actionViewInstanceFolder">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/viewfolder</normaloff>:/icons/toolbar/viewfolder</iconset>
|
||||
<iconset theme="viewfolder">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Instance Folder</string>
|
||||
@@ -182,8 +188,9 @@
|
||||
</action>
|
||||
<action name="actionRefresh">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/refresh</normaloff>:/icons/toolbar/refresh</iconset>
|
||||
<iconset theme="refresh">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
@@ -197,8 +204,9 @@
|
||||
</action>
|
||||
<action name="actionViewCentralModsFolder">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/centralmods</normaloff>:/icons/toolbar/centralmods</iconset>
|
||||
<iconset theme="centralmods">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>View Central Mods Folder</string>
|
||||
@@ -212,8 +220,9 @@
|
||||
</action>
|
||||
<action name="actionCheckUpdate">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset>
|
||||
<iconset theme="checkupdate">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Check for Updates</string>
|
||||
@@ -227,8 +236,9 @@
|
||||
</action>
|
||||
<action name="actionSettings">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/settings</normaloff>:/icons/toolbar/settings</iconset>
|
||||
<iconset theme="settings">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Settings</string>
|
||||
@@ -245,8 +255,9 @@
|
||||
</action>
|
||||
<action name="actionReportBug">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/bug</normaloff>:/icons/toolbar/bug</iconset>
|
||||
<iconset theme="bug">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Report a Bug</string>
|
||||
@@ -258,10 +269,27 @@
|
||||
<string>Open the bug tracker to report a bug with MultiMC.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPatreon">
|
||||
<property name="icon">
|
||||
<iconset theme="patreon">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Support us on Patreon!</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Open the MultiMC Patreon page.</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Open the MultiMC Patreon page.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionMoreNews">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/news</normaloff>:/icons/toolbar/news</iconset>
|
||||
<iconset theme="news">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>More News</string>
|
||||
@@ -278,8 +306,9 @@
|
||||
</action>
|
||||
<action name="actionAbout">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/about</normaloff>:/icons/toolbar/about</iconset>
|
||||
<iconset theme="about">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About MultiMC</string>
|
||||
@@ -332,7 +361,7 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<iconset resource="../resources/instances/instances.qrc">
|
||||
<normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
@@ -472,20 +501,22 @@
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/cat</normaloff>:/icons/toolbar/cat</iconset>
|
||||
<iconset theme="cat">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Meow</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p align="center"><span style=" font-weight:600; color:#ff0004;">Catnarok!</span></p><p align="center">Or just a cat with a ball of yarn?</p><p align="center"><span style=" font-style:italic;">WHO KNOWS?!</span></p><p align="center"><img src=":/icons/instances/tnt"/></p></body></html></string>
|
||||
<string><html><head/><body><p align="center">It's a fluffy kitty :3</p></body></html></string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopyInstance">
|
||||
<property name="icon">
|
||||
<iconset resource="../graphics.qrc">
|
||||
<normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset>
|
||||
<iconset theme="copy">
|
||||
<normaloff/>
|
||||
</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy Instance</string>
|
||||
@@ -505,10 +536,30 @@
|
||||
<string>Manage your Mojang or Minecraft accounts.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLaunchInstanceOffline">
|
||||
<property name="text">
|
||||
<string>Play Offline</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Launch the selected instance in offline mode.</string>
|
||||
</property>
|
||||
<property name="statusTip">
|
||||
<string>Launch the selected instance.</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionScreenshots">
|
||||
<property name="text">
|
||||
<string>Manage Screenshots</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>View and upload screenshots for this instance</p></body></html></string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
<include location="../graphics.qrc"/>
|
||||
<include location="../resources/multimc/multimc.qrc"/>
|
||||
<include location="../resources/instances/instances.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -18,20 +18,111 @@
|
||||
#include <QIcon>
|
||||
#include "MultiMC.h"
|
||||
#include "gui/Platform.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include <logic/net/NetJob.h>
|
||||
|
||||
// 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)
|
||||
{
|
||||
QString creditsHtml =
|
||||
"<!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 <<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>></p>"
|
||||
"<p>Petr Mrázek <<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>></p>"
|
||||
"<p>Sky <<a href='https://www.twitter.com/drayshak'>@drayshak</a>></p>"
|
||||
"<p>Jan (02JanDal) <<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>></p>"
|
||||
""
|
||||
"<h3>With thanks to</h3>"
|
||||
"<p>Orochimarufan <<a href='mailto:orochimarufan.x3@gmail.com'>orochimarufan.x3@gmail.com</a>></p>"
|
||||
"<p>TakSuyu <<a href='mailto:taksuyu@gmail.com'>taksuyu@gmail.com</a>></p>"
|
||||
"<p>Kilobyte <<a href='mailto:stiepen22@gmx.de'>stiepen22@gmx.de</a>></p>"
|
||||
"<p>Robotbrain <<a href='https://twitter.com/skylordelros'>@skylordelros</a>></p>"
|
||||
"<p>Rootbear75 <<a href='https://twitter.com/rootbear75'>@rootbear75</a>> (build server)</p>"
|
||||
""
|
||||
"<h3>Patreon Patrons</h3>"
|
||||
"%1"
|
||||
""
|
||||
"</body>"
|
||||
"</html>";
|
||||
if (patrons.isEmpty())
|
||||
return creditsHtml.arg("<p>Loading...</p>");
|
||||
else
|
||||
{
|
||||
QString patronsStr;
|
||||
for (QString patron : patrons)
|
||||
{
|
||||
patronsStr.append(QString("<p>%1</p>").arg(patron));
|
||||
}
|
||||
|
||||
return creditsHtml.arg(patronsStr);
|
||||
}
|
||||
}
|
||||
|
||||
AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
|
||||
QString chtml = getCreditsHtml(QStringList());
|
||||
ui->creditsText->setHtml(chtml);
|
||||
|
||||
ui->urlLabel->setOpenExternalLinks(true);
|
||||
|
||||
ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
|
||||
ui->title->setText("MultiMC " + MMC->version().toString());
|
||||
ui->title->setText("MultiMC 5 " + BuildConfig.printableVersionString());
|
||||
|
||||
ui->versionLabel->setText(tr("Version") +": " + BuildConfig.printableVersionString());
|
||||
ui->vtypeLabel->setText(tr("Version Type") +": " + BuildConfig.versionTypeName());
|
||||
ui->platformLabel->setText(tr("Platform") +": " + BuildConfig.BUILD_PLATFORM);
|
||||
|
||||
if (BuildConfig.VERSION_BUILD >= 0)
|
||||
ui->buildNumLabel->setText(tr("Build Number") +": " + QString::number(BuildConfig.VERSION_BUILD));
|
||||
else
|
||||
ui->buildNumLabel->setVisible(false);
|
||||
|
||||
if (!BuildConfig.VERSION_CHANNEL.isEmpty())
|
||||
ui->channelLabel->setText(tr("Channel") +": " + BuildConfig.VERSION_CHANNEL);
|
||||
else
|
||||
ui->channelLabel->setVisible(false);
|
||||
|
||||
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
|
||||
|
||||
MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt()));
|
||||
|
||||
loadPatronList();
|
||||
}
|
||||
|
||||
AboutDialog::~AboutDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void AboutDialog::loadPatronList()
|
||||
{
|
||||
NetJob* job = new NetJob("Patreon Patron List");
|
||||
patronListDownload = ByteArrayDownload::make(QUrl("http://files.multimc.org/patrons.txt"));
|
||||
job->addNetAction(patronListDownload);
|
||||
connect(job, &NetJob::succeeded, this, &AboutDialog::patronListLoaded);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void AboutDialog::patronListLoaded()
|
||||
{
|
||||
QString patronListStr(patronListDownload->m_data);
|
||||
QString html = getCreditsHtml(patronListStr.split("\n", QString::SkipEmptyParts));
|
||||
ui->creditsText->setHtml(html);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <logic/net/ByteArrayDownload.h>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class AboutDialog;
|
||||
@@ -30,6 +32,16 @@ public:
|
||||
explicit AboutDialog(QWidget *parent = 0);
|
||||
~AboutDialog();
|
||||
|
||||
public
|
||||
slots:
|
||||
/// Starts loading a list of Patreon patrons.
|
||||
void loadPatronList();
|
||||
|
||||
/// Slot for when the patron list loads successfully.
|
||||
void patronListLoaded();
|
||||
|
||||
private:
|
||||
Ui::AboutDialog *ui;
|
||||
|
||||
ByteArrayDownloadPtr patronListDownload;
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../graphics.qrc">:/icons/multimc/scalable/apps/multimc.svg</pixmap>
|
||||
<pixmap resource="../../resources/multimc/multimc.qrc">:/icons/multimc/scalable/apps/multimc.svg</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -86,7 +86,7 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>MultiMC</string>
|
||||
<string>MultiMC 5</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
@@ -96,21 +96,71 @@
|
||||
<item>
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="aboutPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>685</width>
|
||||
<height>304</height>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>About</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="versionLabel">
|
||||
<property name="text">
|
||||
<string>Version:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="vtypeLabel">
|
||||
<property name="text">
|
||||
<string>Version Type:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="platformLabel">
|
||||
<property name="text">
|
||||
<string>Platform:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="buildNumLabel">
|
||||
<property name="text">
|
||||
<string>Build Number:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="channelLabel">
|
||||
<property name="text">
|
||||
<string>Channel:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="aboutLabel">
|
||||
<property name="enabled">
|
||||
@@ -151,13 +201,26 @@
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://github.com/Forkk/MultiMC5"><span style=" text-decoration: underline; color:#0000ff;">http://github.com/MultiMC/MultiMC5</span></a></p></body></html></string>
|
||||
<string><html><head/><body><p><a href="http://github.com/MultiMC/MultiMC5"><span style=" text-decoration: underline; color:#0000ff;">http://github.com/MultiMC/MultiMC5</span></a></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="QWidget" name="creditsPage">
|
||||
@@ -165,8 +228,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>685</width>
|
||||
<height>304</height>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -182,19 +245,8 @@
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600;">MultiMC</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Sky &lt;</span><a href="https://www.twitter.com/drayshak"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@drayshak</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt; font-weight:600;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt; font-weight:600;">With thanks to</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">TakSuyu &lt;</span><a href="mailto:taksuyu@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">taksuyu@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Kilobyte &lt;</span><a href="mailto:stiepen22@gmx.de"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">stiepen22@gmx.de</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Jan (02JanDal) &lt;</span><a href="mailto:02jandal@gmail.com"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">02jandal@gmail.com</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Robotbrain &lt;</span><a href="https://twitter.com/skylordelros"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@skylordelros</span></a><span style=" font-size:10pt;">&gt;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Rootbear75 &lt;</span><a href="https://twitter.com/rootbear75"><span style=" font-size:10pt; text-decoration: underline; color:#0000ff;">@rootbear75</span></a><span style=" font-size:10pt;">&gt; (build server)</span></p></body></html></string>
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||
@@ -218,8 +270,8 @@ p, li { white-space: pre-wrap; }
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>684</width>
|
||||
<height>290</height>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -246,7 +298,7 @@ p, li { white-space: pre-wrap; }
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||
</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">MultiMC</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright 2012-2014 MultiMC Contributors</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p>
|
||||
@@ -367,7 +419,36 @@ p, li { white-space: pre-wrap; }
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * This file has been put into the public domain.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * You can do whatever you want with this file.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> */</span></p></body></html></string>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> */</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p>
|
||||
<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">Java IconLoader class</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Copyright (c) 2011, Chris Molini</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">All rights reserved.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Redistribution and use in source and binary forms, with or without</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">modification, are permitted provided that the following conditions are met:</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Redistributions of source code must retain the above copyright</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> notice, this list of conditions and the following disclaimer.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Redistributions in binary form must reproduce the above copyright</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> notice, this list of conditions and the following disclaimer in the</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> documentation and/or other materials provided with the distribution.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Neither the name of the &lt;organization&gt; nor the</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> names of its contributors may be used to endorse or promote products</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> derived from this software without specific prior written permission.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">DISCLAIMED. IN NO EVENT SHALL &lt;COPYRIGHT HOLDER&gt; BE LIABLE FOR ANY</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -378,8 +459,8 @@ p, li { white-space: pre-wrap; }
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>684</width>
|
||||
<height>290</height>
|
||||
<width>689</width>
|
||||
<height>331</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
@@ -392,7 +473,7 @@ p, li { white-space: pre-wrap; }
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:7.8pt; font-weight:400; font-style:normal;">
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;">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.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;"><br /></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:11pt;">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 *MultiMC-fork* in the title).</span></p>
|
||||
@@ -445,7 +526,7 @@ p, li { white-space: pre-wrap; }
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../graphics.qrc"/>
|
||||
<include location="../../resources/multimc/multimc.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <gui/dialogs/EditAccountDialog.h>
|
||||
#include <gui/dialogs/ProgressDialog.h>
|
||||
#include <gui/dialogs/AccountSelectDialog.h>
|
||||
#include <gui/dialogs/LoginDialog.h>
|
||||
#include "CustomMessageBox.h"
|
||||
#include <logic/tasks/Task.h>
|
||||
#include <logic/auth/YggdrasilTask.h>
|
||||
@@ -45,10 +46,11 @@ AccountListDialog::AccountListDialog(QWidget *parent)
|
||||
// Expand the account column
|
||||
ui->listView->header()->setSectionResizeMode(1, QHeaderView::Stretch);
|
||||
|
||||
QItemSelectionModel* selectionModel = ui->listView->selectionModel();
|
||||
QItemSelectionModel *selectionModel = ui->listView->selectionModel();
|
||||
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||
[this] (const QItemSelection& sel, const QItemSelection& dsel) { updateButtonStates(); });
|
||||
connect(selectionModel, &QItemSelectionModel::selectionChanged,
|
||||
[this](const QItemSelection &sel, const QItemSelection &dsel)
|
||||
{ updateButtonStates(); });
|
||||
|
||||
connect(m_accounts.get(), SIGNAL(listChanged()), SLOT(listChanged()));
|
||||
connect(m_accounts.get(), SIGNAL(activeAccountChanged()), SLOT(listChanged()));
|
||||
@@ -68,7 +70,8 @@ void AccountListDialog::listChanged()
|
||||
|
||||
void AccountListDialog::on_addAccountBtn_clicked()
|
||||
{
|
||||
addAccount(tr("Please enter your Mojang or Minecraft account username and password to add your account."));
|
||||
addAccount(tr("Please enter your Mojang or Minecraft account username and password to add "
|
||||
"your account."));
|
||||
}
|
||||
|
||||
void AccountListDialog::on_rmAccountBtn_clicked()
|
||||
@@ -87,7 +90,8 @@ void AccountListDialog::on_setDefaultBtn_clicked()
|
||||
if (selection.size() > 0)
|
||||
{
|
||||
QModelIndex selected = selection.first();
|
||||
MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
MojangAccountPtr account =
|
||||
selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
m_accounts->setActiveAccount(account->username());
|
||||
}
|
||||
}
|
||||
@@ -113,48 +117,29 @@ void AccountListDialog::updateButtonStates()
|
||||
ui->noDefaultBtn->setDown(m_accounts->activeAccount().get() == nullptr);
|
||||
}
|
||||
|
||||
void AccountListDialog::addAccount(const QString& errMsg)
|
||||
void AccountListDialog::addAccount(const QString &errMsg)
|
||||
{
|
||||
// TODO: We can use the login dialog for this for now, but we'll have to make something better for it eventually.
|
||||
EditAccountDialog loginDialog(errMsg, this, EditAccountDialog::UsernameField | EditAccountDialog::PasswordField);
|
||||
loginDialog.exec();
|
||||
// TODO: The login dialog isn't quite done yet
|
||||
MojangAccountPtr account = LoginDialog::newAccount(this, errMsg);
|
||||
|
||||
if (loginDialog.result() == QDialog::Accepted)
|
||||
if (account != nullptr)
|
||||
{
|
||||
QString username(loginDialog.username());
|
||||
QString password(loginDialog.password());
|
||||
m_accounts->addAccount(account);
|
||||
if (m_accounts->count() == 1)
|
||||
m_accounts->setActiveAccount(account->username());
|
||||
|
||||
MojangAccountPtr account = MojangAccount::createFromUsername(username);
|
||||
ProgressDialog progDialog(this);
|
||||
auto task = account->login(password);
|
||||
progDialog.exec(task.get());
|
||||
if(task->successful())
|
||||
// Grab associated player skins
|
||||
auto job = new NetJob("Player skins: " + account->username());
|
||||
|
||||
for (AccountProfile profile : account->profiles())
|
||||
{
|
||||
m_accounts->addAccount(account);
|
||||
if (m_accounts->count() == 1)
|
||||
m_accounts->setActiveAccount(account->username());
|
||||
|
||||
// Grab associated player skins
|
||||
auto job = new NetJob("Player skins: " + account->username());
|
||||
|
||||
for(AccountProfile profile : account->profiles())
|
||||
{
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"),
|
||||
meta);
|
||||
job->addNetAction(action);
|
||||
meta->stale = true;
|
||||
}
|
||||
|
||||
job->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto reason = task->failReason();
|
||||
auto dlg = CustomMessageBox::selectable(this, tr("Login error."), reason, QMessageBox::Critical);
|
||||
dlg->exec();
|
||||
delete dlg;
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"), meta);
|
||||
job->addNetAction(action);
|
||||
meta->stale = true;
|
||||
}
|
||||
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "logic/tasks/Task.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
|
||||
CopyInstanceDialog::CopyInstanceDialog(BaseInstance *original, QWidget *parent)
|
||||
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
|
||||
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
#include "logic/BaseVersion.h"
|
||||
#include <logic/BaseInstance.h>
|
||||
|
||||
class BaseInstance;
|
||||
|
||||
@@ -30,7 +31,7 @@ class CopyInstanceDialog : public QDialog
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CopyInstanceDialog(BaseInstance *original, QWidget *parent = 0);
|
||||
explicit CopyInstanceDialog(InstancePtr original, QWidget *parent = 0);
|
||||
~CopyInstanceDialog();
|
||||
|
||||
void updateDialogState();
|
||||
@@ -46,5 +47,5 @@ slots:
|
||||
private:
|
||||
Ui::CopyInstanceDialog *ui;
|
||||
QString InstIconKey;
|
||||
BaseInstance *m_original;
|
||||
InstancePtr m_original;
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ QMessageBox *selectable(QWidget *parent, const QString &title, const QString &te
|
||||
messageBox->setDefaultButton(defaultButton);
|
||||
messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse);
|
||||
messageBox->setIcon(icon);
|
||||
messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
|
||||
return messageBox;
|
||||
}
|
||||
|
||||
@@ -38,5 +38,6 @@ EditNotesDialog::~EditNotesDialog()
|
||||
|
||||
QString EditNotesDialog::getText()
|
||||
{
|
||||
return ui->noteEditor->toPlainText();
|
||||
QString test = ui->noteEditor->toPlainText();
|
||||
return test;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "ui_IconPickerDialog.h"
|
||||
|
||||
#include "gui/Platform.h"
|
||||
#include "gui/widgets/InstanceDelegate.h"
|
||||
#include "gui/groupview/InstanceDelegate.h"
|
||||
|
||||
#include "logic/icons/IconList.h"
|
||||
|
||||
|
||||
@@ -168,6 +168,12 @@
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maxMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The maximum amount of memory Minecraft is allowed to use.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
@@ -198,6 +204,12 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
@@ -214,6 +226,12 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="permGenSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory available to store loaded Java classes.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
|
||||
@@ -208,7 +208,7 @@ void LegacyModEditDialog::on_addCoreBtn_clicked()
|
||||
void LegacyModEditDialog::on_addForgeBtn_clicked()
|
||||
{
|
||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||
vselect.setFilter(1, m_inst->intendedVersionId());
|
||||
vselect.setExactFilter(1, m_inst->intendedVersionId());
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
ForgeVersionPtr forge =
|
||||
|
||||
110
gui/dialogs/LoginDialog.cpp
Normal file
110
gui/dialogs/LoginDialog.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/* Copyright 2014 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 "LoginDialog.h"
|
||||
#include "ui_LoginDialog.h"
|
||||
|
||||
#include "logic/auth/YggdrasilTask.h"
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoginDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->progressBar->setVisible(false);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
LoginDialog::~LoginDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
// Stage 1: User interaction
|
||||
void LoginDialog::accept()
|
||||
{
|
||||
setUserInputsEnabled(false);
|
||||
ui->progressBar->setVisible(true);
|
||||
|
||||
// Setup the login task and start it
|
||||
m_account = MojangAccount::createFromUsername(ui->userTextBox->text());
|
||||
m_loginTask = m_account->login(nullptr, ui->passTextBox->text());
|
||||
connect(m_loginTask.get(), &ProgressProvider::failed, this, &LoginDialog::onTaskFailed);
|
||||
connect(m_loginTask.get(), &ProgressProvider::succeeded, this,
|
||||
&LoginDialog::onTaskSucceeded);
|
||||
connect(m_loginTask.get(), &ProgressProvider::status, this, &LoginDialog::onTaskStatus);
|
||||
connect(m_loginTask.get(), &ProgressProvider::progress, this, &LoginDialog::onTaskProgress);
|
||||
m_loginTask->start();
|
||||
}
|
||||
|
||||
void LoginDialog::setUserInputsEnabled(bool enable)
|
||||
{
|
||||
ui->userTextBox->setEnabled(enable);
|
||||
ui->passTextBox->setEnabled(enable);
|
||||
ui->buttonBox->setEnabled(enable);
|
||||
}
|
||||
|
||||
// Enable the OK button only when both textboxes contain something.
|
||||
void LoginDialog::on_userTextBox_textEdited(const QString &newText)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)
|
||||
->setEnabled(!newText.isEmpty() && !ui->passTextBox->text().isEmpty());
|
||||
}
|
||||
void LoginDialog::on_passTextBox_textEdited(const QString &newText)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)
|
||||
->setEnabled(!newText.isEmpty() && !ui->userTextBox->text().isEmpty());
|
||||
}
|
||||
|
||||
void LoginDialog::onTaskFailed(const QString &reason)
|
||||
{
|
||||
// Set message
|
||||
ui->label->setText("<span style='color:red'>" + reason + "</span>");
|
||||
|
||||
// Re-enable user-interaction
|
||||
setUserInputsEnabled(true);
|
||||
ui->progressBar->setVisible(false);
|
||||
}
|
||||
|
||||
void LoginDialog::onTaskSucceeded()
|
||||
{
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void LoginDialog::onTaskStatus(const QString &status)
|
||||
{
|
||||
ui->label->setText(status);
|
||||
}
|
||||
|
||||
void LoginDialog::onTaskProgress(qint64 current, qint64 total)
|
||||
{
|
||||
ui->progressBar->setMaximum(total);
|
||||
ui->progressBar->setValue(current);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
MojangAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg)
|
||||
{
|
||||
LoginDialog dlg(parent);
|
||||
dlg.ui->label->setText(msg);
|
||||
if (dlg.exec() == QDialog::Accepted)
|
||||
{
|
||||
return dlg.m_account;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
58
gui/dialogs/LoginDialog.h
Normal file
58
gui/dialogs/LoginDialog.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/* Copyright 2014 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 <QtWidgets/QDialog>
|
||||
#include <QtCore/QEventLoop>
|
||||
|
||||
#include "logic/auth/MojangAccount.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class LoginDialog;
|
||||
}
|
||||
|
||||
class LoginDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~LoginDialog();
|
||||
|
||||
static MojangAccountPtr newAccount(QWidget *parent, QString message);
|
||||
|
||||
private:
|
||||
explicit LoginDialog(QWidget *parent = 0);
|
||||
|
||||
void setUserInputsEnabled(bool enable);
|
||||
|
||||
protected
|
||||
slots:
|
||||
void accept();
|
||||
|
||||
void onTaskFailed(const QString &reason);
|
||||
void onTaskSucceeded();
|
||||
void onTaskStatus(const QString &status);
|
||||
void onTaskProgress(qint64 current, qint64 total);
|
||||
|
||||
void on_userTextBox_textEdited(const QString &newText);
|
||||
void on_passTextBox_textEdited(const QString &newText);
|
||||
|
||||
private:
|
||||
Ui::LoginDialog *ui;
|
||||
MojangAccountPtr m_account;
|
||||
std::shared_ptr<Task> m_loginTask;
|
||||
};
|
||||
77
gui/dialogs/LoginDialog.ui
Normal file
77
gui/dialogs/LoginDialog.ui
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>LoginDialog</class>
|
||||
<widget class="QDialog" name="LoginDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>162</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Add Account</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Message label placeholder.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="userTextBox">
|
||||
<property name="placeholderText">
|
||||
<string>Email / Username</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passTextBox">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -50,8 +50,8 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
|
||||
else
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parentDlg, parentDlg->tr("How sad!"),
|
||||
parentDlg->tr("The mod author didn't provide a website link for this mod."),
|
||||
parentDlg, QObject::tr("How sad!"),
|
||||
QObject::tr("The mod author didn't provide a website link for this mod."),
|
||||
QMessageBox::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
84
gui/dialogs/NotificationDialog.cpp
Normal file
84
gui/dialogs/NotificationDialog.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "NotificationDialog.h"
|
||||
#include "ui_NotificationDialog.h"
|
||||
|
||||
#include <QTimerEvent>
|
||||
|
||||
NotificationDialog::NotificationDialog(const NotificationChecker::NotificationEntry &entry, QWidget *parent) :
|
||||
QDialog(parent, Qt::MSWindowsFixedSizeDialogHint | Qt::WindowTitleHint | Qt::CustomizeWindowHint),
|
||||
ui(new Ui::NotificationDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QStyle::StandardPixmap icon;
|
||||
switch (entry.type)
|
||||
{
|
||||
case NotificationChecker::NotificationEntry::Critical:
|
||||
icon = QStyle::SP_MessageBoxCritical;
|
||||
break;
|
||||
case NotificationChecker::NotificationEntry::Warning:
|
||||
icon = QStyle::SP_MessageBoxWarning;
|
||||
break;
|
||||
case NotificationChecker::NotificationEntry::Information:
|
||||
icon = QStyle::SP_MessageBoxInformation;
|
||||
break;
|
||||
}
|
||||
ui->iconLabel->setPixmap(style()->standardPixmap(icon, 0, this));
|
||||
ui->messageLabel->setText(entry.message);
|
||||
|
||||
m_dontShowAgainText = tr("Don't show again");
|
||||
m_closeText = tr("Close");
|
||||
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText + QString(" (%1)").arg(m_dontShowAgainTime));
|
||||
ui->closeBtn->setText(m_closeText + QString(" (%1)").arg(m_closeTime));
|
||||
|
||||
startTimer(1000);
|
||||
}
|
||||
|
||||
NotificationDialog::~NotificationDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NotificationDialog::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
if (m_dontShowAgainTime > 0)
|
||||
{
|
||||
m_dontShowAgainTime--;
|
||||
if (m_dontShowAgainTime == 0)
|
||||
{
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText);
|
||||
ui->dontShowAgainBtn->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->dontShowAgainBtn->setText(m_dontShowAgainText + QString(" (%1)").arg(m_dontShowAgainTime));
|
||||
}
|
||||
}
|
||||
if (m_closeTime > 0)
|
||||
{
|
||||
m_closeTime--;
|
||||
if (m_closeTime == 0)
|
||||
{
|
||||
ui->closeBtn->setText(m_closeText);
|
||||
ui->closeBtn->setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->closeBtn->setText(m_closeText + QString(" (%1)").arg(m_closeTime));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_closeTime == 0 && m_dontShowAgainTime == 0)
|
||||
{
|
||||
killTimer(event->timerId());
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationDialog::on_dontShowAgainBtn_clicked()
|
||||
{
|
||||
done(DontShowAgain);
|
||||
}
|
||||
void NotificationDialog::on_closeBtn_clicked()
|
||||
{
|
||||
done(Normal);
|
||||
}
|
||||
44
gui/dialogs/NotificationDialog.h
Normal file
44
gui/dialogs/NotificationDialog.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef NOTIFICATIONDIALOG_H
|
||||
#define NOTIFICATIONDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "logic/updater/NotificationChecker.h"
|
||||
|
||||
namespace Ui {
|
||||
class NotificationDialog;
|
||||
}
|
||||
|
||||
class NotificationDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NotificationDialog(const NotificationChecker::NotificationEntry &entry, QWidget *parent = 0);
|
||||
~NotificationDialog();
|
||||
|
||||
enum ExitCode
|
||||
{
|
||||
Normal,
|
||||
DontShowAgain
|
||||
};
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *event);
|
||||
|
||||
private:
|
||||
Ui::NotificationDialog *ui;
|
||||
|
||||
int m_dontShowAgainTime = 10;
|
||||
int m_closeTime = 5;
|
||||
|
||||
QString m_dontShowAgainText;
|
||||
QString m_closeText;
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_dontShowAgainBtn_clicked();
|
||||
void on_closeBtn_clicked();
|
||||
};
|
||||
|
||||
#endif // NOTIFICATIONDIALOG_H
|
||||
85
gui/dialogs/NotificationDialog.ui
Normal file
85
gui/dialogs/NotificationDialog.ui
Normal file
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NotificationDialog</class>
|
||||
<widget class="QDialog" name="NotificationDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>240</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="dontShowAgainBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Don't show again</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -34,11 +34,13 @@
|
||||
#include "gui/dialogs/ProgressDialog.h"
|
||||
|
||||
#include "logic/ModList.h"
|
||||
#include "logic/OneSixVersion.h"
|
||||
#include "logic/VersionFinal.h"
|
||||
#include "logic/EnabledItemFilter.h"
|
||||
#include "logic/lists/ForgeVersionList.h"
|
||||
#include "logic/lists/LiteLoaderVersionList.h"
|
||||
#include "logic/ForgeInstaller.h"
|
||||
#include "logic/LiteLoaderInstaller.h"
|
||||
#include "logic/OneSixVersionBuilder.h"
|
||||
|
||||
OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::OneSixModEditDialog), m_inst(inst)
|
||||
@@ -55,7 +57,8 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||
main_model->setSourceModel(m_version.get());
|
||||
ui->libraryTreeView->setModel(main_model);
|
||||
ui->libraryTreeView->installEventFilter(this);
|
||||
ui->mainClassEdit->setText(m_version->mainClass);
|
||||
connect(ui->libraryTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
this, &OneSixModEditDialog::versionCurrent);
|
||||
updateVersionControls();
|
||||
}
|
||||
else
|
||||
@@ -81,6 +84,9 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent)
|
||||
ui->resPackTreeView->installEventFilter(this);
|
||||
m_resourcepacks->startWatching();
|
||||
}
|
||||
|
||||
connect(m_inst, &OneSixInstance::versionReloaded, this,
|
||||
&OneSixModEditDialog::updateVersionControls);
|
||||
}
|
||||
|
||||
OneSixModEditDialog::~OneSixModEditDialog()
|
||||
@@ -92,157 +98,173 @@ OneSixModEditDialog::~OneSixModEditDialog()
|
||||
|
||||
void OneSixModEditDialog::updateVersionControls()
|
||||
{
|
||||
bool customVersion = m_inst->versionIsCustom();
|
||||
ui->customizeBtn->setEnabled(!customVersion);
|
||||
ui->revertBtn->setEnabled(customVersion);
|
||||
ui->forgeBtn->setEnabled(true);
|
||||
ui->liteloaderBtn->setEnabled(LiteLoaderInstaller(m_inst->intendedVersionId()).canApply());
|
||||
ui->customEditorBtn->setEnabled(customVersion);
|
||||
ui->liteloaderBtn->setEnabled(true);
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::disableVersionControls()
|
||||
{
|
||||
ui->customizeBtn->setEnabled(false);
|
||||
ui->revertBtn->setEnabled(false);
|
||||
ui->forgeBtn->setEnabled(false);
|
||||
ui->liteloaderBtn->setEnabled(false);
|
||||
ui->customEditorBtn->setEnabled(false);
|
||||
ui->reloadLibrariesBtn->setEnabled(false);
|
||||
ui->removeLibraryBtn->setEnabled(false);
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_customizeBtn_clicked()
|
||||
bool OneSixModEditDialog::reloadInstanceVersion()
|
||||
{
|
||||
if (m_inst->customizeVersion())
|
||||
try
|
||||
{
|
||||
m_version = m_inst->getFullVersion();
|
||||
main_model->setSourceModel(m_version.get());
|
||||
updateVersionControls();
|
||||
m_inst->reloadVersion();
|
||||
return true;
|
||||
}
|
||||
catch (MMCError &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("Error"),
|
||||
tr("Failed to load the version description file for reasons unknown."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_revertBtn_clicked()
|
||||
void OneSixModEditDialog::on_reloadLibrariesBtn_clicked()
|
||||
{
|
||||
auto response = CustomMessageBox::selectable(
|
||||
this, tr("Revert?"), tr("Do you want to revert the "
|
||||
"version of this instance to its original configuration?"),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
if (response == QMessageBox::Yes)
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_removeLibraryBtn_clicked()
|
||||
{
|
||||
if (ui->libraryTreeView->currentIndex().isValid())
|
||||
{
|
||||
if (m_inst->revertCustomVersion())
|
||||
// FIXME: use actual model, not reloading.
|
||||
if (!m_version->remove(ui->libraryTreeView->currentIndex().row()))
|
||||
{
|
||||
m_version = m_inst->getFullVersion();
|
||||
main_model->setSourceModel(m_version.get());
|
||||
updateVersionControls();
|
||||
QMessageBox::critical(this, tr("Error"), tr("Couldn't remove file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_customEditorBtn_clicked()
|
||||
void OneSixModEditDialog::on_resetLibraryOrderBtn_clicked()
|
||||
{
|
||||
if (m_inst->versionIsCustom())
|
||||
try
|
||||
{
|
||||
if (!MMC->openJsonEditor(m_inst->instanceRoot() + "/custom.json"))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Error"), tr("Unable to open custom.json, check the settings"));
|
||||
}
|
||||
m_version->resetOrder();
|
||||
}
|
||||
catch (MMCError &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_moveLibraryUpBtn_clicked()
|
||||
{
|
||||
if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
|
||||
const int newRow = 0;m_version->move(row, VersionFinal::MoveUp);
|
||||
//ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
catch (MMCError &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_moveLibraryDownBtn_clicked()
|
||||
{
|
||||
if (ui->libraryTreeView->selectionModel()->selectedRows().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
const int row = ui->libraryTreeView->selectionModel()->selectedRows().first().row();
|
||||
const int newRow = 0;m_version->move(row, VersionFinal::MoveDown);
|
||||
//ui->libraryTreeView->selectionModel()->setCurrentIndex(m_version->index(newRow), QItemSelectionModel::ClearAndSelect);
|
||||
}
|
||||
catch (MMCError &e)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), e.cause());
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_forgeBtn_clicked()
|
||||
{
|
||||
// FIXME: use actual model, not reloading. Move logic to model.
|
||||
if (m_version->hasFtbPack())
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Revert?"),
|
||||
tr("This action will remove the FTB pack version patch. Continue?")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_version->removeFtbPack();
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
if (m_version->isCustom())
|
||||
{
|
||||
if (QMessageBox::question(this, tr("Revert?"),
|
||||
tr("This action will remove your custom.json. Continue?")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_version->revertToBase();
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
VersionSelectDialog vselect(MMC->forgelist().get(), tr("Select Forge version"), this);
|
||||
vselect.setFilter(1, m_inst->currentVersionId());
|
||||
vselect.setExactFilter(1, m_inst->currentVersionId());
|
||||
vselect.setEmptyString(tr("No Forge versions are currently available for Minecraft ") +
|
||||
m_inst->currentVersionId());
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
if (m_inst->versionIsCustom())
|
||||
{
|
||||
auto reply = QMessageBox::question(
|
||||
this, tr("Revert?"),
|
||||
tr("This will revert any "
|
||||
"changes you did to the version up to this point. Is that "
|
||||
"OK?"),
|
||||
QMessageBox::Yes | QMessageBox::No);
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
m_inst->revertCustomVersion();
|
||||
m_inst->customizeVersion();
|
||||
{
|
||||
m_version = m_inst->getFullVersion();
|
||||
main_model->setSourceModel(m_version.get());
|
||||
updateVersionControls();
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_inst->customizeVersion();
|
||||
m_version = m_inst->getFullVersion();
|
||||
main_model->setSourceModel(m_version.get());
|
||||
updateVersionControls();
|
||||
}
|
||||
ForgeVersionPtr forgeVersion =
|
||||
std::dynamic_pointer_cast<ForgeVersion>(vselect.selectedVersion());
|
||||
if (!forgeVersion)
|
||||
return;
|
||||
auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename);
|
||||
if (entry->stale)
|
||||
{
|
||||
NetJob *fjob = new NetJob("Forge download");
|
||||
fjob->addNetAction(CacheDownload::make(forgeVersion->installer_url, entry));
|
||||
ProgressDialog dlg(this);
|
||||
dlg.exec(fjob);
|
||||
if (dlg.result() == QDialog::Accepted)
|
||||
{
|
||||
// install
|
||||
QString forgePath = entry->getFullPath();
|
||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||
if (!forge.apply(m_version))
|
||||
{
|
||||
// failure notice
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// failed to download forge :/
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// install
|
||||
QString forgePath = entry->getFullPath();
|
||||
ForgeInstaller forge(forgePath, forgeVersion->universal_url);
|
||||
if (!forge.apply(m_version))
|
||||
{
|
||||
// failure notice
|
||||
}
|
||||
}
|
||||
ProgressDialog dialog(this);
|
||||
dialog.exec(ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
|
||||
}
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::on_liteloaderBtn_clicked()
|
||||
{
|
||||
LiteLoaderInstaller liteloader(m_inst->intendedVersionId());
|
||||
if (!liteloader.canApply())
|
||||
if (m_version->hasFtbPack())
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("LiteLoader"),
|
||||
tr("There is no information available on how to install LiteLoader "
|
||||
"into this version of Minecraft"));
|
||||
return;
|
||||
if (QMessageBox::question(this, tr("Revert?"),
|
||||
tr("This action will remove the FTB pack version patch. Continue?")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_version->removeFtbPack();
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
if (!m_inst->versionIsCustom())
|
||||
if (m_version->isCustom())
|
||||
{
|
||||
m_inst->customizeVersion();
|
||||
m_version = m_inst->getFullVersion();
|
||||
main_model->setSourceModel(m_version.get());
|
||||
updateVersionControls();
|
||||
if (QMessageBox::question(this, tr("Revert?"),
|
||||
tr("This action will remove your custom.json. Continue?")) !=
|
||||
QMessageBox::Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
m_version->revertToBase();
|
||||
reloadInstanceVersion();
|
||||
}
|
||||
if (!liteloader.apply(m_version))
|
||||
VersionSelectDialog vselect(MMC->liteloaderlist().get(), tr("Select LiteLoader version"),
|
||||
this);
|
||||
vselect.setExactFilter(1, m_inst->currentVersionId());
|
||||
vselect.setEmptyString(tr("No LiteLoader versions are currently available for Minecraft ") +
|
||||
m_inst->currentVersionId());
|
||||
if (vselect.exec() && vselect.selectedVersion())
|
||||
{
|
||||
QMessageBox::critical(
|
||||
this, tr("LiteLoader"),
|
||||
tr("For reasons unknown, the LiteLoader installation failed. Check your MultiMC log files for details."));
|
||||
ProgressDialog dialog(this);
|
||||
dialog.exec(LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,3 +384,16 @@ void OneSixModEditDialog::loaderCurrent(QModelIndex current, QModelIndex previou
|
||||
Mod &m = m_mods->operator[](row);
|
||||
ui->frame->updateWithMod(m);
|
||||
}
|
||||
|
||||
void OneSixModEditDialog::versionCurrent(const QModelIndex ¤t,
|
||||
const QModelIndex &previous)
|
||||
{
|
||||
if (!current.isValid())
|
||||
{
|
||||
ui->removeLibraryBtn->setDisabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->removeLibraryBtn->setEnabled(m_version->canRemove(current.row()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,11 @@ slots:
|
||||
void on_buttonBox_rejected();
|
||||
void on_forgeBtn_clicked();
|
||||
void on_liteloaderBtn_clicked();
|
||||
void on_customizeBtn_clicked();
|
||||
void on_revertBtn_clicked();
|
||||
void on_customEditorBtn_clicked();
|
||||
void on_reloadLibrariesBtn_clicked();
|
||||
void on_removeLibraryBtn_clicked();
|
||||
void on_resetLibraryOrderBtn_clicked();
|
||||
void on_moveLibraryUpBtn_clicked();
|
||||
void on_moveLibraryDownBtn_clicked();
|
||||
void updateVersionControls();
|
||||
void disableVersionControls();
|
||||
|
||||
@@ -55,15 +57,19 @@ protected:
|
||||
bool eventFilter(QObject *obj, QEvent *ev);
|
||||
bool loaderListFilter(QKeyEvent *ev);
|
||||
bool resourcePackListFilter(QKeyEvent *ev);
|
||||
/// FIXME: this shouldn't be necessary!
|
||||
bool reloadInstanceVersion();
|
||||
|
||||
private:
|
||||
Ui::OneSixModEditDialog *ui;
|
||||
std::shared_ptr<OneSixVersion> m_version;
|
||||
std::shared_ptr<VersionFinal> m_version;
|
||||
std::shared_ptr<ModList> m_mods;
|
||||
std::shared_ptr<ModList> m_resourcepacks;
|
||||
EnabledItemFilter *main_model;
|
||||
OneSixInstance *m_inst;
|
||||
|
||||
public
|
||||
slots:
|
||||
void loaderCurrent(QModelIndex current, QModelIndex previous);
|
||||
void versionCurrent(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</size>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="libTab">
|
||||
<attribute name="title">
|
||||
@@ -43,26 +43,11 @@
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<attribute name="headerVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Main Class:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mainClassEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
@@ -84,62 +69,24 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="customizeBtn">
|
||||
<property name="toolTip">
|
||||
<string>Create an customized copy of the base version</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Customize</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="revertBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Revert to original base version</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Revert</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addLibraryBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add new libraries</string>
|
||||
</property>
|
||||
<widget class="QPushButton" name="reloadLibrariesBtn">
|
||||
<property name="text">
|
||||
<string>&Add</string>
|
||||
<string>Reload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeLibraryBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Remove selected libraries</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Remove</string>
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -151,9 +98,32 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="customEditorBtn">
|
||||
<widget class="QPushButton" name="moveLibraryUpBtn">
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open custom.json</string>
|
||||
<string>Move up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="moveLibraryDownBtn">
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move down</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetLibraryOrderBtn">
|
||||
<property name="toolTip">
|
||||
<string>This isn't implemented yet.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Reset order</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
78
gui/dialogs/ScreenshotDialog.cpp
Normal file
78
gui/dialogs/ScreenshotDialog.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "ScreenshotDialog.h"
|
||||
#include "ui_ScreenshotDialog.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QMutableListIterator>
|
||||
|
||||
#include "ProgressDialog.h"
|
||||
#include "CustomMessageBox.h"
|
||||
#include "logic/net/NetJob.h"
|
||||
#include "logic/screenshots/ImgurUpload.h"
|
||||
#include "logic/screenshots/ImgurAlbumCreation.h"
|
||||
#include "logic/tasks/SequentialTask.h"
|
||||
|
||||
ScreenshotDialog::ScreenshotDialog(ScreenshotList *list, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::ScreenshotDialog), m_list(list)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->listView->setModel(m_list);
|
||||
}
|
||||
|
||||
ScreenshotDialog::~ScreenshotDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString ScreenshotDialog::message() const
|
||||
{
|
||||
return tr("<a href=\"https://imgur.com/a/%1\">Visit album</a><br/>Delete hash: %2 (save "
|
||||
"this if you want to be able to edit/delete the album)")
|
||||
.arg(m_imgurAlbum->id(), m_imgurAlbum->deleteHash());
|
||||
}
|
||||
|
||||
QList<ScreenshotPtr> ScreenshotDialog::selected() const
|
||||
{
|
||||
QList<ScreenshotPtr> list;
|
||||
QList<ScreenshotPtr> first = m_list->screenshots();
|
||||
for (QModelIndex index : ui->listView->selectionModel()->selectedRows())
|
||||
{
|
||||
list.append(first.at(index.row()));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void ScreenshotDialog::on_uploadBtn_clicked()
|
||||
{
|
||||
m_uploaded = selected();
|
||||
if (m_uploaded.isEmpty())
|
||||
{
|
||||
done(NothingDone);
|
||||
return;
|
||||
}
|
||||
SequentialTask *task = new SequentialTask(this);
|
||||
NetJob *job = new NetJob("Screenshot Upload");
|
||||
for (auto shot : m_uploaded)
|
||||
{
|
||||
job->addNetAction(ImgurUpload::make(shot));
|
||||
}
|
||||
NetJob *albumTask = new NetJob("Imgur Album Creation");
|
||||
albumTask->addNetAction(m_imgurAlbum = ImgurAlbumCreation::make(m_uploaded));
|
||||
task->addTask(NetJobPtr(job));
|
||||
task->addTask(NetJobPtr(albumTask));
|
||||
ProgressDialog prog(this);
|
||||
if (prog.exec(task) == QDialog::Accepted)
|
||||
{
|
||||
accept();
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"),
|
||||
tr("Unknown error"), QMessageBox::Warning)->exec();
|
||||
reject();
|
||||
}
|
||||
}
|
||||
|
||||
void ScreenshotDialog::on_deleteBtn_clicked()
|
||||
{
|
||||
m_list->deleteSelected(this);
|
||||
}
|
||||
40
gui/dialogs/ScreenshotDialog.h
Normal file
40
gui/dialogs/ScreenshotDialog.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "logic/screenshots/ScreenshotList.h"
|
||||
|
||||
class ImgurAlbumCreation;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ScreenshotDialog;
|
||||
}
|
||||
|
||||
class ScreenshotDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ScreenshotDialog(ScreenshotList *list, QWidget *parent = 0);
|
||||
~ScreenshotDialog();
|
||||
|
||||
enum
|
||||
{
|
||||
NothingDone = 0x42
|
||||
};
|
||||
|
||||
QString message() const;
|
||||
QList<ScreenshotPtr> selected() const;
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_uploadBtn_clicked();
|
||||
|
||||
void on_deleteBtn_clicked();
|
||||
|
||||
private:
|
||||
Ui::ScreenshotDialog *ui;
|
||||
ScreenshotList *m_list;
|
||||
QList<ScreenshotPtr> m_uploaded;
|
||||
std::shared_ptr<ImgurAlbumCreation> m_imgurAlbum;
|
||||
};
|
||||
110
gui/dialogs/ScreenshotDialog.ui
Normal file
110
gui/dialogs/ScreenshotDialog.ui
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ScreenshotDialog</class>
|
||||
<widget class="QDialog" name="ScreenshotDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>470</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Screenshot Manager</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../resources/multimc/multimc.qrc">
|
||||
<normaloff>:/icons/multimc/scalable/apps/multimc.svg</normaloff>:/icons/multimc/scalable/apps/multimc.svg</iconset>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QListView" name="listView">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>90</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="flow">
|
||||
<enum>QListView::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="uploadBtn">
|
||||
<property name="text">
|
||||
<string>Upload</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteBtn">
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeBtn">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../resources/multimc/multimc.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>closeBtn</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>ScreenshotDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>315</x>
|
||||
<y>272</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>271</x>
|
||||
<y>258</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include "logic/updater/UpdateChecker.h"
|
||||
|
||||
#include "logic/tools/BaseProfiler.h"
|
||||
|
||||
#include <settingsobject.h>
|
||||
#include <pathutils.h>
|
||||
#include <QFileDialog>
|
||||
@@ -46,12 +48,14 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Se
|
||||
ui->jsonEditorTextBox->setClearButtonEnabled(true);
|
||||
#endif
|
||||
|
||||
restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
|
||||
restoreGeometry(
|
||||
QByteArray::fromBase64(MMC->settings()->get("SettingsGeometry").toByteArray()));
|
||||
|
||||
loadSettings(MMC->settings().get());
|
||||
updateCheckboxStuff();
|
||||
|
||||
QObject::connect(MMC->updateChecker().get(), &UpdateChecker::channelListLoaded, this, &SettingsDialog::refreshUpdateChannelList);
|
||||
QObject::connect(MMC->updateChecker().get(), &UpdateChecker::channelListLoaded, this,
|
||||
&SettingsDialog::refreshUpdateChannelList);
|
||||
|
||||
if (MMC->updateChecker()->hasChannels())
|
||||
{
|
||||
@@ -61,6 +65,10 @@ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Se
|
||||
{
|
||||
MMC->updateChecker()->updateChanList();
|
||||
}
|
||||
connect(ui->proxyGroup, SIGNAL(buttonClicked(int)), SLOT(proxyChanged(int)));
|
||||
ui->mceditLink->setOpenExternalLinks(true);
|
||||
ui->jvisualvmLink->setOpenExternalLinks(true);
|
||||
ui->jprofilerLink->setOpenExternalLinks(true);
|
||||
}
|
||||
|
||||
SettingsDialog::~SettingsDialog()
|
||||
@@ -83,6 +91,10 @@ void SettingsDialog::updateCheckboxStuff()
|
||||
{
|
||||
ui->windowWidthSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
|
||||
ui->windowHeightSpinBox->setEnabled(!ui->maximizedCheckBox->isChecked());
|
||||
ui->proxyAddrBox->setEnabled(!ui->proxyNoneBtn->isChecked() &&
|
||||
!ui->proxyDefaultBtn->isChecked());
|
||||
ui->proxyAuthBox->setEnabled(!ui->proxyNoneBtn->isChecked() &&
|
||||
!ui->proxyDefaultBtn->isChecked());
|
||||
}
|
||||
|
||||
void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
|
||||
@@ -100,8 +112,8 @@ void SettingsDialog::on_ftbLauncherBrowseBtn_clicked()
|
||||
|
||||
void SettingsDialog::on_ftbBrowseBtn_clicked()
|
||||
{
|
||||
QString raw_dir = QFileDialog::getExistingDirectory(this, tr("FTB Directory"),
|
||||
ui->ftbBox->text());
|
||||
QString raw_dir =
|
||||
QFileDialog::getExistingDirectory(this, tr("FTB Directory"), ui->ftbBox->text());
|
||||
QString cooked_dir = NormalizePath(raw_dir);
|
||||
|
||||
// do not allow current dir - it's dirty. Do not allow dirs that don't exist
|
||||
@@ -167,11 +179,11 @@ void SettingsDialog::on_jsonEditorBrowseBtn_clicked()
|
||||
QString raw_file = QFileDialog::getOpenFileName(
|
||||
this, tr("JSON Editor"),
|
||||
ui->jsonEditorTextBox->text().isEmpty()
|
||||
#if defined(Q_OS_LINUX)
|
||||
#if defined(Q_OS_LINUX)
|
||||
? QString("/usr/bin")
|
||||
#else
|
||||
#else
|
||||
? QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).first()
|
||||
#endif
|
||||
#endif
|
||||
: ui->jsonEditorTextBox->text());
|
||||
QString cooked_file = NormalizePath(raw_file);
|
||||
|
||||
@@ -181,14 +193,14 @@ void SettingsDialog::on_jsonEditorBrowseBtn_clicked()
|
||||
}
|
||||
|
||||
// it has to exist and be an executable
|
||||
if (QFileInfo(cooked_file).exists() &&
|
||||
QFileInfo(cooked_file).isExecutable())
|
||||
if (QFileInfo(cooked_file).exists() && QFileInfo(cooked_file).isExecutable())
|
||||
{
|
||||
ui->jsonEditorTextBox->setText(cooked_file);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::warning(this, tr("Invalid"), tr("The file chosen does not seem to be an executable"));
|
||||
QMessageBox::warning(this, tr("Invalid"),
|
||||
tr("The file chosen does not seem to be an executable"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +214,9 @@ void SettingsDialog::on_buttonBox_accepted()
|
||||
{
|
||||
applySettings(MMC->settings().get());
|
||||
|
||||
// Apply proxy settings
|
||||
MMC->updateProxySettings();
|
||||
|
||||
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||
}
|
||||
|
||||
@@ -210,11 +225,18 @@ void SettingsDialog::on_buttonBox_rejected()
|
||||
MMC->settings()->set("SettingsGeometry", saveGeometry().toBase64());
|
||||
}
|
||||
|
||||
void SettingsDialog::proxyChanged(int)
|
||||
{
|
||||
updateCheckboxStuff();
|
||||
}
|
||||
|
||||
void SettingsDialog::refreshUpdateChannelList()
|
||||
{
|
||||
// Stop listening for selection changes. It's going to change a lot while we update it and we don't need to update the
|
||||
// Stop listening for selection changes. It's going to change a lot while we update it and
|
||||
// we don't need to update the
|
||||
// description label constantly.
|
||||
QObject::disconnect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int)));
|
||||
QObject::disconnect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(updateChannelSelectionChanged(int)));
|
||||
|
||||
QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
|
||||
ui->updateChannelComboBox->clear();
|
||||
@@ -222,29 +244,34 @@ void SettingsDialog::refreshUpdateChannelList()
|
||||
for (int i = 0; i < channelList.count(); i++)
|
||||
{
|
||||
UpdateChecker::ChannelListEntry entry = channelList.at(i);
|
||||
|
||||
// When it comes to selection, we'll rely on the indexes of a channel entry being the same in the
|
||||
|
||||
// When it comes to selection, we'll rely on the indexes of a channel entry being the
|
||||
// same in the
|
||||
// combo box as it is in the update checker's channel list.
|
||||
// This probably isn't very safe, but the channel list doesn't change often enough (or at all) for
|
||||
// This probably isn't very safe, but the channel list doesn't change often enough (or
|
||||
// at all) for
|
||||
// this to be a big deal. Hope it doesn't break...
|
||||
ui->updateChannelComboBox->addItem(entry.name);
|
||||
|
||||
// If the update channel we just added was the selected one, set the current index in the combo box to it.
|
||||
// If the update channel we just added was the selected one, set the current index in
|
||||
// the combo box to it.
|
||||
if (entry.id == m_currentUpdateChannel)
|
||||
{
|
||||
QLOG_DEBUG() << "Selected index" << i << "channel id" << m_currentUpdateChannel;
|
||||
selection = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ui->updateChannelComboBox->setCurrentIndex(selection);
|
||||
|
||||
// Start listening for selection changes again and update the description label.
|
||||
QObject::connect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(updateChannelSelectionChanged(int)));
|
||||
QObject::connect(ui->updateChannelComboBox, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(updateChannelSelectionChanged(int)));
|
||||
refreshUpdateChannelDesc();
|
||||
|
||||
// Now that we've updated the channel list, we can enable the combo box.
|
||||
// It starts off disabled so that if the channel list hasn't been loaded, it will be disabled.
|
||||
// It starts off disabled so that if the channel list hasn't been loaded, it will be
|
||||
// disabled.
|
||||
ui->updateChannelComboBox->setEnabled(true);
|
||||
}
|
||||
|
||||
@@ -258,6 +285,10 @@ void SettingsDialog::refreshUpdateChannelDesc()
|
||||
// Get the channel list.
|
||||
QList<UpdateChecker::ChannelListEntry> channelList = MMC->updateChecker()->getChannelList();
|
||||
int selectedIndex = ui->updateChannelComboBox->currentIndex();
|
||||
if (selectedIndex < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (selectedIndex < channelList.count())
|
||||
{
|
||||
// Find the channel list entry with the given index.
|
||||
@@ -273,6 +304,15 @@ void SettingsDialog::refreshUpdateChannelDesc()
|
||||
|
||||
void SettingsDialog::applySettings(SettingsObject *s)
|
||||
{
|
||||
// Language
|
||||
s->set("Language",
|
||||
ui->languageBox->itemData(ui->languageBox->currentIndex()).toLocale().bcp47Name());
|
||||
|
||||
if (ui->resetNotificationsBtn->isChecked())
|
||||
{
|
||||
s->set("ShownNotifications", QString());
|
||||
}
|
||||
|
||||
// Updates
|
||||
s->set("AutoUpdate", ui->autoUpdateCheckBox->isChecked());
|
||||
s->set("UpdateChannel", m_currentUpdateChannel);
|
||||
@@ -291,7 +331,8 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
||||
|
||||
// Editors
|
||||
QString jsonEditor = ui->jsonEditorTextBox->text();
|
||||
if (!jsonEditor.isEmpty() && (!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable()))
|
||||
if (!jsonEditor.isEmpty() &&
|
||||
(!QFileInfo(jsonEditor).exists() || !QFileInfo(jsonEditor).isExecutable()))
|
||||
{
|
||||
QString found = QStandardPaths::findExecutable(jsonEditor);
|
||||
if (!found.isEmpty())
|
||||
@@ -310,6 +351,23 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
||||
s->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
|
||||
s->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
|
||||
|
||||
// Proxy
|
||||
QString proxyType = "None";
|
||||
if (ui->proxyDefaultBtn->isChecked())
|
||||
proxyType = "Default";
|
||||
else if (ui->proxyNoneBtn->isChecked())
|
||||
proxyType = "None";
|
||||
else if (ui->proxySOCKS5Btn->isChecked())
|
||||
proxyType = "SOCKS5";
|
||||
else if (ui->proxyHTTPBtn->isChecked())
|
||||
proxyType = "HTTP";
|
||||
|
||||
s->set("ProxyType", proxyType);
|
||||
s->set("ProxyAddr", ui->proxyAddrEdit->text());
|
||||
s->set("ProxyPort", ui->proxyPortEdit->value());
|
||||
s->set("ProxyUser", ui->proxyUserEdit->text());
|
||||
s->set("ProxyPass", ui->proxyPassEdit->text());
|
||||
|
||||
// Memory
|
||||
s->set("MinMemAlloc", ui->minMemSpinBox->value());
|
||||
s->set("MaxMemAlloc", ui->maxMemSpinBox->value());
|
||||
@@ -337,10 +395,27 @@ void SettingsDialog::applySettings(SettingsObject *s)
|
||||
}
|
||||
|
||||
s->set("PostExitCommand", ui->postExitCmdTextBox->text());
|
||||
|
||||
// Profilers
|
||||
s->set("JProfilerPath", ui->jprofilerPathEdit->text());
|
||||
s->set("JVisualVMPath", ui->jvisualvmPathEdit->text());
|
||||
s->set("MCEditPath", ui->mceditPathEdit->text());
|
||||
}
|
||||
|
||||
void SettingsDialog::loadSettings(SettingsObject *s)
|
||||
{
|
||||
// Language
|
||||
ui->languageBox->clear();
|
||||
ui->languageBox->addItem(tr("English"), QLocale(QLocale::English));
|
||||
foreach(const QString & lang,
|
||||
QDir(MMC->root() + "/translations").entryList(QStringList() << "*.qm", QDir::Files))
|
||||
{
|
||||
QLocale locale(lang.section(QRegExp("[_\.]"), 1));
|
||||
ui->languageBox->addItem(QLocale::languageToString(locale.language()), locale);
|
||||
}
|
||||
ui->languageBox->setCurrentIndex(
|
||||
ui->languageBox->findData(QLocale(s->get("Language").toString())));
|
||||
|
||||
// Updates
|
||||
ui->autoUpdateCheckBox->setChecked(s->get("AutoUpdate").toBool());
|
||||
m_currentUpdateChannel = s->get("UpdateChannel").toString();
|
||||
@@ -384,6 +459,22 @@ void SettingsDialog::loadSettings(SettingsObject *s)
|
||||
ui->sortByNameBtn->setChecked(true);
|
||||
}
|
||||
|
||||
// Proxy
|
||||
QString proxyType = s->get("ProxyType").toString();
|
||||
if (proxyType == "Default")
|
||||
ui->proxyDefaultBtn->setChecked(true);
|
||||
else if (proxyType == "None")
|
||||
ui->proxyNoneBtn->setChecked(true);
|
||||
else if (proxyType == "SOCKS5")
|
||||
ui->proxySOCKS5Btn->setChecked(true);
|
||||
else if (proxyType == "HTTP")
|
||||
ui->proxyHTTPBtn->setChecked(true);
|
||||
|
||||
ui->proxyAddrEdit->setText(s->get("ProxyAddr").toString());
|
||||
ui->proxyPortEdit->setValue(s->get("ProxyPort").value<qint16>());
|
||||
ui->proxyUserEdit->setText(s->get("ProxyUser").toString());
|
||||
ui->proxyPassEdit->setText(s->get("ProxyPass").toString());
|
||||
|
||||
// Java Settings
|
||||
ui->javaPathTextBox->setText(s->get("JavaPath").toString());
|
||||
ui->jvmArgsTextBox->setText(s->get("JvmArgs").toString());
|
||||
@@ -391,6 +482,11 @@ void SettingsDialog::loadSettings(SettingsObject *s)
|
||||
// Custom Commands
|
||||
ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString());
|
||||
ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString());
|
||||
|
||||
// Profilers
|
||||
ui->jprofilerPathEdit->setText(s->get("JProfilerPath").toString());
|
||||
ui->jvisualvmPathEdit->setText(s->get("JVisualVMPath").toString());
|
||||
ui->mceditPathEdit->setText(s->get("MCEditPath").toString());
|
||||
}
|
||||
|
||||
void SettingsDialog::on_javaDetectBtn_clicked()
|
||||
@@ -448,3 +544,125 @@ void SettingsDialog::checkFinished(JavaCheckResult result)
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::on_jprofilerPathBtn_clicked()
|
||||
{
|
||||
QString raw_dir = ui->jprofilerPathEdit->text();
|
||||
QString error;
|
||||
do
|
||||
{
|
||||
raw_dir = QFileDialog::getExistingDirectory(this, tr("JProfiler Directory"), raw_dir);
|
||||
if (raw_dir.isEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
QString cooked_dir = NormalizePath(raw_dir);
|
||||
if (!MMC->profilers()["jprofiler"]->check(cooked_dir, &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking JProfiler install:\n%1").arg(error));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->jprofilerPathEdit->setText(cooked_dir);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
void SettingsDialog::on_jprofilerCheckBtn_clicked()
|
||||
{
|
||||
QString error;
|
||||
if (!MMC->profilers()["jprofiler"]->check(ui->jprofilerPathEdit->text(), &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking JProfiler install:\n%1").arg(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this, tr("OK"), tr("JProfiler setup seems to be OK"));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::on_jvisualvmPathBtn_clicked()
|
||||
{
|
||||
QString raw_dir = ui->jvisualvmPathEdit->text();
|
||||
QString error;
|
||||
do
|
||||
{
|
||||
raw_dir = QFileDialog::getOpenFileName(this, tr("JVisualVM Executable"), raw_dir);
|
||||
if (raw_dir.isEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
QString cooked_dir = NormalizePath(raw_dir);
|
||||
if (!MMC->profilers()["jvisualvm"]->check(cooked_dir, &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking JVisualVM install:\n%1").arg(error));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->jvisualvmPathEdit->setText(cooked_dir);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
void SettingsDialog::on_jvisualvmCheckBtn_clicked()
|
||||
{
|
||||
QString error;
|
||||
if (!MMC->profilers()["jvisualvm"]->check(ui->jvisualvmPathEdit->text(), &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking JVisualVM install:\n%1").arg(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this, tr("OK"), tr("JVisualVM setup seems to be OK"));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::on_mceditPathBtn_clicked()
|
||||
{
|
||||
QString raw_dir = ui->mceditPathEdit->text();
|
||||
QString error;
|
||||
do
|
||||
{
|
||||
#ifdef Q_OS_OSX
|
||||
#warning stuff
|
||||
raw_dir = QFileDialog::getOpenFileName(this, tr("MCEdit Application"), raw_dir);
|
||||
#else
|
||||
raw_dir = QFileDialog::getExistingDirectory(this, tr("MCEdit Directory"), raw_dir);
|
||||
#endif
|
||||
if (raw_dir.isEmpty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
QString cooked_dir = NormalizePath(raw_dir);
|
||||
if (!MMC->tools()["mcedit"]->check(cooked_dir, &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking MCEdit install:\n%1").arg(error));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->mceditPathEdit->setText(cooked_dir);
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void SettingsDialog::on_mceditCheckBtn_clicked()
|
||||
{
|
||||
QString error;
|
||||
if (!MMC->tools()["mcedit"]->check(ui->mceditPathEdit->text(), &error))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("Error while checking MCEdit install:\n%1").arg(error));
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::information(this, tr("OK"), tr("MCEdit setup seems to be OK"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,13 @@ slots:
|
||||
|
||||
void checkFinished(JavaCheckResult result);
|
||||
|
||||
void on_jprofilerPathBtn_clicked();
|
||||
void on_jprofilerCheckBtn_clicked();
|
||||
void on_jvisualvmPathBtn_clicked();
|
||||
void on_jvisualvmCheckBtn_clicked();
|
||||
void on_mceditPathBtn_clicked();
|
||||
void on_mceditCheckBtn_clicked();
|
||||
|
||||
/*!
|
||||
* Updates the list of update channels in the combo box.
|
||||
*/
|
||||
@@ -86,6 +93,7 @@ slots:
|
||||
void refreshUpdateChannelDesc();
|
||||
|
||||
void updateChannelSelectionChanged(int index);
|
||||
void proxyChanged(int);
|
||||
|
||||
private:
|
||||
Ui::SettingsDialog *ui;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>526</width>
|
||||
<height>639</height>
|
||||
<width>545</width>
|
||||
<height>609</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -35,43 +35,11 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="generalTab">
|
||||
<widget class="QWidget" name="featuresTab">
|
||||
<attribute name="title">
|
||||
<string>General</string>
|
||||
<string>Features</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sortingModeBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Sorting Mode</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="sortingModeBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortLastLaunchedBtn">
|
||||
<property name="text">
|
||||
<string>By last launched</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortByNameBtn">
|
||||
<property name="text">
|
||||
<string>By name</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="updateSettingsBox">
|
||||
<property name="title">
|
||||
@@ -86,29 +54,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="channelVerticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelLabel">
|
||||
<property name="text">
|
||||
<string>Update Channel:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="updateChannelComboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelDescLabel">
|
||||
<property name="text">
|
||||
<string>No channel selected.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QLabel" name="updateChannelLabel">
|
||||
<property name="text">
|
||||
<string>Update Channel:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="updateChannelComboBox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelDescLabel">
|
||||
<property name="text">
|
||||
<string>No channel selected.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -277,6 +244,88 @@
|
||||
</layout>
|
||||
</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>
|
||||
<widget class="QWidget" name="generalTab">
|
||||
<attribute name="title">
|
||||
<string>User Interface</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Language (needs restart):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="languageBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="resetNotificationsBtn">
|
||||
<property name="text">
|
||||
<string>Reset hidden notifications</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sortingModeBox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Sorting Mode</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="sortingModeBoxLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortLastLaunchedBtn">
|
||||
<property name="text">
|
||||
<string>By last launched</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="sortByNameBtn">
|
||||
<property name="text">
|
||||
<string>By name</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">sortingModeGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="editorsBox">
|
||||
<property name="title">
|
||||
@@ -437,6 +486,12 @@
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="maxMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The maximum amount of memory Minecraft is allowed to use.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
@@ -467,6 +522,12 @@
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QSpinBox" name="minMemSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory Minecraft is started with.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>256</number>
|
||||
</property>
|
||||
@@ -490,6 +551,12 @@
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="permGenSpinBox">
|
||||
<property name="toolTip">
|
||||
<string>The amount of memory available to store loaded Java classes.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> MB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>64</number>
|
||||
</property>
|
||||
@@ -647,6 +714,296 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="networkTab">
|
||||
<property name="toolTip">
|
||||
<string>Network settings.</string>
|
||||
</property>
|
||||
<attribute name="title">
|
||||
<string>Network</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="proxySettingsBox">
|
||||
<property name="title">
|
||||
<string>Proxy</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="proxyTypeBox">
|
||||
<property name="title">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="proxyDefaultBtn">
|
||||
<property name="toolTip">
|
||||
<string>Uses your system's default proxy settings.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Default</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">proxyGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="proxyNoneBtn">
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">proxyGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="proxySOCKS5Btn">
|
||||
<property name="text">
|
||||
<string>SOCKS5</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">proxyGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="proxyHTTPBtn">
|
||||
<property name="text">
|
||||
<string>HTTP</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">proxyGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="proxyAddrBox">
|
||||
<property name="title">
|
||||
<string>Address and Port</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="proxyAddrEdit">
|
||||
<property name="placeholderText">
|
||||
<string>127.0.0.1</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="proxyPortEdit">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buttonSymbols">
|
||||
<enum>QAbstractSpinBox::PlusMinus</enum>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>8080</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="proxyAuthBox">
|
||||
<property name="title">
|
||||
<string>Authentication</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_5">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="proxyUserEdit"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="proxyUsernameLabel">
|
||||
<property name="text">
|
||||
<string>Username:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="proxyPasswordLabel">
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="proxyPassEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="proxyPlainTextWarningLabel">
|
||||
<property name="text">
|
||||
<string>Note: Proxy username and password are stored in plain text inside MultiMC's configuration file!</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
<widget class="QWidget" name="externalToolsTab">
|
||||
<attribute name="title">
|
||||
<string>External Tools</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>JProfiler</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="jprofilerPathEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="jprofilerPathBtn">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="jprofilerCheckBtn">
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="jprofilerLink">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://www.ej-technologies.com/products/jprofiler/overview.html"><span style=" text-decoration: underline; color:#0000ff;">http://www.ej-technologies.com/products/jprofiler/overview.html</span></a></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>JVisualVM</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_11">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="jvisualvmPathEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="jvisualvmPathBtn">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="jvisualvmCheckBtn">
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="jvisualvmLink">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://visualvm.java.net/"><span style=" text-decoration: underline; color:#0000ff;">http://visualvm.java.net/</span></a></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4">
|
||||
<property name="title">
|
||||
<string>MCEdit</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mceditPathEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mceditPathBtn">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mceditCheckBtn">
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="mceditLink">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><a href="http://www.mcedit.net/"><span style=" text-decoration: underline; color:#0000ff;">http://www.mcedit.net/</span></a></p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -665,20 +1022,6 @@
|
||||
<tabstop>buttonBox</tabstop>
|
||||
<tabstop>sortLastLaunchedBtn</tabstop>
|
||||
<tabstop>sortByNameBtn</tabstop>
|
||||
<tabstop>autoUpdateCheckBox</tabstop>
|
||||
<tabstop>trackFtbBox</tabstop>
|
||||
<tabstop>ftbLauncherBox</tabstop>
|
||||
<tabstop>ftbLauncherBrowseBtn</tabstop>
|
||||
<tabstop>ftbBox</tabstop>
|
||||
<tabstop>ftbBrowseBtn</tabstop>
|
||||
<tabstop>instDirTextBox</tabstop>
|
||||
<tabstop>instDirBrowseBtn</tabstop>
|
||||
<tabstop>modsDirTextBox</tabstop>
|
||||
<tabstop>modsDirBrowseBtn</tabstop>
|
||||
<tabstop>lwjglDirTextBox</tabstop>
|
||||
<tabstop>lwjglDirBrowseBtn</tabstop>
|
||||
<tabstop>iconsDirTextBox</tabstop>
|
||||
<tabstop>iconsDirBrowseBtn</tabstop>
|
||||
<tabstop>jsonEditorTextBox</tabstop>
|
||||
<tabstop>jsonEditorBrowseBtn</tabstop>
|
||||
<tabstop>maximizedCheckBox</tabstop>
|
||||
@@ -737,5 +1080,6 @@
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="sortingModeGroup"/>
|
||||
<buttongroup name="proxyGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -51,6 +51,11 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setEmptyString(QString emptyString)
|
||||
{
|
||||
ui->listView->setEmptyString(emptyString);
|
||||
}
|
||||
|
||||
VersionSelectDialog::~VersionSelectDialog()
|
||||
{
|
||||
delete ui;
|
||||
@@ -77,6 +82,7 @@ void VersionSelectDialog::loadList()
|
||||
Task *loadTask = m_vlist->getLoadTask();
|
||||
loadTask->setParent(taskDlg);
|
||||
taskDlg->exec(loadTask);
|
||||
delete taskDlg;
|
||||
}
|
||||
|
||||
BaseVersionPtr VersionSelectDialog::selectedVersion() const
|
||||
@@ -91,21 +97,16 @@ void VersionSelectDialog::on_refreshButton_clicked()
|
||||
loadList();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setFilter(int column, QString filter)
|
||||
void VersionSelectDialog::setExactFilter(int column, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilterKeyColumn(column);
|
||||
m_proxyModel->setFilterFixedString(filter);
|
||||
/*
|
||||
QStringList filteredTypes;
|
||||
if (!ui->filterSnapshotsCheckbox->isChecked())
|
||||
filteredTypes += "Snapshot";
|
||||
if (!ui->filterMCNostalgiaCheckbox->isChecked())
|
||||
filteredTypes += "Nostalgia";
|
||||
|
||||
QString regexStr = "^.*$";
|
||||
if (filteredTypes.length() > 0)
|
||||
regexStr = QString("^((?!%1).)*$").arg(filteredTypes.join('|'));
|
||||
|
||||
QLOG_DEBUG() << "Filter:" << regexStr;
|
||||
*/
|
||||
// m_proxyModel->setFilterFixedString(filter);
|
||||
m_proxyModel->setFilterRegExp(QRegExp(QString("^%1$").arg(filter.replace(".", "\\.")),
|
||||
Qt::CaseInsensitive, QRegExp::RegExp));
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setFuzzyFilter(int column, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilterKeyColumn(column);
|
||||
m_proxyModel->setFilterWildcard(filter);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,9 @@ public:
|
||||
|
||||
BaseVersionPtr selectedVersion() const;
|
||||
|
||||
void setFilter(int column, QString filter);
|
||||
void setFuzzyFilter(int column, QString filter);
|
||||
void setExactFilter(int column, QString filter);
|
||||
void setEmptyString(QString emptyString);
|
||||
void setResizeOn(int column);
|
||||
|
||||
private
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="listView">
|
||||
<widget class="VersionListView" name="listView">
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
@@ -65,6 +65,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>VersionListView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>gui/widgets/VersionListView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
||||
269
gui/groupview/Group.cpp
Normal file
269
gui/groupview/Group.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "Group.h"
|
||||
|
||||
#include <QModelIndex>
|
||||
#include <QPainter>
|
||||
#include <QtMath>
|
||||
#include <QApplication>
|
||||
|
||||
#include "GroupView.h"
|
||||
|
||||
Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false)
|
||||
{
|
||||
}
|
||||
|
||||
Group::Group(const Group *other)
|
||||
: view(other->view), text(other->text), collapsed(other->collapsed)
|
||||
{
|
||||
}
|
||||
|
||||
void Group::update()
|
||||
{
|
||||
firstItemIndex = firstItem().row();
|
||||
|
||||
rowHeights = QVector<int>(numRows());
|
||||
for (int i = 0; i < numRows(); ++i)
|
||||
{
|
||||
rowHeights[i] = view->categoryRowHeight(
|
||||
view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0));
|
||||
}
|
||||
}
|
||||
|
||||
Group::HitResults Group::hitScan(const QPoint &pos) const
|
||||
{
|
||||
Group::HitResults results = Group::NoHit;
|
||||
int y_start = verticalPosition();
|
||||
int body_start = y_start + headerHeight();
|
||||
int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5?
|
||||
int y = pos.y();
|
||||
// int x = pos.x();
|
||||
if (y < y_start)
|
||||
{
|
||||
results = Group::NoHit;
|
||||
}
|
||||
else if (y < body_start)
|
||||
{
|
||||
results = Group::HeaderHit;
|
||||
int collapseSize = headerHeight() - 4;
|
||||
|
||||
// the icon
|
||||
QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize);
|
||||
if (iconRect.contains(pos))
|
||||
{
|
||||
results |= Group::CheckboxHit;
|
||||
}
|
||||
}
|
||||
else if (y < body_end)
|
||||
{
|
||||
results |= Group::BodyHit;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
|
||||
{
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
const QRect optRect = option.rect;
|
||||
QFont font(QApplication::font());
|
||||
font.setBold(true);
|
||||
const QFontMetrics fontMetrics = QFontMetrics(font);
|
||||
|
||||
QColor outlineColor = option.palette.text().color();
|
||||
outlineColor.setAlphaF(0.35);
|
||||
|
||||
//BEGIN: top left corner
|
||||
{
|
||||
painter->save();
|
||||
painter->setPen(outlineColor);
|
||||
const QPointF topLeft(optRect.topLeft());
|
||||
QRectF arc(topLeft, QSizeF(4, 4));
|
||||
arc.translate(0.5, 0.5);
|
||||
painter->drawArc(arc, 1440, 1440);
|
||||
painter->restore();
|
||||
}
|
||||
//END: top left corner
|
||||
|
||||
//BEGIN: left vertical line
|
||||
{
|
||||
QPoint start(optRect.topLeft());
|
||||
start.ry() += 3;
|
||||
QPoint verticalGradBottom(optRect.topLeft());
|
||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||
QLinearGradient gradient(start, verticalGradBottom);
|
||||
gradient.setColorAt(0, outlineColor);
|
||||
gradient.setColorAt(1, Qt::transparent);
|
||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||
}
|
||||
//END: left vertical line
|
||||
|
||||
//BEGIN: horizontal line
|
||||
{
|
||||
QPoint start(optRect.topLeft());
|
||||
start.rx() += 3;
|
||||
QPoint horizontalGradTop(optRect.topLeft());
|
||||
horizontalGradTop.rx() += optRect.width() - 6;
|
||||
painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
|
||||
}
|
||||
//END: horizontal line
|
||||
|
||||
//BEGIN: top right corner
|
||||
{
|
||||
painter->save();
|
||||
painter->setPen(outlineColor);
|
||||
QPointF topRight(optRect.topRight());
|
||||
topRight.rx() -= 4;
|
||||
QRectF arc(topRight, QSizeF(4, 4));
|
||||
arc.translate(0.5, 0.5);
|
||||
painter->drawArc(arc, 0, 1440);
|
||||
painter->restore();
|
||||
}
|
||||
//END: top right corner
|
||||
|
||||
//BEGIN: right vertical line
|
||||
{
|
||||
QPoint start(optRect.topRight());
|
||||
start.ry() += 3;
|
||||
QPoint verticalGradBottom(optRect.topRight());
|
||||
verticalGradBottom.ry() += fontMetrics.height() + 5;
|
||||
QLinearGradient gradient(start, verticalGradBottom);
|
||||
gradient.setColorAt(0, outlineColor);
|
||||
gradient.setColorAt(1, Qt::transparent);
|
||||
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
|
||||
}
|
||||
//END: right vertical line
|
||||
|
||||
//BEGIN: checkboxy thing
|
||||
{
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, false);
|
||||
painter->setFont(font);
|
||||
QColor penColor(option.palette.text().color());
|
||||
penColor.setAlphaF(0.6);
|
||||
painter->setPen(penColor);
|
||||
QRect iconSubRect(option.rect);
|
||||
iconSubRect.setTop(iconSubRect.top() + 7);
|
||||
iconSubRect.setLeft(iconSubRect.left() + 7);
|
||||
|
||||
int sizing = fontMetrics.height();
|
||||
int even = ( (sizing - 1) % 2 );
|
||||
|
||||
iconSubRect.setHeight(sizing - even);
|
||||
iconSubRect.setWidth(sizing - even);
|
||||
painter->drawRect(iconSubRect);
|
||||
|
||||
|
||||
/*
|
||||
if(collapsed)
|
||||
painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+");
|
||||
else
|
||||
painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-");
|
||||
*/
|
||||
painter->setBrush(option.palette.text());
|
||||
painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2,
|
||||
iconSubRect.width(), 2, penColor);
|
||||
if (collapsed)
|
||||
{
|
||||
painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2,
|
||||
iconSubRect.height(), penColor);
|
||||
}
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
//END: checkboxy thing
|
||||
|
||||
//BEGIN: text
|
||||
{
|
||||
QRect textRect(option.rect);
|
||||
textRect.setTop(textRect.top() + 7);
|
||||
textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7);
|
||||
textRect.setHeight(fontMetrics.height());
|
||||
textRect.setRight(textRect.right() - 7);
|
||||
|
||||
painter->save();
|
||||
painter->setFont(font);
|
||||
QColor penColor(option.palette.text().color());
|
||||
penColor.setAlphaF(0.6);
|
||||
painter->setPen(penColor);
|
||||
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
|
||||
painter->restore();
|
||||
}
|
||||
//END: text
|
||||
}
|
||||
|
||||
int Group::totalHeight() const
|
||||
{
|
||||
return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'?
|
||||
}
|
||||
|
||||
int Group::headerHeight() const
|
||||
{
|
||||
QFont font(QApplication::font());
|
||||
font.setBold(true);
|
||||
QFontMetrics fontMetrics(font);
|
||||
|
||||
const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */
|
||||
+ 11 /* top and bottom separation */;
|
||||
return height;
|
||||
/*
|
||||
int raw = view->viewport()->fontMetrics().height() + 4;
|
||||
// add english. maybe. depends on font height.
|
||||
if (raw % 2 == 0)
|
||||
raw++;
|
||||
return std::min(raw, 25);
|
||||
*/
|
||||
}
|
||||
|
||||
int Group::contentHeight() const
|
||||
{
|
||||
if (collapsed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int result = 0;
|
||||
for (int i = 0; i < rowHeights.size(); ++i)
|
||||
{
|
||||
result += rowHeights[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int Group::numRows() const
|
||||
{
|
||||
return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow()));
|
||||
}
|
||||
|
||||
int Group::verticalPosition() const
|
||||
{
|
||||
return m_verticalPosition;
|
||||
}
|
||||
|
||||
QList<QModelIndex> Group::items() const
|
||||
{
|
||||
QList<QModelIndex> indices;
|
||||
for (int i = 0; i < view->model()->rowCount(); ++i)
|
||||
{
|
||||
const QModelIndex index = view->model()->index(i, 0);
|
||||
if (index.data(GroupViewRoles::GroupRole).toString() == text)
|
||||
{
|
||||
indices.append(index);
|
||||
}
|
||||
}
|
||||
return indices;
|
||||
}
|
||||
|
||||
int Group::numItems() const
|
||||
{
|
||||
return items().size();
|
||||
}
|
||||
|
||||
QModelIndex Group::firstItem() const
|
||||
{
|
||||
QList<QModelIndex> indices = items();
|
||||
return indices.isEmpty() ? QModelIndex() : indices.first();
|
||||
}
|
||||
|
||||
QModelIndex Group::lastItem() const
|
||||
{
|
||||
QList<QModelIndex> indices = items();
|
||||
return indices.isEmpty() ? QModelIndex() : indices.last();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user