mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-13 20:22:13 +00:00
Compare commits
467 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3cd3d5ff1 | ||
|
|
1a9793197f | ||
|
|
9497b7e96c | ||
|
|
a0b47aee5b | ||
|
|
17ad1e64f8 | ||
|
|
71e4b147ec | ||
|
|
f6b2ccb110 | ||
|
|
c943019ab5 | ||
|
|
fc43fd1105 | ||
|
|
401d5b698f | ||
|
|
1a0bbdd9ac | ||
|
|
495d320ce2 | ||
|
|
5e737f42bf | ||
|
|
6e6e2bf262 | ||
|
|
163a3095b1 | ||
|
|
a20e2590da | ||
|
|
1978078662 | ||
|
|
ea08ede4c3 | ||
|
|
b7f75637fa | ||
|
|
4ee1900201 | ||
|
|
ab67d763f4 | ||
|
|
7ca9f92343 | ||
|
|
cbd4b88e91 | ||
|
|
0958bb2fcc | ||
|
|
fbec48080b | ||
|
|
33b6222f9f | ||
|
|
5ecaed21b5 | ||
|
|
9fd66b3bb1 | ||
|
|
47ea2a71c0 | ||
|
|
2c2b960ab4 | ||
|
|
ce326ccdff | ||
|
|
1ed86bfe1a | ||
|
|
781e53cccb | ||
|
|
25991c36af | ||
|
|
5ccfbba435 | ||
|
|
1f0e76a3c1 | ||
|
|
d8b1ae38fb | ||
|
|
59e6b4ed55 | ||
|
|
5ff9f90ce9 | ||
|
|
34bf4ccdc7 | ||
|
|
aa8103adf2 | ||
|
|
b300c4956c | ||
|
|
4d0caf6254 | ||
|
|
d1e344f28f | ||
|
|
e0a9970d59 | ||
|
|
6f92ca843e | ||
|
|
8cf23bd5aa | ||
|
|
c6afa7d73e | ||
|
|
dcb4e0fa6f | ||
|
|
15aaded80b | ||
|
|
b9b5a82c2e | ||
|
|
da2af5e449 | ||
|
|
1363b1d364 | ||
|
|
a008efd24e | ||
|
|
5f57df8110 | ||
|
|
b437988d7b | ||
|
|
07449e514a | ||
|
|
bd2843952a | ||
|
|
5402acb3c6 | ||
|
|
e8063d193d | ||
|
|
7670d72bd9 | ||
|
|
c64a7940c1 | ||
|
|
243c5d1cfb | ||
|
|
478ff11485 | ||
|
|
2db4a595dd | ||
|
|
2da3162206 | ||
|
|
a5b8f22eab | ||
|
|
b5902b739e | ||
|
|
11afc61426 | ||
|
|
384c03c9c5 | ||
|
|
21490a42bd | ||
|
|
d855f4905d | ||
|
|
12c7b11fe6 | ||
|
|
055774dd58 | ||
|
|
fdbe431d6b | ||
|
|
9c01b2294f | ||
|
|
895d8ab469 | ||
|
|
8a4fd8c468 | ||
|
|
61ad480588 | ||
|
|
79ae4ef2f7 | ||
|
|
125abf5027 | ||
|
|
9ad99ac481 | ||
|
|
69989ab54e | ||
|
|
7ef1f88de7 | ||
|
|
432e812da1 | ||
|
|
d5aee5fd23 | ||
|
|
457dd2e94e | ||
|
|
44db72ead5 | ||
|
|
4fbcb3efb9 | ||
|
|
439c6b43a3 | ||
|
|
db926a546e | ||
|
|
4a900a58d4 | ||
|
|
f93f867c3d | ||
|
|
7459eb627c | ||
|
|
ae4216de61 | ||
|
|
ac8ff88061 | ||
|
|
477a1a88c6 | ||
|
|
cf0308c970 | ||
|
|
e2fd299fc5 | ||
|
|
e993b1152d | ||
|
|
877240524d | ||
|
|
b7ff8a4c1c | ||
|
|
22c0d5cf46 | ||
|
|
12b14c3400 | ||
|
|
00994a425e | ||
|
|
ccb5fc6f4a | ||
|
|
1cbe543b39 | ||
|
|
260a2cea59 | ||
|
|
143e24fa04 | ||
|
|
33c3850b40 | ||
|
|
271ad9e4fd | ||
|
|
cca6700134 | ||
|
|
0af04dc060 | ||
|
|
e4df8165f7 | ||
|
|
1ab26ef2ad | ||
|
|
5d5bee4992 | ||
|
|
e60a652b78 | ||
|
|
9ba1cd15e7 | ||
|
|
b107617112 | ||
|
|
0a187d0ad3 | ||
|
|
8d3f13c447 | ||
|
|
dd8eacee1b | ||
|
|
e38cc1d480 | ||
|
|
cfd5976471 | ||
|
|
8ef07ec634 | ||
|
|
2315f463a8 | ||
|
|
38901ed21d | ||
|
|
ec3472f21d | ||
|
|
bd96a25f7a | ||
|
|
877fc94f50 | ||
|
|
db5816b0a2 | ||
|
|
a1fd50e920 | ||
|
|
51070a13f7 | ||
|
|
36dbf1fb43 | ||
|
|
1ca9fc8961 | ||
|
|
b8cdcdb96b | ||
|
|
38693e1d6c | ||
|
|
40b233448c | ||
|
|
7d8c71aad8 | ||
|
|
498dc8fc03 | ||
|
|
c3480d6fe4 | ||
|
|
b5d5490714 | ||
|
|
83434a9be5 | ||
|
|
583e5946f4 | ||
|
|
16df6c16f3 | ||
|
|
6148023ad6 | ||
|
|
6496c65285 | ||
|
|
db5e55e026 | ||
|
|
23d0bd8edd | ||
|
|
cd108fd029 | ||
|
|
151a0ca11e | ||
|
|
bc917668ff | ||
|
|
961c1c61b8 | ||
|
|
0d15247247 | ||
|
|
b6ec2ac4b0 | ||
|
|
c5bb33c716 | ||
|
|
40ed2654c7 | ||
|
|
875c707358 | ||
|
|
e5f7676622 | ||
|
|
cc4d0a0a8e | ||
|
|
d0e88011dc | ||
|
|
6858f1dd62 | ||
|
|
9681f724e5 | ||
|
|
5bc29b06a9 | ||
|
|
96fdaebb5c | ||
|
|
4e3af265da | ||
|
|
d7b3887fe1 | ||
|
|
0adb572a07 | ||
|
|
8ed10c5b81 | ||
|
|
d8caab515a | ||
|
|
6310f6569c | ||
|
|
2fc18921b0 | ||
|
|
61c5a67777 | ||
|
|
8e7caf4e25 | ||
|
|
5dd48e89f5 | ||
|
|
5133b0f34f | ||
|
|
5f41886d76 | ||
|
|
7f1320390c | ||
|
|
526a511f45 | ||
|
|
5628d3d379 | ||
|
|
f86a39c21c | ||
|
|
34ddfc7ecc | ||
|
|
d14a61b0df | ||
|
|
bbba63eca5 | ||
|
|
d403d12d6a | ||
|
|
b343434f99 | ||
|
|
f723721bd0 | ||
|
|
b427a652ad | ||
|
|
9684d3b0a0 | ||
|
|
1feb4bb387 | ||
|
|
dd97ea8029 | ||
|
|
88f5c8d347 | ||
|
|
15b7c3039a | ||
|
|
22c5ced5dc | ||
|
|
64b70acac1 | ||
|
|
82e05661d2 | ||
|
|
166813cb91 | ||
|
|
38e42ad794 | ||
|
|
6d7bff2476 | ||
|
|
977e11ef8d | ||
|
|
634bdcdbcb | ||
|
|
1e51b62c88 | ||
|
|
24db645167 | ||
|
|
dde35a0eb8 | ||
|
|
57b75dfcf7 | ||
|
|
06a67fbd38 | ||
|
|
3a8b238052 | ||
|
|
161dc66c2c | ||
|
|
678da0b639 | ||
|
|
db69a3dacd | ||
|
|
47f919173e | ||
|
|
c1f7dda8fe | ||
|
|
678c4793f9 | ||
|
|
405cea1778 | ||
|
|
96c497f654 | ||
|
|
10b3906b53 | ||
|
|
6fd18a5cce | ||
|
|
9920062003 | ||
|
|
99f248ecd4 | ||
|
|
b9e06b5da0 | ||
|
|
ff64b6cf1d | ||
|
|
84757f485b | ||
|
|
b7f8241968 | ||
|
|
a98e1df10c | ||
|
|
f9e186ab70 | ||
|
|
50a4a1e19e | ||
|
|
2f087b55b9 | ||
|
|
2dcedcfde3 | ||
|
|
c1c23e47a7 | ||
|
|
8fb5d4add3 | ||
|
|
185ff238c2 | ||
|
|
09673cc16e | ||
|
|
dfb0a3b724 | ||
|
|
ce99fabe13 | ||
|
|
0e0ddf5494 | ||
|
|
12b9a90c4c | ||
|
|
8715746774 | ||
|
|
cfdfd0e811 | ||
|
|
81b37dae18 | ||
|
|
29ce36c7bc | ||
|
|
22a0294a33 | ||
|
|
5334d88c1d | ||
|
|
06080108f3 | ||
|
|
08898c7c63 | ||
|
|
1bc2fbef11 | ||
|
|
a296ec914b | ||
|
|
53d8c9169d | ||
|
|
4c11ce8063 | ||
|
|
cff2e4823a | ||
|
|
d0b31da4b5 | ||
|
|
2ad9e6393f | ||
|
|
743af4769e | ||
|
|
6ab6a450f6 | ||
|
|
3ed467e1fa | ||
|
|
44d76f5d82 | ||
|
|
ff715f7785 | ||
|
|
43c777f386 | ||
|
|
a39fb1ef17 | ||
|
|
c75cac684e | ||
|
|
f2026df597 | ||
|
|
416e08f741 | ||
|
|
5bbe1c7132 | ||
|
|
bd1a28d863 | ||
|
|
ffcb5ab1ef | ||
|
|
88f975eff7 | ||
|
|
11c376f6f1 | ||
|
|
757b4e260b | ||
|
|
2a4647125d | ||
|
|
9598f80335 | ||
|
|
34a5e59007 | ||
|
|
1271188019 | ||
|
|
4c6edc9fd4 | ||
|
|
49d3705d16 | ||
|
|
c09dc85090 | ||
|
|
c10a4a54d9 | ||
|
|
1b884d0a9d | ||
|
|
8e9d5f56b5 | ||
|
|
5779ffd664 | ||
|
|
4fc4a17256 | ||
|
|
bb01c91469 | ||
|
|
fb3c9efc8a | ||
|
|
55f9117ce3 | ||
|
|
994c815bb9 | ||
|
|
32f45578fd | ||
|
|
2af03ba0d9 | ||
|
|
aea51a0876 | ||
|
|
75dfbc61fc | ||
|
|
f8650e3965 | ||
|
|
84549ed807 | ||
|
|
d5c79db12c | ||
|
|
f623dc54ef | ||
|
|
dc279fbfdc | ||
|
|
8fa58dc244 | ||
|
|
07bebddac9 | ||
|
|
4f417d527e | ||
|
|
c7c81463fd | ||
|
|
6cfac115b1 | ||
|
|
3507ccaf50 | ||
|
|
28aa8f342e | ||
|
|
4d8f068f9c | ||
|
|
1f9dd45e49 | ||
|
|
f061bf7a27 | ||
|
|
d8ea3501eb | ||
|
|
9df2f1fa5c | ||
|
|
fe540e5dda | ||
|
|
c7398dfdc5 | ||
|
|
0220fe4f9d | ||
|
|
58840ac10c | ||
|
|
3d3725f088 | ||
|
|
47bbc349eb | ||
|
|
c8687a8d05 | ||
|
|
234f57b8e6 | ||
|
|
d4d8cb4891 | ||
|
|
d1ba972c59 | ||
|
|
db877ba121 | ||
|
|
4730f54df7 | ||
|
|
7a71ecd8af | ||
|
|
4e94de413b | ||
|
|
141e0a02a0 | ||
|
|
473971b6e7 | ||
|
|
b47e196b32 | ||
|
|
cd9d37aac4 | ||
|
|
28a39ef7ac | ||
|
|
d313e9ab09 | ||
|
|
382ae78a0b | ||
|
|
aa70ed2244 | ||
|
|
154d19bb74 | ||
|
|
c088d3bef0 | ||
|
|
6775e3e72b | ||
|
|
8b4e22bbb8 | ||
|
|
c7b39fe116 | ||
|
|
865b200571 | ||
|
|
dc84ac3682 | ||
|
|
695bfd5f7c | ||
|
|
5ff2681da6 | ||
|
|
5359f4499a | ||
|
|
04b45f3629 | ||
|
|
9249768db5 | ||
|
|
6f3aa65bd6 | ||
|
|
e508728246 | ||
|
|
360ec557b2 | ||
|
|
7334b8e520 | ||
|
|
791221e923 | ||
|
|
593111b144 | ||
|
|
3b6574181e | ||
|
|
eae544f0eb | ||
|
|
2eb3ec39bf | ||
|
|
a27c341781 | ||
|
|
02f82e9694 | ||
|
|
405833bbbe | ||
|
|
8be865fb2a | ||
|
|
568a79e7b1 | ||
|
|
06c9a64a87 | ||
|
|
ceec70e014 | ||
|
|
ef34cafe17 | ||
|
|
93b247592d | ||
|
|
b8a8b09796 | ||
|
|
a53f8d506e | ||
|
|
f9a17eb9de | ||
|
|
c6c5134398 | ||
|
|
49ff31f131 | ||
|
|
e25e076d2e | ||
|
|
125ddc5f93 | ||
|
|
d03dbea1b7 | ||
|
|
c6427caa9e | ||
|
|
0be0e822e4 | ||
|
|
55e5322fbe | ||
|
|
0886786bb5 | ||
|
|
1151037f96 | ||
|
|
acb3346409 | ||
|
|
85756d0e78 | ||
|
|
5c599d8658 | ||
|
|
4db31aacd6 | ||
|
|
a30a9559c7 | ||
|
|
01f44e0f39 | ||
|
|
bbcd44a657 | ||
|
|
a060d79c12 | ||
|
|
28140b1db6 | ||
|
|
5af1f0cf50 | ||
|
|
7778c84121 | ||
|
|
4ae0d8e0af | ||
|
|
9f14b319df | ||
|
|
a9af17eebb | ||
|
|
d3c2230a24 | ||
|
|
f8bd687994 | ||
|
|
fa8d3c564d | ||
|
|
80d3f734c6 | ||
|
|
9ad9826d08 | ||
|
|
6a09fd2898 | ||
|
|
03a25c9a5d | ||
|
|
83b90d8bfb | ||
|
|
1d5c09051c | ||
|
|
940f160091 | ||
|
|
3d2de6add8 | ||
|
|
b7c4284019 | ||
|
|
2315bf7bc5 | ||
|
|
41bd2a6634 | ||
|
|
5711b1be95 | ||
|
|
90eea4f05c | ||
|
|
cbb1e0ea45 | ||
|
|
ec4805cce8 | ||
|
|
a2ac9c5a3a | ||
|
|
551e101146 | ||
|
|
1dd8978f8c | ||
|
|
24a0635b62 | ||
|
|
2e9284951c | ||
|
|
f9a7c1cf21 | ||
|
|
28eebc09fc | ||
|
|
cc19159f4d | ||
|
|
587fedce84 | ||
|
|
fa42a27525 | ||
|
|
84723add8f | ||
|
|
992ba0c3f8 | ||
|
|
7d1dd2a32f | ||
|
|
add23a9a0b | ||
|
|
d9b2f0ed42 | ||
|
|
9217d9263e | ||
|
|
a3a5afe119 | ||
|
|
7fb6cafe9e | ||
|
|
c1b6f42551 | ||
|
|
3d1426b559 | ||
|
|
095640ed01 | ||
|
|
9e8a74cc89 | ||
|
|
547f6f77d0 | ||
|
|
8f7aec032b | ||
|
|
92560bf0cd | ||
|
|
2dd2b7a291 | ||
|
|
b4122cff89 | ||
|
|
43b9706b5c | ||
|
|
bbdf5c1395 | ||
|
|
382e167d64 | ||
|
|
de2bb0c6f3 | ||
|
|
2083ac8cc1 | ||
|
|
16955c6188 | ||
|
|
b00e63dbe8 | ||
|
|
20cb97a35a | ||
|
|
36efcf8d3c | ||
|
|
a59e83cd12 | ||
|
|
febf3645d0 | ||
|
|
01ca3d6aad | ||
|
|
6deb41e32d | ||
|
|
74f7bd9a1c | ||
|
|
7d8c5ac9b5 | ||
|
|
d172daf3a9 | ||
|
|
56b16320dd | ||
|
|
376467740b | ||
|
|
814d5d3315 | ||
|
|
fd6706391b | ||
|
|
21597da33d | ||
|
|
c0254d9a75 | ||
|
|
151fbde8d0 | ||
|
|
03b13b0b3f | ||
|
|
9b82c87c92 | ||
|
|
75cb329f17 | ||
|
|
bef869ff76 | ||
|
|
0a64579401 | ||
|
|
d71697efb3 | ||
|
|
e5b393318f | ||
|
|
1ed90293ac | ||
|
|
bc05ad30aa | ||
|
|
e178284172 | ||
|
|
c91adfb3d1 | ||
|
|
c767707c95 | ||
|
|
66fa241257 | ||
|
|
8a56ab6780 | ||
|
|
71575a5022 | ||
|
|
222c26f9cf |
@@ -10,7 +10,7 @@ NamespaceIndentation: None
|
||||
BreakBeforeBraces: Allman
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
ColumnLimit: 96
|
||||
ColumnLimit: 160
|
||||
MaxEmptyLinesToKeep: 1
|
||||
|
||||
Standard: Cpp11
|
||||
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.pem -crlf
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "depends/libnbtplusplus"]
|
||||
path = depends/libnbtplusplus
|
||||
url = https://github.com/MultiMC/libnbtplusplus.git
|
||||
68
.travis.yml
68
.travis.yml
@@ -1,29 +1,55 @@
|
||||
# General set up
|
||||
language: cpp
|
||||
cache: apt
|
||||
notifications:
|
||||
irc: "irc.esper.net#MultiMC"
|
||||
email: false
|
||||
|
||||
# Build matrix set up
|
||||
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
|
||||
# - clang
|
||||
os:
|
||||
- linux
|
||||
# - osx
|
||||
env:
|
||||
- QT_VERSION=5.0.2
|
||||
- QT_VERSION=5.1.1
|
||||
- QT_VERSION=5.2.1
|
||||
- QT_VERSION=5.3.2
|
||||
- QT_VERSION=5.4.2
|
||||
- QT_VERSION=5.5.1 # latest
|
||||
matrix:
|
||||
exclude:
|
||||
# only use clang on OS X
|
||||
- os: osx
|
||||
compiler: gcc
|
||||
# only use the qt available from homebrew
|
||||
- os: osx
|
||||
env: QT_VERSION=5.0.2
|
||||
- os: osx
|
||||
env: QT_VERSION=5.1.1
|
||||
- os: osx
|
||||
env: QT_VERSION=5.2.1
|
||||
- os: osx
|
||||
env: QT_VERSION=5.3.2
|
||||
- os: osx
|
||||
env: QT_VERSION=5.4.2
|
||||
- os: osx
|
||||
env: QT_VERSION=5.5.1
|
||||
allow_failures:
|
||||
- env: QT_VERSION=5.0.2
|
||||
- env: QT_VERSION=5.1.1
|
||||
- env: QT_VERSION=5.2.1
|
||||
|
||||
# Install dependencies
|
||||
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
|
||||
- source travis/prepare.sh # installs qt and cmake. need to source because some env vars are set from there
|
||||
|
||||
# Actual work
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake ..
|
||||
- cmake -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ..
|
||||
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
|
||||
- make -j4 && make test ARGS="-V"
|
||||
|
||||
147
BUILD.md
147
BUILD.md
@@ -2,64 +2,83 @@ Build Instructions
|
||||
==================
|
||||
|
||||
# Contents
|
||||
|
||||
* [Note](#note)
|
||||
* [Getting the source](#source)
|
||||
* [Linux](#linux)
|
||||
* [Windows](#windows)
|
||||
* [OS X](#os-x)
|
||||
|
||||
# Note
|
||||
|
||||
MultiMC is a portable application and is not supposed to be installed into any system folders.
|
||||
That would be anything outside your home folder. Before runing `make install`, make sure
|
||||
you set the install path to something you have write access to. Never build this under
|
||||
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
|
||||
|
||||
|
||||
# Getting the source
|
||||
|
||||
Clone the source code using git and grab all the submodules:
|
||||
|
||||
```
|
||||
git clone git@github.com:MultiMC/MultiMC5.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
```
|
||||
|
||||
# Linux
|
||||
|
||||
Getting the project to build and run on Linux is easy if you use Ubuntu 13.10 (or 13.04) and Qt's IDE, Qt Creator.
|
||||
Getting the project to build and run on Linux is easy if you use any modern and up-to-date linux distribution.
|
||||
|
||||
## Dependencies
|
||||
* Qt 5.1.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)")
|
||||
* A copy of the MultiMC source (clone it with git)
|
||||
* cmake
|
||||
* build-essential
|
||||
* zlib (for example, zlib1g-dev)
|
||||
* java (for example, openjdk-7-jdk)
|
||||
* GL headers (for example, libgl1-mesa-dev)
|
||||
## Build dependencies
|
||||
* Ideally a compiler capable of building C++14 code (for example, GCC 5.2 and above).
|
||||
* Qt 5.5.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager
|
||||
* cmake 3.1 or newer
|
||||
* zlib (for example, `zlib1g-dev`)
|
||||
* java (for example, `openjdk-8-jdk`)
|
||||
* GL headers (for example, `libgl1-mesa-dev`)
|
||||
|
||||
## Getting set up
|
||||
|
||||
### Installing dependencies
|
||||
Just run `sudo apt-get install <dependency>` for each dependency (other than Qt and the MultiMC source) from above.
|
||||
|
||||
### Installing Qt
|
||||
1. Run the Qt installer
|
||||
2. Choose a place to install Qt,
|
||||
3. Choose the components you want to install
|
||||
- You need Qt 5.1.1/gcc 64-bit ticked,
|
||||
- You need Tools/Qt Creator ticked,
|
||||
### Installing Qt using the installer
|
||||
1. Run the Qt installer.
|
||||
2. Choose a place to install Qt.
|
||||
3. Choose the components you want to install.
|
||||
- You need Qt 5.5.1/gcc 64-bit ticked.
|
||||
- You need Tools/Qt Creator ticked.
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements,
|
||||
5. Double check the install details and then click "Install"
|
||||
4. Accept the license agreements.
|
||||
5. Double check the install details and then click "Install".
|
||||
- Installation can take a very long time, go grab a cup of tea or something and let it work.
|
||||
|
||||
### Loading the project
|
||||
1. Open Qt Creator,
|
||||
2. Choose File->Open File or Project,
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt,
|
||||
4. Read the instructions that just popped up about a build location and choose one,
|
||||
5. You should see "Run CMake" in the window,
|
||||
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.1.1 GCC 64bit)",
|
||||
- Hit the "Run CMake" button,
|
||||
### Loading the project in Qt Creator
|
||||
1. Open Qt Creator.
|
||||
2. Choose `File->Open File or Project`.
|
||||
3. Navigate to the MultiMC5 source folder you cloned and choose CMakeLists.txt.
|
||||
4. Read the instructions that just popped up about a build location and choose one.
|
||||
5. You should see "Run CMake" in the window.
|
||||
- Make sure that Generator is set to "Unix Generator (Desktop Qt 5.4.1 GCC 64bit)".
|
||||
- Hit the "Run CMake" button.
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
6. Cross your fingers and press the Run button (bottom left of Qt Creator)!
|
||||
6. Cross your fingers and press the Run button (bottom left of Qt Creator).
|
||||
- If the project builds successfully it will run and the MultiMC5 window will pop up.
|
||||
|
||||
*These build instructions worked for me (Drayshak) on a fresh Ubuntu 13.10 x64 install. If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
|
||||
**If this doesn't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
|
||||
# Windows
|
||||
|
||||
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using VC's build tools as it uses some C++11 features that aren't implemented in it at the time of writing.
|
||||
Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt Creator. The project will simply not compile using Microsoft build tools, because that's not something we do. If it does compile, it is by chance only.
|
||||
|
||||
## 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 \<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)
|
||||
* [Qt 5.5.1+ Development tools](http://qt-project.org/downloads) -- Qt Online Installer for Windows
|
||||
* [OpenSSL](http://slproweb.com/products/Win32OpenSSL.html) -- Newest Win32 OpenSSL 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.
|
||||
- We use a custom build of OpenSSL that doesn't have this dependency. For normal development, the custom build is not necessary though.
|
||||
* [zlib 1.2.8+](http://zlib.net/zlib128-dll.zip)
|
||||
* [CMake](http://www.cmake.org/cmake/resources/software.html) -- Windows (Win32 Installer)
|
||||
* [patch.exe from the GnuWin project](http://gnuwin32.sourceforge.net/packages/patch.htm).
|
||||
|
||||
Put it somewhere on the `PATH`, so that it is accessible from the console.
|
||||
|
||||
## Getting set up
|
||||
|
||||
@@ -67,7 +86,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
1. Run the Qt installer
|
||||
2. Choose a place to install Qt (C:\Qt is the default),
|
||||
3. Choose the components you want to install
|
||||
- You need Qt 5.1.1/MinGW 4.8 (32 bit) ticked,
|
||||
- You need Qt 5.4.1/MinGW 4.9 (32 bit) ticked,
|
||||
- You need Tools/Qt Creator ticked,
|
||||
- Other components are selected by default, you can untick them if you don't need them.
|
||||
4. Accept the license agreements,
|
||||
@@ -76,8 +95,8 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
|
||||
### Installing OpenSSL
|
||||
1. Run the OpenSSL installer,
|
||||
2. It's best to choose the option to copy OpenSSL DLLs to the /bin directory
|
||||
- If you do this you'll need to add that directory (the default being C:\OpenSSL-Win32\bin) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
|
||||
2. It's best to choose the option to copy OpenSSL DLLs to the `/bin` directory
|
||||
- If you do this you'll need to add that directory (the default being `C:\OpenSSL-Win32\bin`) to your PATH system variable (Google how to do this, or use this guide for Java: http://www.java.com/en/download/help/path.xml).
|
||||
|
||||
### Installing CMake
|
||||
1. Run the CMake installer,
|
||||
@@ -92,7 +111,7 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
5. If you chose not to add CMake to the system PATH, tell Qt Creator where you installed it,
|
||||
- Otherwise you can skip this step.
|
||||
6. You should see "Run CMake" in the window,
|
||||
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.1.1 MinGW 32bit)",
|
||||
- Make sure that Generator is set to "MinGW Generator (Desktop Qt 5.4.1 MinGW 32bit)",
|
||||
- Hit the "Run CMake" button,
|
||||
- You'll see warnings and it might not be clear that it succeeded until you scroll to the bottom of the window.
|
||||
- Hit "Finish" if CMake ran successfully.
|
||||
@@ -100,26 +119,38 @@ Getting the project to build and run on Windows is easy if you use Qt's IDE, Qt
|
||||
- If the project builds successfully it will run and the MultiMC5 window will pop up,
|
||||
- Test OpenSSL by making an instance and trying to log in. If Qt Creator couldn't find OpenSSL during the CMake stage, login will fail and you'll get an error.
|
||||
|
||||
*These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
|
||||
**These build instructions worked for me (Drayshak) on a fresh Windows 8 x64 Professional install. If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
|
||||
# OS X
|
||||
|
||||
### Install prerequisites:
|
||||
1. install homebrew
|
||||
2. brew install qt5
|
||||
3. brew tap homebrew/versions
|
||||
4. brew install gcc48
|
||||
5. brew install cmake
|
||||
* install homebrew
|
||||
* then:
|
||||
|
||||
```
|
||||
brew install qt5
|
||||
brew tap homebrew/versions
|
||||
brew install gcc48
|
||||
brew install cmake
|
||||
```
|
||||
|
||||
### Build
|
||||
1. git clone https://github.com/MultiMC/MultiMC5.git
|
||||
2. cd MultiMC5
|
||||
3. mkdir build
|
||||
4. cd build
|
||||
5. export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
||||
6. export CC=/usr/local/bin/gcc-4.8
|
||||
7. export CXX=/usr/local/bin/g++-4.8
|
||||
8. cmake ..
|
||||
9. make
|
||||
|
||||
Pick an installation path - this is where the final `.app` will be constructed when you run `make install`. Supply it as the `CMAKE_INSTALL_PREFIX` argument during CMake configuration.
|
||||
|
||||
```
|
||||
git clone https://github.com/MultiMC/MultiMC5.git
|
||||
git submodule init
|
||||
git submodule update
|
||||
cd MultiMC5
|
||||
mkdir build
|
||||
cd build
|
||||
export CMAKE_PREFIX_PATH=/usr/local/opt/qt5
|
||||
export CC=/usr/local/bin/gcc-4.8
|
||||
export CXX=/usr/local/bin/g++-4.8
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX:PATH=/Users/YOU/some/path/that/makes/sense/
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
*These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC (Esper/#MultiMC)!*
|
||||
**These build instructions were taken and adapted from https://gist.github.com/number5/7250865 If they don't work for you, let us know on IRC ([Esper/#MultiMC](http://webchat.esper.net/?nick=&channels=MultiMC))!**
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
#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@";
|
||||
GIT_COMMIT_CSTR = "@MultiMC_GIT_COMMIT@";
|
||||
VERSION_STR = "@MultiMC_VERSION_STRING@";
|
||||
VERSION_CSTR = "@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;
|
||||
}
|
||||
861
CMakeLists.txt
861
CMakeLists.txt
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 2.8.9)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BUILD_DIR}" IS_IN_SOURCE_BUILD)
|
||||
if(IS_IN_SOURCE_BUILD)
|
||||
@@ -16,16 +16,16 @@ enable_testing()
|
||||
######## Set CMake options ########
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
set(FILES_TO_TRANSLATE )
|
||||
|
||||
######## Set module path ########
|
||||
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
|
||||
# Only used for test coverage
|
||||
set(MMC_SRC "${PROJECT_SOURCE_DIR}")
|
||||
set(MMC_BIN "${PROJECT_BINARY_DIR}")
|
||||
|
||||
# Output all executables and shared libs in the main build folder, not in subfolders.
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(UNIX)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
endif()
|
||||
@@ -33,834 +33,89 @@ endif()
|
||||
set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
|
||||
|
||||
######## Set compiler flags ########
|
||||
include(UseCXX11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED true)
|
||||
set(CMAKE_C_STANDARD_REQUIRED true)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
include(Coverage)
|
||||
set(CMAKE_CXX_FLAGS " -Wall ${CMAKE_CXX_FLAGS}")
|
||||
include(GenerateExportHeader)
|
||||
set(CMAKE_CXX_FLAGS " -Wall -D_GLIBCXX_USE_CXX11_ABI=0 ${CMAKE_CXX_FLAGS}")
|
||||
if(UNIX AND APPLE)
|
||||
set(CMAKE_CXX_FLAGS " -stdlib=libc++ ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
|
||||
|
||||
# cmake code needed for the coverity scan upload
|
||||
include(Coverity)
|
||||
|
||||
################################ 3rd Party Libs ################################
|
||||
|
||||
# Find the required Qt parts
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Test REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
find_package(Qt5LinguistTools REQUIRED)
|
||||
|
||||
include_directories(
|
||||
${Qt5Core_INCLUDE_DIRS}
|
||||
${Qt5Widgets_INCLUDE_DIRS}
|
||||
${Qt5Concurrent_INCLUDE_DIRS}
|
||||
${Qt5Network_INCLUDE_DIRS}
|
||||
${Qt5Test_INCLUDE_DIRS}
|
||||
)
|
||||
find_package(Qt5Core)
|
||||
find_package(Qt5Widgets)
|
||||
find_package(Qt5Concurrent)
|
||||
find_package(Qt5Network)
|
||||
find_package(Qt5Test)
|
||||
find_package(Qt5Xml)
|
||||
|
||||
# The Qt5 cmake files don't provide its install paths, so ask qmake.
|
||||
include(QMakeQuery)
|
||||
query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
|
||||
query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
|
||||
query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
|
||||
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
|
||||
query_qmake(QT_HOST_DATA QT_DATA_DIR)
|
||||
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
|
||||
|
||||
|
||||
################################ SET UP BUILD OPTIONS ################################
|
||||
|
||||
######## Check endianness ########
|
||||
include(TestBigEndian)
|
||||
test_big_endian(BIGENDIAN)
|
||||
if(${BIGENDIAN})
|
||||
add_definitions(-DMULTIMC_BIG_ENDIAN)
|
||||
endif(${BIGENDIAN})
|
||||
|
||||
|
||||
######## Dark magic crash reports ########
|
||||
option(MultiMC_HANDLE_SEGV "Handle fatal crashes and generate crash reports." OFF)
|
||||
set(CRASH_HANDLER_IMPL "")
|
||||
message(STATUS "Crash dumps are ${MultiMC_HANDLE_SEGV}")
|
||||
if (MultiMC_HANDLE_SEGV)
|
||||
add_definitions(-DHANDLE_SEGV)
|
||||
if (WIN32)
|
||||
find_package(DbgHelp)
|
||||
set(MultiMC_LINK_ADDITIONAL_LIBS "${MultiMC_LINK_ADDITIONAL_LIBS}dbghelp")
|
||||
set(MultiMC_CRASH_HANDLER_EXTRA_H "WinBacktrace.h")
|
||||
set(MultiMC_CRASH_HANDLER_EXTRA_CPP "WinBacktrace.cpp")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
option(MultiMC_TEST_SEGV "Intentionally segfault sometimes to test crash handling." OFF)
|
||||
if (MultiMC_TEST_SEGV)
|
||||
# TODO: Make this a unit test instead.
|
||||
message(WARNING "You have enabled crash handler testing. MULTIMC WILL INTENTIONALLY CRASH ITSELF. Crashes should occur on exit and when the new instance button is pressed.")
|
||||
add_definitions(-DTEST_SEGV)
|
||||
endif ()
|
||||
|
||||
|
||||
######## Set URLs ########
|
||||
set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
|
||||
|
||||
|
||||
######## Set version numbers ########
|
||||
set(MultiMC_VERSION_MAJOR 0)
|
||||
set(MultiMC_VERSION_MINOR 4)
|
||||
set(MultiMC_VERSION_HOTFIX 1)
|
||||
|
||||
# Build number
|
||||
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
|
||||
# Version type
|
||||
set(MultiMC_VERSION_TYPE "Custom" CACHE STRING "MultiMC's version type. This should be one of 'Custom', 'Release', 'ReleaseCandidate', or 'Development', depending on what type of version this is.")
|
||||
|
||||
# Build platform.
|
||||
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
||||
|
||||
# Version channel
|
||||
set(MultiMC_VERSION_CHANNEL "" CACHE STRING "The current build's channel. Included in the version string.")
|
||||
|
||||
# Channel list URL
|
||||
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
|
||||
|
||||
# Updater enabled?
|
||||
set(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.")
|
||||
|
||||
# Notification URL
|
||||
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
|
||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
|
||||
if(MultiMC_VERSION_HOTFIX GREATER 0)
|
||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_RELEASE_VERSION_NAME}.${MultiMC_VERSION_HOTFIX}")
|
||||
if (Qt5_POSITION_INDEPENDENT_CODE)
|
||||
SET(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
# Build a version string to display in the configure logs.
|
||||
if(MultiMC_VERSION_TYPE STREQUAL "Custom")
|
||||
message(STATUS "Version Type: Custom")
|
||||
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
|
||||
elseif(MultiMC_VERSION_TYPE STREQUAL "Release")
|
||||
message(STATUS "Version Type: Stable Release")
|
||||
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
|
||||
elseif(MultiMC_VERSION_TYPE STREQUAL "ReleaseCandidate")
|
||||
message(STATUS "Version Type: Release Candidate")
|
||||
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-rc${MultiMC_VERSION_BUILD}")
|
||||
elseif(MultiMC_VERSION_TYPE STREQUAL "Development")
|
||||
message(STATUS "Version Type: Development")
|
||||
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-dev${MultiMC_VERSION_BUILD}")
|
||||
else()
|
||||
message(ERROR "Invalid build type.")
|
||||
endif()
|
||||
|
||||
message(STATUS "MultiMC 5 Version: ${MultiMC_VERSION_STRING}")
|
||||
|
||||
# If the update system is enabled, make sure MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL are set.
|
||||
if(MultiMC_UPDATER)
|
||||
if(MultiMC_VERSION_CHANNEL STREQUAL "")
|
||||
message(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_CHANNEL is not set.\n"
|
||||
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
|
||||
endif()
|
||||
if(MultiMC_CHANLIST_URL STREQUAL "")
|
||||
message(FATAL_ERROR "Update system is enabled, but MultiMC_CHANLIST_URL is not set.\n"
|
||||
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
|
||||
endif()
|
||||
if(MultiMC_VERSION_BUILD LESS 0)
|
||||
message(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_BUILD is not set.\n"
|
||||
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
|
||||
endif()
|
||||
message(STATUS "Updater is enabled. Channel list URL: ${MultiMC_CHANLIST_URL}")
|
||||
endif()
|
||||
|
||||
#### Updater-related build config options ####
|
||||
option(MultiMC_UPDATER_DRY_RUN "Enable updater dry-run mode -- for updater development." OFF)
|
||||
option(MultiMC_UPDATER_FORCE_LOCAL "Do not download updated updater -- for updater development." OFF)
|
||||
|
||||
if(MultiMC_UPDATER_DRY_RUN)
|
||||
set(MultiMC_UPDATER_DRY_RUN_value "true")
|
||||
else()
|
||||
set(MultiMC_UPDATER_DRY_RUN_value "false")
|
||||
endif()
|
||||
|
||||
if(MultiMC_UPDATER_FORCE_LOCAL)
|
||||
set(MultiMC_UPDATER_FORCE_LOCAL_value "true")
|
||||
else()
|
||||
set(MultiMC_UPDATER_FORCE_LOCAL_value "false")
|
||||
endif()
|
||||
|
||||
#### Custom target to just print the version.
|
||||
add_custom_target(version echo "Version: ${MultiMC_VERSION_STRING}")
|
||||
|
||||
#### Check the current Git commit
|
||||
include(GitFunctions)
|
||||
git_run(COMMAND rev-parse HEAD DEFAULT "Unknown" OUTPUT_VAR MultiMC_GIT_COMMIT)
|
||||
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
|
||||
|
||||
######## Configure header ########
|
||||
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
|
||||
|
||||
######## Packaging/install paths setup ########
|
||||
|
||||
if(UNIX AND APPLE)
|
||||
set(BINARY_DEST_DIR MultiMC.app/Contents/MacOS)
|
||||
set(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
|
||||
set(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2013 MultiMC Contributors")
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR bin)
|
||||
set(PLUGIN_DEST_DIR plugins)
|
||||
set(QTCONF_DEST_DIR .)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
|
||||
elseif(WIN32)
|
||||
set(BINARY_DEST_DIR .)
|
||||
set(PLUGIN_DEST_DIR .)
|
||||
set(QTCONF_DEST_DIR .)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
|
||||
endif()
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS "${QT_LIBS_DIR}")
|
||||
|
||||
################################ Included Libs ################################
|
||||
|
||||
include(ExternalProject)
|
||||
set_directory_properties(PROPERTIES EP_BASE External)
|
||||
|
||||
# Add quazip
|
||||
add_definitions(-DQUAZIP_STATIC)
|
||||
add_subdirectory(depends/quazip)
|
||||
include_directories(depends/quazip)
|
||||
set(QUAZIP_VERSION "0.7.1")
|
||||
if(NOT EXISTS ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz)
|
||||
file(DOWNLOAD http://downloads.sourceforge.net/project/quazip/quazip/${QUAZIP_VERSION}/quazip-${QUAZIP_VERSION}.tar.gz ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz)
|
||||
endif()
|
||||
ExternalProject_Add(QuaZIP
|
||||
SOURCE_DIR <BINARY_DIR>/../Source/quazip-${QUAZIP_VERSION}
|
||||
DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E chdir <SOURCE_DIR>/.. ${CMAKE_COMMAND} -E tar xzf ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz
|
||||
PATCH_COMMAND patch -p0 -i ${CMAKE_SOURCE_DIR}/quazip.patch
|
||||
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
|
||||
)
|
||||
include_directories("${CMAKE_BINARY_DIR}/External/Install/QuaZIP/include/quazip")
|
||||
if(UNIX)
|
||||
set(QUAZIP_LIBRARIES -L"${CMAKE_BINARY_DIR}/External/Install/QuaZIP/lib" quazip z)
|
||||
else()
|
||||
set(QUAZIP_LIBRARIES -L"${CMAKE_BINARY_DIR}/External/Install/QuaZIP/lib" quazip)
|
||||
endif()
|
||||
|
||||
# Add the java launcher and checker
|
||||
add_subdirectory(depends/launcher)
|
||||
add_subdirectory(depends/javacheck)
|
||||
add_subdirectory(depends/hoedown) # markdown parser
|
||||
add_subdirectory(depends/launcher) # java based launcher part for Minecraft
|
||||
add_subdirectory(depends/javacheck) # java compatibility checker
|
||||
add_subdirectory(depends/xz-embedded) # xz compression
|
||||
add_subdirectory(depends/pack200) # java pack200 compression
|
||||
add_subdirectory(depends/rainbow) # Qt extension for colors
|
||||
|
||||
# Add xz decompression
|
||||
add_subdirectory(depends/xz-embedded)
|
||||
include_directories(${XZ_INCLUDE_DIR})
|
||||
|
||||
# Add pack200 decompression
|
||||
add_subdirectory(depends/pack200)
|
||||
include_directories(${PACK200_INCLUDE_DIR})
|
||||
option(NBT_BUILD_SHARED "Build NBT shared library" ON)
|
||||
option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
|
||||
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
|
||||
add_subdirectory(depends/libnbtplusplus)
|
||||
|
||||
######## MultiMC Libs ########
|
||||
|
||||
# Add the util library.
|
||||
add_definitions(-DLIBUTIL_STATIC)
|
||||
add_subdirectory(depends/util)
|
||||
include_directories(${LIBUTIL_INCLUDE_DIR})
|
||||
|
||||
# Add the updater
|
||||
add_subdirectory(mmc_updater)
|
||||
|
||||
################################ FILES ################################
|
||||
|
||||
######## Sources and headers ########
|
||||
SET(MULTIMC_SOURCES
|
||||
# Application base
|
||||
MultiMC.h
|
||||
MultiMC.cpp
|
||||
MMCError.h
|
||||
BuildConfig.h
|
||||
${PROJECT_BINARY_DIR}/BuildConfig.cpp
|
||||
|
||||
# Crash handling
|
||||
HandleCrash.h
|
||||
HandleCrash.cpp
|
||||
${MultiMC_CRASH_HANDLER_EXTRA_H} # Extra platform specific stuff
|
||||
${MultiMC_CRASH_HANDLER_EXTRA_CPP}
|
||||
|
||||
# Logging
|
||||
logger/QsDebugOutput.cpp
|
||||
logger/QsDebugOutput.h
|
||||
logger/QsLog.cpp
|
||||
logger/QsLog.h
|
||||
logger/QsLogDest.cpp
|
||||
logger/QsLogDest.h
|
||||
|
||||
# GUI - general utilities
|
||||
gui/GuiUtil.h
|
||||
gui/GuiUtil.cpp
|
||||
|
||||
# GUI - windows
|
||||
gui/MainWindow.h
|
||||
gui/MainWindow.cpp
|
||||
gui/ConsoleWindow.h
|
||||
gui/ConsoleWindow.cpp
|
||||
|
||||
# GUI - page dialog pages
|
||||
gui/pages/BasePage.h
|
||||
gui/pages/VersionPage.cpp
|
||||
gui/pages/VersionPage.h
|
||||
gui/pages/TexturePackPage.h
|
||||
gui/pages/ResourcePackPage.h
|
||||
gui/pages/ModFolderPage.cpp
|
||||
gui/pages/ModFolderPage.h
|
||||
gui/pages/NotesPage.cpp
|
||||
gui/pages/NotesPage.h
|
||||
gui/pages/LegacyUpgradePage.cpp
|
||||
gui/pages/LegacyUpgradePage.h
|
||||
gui/pages/LegacyJarModPage.cpp
|
||||
gui/pages/LegacyJarModPage.h
|
||||
gui/pages/LogPage.cpp
|
||||
gui/pages/LogPage.h
|
||||
gui/pages/InstanceSettingsPage.cpp
|
||||
gui/pages/InstanceSettingsPage.h
|
||||
gui/pages/ScreenshotsPage.cpp
|
||||
gui/pages/ScreenshotsPage.h
|
||||
gui/pages/OtherLogsPage.cpp
|
||||
gui/pages/OtherLogsPage.h
|
||||
|
||||
# GUI - dialogs
|
||||
gui/dialogs/AboutDialog.cpp
|
||||
gui/dialogs/AboutDialog.h
|
||||
gui/dialogs/AccountListDialog.cpp
|
||||
gui/dialogs/AccountListDialog.h
|
||||
gui/dialogs/AccountSelectDialog.cpp
|
||||
gui/dialogs/AccountSelectDialog.h
|
||||
gui/dialogs/CopyInstanceDialog.cpp
|
||||
gui/dialogs/CopyInstanceDialog.h
|
||||
gui/dialogs/CustomMessageBox.cpp
|
||||
gui/dialogs/CustomMessageBox.h
|
||||
gui/dialogs/EditAccountDialog.cpp
|
||||
gui/dialogs/EditAccountDialog.h
|
||||
gui/dialogs/IconPickerDialog.cpp
|
||||
gui/dialogs/IconPickerDialog.h
|
||||
gui/dialogs/LoginDialog.cpp
|
||||
gui/dialogs/LoginDialog.h
|
||||
gui/dialogs/LwjglSelectDialog.cpp
|
||||
gui/dialogs/LwjglSelectDialog.h
|
||||
gui/dialogs/ModEditDialogCommon.cpp
|
||||
gui/dialogs/ModEditDialogCommon.h
|
||||
gui/dialogs/NewInstanceDialog.cpp
|
||||
gui/dialogs/NewInstanceDialog.h
|
||||
gui/dialogs/NotificationDialog.cpp
|
||||
gui/dialogs/NotificationDialog.h
|
||||
gui/pagedialog/PageDialog.cpp
|
||||
gui/pagedialog/PageDialog.h
|
||||
gui/dialogs/ProgressDialog.cpp
|
||||
gui/dialogs/ProgressDialog.h
|
||||
gui/dialogs/SettingsDialog.cpp
|
||||
gui/dialogs/SettingsDialog.h
|
||||
gui/dialogs/UpdateDialog.cpp
|
||||
gui/dialogs/UpdateDialog.h
|
||||
gui/dialogs/VersionSelectDialog.cpp
|
||||
gui/dialogs/VersionSelectDialog.h
|
||||
|
||||
|
||||
# GUI - widgets
|
||||
gui/widgets/Common.cpp
|
||||
gui/widgets/Common.h
|
||||
gui/widgets/IconLabel.cpp
|
||||
gui/widgets/IconLabel.h
|
||||
gui/widgets/LabeledToolButton.cpp
|
||||
gui/widgets/LabeledToolButton.h
|
||||
gui/widgets/LineSeparator.cpp
|
||||
gui/widgets/LineSeparator.h
|
||||
gui/widgets/MCModInfoFrame.cpp
|
||||
gui/widgets/MCModInfoFrame.h
|
||||
gui/widgets/ModListView.cpp
|
||||
gui/widgets/ModListView.h
|
||||
gui/widgets/PageContainer.cpp
|
||||
gui/widgets/PageContainer.h
|
||||
gui/widgets/PageContainer_p.h
|
||||
gui/widgets/ServerStatus.cpp
|
||||
gui/widgets/ServerStatus.h
|
||||
gui/widgets/VersionListView.cpp
|
||||
gui/widgets/VersionListView.h
|
||||
|
||||
|
||||
# GUI - instance group view
|
||||
gui/groupview/GroupedProxyModel.cpp
|
||||
gui/groupview/GroupedProxyModel.h
|
||||
gui/groupview/GroupView.cpp
|
||||
gui/groupview/GroupView.h
|
||||
gui/groupview/InstanceDelegate.cpp
|
||||
gui/groupview/InstanceDelegate.h
|
||||
gui/groupview/VisualGroup.cpp
|
||||
gui/groupview/VisualGroup.h
|
||||
|
||||
# LOGIC - Base classes and infrastructure
|
||||
logic/BaseVersion.h
|
||||
logic/InstanceFactory.h
|
||||
logic/InstanceFactory.cpp
|
||||
logic/BaseInstance.h
|
||||
logic/BaseInstance.cpp
|
||||
logic/BaseInstance_p.h
|
||||
logic/Mod.h
|
||||
logic/Mod.cpp
|
||||
logic/ModList.h
|
||||
logic/ModList.cpp
|
||||
|
||||
# sets and maps for deciding based on versions
|
||||
logic/VersionFilterData.h
|
||||
logic/VersionFilterData.cpp
|
||||
|
||||
# Instance launch
|
||||
logic/InstanceLauncher.h
|
||||
logic/InstanceLauncher.cpp
|
||||
logic/MinecraftProcess.h
|
||||
logic/MinecraftProcess.cpp
|
||||
|
||||
# URN parser/resolver
|
||||
logic/URNResolver.cpp
|
||||
logic/URNResolver.h
|
||||
|
||||
# Annoying nag screen logic
|
||||
logic/NagUtils.h
|
||||
logic/NagUtils.cpp
|
||||
|
||||
# Player skin utilities
|
||||
logic/SkinUtils.h
|
||||
logic/SkinUtils.cpp
|
||||
|
||||
# misc model filter
|
||||
logic/EnabledItemFilter.h
|
||||
logic/EnabledItemFilter.cpp
|
||||
|
||||
# JSON parsing helpers
|
||||
logic/MMCJson.h
|
||||
logic/MMCJson.cpp
|
||||
|
||||
# RW lock protected map
|
||||
logic/RWStorage.h
|
||||
|
||||
# network stuffs
|
||||
logic/net/NetAction.h
|
||||
logic/net/MD5EtagDownload.h
|
||||
logic/net/MD5EtagDownload.cpp
|
||||
logic/net/ByteArrayDownload.h
|
||||
logic/net/ByteArrayDownload.cpp
|
||||
logic/net/CacheDownload.h
|
||||
logic/net/CacheDownload.cpp
|
||||
logic/net/NetJob.h
|
||||
logic/net/NetJob.cpp
|
||||
logic/net/HttpMetaCache.h
|
||||
logic/net/HttpMetaCache.cpp
|
||||
logic/net/PasteUpload.h
|
||||
logic/net/PasteUpload.cpp
|
||||
logic/net/URLConstants.h
|
||||
logic/net/URLConstants.cpp
|
||||
|
||||
# Yggdrasil login stuff
|
||||
logic/auth/AuthSession.h
|
||||
logic/auth/AuthSession.cpp
|
||||
logic/auth/MojangAccountList.h
|
||||
logic/auth/MojangAccountList.cpp
|
||||
logic/auth/MojangAccount.h
|
||||
logic/auth/MojangAccount.cpp
|
||||
logic/auth/YggdrasilTask.h
|
||||
logic/auth/YggdrasilTask.cpp
|
||||
logic/auth/flows/AuthenticateTask.h
|
||||
logic/auth/flows/AuthenticateTask.cpp
|
||||
logic/auth/flows/RefreshTask.cpp
|
||||
logic/auth/flows/RefreshTask.cpp
|
||||
logic/auth/flows/ValidateTask.h
|
||||
logic/auth/flows/ValidateTask.cpp
|
||||
|
||||
# Update system
|
||||
logic/updater/UpdateChecker.h
|
||||
logic/updater/UpdateChecker.cpp
|
||||
logic/updater/DownloadUpdateTask.h
|
||||
logic/updater/DownloadUpdateTask.cpp
|
||||
logic/updater/NotificationChecker.h
|
||||
logic/updater/NotificationChecker.cpp
|
||||
|
||||
# News System
|
||||
logic/news/NewsChecker.h
|
||||
logic/news/NewsChecker.cpp
|
||||
logic/news/NewsEntry.h
|
||||
logic/news/NewsEntry.cpp
|
||||
|
||||
# Status system
|
||||
logic/status/StatusChecker.h
|
||||
logic/status/StatusChecker.cpp
|
||||
|
||||
# legacy instances
|
||||
logic/LegacyInstance.h
|
||||
logic/LegacyInstance.cpp
|
||||
logic/LegacyInstance_p.h
|
||||
logic/LegacyUpdate.h
|
||||
logic/LegacyUpdate.cpp
|
||||
|
||||
# OneSix instances
|
||||
logic/OneSixUpdate.h
|
||||
logic/OneSixUpdate.cpp
|
||||
logic/OneSixInstance.h
|
||||
logic/OneSixInstance.cpp
|
||||
logic/OneSixInstance_p.h
|
||||
|
||||
# OneSix version json infrastructure
|
||||
logic/minecraft/InstanceVersion.cpp
|
||||
logic/minecraft/InstanceVersion.h
|
||||
logic/minecraft/JarMod.cpp
|
||||
logic/minecraft/JarMod.h
|
||||
logic/minecraft/MinecraftVersion.cpp
|
||||
logic/minecraft/MinecraftVersion.h
|
||||
logic/minecraft/MinecraftVersionList.cpp
|
||||
logic/minecraft/MinecraftVersionList.h
|
||||
logic/minecraft/OneSixLibrary.cpp
|
||||
logic/minecraft/OneSixLibrary.h
|
||||
logic/minecraft/OneSixRule.cpp
|
||||
logic/minecraft/OneSixRule.h
|
||||
logic/minecraft/OpSys.cpp
|
||||
logic/minecraft/OpSys.h
|
||||
logic/minecraft/ParseUtils.cpp
|
||||
logic/minecraft/ParseUtils.h
|
||||
logic/minecraft/RawLibrary.cpp
|
||||
logic/minecraft/RawLibrary.h
|
||||
logic/minecraft/VersionBuilder.cpp
|
||||
logic/minecraft/VersionBuilder.h
|
||||
logic/minecraft/VersionBuildError.h
|
||||
logic/minecraft/VersionFile.cpp
|
||||
logic/minecraft/VersionFile.h
|
||||
logic/minecraft/VersionPatch.h
|
||||
logic/minecraft/VersionSource.h
|
||||
|
||||
# A Recursive file system watcher
|
||||
logic/RecursiveFileSystemWatcher.h
|
||||
logic/RecursiveFileSystemWatcher.cpp
|
||||
|
||||
# Various base classes
|
||||
logic/BaseInstaller.h
|
||||
logic/BaseInstaller.cpp
|
||||
logic/BaseVersionList.h
|
||||
logic/BaseVersionList.cpp
|
||||
|
||||
logic/InstanceList.h
|
||||
logic/InstanceList.cpp
|
||||
logic/LwjglVersionList.h
|
||||
logic/LwjglVersionList.cpp
|
||||
|
||||
# FTB
|
||||
logic/OneSixFTBInstance.h
|
||||
logic/OneSixFTBInstance.cpp
|
||||
logic/LegacyFTBInstance.h
|
||||
logic/LegacyFTBInstance.cpp
|
||||
|
||||
# the screenshots feature
|
||||
logic/screenshots/Screenshot.h
|
||||
logic/screenshots/ImgurUpload.h
|
||||
logic/screenshots/ImgurUpload.cpp
|
||||
logic/screenshots/ImgurAlbumCreation.h
|
||||
logic/screenshots/ImgurAlbumCreation.cpp
|
||||
|
||||
# Icons
|
||||
logic/icons/MMCIcon.h
|
||||
logic/icons/MMCIcon.cpp
|
||||
logic/icons/IconList.h
|
||||
logic/icons/IconList.cpp
|
||||
|
||||
# Tasks
|
||||
logic/tasks/ProgressProvider.h
|
||||
logic/tasks/Task.h
|
||||
logic/tasks/Task.cpp
|
||||
logic/tasks/ThreadTask.h
|
||||
logic/tasks/ThreadTask.cpp
|
||||
logic/tasks/SequentialTask.h
|
||||
logic/tasks/SequentialTask.cpp
|
||||
|
||||
# Settings
|
||||
logic/settings/INIFile.cpp
|
||||
logic/settings/INIFile.h
|
||||
logic/settings/INISettingsObject.cpp
|
||||
logic/settings/INISettingsObject.h
|
||||
logic/settings/OverrideSetting.cpp
|
||||
logic/settings/OverrideSetting.h
|
||||
logic/settings/Setting.cpp
|
||||
logic/settings/Setting.h
|
||||
logic/settings/SettingsObject.cpp
|
||||
logic/settings/SettingsObject.h
|
||||
|
||||
# Java related code
|
||||
logic/java/JavaChecker.h
|
||||
logic/java/JavaChecker.cpp
|
||||
logic/java/JavaUtils.h
|
||||
logic/java/JavaUtils.cpp
|
||||
logic/java/JavaVersionList.h
|
||||
logic/java/JavaVersionList.cpp
|
||||
logic/java/JavaCheckerJob.h
|
||||
logic/java/JavaCheckerJob.cpp
|
||||
|
||||
# Assets
|
||||
logic/assets/AssetsMigrateTask.h
|
||||
logic/assets/AssetsMigrateTask.cpp
|
||||
logic/assets/AssetsUtils.h
|
||||
logic/assets/AssetsUtils.cpp
|
||||
|
||||
# Tools
|
||||
logic/tools/BaseExternalTool.h
|
||||
logic/tools/BaseExternalTool.cpp
|
||||
logic/tools/MCEditTool.h
|
||||
logic/tools/MCEditTool.cpp
|
||||
logic/tools/BaseProfiler.h
|
||||
logic/tools/BaseProfiler.cpp
|
||||
logic/tools/JProfiler.h
|
||||
logic/tools/JProfiler.cpp
|
||||
logic/tools/JVisualVM.h
|
||||
logic/tools/JVisualVM.cpp
|
||||
|
||||
# Forge and all things forge related
|
||||
logic/forge/ForgeVersion.h
|
||||
logic/forge/ForgeVersion.cpp
|
||||
logic/forge/ForgeVersionList.h
|
||||
logic/forge/ForgeVersionList.cpp
|
||||
logic/forge/ForgeMirror.h
|
||||
logic/forge/ForgeMirrors.h
|
||||
logic/forge/ForgeMirrors.cpp
|
||||
logic/forge/ForgeXzDownload.h
|
||||
logic/forge/ForgeXzDownload.cpp
|
||||
logic/forge/LegacyForge.h
|
||||
logic/forge/LegacyForge.cpp
|
||||
logic/forge/ForgeInstaller.h
|
||||
logic/forge/ForgeInstaller.cpp
|
||||
|
||||
# Liteloader and related things
|
||||
logic/liteloader/LiteLoaderInstaller.h
|
||||
logic/liteloader/LiteLoaderInstaller.cpp
|
||||
logic/liteloader/LiteLoaderVersionList.h
|
||||
logic/liteloader/LiteLoaderVersionList.cpp
|
||||
)
|
||||
|
||||
|
||||
######## UIs ########
|
||||
SET(MULTIMC_UIS
|
||||
# Windows
|
||||
gui/MainWindow.ui
|
||||
|
||||
# Option pages
|
||||
gui/pages/VersionPage.ui
|
||||
gui/pages/ModFolderPage.ui
|
||||
gui/pages/LegacyUpgradePage.ui
|
||||
gui/pages/LegacyJarModPage.ui
|
||||
gui/pages/LogPage.ui
|
||||
gui/pages/InstanceSettingsPage.ui
|
||||
gui/pages/NotesPage.ui
|
||||
gui/pages/ScreenshotsPage.ui
|
||||
gui/pages/OtherLogsPage.ui
|
||||
|
||||
# Dialogs
|
||||
gui/dialogs/SettingsDialog.ui
|
||||
gui/dialogs/CopyInstanceDialog.ui
|
||||
gui/dialogs/NewInstanceDialog.ui
|
||||
gui/dialogs/AboutDialog.ui
|
||||
gui/dialogs/VersionSelectDialog.ui
|
||||
gui/dialogs/LwjglSelectDialog.ui
|
||||
gui/dialogs/ProgressDialog.ui
|
||||
gui/dialogs/IconPickerDialog.ui
|
||||
gui/dialogs/AccountListDialog.ui
|
||||
gui/dialogs/AccountSelectDialog.ui
|
||||
gui/dialogs/EditAccountDialog.ui
|
||||
gui/dialogs/LoginDialog.ui
|
||||
gui/dialogs/UpdateDialog.ui
|
||||
gui/dialogs/NotificationDialog.ui
|
||||
|
||||
# Widgets/other
|
||||
gui/widgets/MCModInfoFrame.ui
|
||||
)
|
||||
|
||||
set(FILES_TO_TRANSLATE)
|
||||
foreach(file ${MULTIMC_SOURCES})
|
||||
get_filename_component(absfile "${file}" ABSOLUTE)
|
||||
list(APPEND FILES_TO_TRANSLATE "${absfile}")
|
||||
endforeach()
|
||||
|
||||
foreach(file ${MULTIMC_UIS})
|
||||
get_filename_component(absfile "${file}" ABSOLUTE)
|
||||
list(APPEND FILES_TO_TRANSLATE "${absfile}")
|
||||
endforeach()
|
||||
|
||||
set(MULTIMC_QRCS
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/pe_dark/pe_dark.qrc
|
||||
resources/pe_light/pe_light.qrc
|
||||
resources/instances/instances.qrc
|
||||
resources/versions/versions.qrc
|
||||
)
|
||||
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(MULTIMC_RCS resources/multimc.rc)
|
||||
endif()
|
||||
|
||||
####### X11 Stuff #######
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(MultiMC_QT_ADDITIONAL_MODULES ${MultiMC_QT_ADDITIONAL_MODULES} X11Extras)
|
||||
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} xcb)
|
||||
list(APPEND MULTIMC_SOURCES gui/Platform_X11.cpp)
|
||||
else()
|
||||
list(APPEND MULTIMC_SOURCES gui/Platform_Other.cpp)
|
||||
endif()
|
||||
|
||||
|
||||
################################ COMPILE ################################
|
||||
|
||||
# Link additional libraries
|
||||
if(WIN32)
|
||||
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} Qt5::WinMain)
|
||||
endif(WIN32)
|
||||
|
||||
# Tell CMake that MultiMCLauncher.jar is generated.
|
||||
#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/launcher/MultiMCLauncher.jar GENERATED)
|
||||
#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/javacheck/JavaCheck.jar GENERATED)
|
||||
|
||||
# Qt 5 stuff
|
||||
qt5_wrap_ui(MULTIMC_UI ${MULTIMC_UIS})
|
||||
qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
|
||||
|
||||
# Add common library
|
||||
add_library(MultiMC_common STATIC ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES})
|
||||
|
||||
# Add executable
|
||||
add_executable(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
|
||||
|
||||
# Link
|
||||
target_link_libraries(MultiMC MultiMC_common)
|
||||
target_link_libraries(MultiMC_common xz-embedded unpack200 quazip libUtil ${MultiMC_LINK_ADDITIONAL_LIBS})
|
||||
qt5_use_modules(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||
qt5_use_modules(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
|
||||
|
||||
################################ INSTALLATION AND PACKAGING ################################
|
||||
|
||||
######## Install ########
|
||||
|
||||
#### Executable ####
|
||||
if(APPLE AND UNIX) ## OSX
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
|
||||
)
|
||||
|
||||
elseif(UNIX) ## LINUX and similar
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime
|
||||
)
|
||||
install(PROGRAMS package/linux/MultiMC DESTINATION .)
|
||||
|
||||
elseif(WIN32) ## WINDOWS
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
LIBRARY DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION . COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
#### Dist package logic ####
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng" EXCLUDE
|
||||
)
|
||||
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
)
|
||||
else()
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
if(APPLE)
|
||||
# Accessible plugin to make buttons look decent on osx
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/accessible"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "quick" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# qtconf
|
||||
install(
|
||||
CODE "
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
|
||||
"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# ICNS file for OS X
|
||||
if(APPLE)
|
||||
install(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||
@ONLY
|
||||
)
|
||||
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
|
||||
|
||||
|
||||
|
||||
######## Package ########
|
||||
|
||||
# Package with CPack
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
else()
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
endif()
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "MultiMC 5")
|
||||
set(CPACK_PACKAGE_VENDOR "")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MultiMC - Minecraft launcher and management tool.")
|
||||
set(CPACK_PACKAGE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${MultiMC_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${MultiMC_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${MultiMC_VERSION_REV})
|
||||
|
||||
if(CPACK_GENERATOR STREQUAL "NSIS")
|
||||
set(CPACK_PACKAGE_FILE_NAME "Setup-MultiMC")
|
||||
else()
|
||||
set(CPACK_PACKAGE_FILE_NAME "MultiMC")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "MultiMC 5")
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
add_subdirectory(depends/LogicalGui) # GUI -> Logic connection
|
||||
add_subdirectory(depends/iconfix) # fork of Qt's QIcon loader
|
||||
|
||||
include(Coverity)
|
||||
|
||||
# Translations
|
||||
add_subdirectory(translations)
|
||||
############################### Built Artifacts ###############################
|
||||
|
||||
# Tests
|
||||
add_subdirectory(tests)
|
||||
add_subdirectory(logic)
|
||||
add_subdirectory(application)
|
||||
|
||||
205
COPYING.md
Normal file
205
COPYING.md
Normal file
@@ -0,0 +1,205 @@
|
||||
#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.
|
||||
|
||||
#MinGW runtime (Windows)
|
||||
|
||||
Copyright (c) 2012 MinGW.org project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice, this permission notice and the below disclaimer
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#Qt 5
|
||||
|
||||
Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
Contact: http://www.qt-project.org/legal
|
||||
|
||||
Licensed under LGPL v2.1
|
||||
|
||||
#libnbt++
|
||||
|
||||
libnbt++ - A library for the Minecraft Named Binary Tag format.
|
||||
Copyright (C) 2013, 2015 ljfa-ag
|
||||
|
||||
libnbt++ is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
libnbt++ 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#Group View
|
||||
|
||||
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.
|
||||
|
||||
#rainbow (KGuiAddons)
|
||||
|
||||
Copyright (C) 2007 Matthew Woehlke <mw_triad@users.sourceforge.net>
|
||||
Copyright (C) 2007 Olaf Schmidt <ojschmidt@kde.org>
|
||||
Copyright (C) 2007 Thomas Zander <zander@kde.org>
|
||||
Copyright (C) 2007 Zack Rusin <zack@kde.org>
|
||||
Copyright (C) 2015 Petr Mrazek <peterix@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
#Hoedown
|
||||
|
||||
Copyright (c) 2008, Natacha Porté
|
||||
Copyright (c) 2011, Vicent Martí
|
||||
Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#Batch icon set
|
||||
|
||||
You are free to use Batch (the "icon set") or any part thereof (the "icons")
|
||||
in any personal, open-source or commercial work without obligation of payment
|
||||
(monetary or otherwise) or attribution. Do not sell the icon set, host
|
||||
the icon set or rent the icon set (either in existing or modified form).
|
||||
|
||||
While attribution is optional, it is always appreciated.
|
||||
|
||||
Intellectual property rights are not transferred with the download of the icons.
|
||||
|
||||
EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT
|
||||
BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,
|
||||
PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,
|
||||
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
#Pack200
|
||||
|
||||
The GNU General Public License (GPL)
|
||||
|
||||
Version 2, June 1991
|
||||
|
||||
+ "CLASSPATH" EXCEPTION TO THE GPL
|
||||
|
||||
Certain source files distributed by Oracle America and/or its affiliates are
|
||||
subject to the following clarification and special exception to the GPL, but
|
||||
only where Oracle has expressly included in the particular source file's header
|
||||
the words "Oracle designates this particular file as subject to the "Classpath"
|
||||
exception as provided by Oracle in the LICENSE file that accompanied this code."
|
||||
|
||||
Linking this library statically or dynamically with other modules is making
|
||||
a combined work based on this library. Thus, the terms and conditions of
|
||||
the GNU General Public License cover the whole combination.
|
||||
|
||||
As a special exception, the copyright holders of this library give you
|
||||
permission to link this library with independent modules to produce an
|
||||
executable, regardless of the license terms of these independent modules,
|
||||
and to copy and distribute the resulting executable under terms of your
|
||||
choice, provided that you also meet, for each linked independent module,
|
||||
the terms and conditions of the license of that module. An independent
|
||||
module is a module which is not derived from or based on this library. If
|
||||
you modify this library, you may extend this exception to your version of
|
||||
the library, but you are not obligated to do so. If you do not wish to do
|
||||
so, delete this exception statement from your version.
|
||||
|
||||
#Quazip
|
||||
|
||||
Copyright (C) 2005-2011 Sergey A. Tachenov
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
This program 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 Lesser
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
See COPYING file for the full LGPL text.
|
||||
|
||||
Original ZIP package is copyrighted by Gilles Vollant, see
|
||||
quazip/(un)zip.h files for details, basically it's zlib license.
|
||||
|
||||
#xz-minidec
|
||||
|
||||
XZ decompressor
|
||||
|
||||
Authors: Lasse Collin <lasse.collin@tukaani.org>
|
||||
Igor Pavlov <http://7-zip.org/>
|
||||
|
||||
This file has been put into the public domain.
|
||||
You can do whatever you want with this file.
|
||||
|
||||
#ColumnResizer
|
||||
|
||||
Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||
License: LGPL v2.1 or later (see COPYING)
|
||||
375
HandleCrash.cpp
375
HandleCrash.cpp
@@ -1,375 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// This is the Unix implementation of MultiMC's crash handling system.
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <MultiMC.h>
|
||||
|
||||
#if defined Q_OS_UNIX
|
||||
#include <sys/utsname.h>
|
||||
#include <execinfo.h>
|
||||
#elif defined Q_OS_WIN32
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <WinBacktrace.h>
|
||||
#endif
|
||||
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "HandleCrash.h"
|
||||
|
||||
// The maximum number of frames to include in the backtrace.
|
||||
#define BT_SIZE 20
|
||||
|
||||
|
||||
#define DUMPF_NAME_FMT "mmc-crash-%X.bm" // Black magic? Bowel movement? Dump?
|
||||
// 1234567890 1234
|
||||
// The maximum number of digits in a unix timestamp when encoded in hexadecimal is about 17.
|
||||
// Our format string is ~14 characters long.
|
||||
// The maximum length of the dump file's filename should be well over both of these. 42 is a good number.
|
||||
#define DUMPF_NAME_LEN 42
|
||||
|
||||
// {{{ Platform hackery
|
||||
|
||||
#if defined Q_OS_UNIX
|
||||
|
||||
struct CrashData
|
||||
{
|
||||
int signal = 0;
|
||||
};
|
||||
|
||||
// This has to be declared here, after the CrashData struct, but before the function that uses it.
|
||||
void handleCrash(CrashData);
|
||||
|
||||
void handler(int sig)
|
||||
{
|
||||
CrashData cData;
|
||||
cData.signal = sig;
|
||||
handleCrash(cData);
|
||||
}
|
||||
|
||||
#elif defined Q_OS_WIN32
|
||||
|
||||
// Struct for storing platform specific crash information.
|
||||
// This gets passed into the generic handler, which will use
|
||||
// it to access platform specific information.
|
||||
struct CrashData
|
||||
{
|
||||
EXCEPTION_RECORD* exceptionInfo;
|
||||
CONTEXT* context;
|
||||
};
|
||||
|
||||
void handleCrash(CrashData);
|
||||
|
||||
LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS* eInfo)
|
||||
{
|
||||
CrashData cData;
|
||||
cData.exceptionInfo = eInfo->ExceptionRecord;
|
||||
cData.context = eInfo->ContextRecord;
|
||||
handleCrash(cData);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Handling
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
// #ThanksMicrosoft
|
||||
// Blame Microsoft for this atrocity.
|
||||
void dprintf(int fd, const char* fmt...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
char buffer[10240];
|
||||
// Just sprintf to a really long string and hope it works...
|
||||
// This is a hack, but I can't think of a better way to do it easily.
|
||||
int len = vsnprintf(buffer, 10240, fmt, args);
|
||||
printf(buffer, fmt, args);
|
||||
write(fd, buffer, len);
|
||||
va_end(args);
|
||||
}
|
||||
#endif
|
||||
|
||||
void getVsnType(char* out);
|
||||
void readFromTo(int from, int to);
|
||||
|
||||
void dumpErrorInfo(int dumpFile, CrashData crash)
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
// TODO: Moar unix
|
||||
dprintf(dumpFile, "Signal: %d\n", crash.signal);
|
||||
#elif defined Q_OS_WIN32
|
||||
EXCEPTION_RECORD* excInfo = crash.exceptionInfo;
|
||||
|
||||
dprintf(dumpFile, "Exception Code: %d\n", excInfo->ExceptionCode);
|
||||
dprintf(dumpFile, "Exception Address: 0x%0X\n", excInfo->ExceptionAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
void dumpMiscInfo(int dumpFile)
|
||||
{
|
||||
char vsnType[42]; // The version type. If it's more than 42 chars, the universe might implode...
|
||||
|
||||
// Get MMC info.
|
||||
getVsnType(vsnType);
|
||||
|
||||
// Get MMC info.
|
||||
getVsnType(vsnType);
|
||||
|
||||
dprintf(dumpFile, "MultiMC Version: %s\n", BuildConfig.VERSION_CSTR);
|
||||
dprintf(dumpFile, "MultiMC Version Type: %s\n", vsnType);
|
||||
}
|
||||
|
||||
void dumpBacktrace(int dumpFile, CrashData crash)
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
// Variables for storing crash info.
|
||||
void* trace[BT_SIZE]; // Backtrace frames
|
||||
size_t size; // The backtrace size
|
||||
|
||||
// Get the backtrace.
|
||||
size = backtrace(trace, BT_SIZE);
|
||||
|
||||
// Dump the backtrace
|
||||
dprintf(dumpFile, "---- BEGIN BACKTRACE ----\n");
|
||||
backtrace_symbols_fd(trace, size, dumpFile);
|
||||
dprintf(dumpFile, "---- END BACKTRACE ----\n");
|
||||
#elif defined Q_OS_WIN32
|
||||
dprintf(dumpFile, "---- BEGIN BACKTRACE ----\n");
|
||||
|
||||
StackFrame stack[BT_SIZE];
|
||||
size_t size;
|
||||
|
||||
SYMBOL_INFO *symbol;
|
||||
HANDLE process;
|
||||
|
||||
size = getBacktrace(stack, BT_SIZE, *crash.context);
|
||||
|
||||
// FIXME: Accessing heap memory is supposedly "dangerous",
|
||||
// but I can't find another way of doing this.
|
||||
|
||||
// Initialize
|
||||
process = GetCurrentProcess();
|
||||
if (!SymInitialize(process, NULL, true))
|
||||
{
|
||||
dprintf(dumpFile, "Failed to initialize symbol handler. Can't print stack trace.\n");
|
||||
dprintf(dumpFile, "Here's a list of addresses in the call stack instead:\n");
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
dprintf(dumpFile, "0x%0X\n", (DWORD64)stack[i].address);
|
||||
}
|
||||
} else {
|
||||
// Allocate memory... ._.
|
||||
symbol = (SYMBOL_INFO *) calloc(sizeof(SYMBOL_INFO) + 256 * sizeof(char), 1);
|
||||
symbol->MaxNameLen = 255;
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
|
||||
// Dump stacktrace
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
DWORD64 addr = (DWORD64)stack[i].address;
|
||||
if (!SymFromAddr(process, (DWORD64)(addr), 0, symbol))
|
||||
dprintf(dumpFile, "?? - 0x%0X\n", addr);
|
||||
else
|
||||
dprintf(dumpFile, "%s - 0x%0X\n", symbol->Name, symbol->Address);
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
}
|
||||
|
||||
dprintf(dumpFile, "---- END BACKTRACE ----\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void dumpSysInfo(int dumpFile)
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
utsname sysinfo; // System information
|
||||
|
||||
// Dump system info
|
||||
if (uname(&sysinfo) >= 0)
|
||||
{
|
||||
dprintf(dumpFile, "OS System: %s\n", sysinfo.sysname);
|
||||
dprintf(dumpFile, "OS Machine: %s\n", sysinfo.machine);
|
||||
dprintf(dumpFile, "OS Release: %s\n", sysinfo.release);
|
||||
dprintf(dumpFile, "OS Version: %s\n", sysinfo.version);
|
||||
} else {
|
||||
dprintf(dumpFile, "OS System: Unknown Unix");
|
||||
}
|
||||
#else
|
||||
// TODO: Get more information here.
|
||||
dprintf(dumpFile, "OS System: Windows");
|
||||
#endif
|
||||
}
|
||||
|
||||
void dumpLogs(int dumpFile)
|
||||
{
|
||||
int otherFile;
|
||||
|
||||
// Attempt to attach the log file if the logger was initialized.
|
||||
dprintf(dumpFile, "---- BEGIN LOGS ----\n");
|
||||
if (loggerInitialized)
|
||||
{
|
||||
otherFile = open("MultiMC-0.log", O_RDONLY);
|
||||
readFromTo(otherFile, dumpFile);
|
||||
} else {
|
||||
dprintf(dumpFile, "Logger not initialized.\n");
|
||||
}
|
||||
dprintf(dumpFile, "---- END LOGS ----\n");
|
||||
}
|
||||
|
||||
// The signal handler. If this function is called, it means shit has probably collided with some sort of device one might use to keep oneself cool.
|
||||
// This is the generic part of the code that will be called after platform specific handling is finished.
|
||||
void handleCrash(CrashData crash)
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
fprintf(stderr, "Fatal error! Received signal %d\n", crash.signal);
|
||||
#endif
|
||||
|
||||
time_t unixTime = 0; // Unix timestamp. Used to give our crash dumps "unique" names.
|
||||
|
||||
char dumpFileName[DUMPF_NAME_LEN]; // The name of the file we're dumping to.
|
||||
int dumpFile; // File descriptor for our dump file.
|
||||
|
||||
// Determine what our dump file should be called.
|
||||
// We'll just call it "mmc-crash-<unixtime>.dump"
|
||||
// First, check the time.
|
||||
time(&unixTime);
|
||||
|
||||
// Now we get to do some !!FUN!! hackery to ensure we don't use the stack when we convert
|
||||
// the timestamp from an int to a string. To do this, we just allocate a fixed size array
|
||||
// of chars on the stack, and sprintf into it. We know the timestamp won't ever be longer
|
||||
// than a certain number of digits, so this should work just fine.
|
||||
// sprintf doesn't support writing signed values as hex, so this breaks on negative timestamps.
|
||||
// It really shouldn't matter, though...
|
||||
sprintf(dumpFileName, DUMPF_NAME_FMT, unixTime);
|
||||
|
||||
// Now, we need to open the file.
|
||||
// Fail if it already exists. This should never happen.
|
||||
dumpFile = open(dumpFileName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
|
||||
|
||||
if (dumpFile >= 0)
|
||||
{
|
||||
// If we opened the dump file successfully.
|
||||
// Dump everything we can and GTFO.
|
||||
fprintf(stderr, "Dumping crash report to %s\n", dumpFileName);
|
||||
|
||||
// Dump misc info
|
||||
dprintf(dumpFile, "Unix Time: %d\n", unixTime);
|
||||
dumpErrorInfo(dumpFile, crash);
|
||||
dumpMiscInfo(dumpFile);
|
||||
|
||||
dprintf(dumpFile, "\n");
|
||||
|
||||
dumpSysInfo(dumpFile);
|
||||
|
||||
dprintf(dumpFile, "\n");
|
||||
|
||||
dumpBacktrace(dumpFile, crash);
|
||||
|
||||
dprintf(dumpFile, "\n");
|
||||
|
||||
// DIE DIE DIE!
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Failed to open dump file %s to write crash info (ERRNO: %d)\n", dumpFileName, errno);
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Reads data from the file descriptor on the first argument into the second argument.
|
||||
void readFromTo(int from, int to)
|
||||
{
|
||||
char buffer[1024];
|
||||
size_t lastread = 1;
|
||||
while (lastread > 0)
|
||||
{
|
||||
lastread = read(from, buffer, 1024);
|
||||
if (lastread > 0) write(to, buffer, lastread);
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the current version type to the given char buffer.
|
||||
void getVsnType(char* out)
|
||||
{
|
||||
switch (BuildConfig.versionTypeEnum)
|
||||
{
|
||||
case Config::Release:
|
||||
sprintf(out, "Release");
|
||||
break;
|
||||
case Config::ReleaseCandidate:
|
||||
sprintf(out, "ReleaseCandidate");
|
||||
break;
|
||||
case Config::Development:
|
||||
sprintf(out, "Development");
|
||||
break;
|
||||
default:
|
||||
sprintf(out, "Unknown");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
// {{{ Misc
|
||||
|
||||
#if defined TEST_SEGV
|
||||
// Causes a crash. For testing.
|
||||
void testCrash()
|
||||
{
|
||||
char* lol = (char*)MMC->settings().get();
|
||||
lol -= 8;
|
||||
|
||||
// Throw shit at the fan.
|
||||
for (int i = 0; i < 8; i++)
|
||||
lol[i] = 'f';
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initializes the Unix crash handler.
|
||||
void initBlackMagic()
|
||||
{
|
||||
#ifdef Q_OS_UNIX
|
||||
// Register the handler.
|
||||
signal(SIGSEGV, handler);
|
||||
signal(SIGABRT, handler);
|
||||
#elif defined Q_OS_WIN32
|
||||
// I hate Windows
|
||||
SetUnhandledExceptionFilter(ExceptionFilter);
|
||||
#endif
|
||||
|
||||
#ifdef TEST_SEGV
|
||||
testCrash();
|
||||
#endif
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// This is a simple header file for the crash handling system. It exposes only one method,
|
||||
// initBlackMagic, which initializes the system, registering signal handlers, or doing
|
||||
// whatever stupid things need to be done on Windows.
|
||||
// The platform specific implementations for this system are in UnixCrash.cpp and
|
||||
// WinCrash.cpp.
|
||||
|
||||
#if defined Q_OS_WIN
|
||||
#warning Crash handling is not yet implemented on Windows.
|
||||
#elif defined Q_OS_UNIX
|
||||
#else
|
||||
#warning Crash handling is not supported on this platform.
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initializes the crash handling system.
|
||||
*/
|
||||
void initBlackMagic();
|
||||
|
||||
39
ISSUE_TEMPLATE.md
Normal file
39
ISSUE_TEMPLATE.md
Normal file
@@ -0,0 +1,39 @@
|
||||
Before submitting this issue, please make sure you have:
|
||||
---
|
||||
#### 1. Filled out this form completely, the only optional field is "additional info".
|
||||
##### - Use as many details as possible and state the problem clearly.
|
||||
#### 2. Proof-read your ENTIRE issue report.
|
||||
##### - Grammar and spelling mistakes make issue reports harder to understand.
|
||||
#### 3. Made sure your problem is not caused by an issue in your own modpack.
|
||||
##### - We provide support for MultiMC, not your modpack. Problems with your modpack will be ignored.
|
||||
#### 4. Given the issue a descriptive title.
|
||||
##### - A good title includes a brief summary of the issue and avoids things such as "Help" and "What?!".
|
||||
|
||||
System Information
|
||||
---
|
||||
MultiMC version:
|
||||
|
||||
Operating System:
|
||||
|
||||
Summary of the issue or suggestion:
|
||||
---
|
||||
|
||||
What should happen:
|
||||
---
|
||||
|
||||
Steps to reproduce the issue (Add more if needed):
|
||||
---
|
||||
1.
|
||||
|
||||
2.
|
||||
|
||||
3.
|
||||
|
||||
Suspected cause:
|
||||
---
|
||||
|
||||
Logs/Screenshots:
|
||||
---
|
||||
|
||||
Additional Info:
|
||||
---
|
||||
5
LGPL_EXCEPTION.txt
Normal file
5
LGPL_EXCEPTION.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
As a special exception to the GNU Lesser General Public License version 2.1, the object code form of a "work
|
||||
that uses the Library" may incorporate material from a header file that is part of the Library. You may
|
||||
distribute such object code under terms of your choice, provided that the incorporated material (i) does not
|
||||
exceed more than 5% of the total size of the Library; and (ii) is limited to numerical parameters, data
|
||||
structure layouts, accessors, macros, inline functions and templates.
|
||||
502
LICENSE.LPGLv21
Normal file
502
LICENSE.LPGLv21
Normal file
@@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
0the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
25
MMCError.h
25
MMCError.h
@@ -1,25 +0,0 @@
|
||||
#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;
|
||||
};
|
||||
741
MultiMC.cpp
741
MultiMC.cpp
@@ -1,741 +0,0 @@
|
||||
#include "MultiMC.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTranslator>
|
||||
#include <QLibraryInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QStringList>
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "gui/dialogs/VersionSelectDialog.h"
|
||||
#include "logic/InstanceList.h"
|
||||
#include "logic/auth/MojangAccountList.h"
|
||||
#include "logic/icons/IconList.h"
|
||||
#include "logic/LwjglVersionList.h"
|
||||
#include "logic/minecraft/MinecraftVersionList.h"
|
||||
#include "logic/liteloader/LiteLoaderVersionList.h"
|
||||
|
||||
#include "logic/forge/ForgeVersionList.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/java/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 "logic/URNResolver.h"
|
||||
|
||||
#include "pathutils.h"
|
||||
#include "cmdutils.h"
|
||||
#include "logic/settings/INISettingsObject.h"
|
||||
#include "logic/settings/Setting.h"
|
||||
#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)
|
||||
{
|
||||
setOrganizationName("MultiMC");
|
||||
setApplicationName("MultiMC5");
|
||||
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Commandline parsing
|
||||
QHash<QString, QVariant> args;
|
||||
{
|
||||
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
||||
|
||||
// --help
|
||||
parser.addSwitch("help");
|
||||
parser.addShortOpt("help", 'h');
|
||||
parser.addDocumentation("help", "display this help and exit.");
|
||||
// --version
|
||||
parser.addSwitch("version");
|
||||
parser.addShortOpt("version", 'V');
|
||||
parser.addDocumentation("version", "display program version and exit.");
|
||||
// --dir
|
||||
parser.addOption("dir", applicationDirPath());
|
||||
parser.addShortOpt("dir", 'd');
|
||||
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of "
|
||||
"the binary location (use '.' for current)");
|
||||
// WARNING: disabled until further notice
|
||||
/*
|
||||
// --launch
|
||||
parser.addOption("launch");
|
||||
parser.addShortOpt("launch", 'l');
|
||||
parser.addDocumentation("launch", "tries to launch the given instance", "<inst>");
|
||||
*/
|
||||
|
||||
// parse the arguments
|
||||
try
|
||||
{
|
||||
args = parser.parse(arguments());
|
||||
}
|
||||
catch (ParsingError e)
|
||||
{
|
||||
std::cerr << "CommandLineError: " << e.what() << std::endl;
|
||||
std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters."
|
||||
<< std::endl;
|
||||
m_status = MultiMC::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// display help and exit
|
||||
if (args["help"].toBool())
|
||||
{
|
||||
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
||||
m_status = MultiMC::Succeeded;
|
||||
return;
|
||||
}
|
||||
|
||||
// display version and exit
|
||||
if (args["version"].toBool())
|
||||
{
|
||||
std::cout << "Version " << BuildConfig.VERSION_STR.toStdString() << std::endl;
|
||||
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
||||
m_status = MultiMC::Succeeded;
|
||||
return;
|
||||
}
|
||||
}
|
||||
origcwdPath = QDir::currentPath();
|
||||
binPath = applicationDirPath();
|
||||
QString adjustedBy;
|
||||
// change directory
|
||||
QString dirParam = args["dir"].toString();
|
||||
if (!dirParam.isEmpty())
|
||||
{
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
// on command line
|
||||
adjustedBy += "Command line " + dirParam;
|
||||
dataPath = dirParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataPath = applicationDirPath();
|
||||
adjustedBy += "Fallback to binary path " + dataPath;
|
||||
}
|
||||
|
||||
if(!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
|
||||
{
|
||||
// BAD STUFF. WHAT DO?
|
||||
initLogger();
|
||||
QLOG_ERROR() << "Failed to set work path. Will exit. NOW.";
|
||||
m_status = MultiMC::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (root_override)
|
||||
{
|
||||
rootPath = binPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
QDir foo(PathCombine(binPath, ".."));
|
||||
rootPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
rootPath = binPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(PathCombine(binPath, "../.."));
|
||||
rootPath = foo.absolutePath();
|
||||
#endif
|
||||
}
|
||||
|
||||
// static data paths... mostly just for translations
|
||||
#ifdef Q_OS_LINUX
|
||||
QDir foo(PathCombine(binPath, ".."));
|
||||
staticDataPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
staticDataPath = binPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(PathCombine(rootPath, "Contents/Resources"));
|
||||
staticDataPath = foo.absolutePath();
|
||||
#endif
|
||||
|
||||
// init the logger
|
||||
initLogger();
|
||||
|
||||
QLOG_INFO() << "MultiMC 5, (c) 2013 MultiMC Contributors";
|
||||
QLOG_INFO() << "Version : " << BuildConfig.VERSION_STR;
|
||||
QLOG_INFO() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
if (adjustedBy.size())
|
||||
{
|
||||
QLOG_INFO() << "Work dir before adjustment : " << origcwdPath;
|
||||
QLOG_INFO() << "Work dir after adjustment : " << QDir::currentPath();
|
||||
QLOG_INFO() << "Adjusted by : " << adjustedBy;
|
||||
}
|
||||
else
|
||||
{
|
||||
QLOG_INFO() << "Work dir : " << QDir::currentPath();
|
||||
}
|
||||
QLOG_INFO() << "Binary path : " << binPath;
|
||||
QLOG_INFO() << "Application root path : " << rootPath;
|
||||
QLOG_INFO() << "Static data path : " << staticDataPath;
|
||||
|
||||
// load settings
|
||||
initGlobalSettings();
|
||||
|
||||
// load translations
|
||||
initTranslations();
|
||||
|
||||
// initialize the updater
|
||||
m_updateChecker.reset(new UpdateChecker());
|
||||
|
||||
// initialize the notification checker
|
||||
m_notificationChecker.reset(new NotificationChecker());
|
||||
|
||||
// initialize the news checker
|
||||
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");
|
||||
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
|
||||
QLOG_INFO() << "Loading Instances...";
|
||||
m_instances->loadList();
|
||||
connect(InstDirSetting.get(), SIGNAL(SettingChanged(const Setting &, QVariant)),
|
||||
m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
|
||||
|
||||
// and accounts
|
||||
m_accounts.reset(new MojangAccountList(this));
|
||||
QLOG_INFO() << "Loading accounts...";
|
||||
m_accounts->setListFilePath("accounts.json", true);
|
||||
m_accounts->loadList();
|
||||
|
||||
// init the http meta cache
|
||||
initHttpMetaCache();
|
||||
|
||||
// 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
|
||||
/*
|
||||
if (!args["launch"].isNull())
|
||||
{
|
||||
if (InstanceLauncher(args["launch"].toString()).launch())
|
||||
m_status = MultiMC::Succeeded;
|
||||
else
|
||||
m_status = MultiMC::Failed;
|
||||
return;
|
||||
}
|
||||
*/
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(onExit()));
|
||||
m_status = MultiMC::Initialized;
|
||||
}
|
||||
|
||||
MultiMC::~MultiMC()
|
||||
{
|
||||
if (m_mmc_translator)
|
||||
{
|
||||
removeTranslator(m_mmc_translator.get());
|
||||
}
|
||||
if (m_qt_translator)
|
||||
{
|
||||
removeTranslator(m_qt_translator.get());
|
||||
}
|
||||
}
|
||||
|
||||
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_" + locale.bcp47Name(),
|
||||
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
{
|
||||
QLOG_DEBUG() << "Loading Qt Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_qt_translator.get()))
|
||||
{
|
||||
QLOG_ERROR() << "Loading Qt Language File failed.";
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
|
||||
m_mmc_translator.reset(new QTranslator());
|
||||
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(),
|
||||
MMC->staticData() + "/translations"))
|
||||
{
|
||||
QLOG_DEBUG() << "Loading MMC Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_mmc_translator.get()))
|
||||
{
|
||||
QLOG_ERROR() << "Loading MMC Language File failed.";
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void moveFile(const QString &oldName, const QString &newName)
|
||||
{
|
||||
QFile::remove(newName);
|
||||
QFile::copy(oldName, newName);
|
||||
QFile::remove(oldName);
|
||||
}
|
||||
void MultiMC::initLogger()
|
||||
{
|
||||
static const QString logBase = "MultiMC-%0.log";
|
||||
|
||||
moveFile(logBase.arg(3), logBase.arg(4));
|
||||
moveFile(logBase.arg(2), logBase.arg(3));
|
||||
moveFile(logBase.arg(1), logBase.arg(2));
|
||||
moveFile(logBase.arg(0), logBase.arg(1));
|
||||
|
||||
// init the logging mechanism
|
||||
QsLogging::Logger &logger = QsLogging::Logger::instance();
|
||||
logger.setLoggingLevel(QsLogging::TraceLevel);
|
||||
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
|
||||
m_debugDestination = QsLogging::DestinationFactory::MakeDebugOutputDestination();
|
||||
logger.addDestination(m_fileDestination.get());
|
||||
logger.addDestination(m_debugDestination.get());
|
||||
// log all the things
|
||||
logger.setLoggingLevel(QsLogging::TraceLevel);
|
||||
loggerInitialized = true;
|
||||
}
|
||||
|
||||
bool loggerInitialized = false;
|
||||
|
||||
void MultiMC::initGlobalSettings()
|
||||
{
|
||||
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
||||
// Updates
|
||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||
m_settings->registerSetting("AutoUpdate", true);
|
||||
m_settings->registerSetting("IconTheme", QString("multimc"));
|
||||
|
||||
// Notifications
|
||||
m_settings->registerSetting("ShownNotifications", QString());
|
||||
|
||||
// FTB
|
||||
m_settings->registerSetting("TrackFTBInstances", false);
|
||||
QString ftbDataDefault;
|
||||
#ifdef Q_OS_LINUX
|
||||
QString ftbDefault = ftbDataDefault = QDir::home().absoluteFilePath(".ftblauncher");
|
||||
#elif defined(Q_OS_WIN32)
|
||||
wchar_t buf[APPDATA_BUFFER_SIZE];
|
||||
wchar_t newBuf[APPDATA_BUFFER_SIZE];
|
||||
QString ftbDefault, newFtbDefault, oldFtbDefault;
|
||||
if (!GetEnvironmentVariableW(L"LOCALAPPDATA", newBuf, APPDATA_BUFFER_SIZE))
|
||||
{
|
||||
QLOG_FATAL() << "Your LOCALAPPDATA 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
|
||||
{
|
||||
newFtbDefault = QDir(QString::fromWCharArray(newBuf)).absoluteFilePath("ftblauncher");
|
||||
}
|
||||
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
|
||||
{
|
||||
oldFtbDefault = QDir(QString::fromWCharArray(buf)).absoluteFilePath("ftblauncher");
|
||||
}
|
||||
if (QFile::exists(QDir(newFtbDefault).absoluteFilePath("ftblaunch.cfg")))
|
||||
{
|
||||
QLOG_INFO() << "Old FTB setup";
|
||||
ftbDefault = ftbDataDefault = oldFtbDefault;
|
||||
}
|
||||
else
|
||||
{
|
||||
QLOG_INFO() << "New FTB setup";
|
||||
ftbDefault = oldFtbDefault;
|
||||
ftbDataDefault = newFtbDefault;
|
||||
}
|
||||
#elif defined(Q_OS_MAC)
|
||||
QString ftbDefault = ftbDataDefault =
|
||||
PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
|
||||
#endif
|
||||
m_settings->registerSetting("FTBLauncherDataRoot", ftbDataDefault);
|
||||
m_settings->registerSetting("FTBLauncherRoot", ftbDefault);
|
||||
QLOG_INFO() << "FTB Launcher paths:"
|
||||
<< m_settings->get("FTBLauncherDataRoot").toString()
|
||||
<< "and"
|
||||
<< m_settings->get("FTBLauncherRoot").toString();
|
||||
|
||||
m_settings->registerSetting("FTBRoot");
|
||||
if (m_settings->get("FTBRoot").isNull())
|
||||
{
|
||||
QString ftbRoot;
|
||||
QFile f(QDir(m_settings->get("FTBLauncherRoot").toString())
|
||||
.absoluteFilePath("ftblaunch.cfg"));
|
||||
QLOG_INFO() << "Attempting to read" << f.fileName();
|
||||
if (f.open(QFile::ReadOnly))
|
||||
{
|
||||
const QString data = QString::fromLatin1(f.readAll());
|
||||
QRegularExpression exp("installPath=(.*)");
|
||||
ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
|
||||
#ifdef Q_OS_WIN32
|
||||
if (!ftbRoot.isEmpty())
|
||||
{
|
||||
if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
|
||||
{
|
||||
ftbRoot.remove(1, 1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (ftbRoot.isEmpty())
|
||||
{
|
||||
QLOG_INFO() << "Failed to get FTB root path";
|
||||
}
|
||||
else
|
||||
{
|
||||
QLOG_INFO() << "FTB is installed at" << ftbRoot;
|
||||
m_settings->set("FTBRoot", ftbRoot);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QLOG_WARN() << "Couldn't open" << f.fileName() << ":" << f.errorString();
|
||||
QLOG_WARN() << "This is perfectly normal if you don't have FTB installed";
|
||||
}
|
||||
}
|
||||
|
||||
// Folders
|
||||
m_settings->registerSetting("InstanceDir", "instances");
|
||||
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
||||
m_settings->registerSetting({"LWJGLDir", "LwjglDir"}, "lwjgl");
|
||||
m_settings->registerSetting("IconsDir", "icons");
|
||||
|
||||
// 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));
|
||||
// m_settings->registerSetting("StdOutColor", QColor(Qt::black));
|
||||
// m_settings->registerSetting("StdErrColor", QColor(Qt::red));
|
||||
|
||||
// Window Size
|
||||
m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
|
||||
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);
|
||||
m_settings->registerSetting("PermGen", 64);
|
||||
|
||||
// Java Settings
|
||||
m_settings->registerSetting("JavaPath", "");
|
||||
m_settings->registerSetting("LastHostname", "");
|
||||
m_settings->registerSetting("JavaDetectionHack", "");
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
|
||||
// Custom Commands
|
||||
m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
|
||||
m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
|
||||
|
||||
// The cat
|
||||
m_settings->registerSetting("TheCat", false);
|
||||
|
||||
m_settings->registerSetting("InstSortMode", "Name");
|
||||
m_settings->registerSetting("SelectedInstance", QString());
|
||||
|
||||
// Window state and geometry
|
||||
m_settings->registerSetting("MainWindowState", "");
|
||||
m_settings->registerSetting("MainWindowGeometry", "");
|
||||
|
||||
m_settings->registerSetting("ConsoleWindowState", "");
|
||||
m_settings->registerSetting("ConsoleWindowGeometry", "");
|
||||
|
||||
m_settings->registerSetting("SettingsGeometry", "");
|
||||
|
||||
m_settings->registerSetting("PagedGeometry", "");
|
||||
}
|
||||
|
||||
void MultiMC::initHttpMetaCache()
|
||||
{
|
||||
m_metacache.reset(new HttpMetaCache("metacache"));
|
||||
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
|
||||
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
|
||||
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)
|
||||
{
|
||||
m_icons.reset(new IconList);
|
||||
}
|
||||
return m_icons;
|
||||
}
|
||||
|
||||
std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
|
||||
{
|
||||
if (!m_lwjgllist)
|
||||
{
|
||||
m_lwjgllist.reset(new LWJGLVersionList());
|
||||
}
|
||||
return m_lwjgllist;
|
||||
}
|
||||
|
||||
std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
|
||||
{
|
||||
if (!m_forgelist)
|
||||
{
|
||||
m_forgelist.reset(new ForgeVersionList());
|
||||
}
|
||||
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)
|
||||
{
|
||||
m_minecraftlist.reset(new MinecraftVersionList());
|
||||
}
|
||||
return m_minecraftlist;
|
||||
}
|
||||
|
||||
std::shared_ptr<JavaVersionList> MultiMC::javalist()
|
||||
{
|
||||
if (!m_javalist)
|
||||
{
|
||||
m_javalist.reset(new JavaVersionList());
|
||||
}
|
||||
return m_javalist;
|
||||
}
|
||||
|
||||
std::shared_ptr<URNResolver> MultiMC::resolver()
|
||||
{
|
||||
if (!m_resolver)
|
||||
{
|
||||
m_resolver.reset(new URNResolver());
|
||||
}
|
||||
return m_resolver;
|
||||
}
|
||||
|
||||
|
||||
void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
|
||||
{
|
||||
// if we are going to update on exit, save the params now
|
||||
if(flags & OnExit)
|
||||
{
|
||||
m_updateOnExitPath = updateFilesDir;
|
||||
m_updateOnExitFlags = flags & ~OnExit;
|
||||
return;
|
||||
}
|
||||
// otherwise if there already were some params for on exit update, clear them and continue
|
||||
else if(m_updateOnExitPath.size())
|
||||
{
|
||||
m_updateOnExitFlags = None;
|
||||
m_updateOnExitPath.clear();
|
||||
}
|
||||
QLOG_INFO() << "Installing updates.";
|
||||
#ifdef WINDOWS
|
||||
QString finishCmd = MMC->applicationFilePath();
|
||||
QString updaterBinary = PathCombine(bin(), "updater.exe");
|
||||
#elif LINUX
|
||||
QString finishCmd = PathCombine(root(), "MultiMC");
|
||||
QString updaterBinary = PathCombine(bin(), "updater");
|
||||
#elif OSX
|
||||
QString finishCmd = MMC->applicationFilePath();
|
||||
QString updaterBinary = PathCombine(bin(), "updater");
|
||||
#else
|
||||
#error Unsupported operating system.
|
||||
#endif
|
||||
|
||||
QStringList args;
|
||||
// ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script
|
||||
// $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main
|
||||
args << "--install-dir" << root();
|
||||
args << "--package-dir" << updateFilesDir;
|
||||
args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
|
||||
args << "--wait" << QString::number(MMC->applicationPid());
|
||||
if(flags & DryRun)
|
||||
args << "--dry-run";
|
||||
if (flags & RestartOnFinish)
|
||||
{
|
||||
args << "--finish-cmd" << finishCmd;
|
||||
args << "--finish-dir" << data();
|
||||
}
|
||||
QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
|
||||
QFile::setPermissions(updaterBinary, (QFileDevice::Permission)0x7755);
|
||||
|
||||
if (!QProcess::startDetached(updaterBinary, args/*, root()*/))
|
||||
{
|
||||
QLOG_ERROR() << "Failed to start the updater process!";
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that we've started the updater, quit MultiMC.
|
||||
MMC->quit();
|
||||
}
|
||||
|
||||
void MultiMC::onExit()
|
||||
{
|
||||
if(m_updateOnExitPath.size())
|
||||
{
|
||||
installUpdates(m_updateOnExitPath, m_updateOnExitFlags);
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiMC::openJsonEditor(const QString &filename)
|
||||
{
|
||||
const QString file = QDir::current().absoluteFilePath(filename);
|
||||
if (m_settings->get("JsonEditor").toString().isEmpty())
|
||||
{
|
||||
return QDesktopServices::openUrl(QUrl::fromLocalFile(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
return QProcess::startDetached(m_settings->get("JsonEditor").toString(), QStringList()
|
||||
<< file);
|
||||
}
|
||||
}
|
||||
|
||||
#include "MultiMC.moc"
|
||||
32
README.md
32
README.md
@@ -1,17 +1,33 @@
|
||||

|
||||
|
||||
MultiMC 5 [](https://travis-ci.org/MultiMC/MultiMC5)
|
||||
MultiMC 5
|
||||
=========
|
||||
|
||||
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.
|
||||
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. Here are the current [features](https://github.com/MultiMC/MultiMC5/wiki#features) of MultiMC.
|
||||
|
||||
## Building
|
||||
Check [BUILD.md](BUILD.md) for build instructions.
|
||||
|
||||
## Contributing
|
||||
The repository is currently managed by @peterix and @drayshak - we're the ones likely to review pull requests. If you'd like to contribute to the project please talk to us on IRC (Esper/#MultiMC) first! This helps us organise ideas and keep in contact with you, and we're unlikely to accept anything blindly.
|
||||
## Development
|
||||
The project uses C++ and Qt5 as the language and base framework. This might seem odd in the Minecraft community, but allows using 25MB of RAM, where other tools use an excessive amount of resources for no reason.
|
||||
|
||||
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted, but there are issues with it on Windows. If you have trouble setting it up, check [.clang-format](.clang-format) manually. We don't accept pull requests with poor formatting. If you have questions, talk to us on IRC (Esper/#MultiMC) _before_ submitting a pull request.
|
||||
We can do more, with less, on worse hardware and leave more resources for the game while keeping the launcher running and providing extra features.
|
||||
|
||||
If you want to contribute, either talk to us on [IRC](http://webchat.esper.net/?nick=&channels=MultiMC)(esper.net/#MultiMC), or pick up one of the issues that are ready for development: [](http://waffle.io/MultiMC/MultiMC5)
|
||||
|
||||
### Building
|
||||
If you want to build MultiMC yourself, check [BUILD.md](BUILD.md) for build instructions.
|
||||
|
||||
The ci server is running at [ci.multimc.org](http://ci.multimc.org), where you can watch the builds happen in (or very close to) real time. It can also serve as a nice reference on how to set up the build environment on your end.
|
||||
|
||||
According to travis.ci, the builds are currently [](https://travis-ci.org/MultiMC/MultiMC5)
|
||||
|
||||
### Code formatting
|
||||
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted.
|
||||
|
||||
|
||||
## Translations
|
||||
Translations can be done either directly in the [translations repository](https://github.com/MultiMC/MultiMC5) or using our [translation server](http://translate.multimc.org). For more details, see: [Translating-MultiMC](https://github.com/MultiMC/MultiMC5/wiki/Translating-MultiMC).
|
||||
|
||||
Currently, MultiMC is [](http://translate.multimc.org/engage/multimc/?utm_source=widget)
|
||||
|
||||
## Forking/Redistributing
|
||||
We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.
|
||||
@@ -22,7 +38,7 @@ Apache covers reasonable use for the name - a mention of the project's origins i
|
||||
|
||||
|
||||
## License
|
||||
Copyright © 2013 MultiMC Contributors
|
||||
Copyright © 2013-2015 MultiMC Contributors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// CAUTION:
|
||||
// This file contains all manner of hackery and insanity.
|
||||
// I will not be responsible for any loss of sanity due to reading this code.
|
||||
// Here be dragons!
|
||||
|
||||
#include "WinBacktrace.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef __i386__
|
||||
#error WinBacktrace is only supported on x86 architectures.
|
||||
#endif
|
||||
|
||||
// We need to do some crazy shit to walk through the stack.
|
||||
// Windows unwinds the stack when an exception is thrown, so we
|
||||
// need to examine the EXCEPTION_POINTERS's CONTEXT.
|
||||
size_t getBacktrace(StackFrame *stack, size_t size, CONTEXT ctx)
|
||||
{
|
||||
// Written using information and a bit of pseudocode from
|
||||
// http://www.eptacom.net/pubblicazioni/pub_eng/except.html
|
||||
// This is probably one of the most horrifying things I've ever written.
|
||||
|
||||
// This tracks whether the current EBP is valid.
|
||||
// When an invalid EBP is encountered, we stop walking the stack.
|
||||
bool validEBP = true;
|
||||
DWORD ebp = ctx.Ebp; // The current EBP (Extended Base Pointer)
|
||||
DWORD eip = ctx.Eip;
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
if (ebp & 3)
|
||||
validEBP = false;
|
||||
// FIXME: This function is obsolete, according to MSDN.
|
||||
else if (IsBadReadPtr((void*) ebp, 8))
|
||||
validEBP = false;
|
||||
|
||||
if (!validEBP) break;
|
||||
|
||||
// Find the caller.
|
||||
// On the first iteration, the caller is whatever EIP points to.
|
||||
// On successive iterations, the caller is the byte after EBP.
|
||||
BYTE* caller = !i ? (BYTE*)eip : *((BYTE**) ebp + 1);
|
||||
// The first ebp is the EBP from the CONTEXT.
|
||||
// On successive iterations, the EBP is the DWORD that the previous EBP points to.
|
||||
ebp = !i ? ebp : *(DWORD*)ebp;
|
||||
|
||||
// Find the caller's module.
|
||||
// We'll use VirtualQuery to get information about the caller's address.
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
VirtualQuery(caller, &mbi, sizeof(mbi));
|
||||
|
||||
// We can get the instance handle from the allocation base.
|
||||
HINSTANCE hInst = (HINSTANCE)mbi.AllocationBase;
|
||||
|
||||
// If the handle is 0, then the EBP is invalid.
|
||||
if (hInst == 0) validEBP = false;
|
||||
// Otherwise, dump info about the caller.
|
||||
else stack[i].address = (void*)caller;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* 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 <windows.h>
|
||||
|
||||
#ifndef SF_STR_LEN
|
||||
// The max length of all strings in the StackFrame struct.
|
||||
// Because it must be stack allocated, this must be known at compile time.
|
||||
// Stuff longer than this will be truncated.
|
||||
// Defaults to 4096 (4kB)
|
||||
#define SF_STR_LEN 4096
|
||||
#endif
|
||||
|
||||
// Data structure for holding information about a stack frame.
|
||||
// There's some more hackery in here so it can be allocated on the stack.
|
||||
struct StackFrame
|
||||
{
|
||||
// The address of this stack frame.
|
||||
void* address;
|
||||
|
||||
// The name of the function at this address.
|
||||
char funcName[SF_STR_LEN];
|
||||
};
|
||||
|
||||
// This function walks through the given CONTEXT structure, extracting a
|
||||
// backtrace from it.
|
||||
// The backtrace will be put into the array given by the `stack` argument
|
||||
// with a maximum length of `size`.
|
||||
// This function returns the size of the backtrace retrieved.
|
||||
size_t getBacktrace(StackFrame* stack, size_t size, CONTEXT ctx);
|
||||
59
application/BuildConfig.cpp.in
Normal file
59
application/BuildConfig.cpp.in
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "BuildConfig.h"
|
||||
#include <QObject>
|
||||
|
||||
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@;
|
||||
|
||||
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@";
|
||||
|
||||
GIT_COMMIT = "@MultiMC_GIT_COMMIT@";
|
||||
GIT_REFSPEC = "@MultiMC_GIT_REFSPEC@";
|
||||
if(GIT_REFSPEC.startsWith("refs/heads/") && !CHANLIST_URL.isEmpty() && VERSION_BUILD >= 0)
|
||||
{
|
||||
VERSION_CHANNEL = GIT_REFSPEC;
|
||||
VERSION_CHANNEL.remove("refs/heads/");
|
||||
UPDATER_ENABLED = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
VERSION_CHANNEL = QObject::tr("custom");
|
||||
}
|
||||
|
||||
VERSION_STR = "@MultiMC_VERSION_STRING@";
|
||||
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
|
||||
PASTE_EE_KEY = "@MultiMC_PASTE_EE_API_KEY@";
|
||||
}
|
||||
|
||||
QString Config::printableVersionString() const
|
||||
{
|
||||
QString vstr = QString("%1.%2").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR));
|
||||
|
||||
// if this is a hotfix release, append that
|
||||
if (VERSION_HOTFIX > 0)
|
||||
{
|
||||
vstr += "." + QString::number(VERSION_HOTFIX);
|
||||
}
|
||||
|
||||
// If the build is not a main release, append the channel
|
||||
if(VERSION_CHANNEL != "stable")
|
||||
{
|
||||
vstr += "-" + VERSION_CHANNEL;
|
||||
}
|
||||
|
||||
// if a build number is set, also add it to the end
|
||||
if(VERSION_BUILD >= 0)
|
||||
{
|
||||
vstr += "-" + QString::number(VERSION_BUILD);
|
||||
}
|
||||
return vstr;
|
||||
}
|
||||
@@ -16,24 +16,6 @@ public:
|
||||
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
|
||||
@@ -41,6 +23,8 @@ public:
|
||||
*/
|
||||
QString VERSION_CHANNEL;
|
||||
|
||||
bool UPDATER_ENABLED = false;
|
||||
|
||||
/// A short string identifying this build's platform. For example, "lin64" or "win32".
|
||||
QString BUILD_PLATFORM;
|
||||
|
||||
@@ -53,38 +37,31 @@ public:
|
||||
/// 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
|
||||
/// The git commit hash of this build
|
||||
QString GIT_COMMIT;
|
||||
const char* GIT_COMMIT_CSTR;
|
||||
|
||||
/// The git refspec of this build
|
||||
QString GIT_REFSPEC;
|
||||
|
||||
/// This is printed on start to standard output
|
||||
QString VERSION_STR;
|
||||
|
||||
/// Version string as a char string. Used by the crash handling system to avoid touching heap memory.
|
||||
const char* VERSION_CSTR;
|
||||
|
||||
/**
|
||||
* This is used to fetch the news RSS feed.
|
||||
* It defaults in CMakeLists.txt to "http://multimc.org/rss.xml"
|
||||
*/
|
||||
QString NEWS_RSS_URL;
|
||||
|
||||
/**
|
||||
* API key you can get from paste.ee when you register an account
|
||||
*/
|
||||
QString PASTE_EE_KEY;
|
||||
|
||||
/**
|
||||
* \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;
|
||||
468
application/CMakeLists.txt
Normal file
468
application/CMakeLists.txt
Normal file
@@ -0,0 +1,468 @@
|
||||
project(application)
|
||||
|
||||
######## Set URLs ########
|
||||
set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
|
||||
|
||||
######## Set version numbers ########
|
||||
set(MultiMC_VERSION_MAJOR 0)
|
||||
set(MultiMC_VERSION_MINOR 4)
|
||||
set(MultiMC_VERSION_HOTFIX 11)
|
||||
|
||||
# Build number
|
||||
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
|
||||
|
||||
# Build platform.
|
||||
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
|
||||
|
||||
# Channel list URL
|
||||
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
|
||||
|
||||
# Notification URL
|
||||
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
|
||||
|
||||
# paste.ee API key
|
||||
set(MultiMC_PASTE_EE_API_KEY "" CACHE STRING "API key you can get from paste.ee when you register an account")
|
||||
|
||||
#### Check the current Git commit and branch
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(MultiMC_GIT_REFSPEC MultiMC_GIT_COMMIT)
|
||||
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
|
||||
message(STATUS "Git refspec: ${MultiMC_GIT_REFSPEC}")
|
||||
|
||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
|
||||
if(MultiMC_VERSION_HOTFIX GREATER 0)
|
||||
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_RELEASE_VERSION_NAME}.${MultiMC_VERSION_HOTFIX}")
|
||||
endif()
|
||||
|
||||
#### Custom target to just print the version.
|
||||
add_custom_target(version echo "Version: ${MultiMC_RELEASE_VERSION_NAME}")
|
||||
|
||||
######## Configure header ########
|
||||
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
|
||||
|
||||
######## Packaging/install paths setup ########
|
||||
|
||||
if(UNIX AND APPLE)
|
||||
set(BINARY_DEST_DIR MultiMC.app/Contents/MacOS)
|
||||
set(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
|
||||
set(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
|
||||
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
|
||||
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
|
||||
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015 MultiMC Contributors")
|
||||
elseif(UNIX)
|
||||
set(BINARY_DEST_DIR bin)
|
||||
set(PLUGIN_DEST_DIR plugins)
|
||||
set(QTCONF_DEST_DIR .)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
|
||||
elseif(WIN32)
|
||||
set(BINARY_DEST_DIR .)
|
||||
set(PLUGIN_DEST_DIR .)
|
||||
set(QTCONF_DEST_DIR .)
|
||||
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
|
||||
endif()
|
||||
|
||||
# directories to look for dependencies
|
||||
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
||||
|
||||
################################ FILES ################################
|
||||
|
||||
######## Sources and headers ########
|
||||
SET(MULTIMC_SOURCES
|
||||
# Application base
|
||||
main.cpp
|
||||
MultiMC.h
|
||||
MultiMC.cpp
|
||||
BuildConfig.h
|
||||
${PROJECT_BINARY_DIR}/BuildConfig.cpp
|
||||
|
||||
# Resource handlers and transformers
|
||||
handlers/IconResourceHandler.cpp
|
||||
handlers/IconResourceHandler.h
|
||||
handlers/WebResourceHandler.cpp
|
||||
handlers/WebResourceHandler.h
|
||||
|
||||
# GUI - general utilities
|
||||
GuiUtil.h
|
||||
GuiUtil.cpp
|
||||
ColumnResizer.h
|
||||
ColumnResizer.cpp
|
||||
InstanceProxyModel.h
|
||||
InstanceProxyModel.cpp
|
||||
VersionProxyModel.h
|
||||
VersionProxyModel.cpp
|
||||
ColorCache.h
|
||||
ColorCache.cpp
|
||||
|
||||
# GUI - windows
|
||||
MainWindow.h
|
||||
MainWindow.cpp
|
||||
ConsoleWindow.h
|
||||
ConsoleWindow.cpp
|
||||
|
||||
# GUI - settings-specific wrappers for paged dialog
|
||||
SettingsUI.h
|
||||
SettingsUI.cpp
|
||||
|
||||
# Processes
|
||||
LaunchInteraction.h
|
||||
LaunchInteraction.cpp
|
||||
|
||||
# page provider for instances
|
||||
InstancePageProvider.h
|
||||
InstancePageProvider.cpp
|
||||
|
||||
# Common java checking UI
|
||||
JavaCommon.h
|
||||
JavaCommon.cpp
|
||||
|
||||
# GUI - page dialog pages
|
||||
pages/BasePage.h
|
||||
pages/BasePageContainer.h
|
||||
pages/VersionPage.cpp
|
||||
pages/VersionPage.h
|
||||
pages/TexturePackPage.h
|
||||
pages/ResourcePackPage.h
|
||||
pages/ModFolderPage.cpp
|
||||
pages/ModFolderPage.h
|
||||
pages/NotesPage.cpp
|
||||
pages/NotesPage.h
|
||||
pages/LogPage.cpp
|
||||
pages/LogPage.h
|
||||
pages/InstanceSettingsPage.cpp
|
||||
pages/InstanceSettingsPage.h
|
||||
pages/ScreenshotsPage.cpp
|
||||
pages/ScreenshotsPage.h
|
||||
pages/OtherLogsPage.cpp
|
||||
pages/OtherLogsPage.h
|
||||
pages/LegacyJarModPage.cpp
|
||||
pages/LegacyJarModPage.h
|
||||
pages/LegacyUpgradePage.cpp
|
||||
pages/LegacyUpgradePage.h
|
||||
pages/WorldListPage.cpp
|
||||
pages/WorldListPage.h
|
||||
|
||||
# GUI - global settings pages
|
||||
pages/global/AccountListPage.cpp
|
||||
pages/global/AccountListPage.h
|
||||
pages/global/ExternalToolsPage.cpp
|
||||
pages/global/ExternalToolsPage.h
|
||||
pages/global/JavaPage.cpp
|
||||
pages/global/JavaPage.h
|
||||
pages/global/MinecraftPage.cpp
|
||||
pages/global/MinecraftPage.h
|
||||
pages/global/MultiMCPage.cpp
|
||||
pages/global/MultiMCPage.h
|
||||
pages/global/ProxyPage.cpp
|
||||
pages/global/ProxyPage.h
|
||||
pages/global/PasteEEPage.cpp
|
||||
pages/global/PasteEEPage.h
|
||||
|
||||
# GUI - dialogs
|
||||
dialogs/AboutDialog.cpp
|
||||
dialogs/AboutDialog.h
|
||||
dialogs/AccountSelectDialog.cpp
|
||||
dialogs/AccountSelectDialog.h
|
||||
dialogs/CopyInstanceDialog.cpp
|
||||
dialogs/CopyInstanceDialog.h
|
||||
dialogs/CustomMessageBox.cpp
|
||||
dialogs/CustomMessageBox.h
|
||||
dialogs/EditAccountDialog.cpp
|
||||
dialogs/EditAccountDialog.h
|
||||
dialogs/ExportInstanceDialog.cpp
|
||||
dialogs/ExportInstanceDialog.h
|
||||
dialogs/IconPickerDialog.cpp
|
||||
dialogs/IconPickerDialog.h
|
||||
dialogs/LoginDialog.cpp
|
||||
dialogs/LoginDialog.h
|
||||
dialogs/ModEditDialogCommon.cpp
|
||||
dialogs/ModEditDialogCommon.h
|
||||
dialogs/NewInstanceDialog.cpp
|
||||
dialogs/NewInstanceDialog.h
|
||||
dialogs/NotificationDialog.cpp
|
||||
dialogs/NotificationDialog.h
|
||||
pagedialog/PageDialog.cpp
|
||||
pagedialog/PageDialog.h
|
||||
dialogs/ProgressDialog.cpp
|
||||
dialogs/ProgressDialog.h
|
||||
dialogs/UpdateDialog.cpp
|
||||
dialogs/UpdateDialog.h
|
||||
dialogs/VersionSelectDialog.cpp
|
||||
dialogs/VersionSelectDialog.h
|
||||
|
||||
|
||||
# GUI - widgets
|
||||
widgets/Common.cpp
|
||||
widgets/Common.h
|
||||
widgets/FocusLineEdit.cpp
|
||||
widgets/FocusLineEdit.h
|
||||
widgets/IconLabel.cpp
|
||||
widgets/IconLabel.h
|
||||
widgets/LabeledToolButton.cpp
|
||||
widgets/LabeledToolButton.h
|
||||
widgets/LineSeparator.cpp
|
||||
widgets/LineSeparator.h
|
||||
widgets/MCModInfoFrame.cpp
|
||||
widgets/MCModInfoFrame.h
|
||||
widgets/ModListView.cpp
|
||||
widgets/ModListView.h
|
||||
widgets/PageContainer.cpp
|
||||
widgets/PageContainer.h
|
||||
widgets/PageContainer_p.h
|
||||
widgets/ServerStatus.cpp
|
||||
widgets/ServerStatus.h
|
||||
widgets/VersionListView.cpp
|
||||
widgets/VersionListView.h
|
||||
widgets/ProgressWidget.h
|
||||
widgets/ProgressWidget.cpp
|
||||
|
||||
|
||||
# GUI - instance group view
|
||||
groupview/GroupedProxyModel.cpp
|
||||
groupview/GroupedProxyModel.h
|
||||
groupview/GroupView.cpp
|
||||
groupview/GroupView.h
|
||||
groupview/InstanceDelegate.cpp
|
||||
groupview/InstanceDelegate.h
|
||||
groupview/VisualGroup.cpp
|
||||
groupview/VisualGroup.h
|
||||
)
|
||||
|
||||
######## UIs ########
|
||||
SET(MULTIMC_UIS
|
||||
# Option pages
|
||||
pages/VersionPage.ui
|
||||
pages/ModFolderPage.ui
|
||||
pages/LogPage.ui
|
||||
pages/InstanceSettingsPage.ui
|
||||
pages/NotesPage.ui
|
||||
pages/ScreenshotsPage.ui
|
||||
pages/OtherLogsPage.ui
|
||||
pages/LegacyJarModPage.ui
|
||||
pages/LegacyUpgradePage.ui
|
||||
pages/WorldListPage.ui
|
||||
|
||||
# Global settings pages
|
||||
pages/global/AccountListPage.ui
|
||||
pages/global/ExternalToolsPage.ui
|
||||
pages/global/JavaPage.ui
|
||||
pages/global/MinecraftPage.ui
|
||||
pages/global/MultiMCPage.ui
|
||||
pages/global/ProxyPage.ui
|
||||
pages/global/PasteEEPage.ui
|
||||
|
||||
# Dialogs
|
||||
dialogs/CopyInstanceDialog.ui
|
||||
dialogs/NewInstanceDialog.ui
|
||||
dialogs/AboutDialog.ui
|
||||
dialogs/VersionSelectDialog.ui
|
||||
dialogs/ProgressDialog.ui
|
||||
dialogs/IconPickerDialog.ui
|
||||
dialogs/AccountSelectDialog.ui
|
||||
dialogs/EditAccountDialog.ui
|
||||
dialogs/ExportInstanceDialog.ui
|
||||
dialogs/LoginDialog.ui
|
||||
dialogs/UpdateDialog.ui
|
||||
dialogs/NotificationDialog.ui
|
||||
|
||||
# Widgets/other
|
||||
widgets/MCModInfoFrame.ui
|
||||
)
|
||||
|
||||
set(MULTIMC_QRCS
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/pe_dark/pe_dark.qrc
|
||||
resources/pe_light/pe_light.qrc
|
||||
resources/pe_colored/pe_colored.qrc
|
||||
resources/pe_blue/pe_blue.qrc
|
||||
resources/OSX/OSX.qrc
|
||||
resources/iOS/iOS.qrc
|
||||
resources/instances/instances.qrc
|
||||
resources/versions/versions.qrc
|
||||
resources/certs/certs.qrc
|
||||
)
|
||||
|
||||
set(MultiMC_OSX_source
|
||||
CertWorkaround.cpp
|
||||
CertWorkaround.h
|
||||
)
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND MULTIMC_SOURCES ${MultiMC_OSX_source})
|
||||
endif()
|
||||
|
||||
######## Windows resource files ########
|
||||
if(WIN32)
|
||||
set(MULTIMC_RCS resources/multimc.rc)
|
||||
endif()
|
||||
|
||||
# Qt 5 stuff
|
||||
qt5_wrap_ui(MULTIMC_UI ${MULTIMC_UIS})
|
||||
qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
|
||||
|
||||
# Add executable
|
||||
add_executable(MultiMC MACOSX_BUNDLE WIN32 ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES} ${MULTIMC_RCS})
|
||||
target_link_libraries(MultiMC MultiMC_logic xz-embedded unpack200 iconfix ${QUAZIP_LIBRARIES} hoedown rainbow)
|
||||
|
||||
if(APPLE)
|
||||
find_library(OSX_CORE_FOUNDATION CoreFoundation)
|
||||
find_library(OSX_SECURITY Security)
|
||||
if (NOT OSX_CORE_FOUNDATION OR NOT OSX_SECURITY)
|
||||
message(FATAL_ERROR "OSX framerworks not found: CoreFoundation, Security")
|
||||
endif()
|
||||
|
||||
target_link_libraries(MultiMC ${OSX_CORE_FOUNDATION} ${OSX_SECURITY})
|
||||
endif()
|
||||
|
||||
################################ INSTALLATION AND PACKAGING ################################
|
||||
|
||||
######## Install ########
|
||||
|
||||
#### Executable ####
|
||||
if(APPLE AND UNIX) ## OSX
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
|
||||
)
|
||||
|
||||
elseif(UNIX) ## LINUX and similar
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION bin COMPONENT Runtime
|
||||
)
|
||||
install(PROGRAMS package/linux/MultiMC DESTINATION .)
|
||||
|
||||
elseif(WIN32) ## WINDOWS
|
||||
install(TARGETS MultiMC
|
||||
BUNDLE DESTINATION . COMPONENT Runtime
|
||||
LIBRARY DESTINATION . COMPONENT Runtime
|
||||
RUNTIME DESTINATION . COMPONENT Runtime
|
||||
)
|
||||
endif()
|
||||
|
||||
#### Java bits ####
|
||||
install_jar(JavaCheck "${BINARY_DEST_DIR}/jars")
|
||||
install_jar(NewLaunch "${BINARY_DEST_DIR}/jars")
|
||||
|
||||
#### Dist package logic ####
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng" EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
)
|
||||
else()
|
||||
# Image formats
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "tga|tiff|mng" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
# Icon engines
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "fontawesome" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
# Platform plugins
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "minimal|linuxfb|offscreen" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
if(APPLE)
|
||||
# Accessible plugin to make buttons look decent on osx
|
||||
if("${Qt5Core_VERSION_STRING}" VERSION_LESS "5.4.0")
|
||||
message(STATUS "Packaging the Qt accessible plugins because we're on Qt ${Qt5Core_VERSION_STRING}")
|
||||
install(
|
||||
DIRECTORY "${QT_PLUGINS_DIR}/accessible"
|
||||
DESTINATION ${PLUGIN_DEST_DIR}
|
||||
COMPONENT Runtime
|
||||
REGEX "quick" EXCLUDE
|
||||
REGEX "d\\." EXCLUDE
|
||||
REGEX "_debug\\." EXCLUDE
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# qtconf
|
||||
install(
|
||||
CODE "
|
||||
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
|
||||
"
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# ICNS file for OS X
|
||||
if(APPLE)
|
||||
install(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
|
||||
@ONLY
|
||||
)
|
||||
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
|
||||
|
||||
|
||||
|
||||
######## Package ########
|
||||
|
||||
# Package with CPack
|
||||
if(UNIX)
|
||||
if(APPLE)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
else()
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
endif()
|
||||
elseif(WIN32)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
endif()
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||
|
||||
set(CPACK_PACKAGE_NAME "MultiMC 5")
|
||||
set(CPACK_PACKAGE_VENDOR "")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MultiMC - Minecraft launcher and management tool.")
|
||||
set(CPACK_PACKAGE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${MultiMC_VERSION_MAJOR})
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${MultiMC_VERSION_MINOR})
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${MultiMC_VERSION_REV})
|
||||
|
||||
set(CPACK_PACKAGE_FILE_NAME "MultiMC")
|
||||
|
||||
include(CPack)
|
||||
120
application/CertWorkaround.cpp
Normal file
120
application/CertWorkaround.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSslSocket>
|
||||
#include <QDebug>
|
||||
|
||||
#include <Security/Security.h>
|
||||
|
||||
// CFRelease will crash if passed NULL
|
||||
#define SafeCFRelease(ref) \
|
||||
if (ref) \
|
||||
CFRelease(ref);
|
||||
|
||||
/*!
|
||||
* \brief LoadCertificatesFromKeyChain Load all certificates from the KeyChain path provided
|
||||
* and return them as
|
||||
* QSslCertificates.
|
||||
* \param keyChainPath The KeyChain path. Pass an empty string to use the
|
||||
* user's keychain.
|
||||
* \return A list of new QSslCertificates generated from the
|
||||
* KeyChain DER data.
|
||||
*/
|
||||
static QList<QSslCertificate>
|
||||
LoadCertificatesFromKeyChain(const std::string &keyChainPath = std::string())
|
||||
{
|
||||
QList<QSslCertificate> qtCerts;
|
||||
|
||||
SecKeychainRef certsKeyChain = NULL;
|
||||
SecKeychainSearchRef searchItem = NULL;
|
||||
SecKeychainItemRef itemRef = NULL;
|
||||
CSSM_DATA certData = {0, 0};
|
||||
|
||||
try
|
||||
{
|
||||
OSStatus status = errSecSuccess;
|
||||
|
||||
// if a keychain path was provided, obtain a pointer
|
||||
if (!keyChainPath.empty())
|
||||
{
|
||||
status = SecKeychainOpen(keyChainPath.c_str(), &certsKeyChain);
|
||||
if (status != errSecSuccess)
|
||||
{
|
||||
throw status;
|
||||
}
|
||||
}
|
||||
|
||||
// build a search query reference for certificates
|
||||
status = SecKeychainSearchCreateFromAttributes(certsKeyChain, kSecCertificateItemClass,
|
||||
NULL, &searchItem);
|
||||
if (status != errSecSuccess)
|
||||
{
|
||||
throw status;
|
||||
}
|
||||
|
||||
// loop through the certificates
|
||||
while (SecKeychainSearchCopyNext(searchItem, &itemRef) != errSecItemNotFound)
|
||||
{
|
||||
// copy the KeyChain item data into a CSSM_DATA struct - this will be the certs Der
|
||||
// data
|
||||
status = SecKeychainItemCopyContent(itemRef, NULL, NULL,
|
||||
reinterpret_cast<UInt32 *>(&certData.Length),
|
||||
reinterpret_cast<void **>(&certData.Data));
|
||||
|
||||
if (status != errSecSuccess)
|
||||
{
|
||||
throw status;
|
||||
}
|
||||
|
||||
// create a Qt byte array from the data - the data is NOT copied
|
||||
const QByteArray byteArray = QByteArray::fromRawData(
|
||||
reinterpret_cast<const char *>(certData.Data), certData.Length);
|
||||
|
||||
// create a Qt certificate from the data and add it to the list
|
||||
QSslCertificate qtCert(byteArray, QSsl::Der);
|
||||
qDebug() << "COMMON NAME: "
|
||||
<< qtCert.issuerInfo(QSslCertificate::CommonName).join('\n')
|
||||
<< " ORG NAME: "
|
||||
<< qtCert.issuerInfo(QSslCertificate::Organization).join('\n');
|
||||
|
||||
qtCerts << qtCert;
|
||||
}
|
||||
}
|
||||
catch (OSStatus status)
|
||||
{
|
||||
CFStringRef errorMessage = SecCopyErrorMessageString(status, NULL);
|
||||
std::cerr << CFStringGetCStringPtr(errorMessage, kCFStringEncodingMacRoman)
|
||||
<< std::endl;
|
||||
SafeCFRelease(errorMessage);
|
||||
}
|
||||
|
||||
SecKeychainItemFreeContent(NULL, certData.Data);
|
||||
SafeCFRelease(itemRef);
|
||||
SafeCFRelease(searchItem);
|
||||
SafeCFRelease(certsKeyChain);
|
||||
|
||||
return qtCerts;
|
||||
}
|
||||
|
||||
void RebuildQtCertificates()
|
||||
{
|
||||
const QList<QSslCertificate> existingCerts = QSslSocket::defaultCaCertificates();
|
||||
QList<QSslCertificate> certs = LoadCertificatesFromKeyChain();
|
||||
certs += LoadCertificatesFromKeyChain(
|
||||
"/System/Library/Keychains/SystemRootCertificates.keychain");
|
||||
|
||||
Q_FOREACH (const QSslCertificate qtCert, certs)
|
||||
{
|
||||
if (!existingCerts.contains(qtCert))
|
||||
{
|
||||
qDebug() << "cert not known to Qt - adding";
|
||||
qDebug() << "COMMON NAME: "
|
||||
<< qtCert.issuerInfo(QSslCertificate::CommonName).join('\n')
|
||||
<< " ORG NAME: "
|
||||
<< qtCert.issuerInfo(QSslCertificate::Organization).join('\n');
|
||||
|
||||
QSslSocket::addDefaultCaCertificate(qtCert);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
application/CertWorkaround.h
Normal file
3
application/CertWorkaround.h
Normal file
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
void RebuildQtCertificates();
|
||||
35
application/ColorCache.cpp
Normal file
35
application/ColorCache.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "ColorCache.h"
|
||||
|
||||
|
||||
/**
|
||||
* Blend the color with the front color, adapting to the back color
|
||||
*/
|
||||
QColor ColorCache::blend(QColor color)
|
||||
{
|
||||
if (Rainbow::luma(m_front) > Rainbow::luma(m_back))
|
||||
{
|
||||
// for dark color schemes, produce a fitting color first
|
||||
color = Rainbow::tint(m_front, color, 0.5);
|
||||
}
|
||||
// adapt contrast
|
||||
return Rainbow::mix(m_front, color, m_bias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blend the color with the back color
|
||||
*/
|
||||
QColor ColorCache::blendBackground(QColor color)
|
||||
{
|
||||
// adapt contrast
|
||||
return Rainbow::mix(m_back, color, m_bias);
|
||||
}
|
||||
|
||||
void ColorCache::recolorAll()
|
||||
{
|
||||
auto iter = m_colors.begin();
|
||||
while(iter != m_colors.end())
|
||||
{
|
||||
iter->front = blend(iter->original);
|
||||
iter->back = blendBackground(iter->original);
|
||||
}
|
||||
}
|
||||
119
application/ColorCache.h
Normal file
119
application/ColorCache.h
Normal file
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
#include <QtGui/QColor>
|
||||
#include <rainbow.h>
|
||||
#include <launch/MessageLevel.h>
|
||||
#include <QMap>
|
||||
|
||||
class ColorCache
|
||||
{
|
||||
public:
|
||||
ColorCache(QColor front, QColor back, qreal bias)
|
||||
{
|
||||
m_front = front;
|
||||
m_back = back;
|
||||
m_bias = bias;
|
||||
};
|
||||
|
||||
void addColor(int key, QColor color)
|
||||
{
|
||||
m_colors[key] = {color, blend(color), blendBackground(color)};
|
||||
}
|
||||
|
||||
void setForeground(QColor front)
|
||||
{
|
||||
if(m_front != front)
|
||||
{
|
||||
m_front = front;
|
||||
recolorAll();
|
||||
}
|
||||
}
|
||||
|
||||
void setBackground(QColor back)
|
||||
{
|
||||
if(m_back != back)
|
||||
{
|
||||
m_back = back;
|
||||
recolorAll();
|
||||
}
|
||||
}
|
||||
|
||||
QColor getFront(int key)
|
||||
{
|
||||
auto iter = m_colors.find(key);
|
||||
if(iter == m_colors.end())
|
||||
{
|
||||
return QColor();
|
||||
}
|
||||
return (*iter).front;
|
||||
}
|
||||
|
||||
QColor getBack(int key)
|
||||
{
|
||||
auto iter = m_colors.find(key);
|
||||
if(iter == m_colors.end())
|
||||
{
|
||||
return QColor();
|
||||
}
|
||||
return (*iter).back;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blend the color with the front color, adapting to the back color
|
||||
*/
|
||||
QColor blend(QColor color);
|
||||
|
||||
/**
|
||||
* Blend the color with the back color
|
||||
*/
|
||||
QColor blendBackground(QColor color);
|
||||
|
||||
protected:
|
||||
void recolorAll();
|
||||
|
||||
protected:
|
||||
struct ColorEntry
|
||||
{
|
||||
QColor original;
|
||||
QColor front;
|
||||
QColor back;
|
||||
};
|
||||
|
||||
protected:
|
||||
qreal m_bias;
|
||||
QColor m_front;
|
||||
QColor m_back;
|
||||
QMap<int, ColorEntry> m_colors;
|
||||
};
|
||||
|
||||
class LogColorCache : public ColorCache
|
||||
{
|
||||
public:
|
||||
LogColorCache(QColor front, QColor back)
|
||||
: ColorCache(front, back, 1.0)
|
||||
{
|
||||
addColor((int)MessageLevel::MultiMC, QColor("purple"));
|
||||
addColor((int)MessageLevel::Debug, QColor("green"));
|
||||
addColor((int)MessageLevel::Warning, QColor("orange"));
|
||||
addColor((int)MessageLevel::Error, QColor("red"));
|
||||
addColor((int)MessageLevel::Fatal, QColor("red"));
|
||||
addColor((int)MessageLevel::Message, front);
|
||||
}
|
||||
|
||||
QColor getFront(MessageLevel::Enum level)
|
||||
{
|
||||
if(!m_colors.contains((int) level))
|
||||
{
|
||||
return ColorCache::getFront((int)MessageLevel::Message);
|
||||
}
|
||||
return ColorCache::getFront((int)level);
|
||||
}
|
||||
|
||||
QColor getBack(MessageLevel::Enum level)
|
||||
{
|
||||
if(level == MessageLevel::Fatal)
|
||||
{
|
||||
return QColor(Qt::black);
|
||||
}
|
||||
return QColor(Qt::transparent);
|
||||
}
|
||||
};
|
||||
202
application/ColumnResizer.cpp
Normal file
202
application/ColumnResizer.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||
* License: LGPL v2.1 or later (see COPYING)
|
||||
*/
|
||||
#include "ColumnResizer.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QEvent>
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
class FormLayoutWidgetItem : public QWidgetItem
|
||||
{
|
||||
public:
|
||||
FormLayoutWidgetItem(QWidget* widget, QFormLayout* formLayout, QFormLayout::ItemRole itemRole)
|
||||
: QWidgetItem(widget)
|
||||
, m_width(-1)
|
||||
, m_formLayout(formLayout)
|
||||
, m_itemRole(itemRole)
|
||||
{}
|
||||
|
||||
QSize sizeHint() const
|
||||
{
|
||||
QSize size = QWidgetItem::sizeHint();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize minimumSize() const
|
||||
{
|
||||
QSize size = QWidgetItem::minimumSize();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
QSize maximumSize() const
|
||||
{
|
||||
QSize size = QWidgetItem::maximumSize();
|
||||
if (m_width != -1) {
|
||||
size.setWidth(m_width);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
void setWidth(int width)
|
||||
{
|
||||
if (width != m_width) {
|
||||
m_width = width;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void setGeometry(const QRect& _rect)
|
||||
{
|
||||
QRect rect = _rect;
|
||||
int width = widget()->sizeHint().width();
|
||||
if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
|
||||
rect.setLeft(rect.right() - width);
|
||||
}
|
||||
QWidgetItem::setGeometry(rect);
|
||||
}
|
||||
|
||||
QFormLayout* formLayout() const
|
||||
{
|
||||
return m_formLayout;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_width;
|
||||
QFormLayout* m_formLayout;
|
||||
QFormLayout::ItemRole m_itemRole;
|
||||
};
|
||||
|
||||
typedef QPair<QGridLayout*, int> GridColumnInfo;
|
||||
|
||||
class ColumnResizerPrivate
|
||||
{
|
||||
public:
|
||||
ColumnResizerPrivate(ColumnResizer* q_ptr)
|
||||
: q(q_ptr)
|
||||
, m_updateTimer(new QTimer(q))
|
||||
{
|
||||
m_updateTimer->setSingleShot(true);
|
||||
m_updateTimer->setInterval(0);
|
||||
QObject::connect(m_updateTimer, SIGNAL(timeout()), q, SLOT(updateWidth()));
|
||||
}
|
||||
|
||||
void scheduleWidthUpdate()
|
||||
{
|
||||
m_updateTimer->start();
|
||||
}
|
||||
|
||||
ColumnResizer* q;
|
||||
QTimer* m_updateTimer;
|
||||
QList<QWidget*> m_widgets;
|
||||
QList<FormLayoutWidgetItem*> m_wrWidgetItemList;
|
||||
QList<GridColumnInfo> m_gridColumnInfoList;
|
||||
};
|
||||
|
||||
ColumnResizer::ColumnResizer(QObject* parent)
|
||||
: QObject(parent)
|
||||
, d(new ColumnResizerPrivate(this))
|
||||
{}
|
||||
|
||||
ColumnResizer::~ColumnResizer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidget(QWidget* widget)
|
||||
{
|
||||
d->m_widgets.append(widget);
|
||||
widget->installEventFilter(this);
|
||||
d->scheduleWidthUpdate();
|
||||
}
|
||||
|
||||
void ColumnResizer::updateWidth()
|
||||
{
|
||||
int width = 0;
|
||||
Q_FOREACH(QWidget* widget, d->m_widgets) {
|
||||
width = qMax(widget->sizeHint().width(), width);
|
||||
}
|
||||
Q_FOREACH(FormLayoutWidgetItem* item, d->m_wrWidgetItemList) {
|
||||
item->setWidth(width);
|
||||
item->formLayout()->update();
|
||||
}
|
||||
Q_FOREACH(GridColumnInfo info, d->m_gridColumnInfoList) {
|
||||
info.first->setColumnMinimumWidth(info.second, width);
|
||||
}
|
||||
}
|
||||
|
||||
bool ColumnResizer::eventFilter(QObject*, QEvent* event)
|
||||
{
|
||||
if (event->type() == QEvent::Resize) {
|
||||
d->scheduleWidthUpdate();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromLayout(QLayout* layout, int column)
|
||||
{
|
||||
Q_ASSERT(column >= 0);
|
||||
QGridLayout* gridLayout = qobject_cast<QGridLayout*>(layout);
|
||||
QFormLayout* formLayout = qobject_cast<QFormLayout*>(layout);
|
||||
if (gridLayout) {
|
||||
addWidgetsFromGridLayout(gridLayout, column);
|
||||
} else if (formLayout) {
|
||||
if (column > QFormLayout::SpanningRole) {
|
||||
qCritical() << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
|
||||
return;
|
||||
}
|
||||
QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
|
||||
addWidgetsFromFormLayout(formLayout, role);
|
||||
} else {
|
||||
qCritical() << "Don't know how to handle layout" << layout;
|
||||
}
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromGridLayout(QGridLayout* layout, int column)
|
||||
{
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem* item = layout->itemAtPosition(row, column);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
QWidget* widget = item->widget();
|
||||
if (!widget) {
|
||||
continue;
|
||||
}
|
||||
addWidget(widget);
|
||||
}
|
||||
d->m_gridColumnInfoList << GridColumnInfo(layout, column);
|
||||
}
|
||||
|
||||
void ColumnResizer::addWidgetsFromFormLayout(QFormLayout* layout, QFormLayout::ItemRole role)
|
||||
{
|
||||
for (int row = 0; row < layout->rowCount(); ++row) {
|
||||
QLayoutItem* item = layout->itemAt(row, role);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
QWidget* widget = item->widget();
|
||||
if (!widget) {
|
||||
continue;
|
||||
}
|
||||
layout->removeItem(item);
|
||||
delete item;
|
||||
FormLayoutWidgetItem* newItem = new FormLayoutWidgetItem(widget, layout, role);
|
||||
layout->setItem(row, role, newItem);
|
||||
addWidget(widget);
|
||||
d->m_wrWidgetItemList << newItem;
|
||||
}
|
||||
}
|
||||
|
||||
#include <ColumnResizer.moc>
|
||||
// vi: ts=4 sw=4 et
|
||||
38
application/ColumnResizer.h
Normal file
38
application/ColumnResizer.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
|
||||
* License: LGPL v2.1 or later (see COPYING)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QFormLayout>
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QList>
|
||||
|
||||
class QEvent;
|
||||
class QGridLayout;
|
||||
class QLayout;
|
||||
class QWidget;
|
||||
|
||||
class ColumnResizerPrivate;
|
||||
class ColumnResizer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColumnResizer(QObject* parent = 0);
|
||||
~ColumnResizer();
|
||||
|
||||
void addWidget(QWidget* widget);
|
||||
void addWidgetsFromLayout(QLayout*, int column);
|
||||
void addWidgetsFromGridLayout(QGridLayout*, int column);
|
||||
void addWidgetsFromFormLayout(QFormLayout*, QFormLayout::ItemRole role);
|
||||
|
||||
private Q_SLOTS:
|
||||
void updateWidth();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject*, QEvent* event);
|
||||
|
||||
private:
|
||||
ColumnResizerPrivate* const d;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,14 +22,15 @@
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <qlayoutitem.h>
|
||||
#include <QCloseEvent>
|
||||
|
||||
#include <gui/Platform.h>
|
||||
#include <gui/dialogs/CustomMessageBox.h>
|
||||
#include <gui/dialogs/ProgressDialog.h>
|
||||
#include <dialogs/CustomMessageBox.h>
|
||||
#include <dialogs/ProgressDialog.h>
|
||||
#include "widgets/PageContainer.h"
|
||||
#include "pages/LogPage.h"
|
||||
#include "InstancePageProvider.h"
|
||||
|
||||
#include "logic/icons/IconList.h"
|
||||
#include "icons/IconList.h"
|
||||
|
||||
class LogPageProvider : public BasePageProvider
|
||||
{
|
||||
@@ -51,13 +52,13 @@ private:
|
||||
BasePage * m_log_page;
|
||||
};
|
||||
|
||||
ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
|
||||
: QMainWindow(parent), m_proc(mcproc)
|
||||
ConsoleWindow::ConsoleWindow(std::shared_ptr<LaunchTask> proc, QWidget *parent)
|
||||
: QMainWindow(parent), m_proc(proc)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
auto instance = m_proc->instance();
|
||||
auto icon = MMC->icons()->getIcon(instance->iconKey());
|
||||
auto icon = ENV.icons()->getIcon(instance->iconKey());
|
||||
QString windowTitle = tr("Console window for ") + instance->name();
|
||||
|
||||
// Set window properties
|
||||
@@ -69,8 +70,9 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
|
||||
// Add page container
|
||||
{
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
auto provider = std::dynamic_pointer_cast<BasePageProvider>(m_proc->instance());
|
||||
auto proxy_provider = std::make_shared<LogPageProvider>(provider, new LogPage(m_proc));
|
||||
auto provider = std::make_shared<InstancePageProvider>(m_proc->instance());
|
||||
auto baseprovider = std::dynamic_pointer_cast<BasePageProvider>(provider);
|
||||
auto proxy_provider = std::make_shared<LogPageProvider>(baseprovider, new LogPage(m_proc));
|
||||
m_container = new PageContainer(proxy_provider, "console", this);
|
||||
mainLayout->addWidget(m_container);
|
||||
mainLayout->setSpacing(0);
|
||||
@@ -118,23 +120,20 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent)
|
||||
{
|
||||
m_trayIcon = new QSystemTrayIcon(icon, this);
|
||||
m_trayIcon->setToolTip(windowTitle);
|
||||
|
||||
|
||||
connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
|
||||
m_trayIcon->show();
|
||||
}
|
||||
|
||||
// Set up signal connections
|
||||
connect(mcproc, SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this,
|
||||
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
|
||||
connect(mcproc, SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this,
|
||||
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
|
||||
connect(mcproc, SIGNAL(launch_failed(InstancePtr)), this,
|
||||
SLOT(onLaunchFailed(InstancePtr)));
|
||||
connect(m_proc.get(), &LaunchTask::succeeded, this, &ConsoleWindow::onSucceeded);
|
||||
connect(m_proc.get(), &LaunchTask::failed, this, &ConsoleWindow::onFailed);
|
||||
connect(m_proc.get(), &LaunchTask::requestProgress, this, &ConsoleWindow::onProgressRequested);
|
||||
|
||||
setMayClose(false);
|
||||
|
||||
if (mcproc->instance()->settings().get("ShowConsole").toBool())
|
||||
if (m_proc->instance()->settings()->get("ShowConsole").toBool())
|
||||
{
|
||||
show();
|
||||
}
|
||||
@@ -169,7 +168,6 @@ void ConsoleWindow::setMayClose(bool mayclose)
|
||||
|
||||
void ConsoleWindow::toggleConsole()
|
||||
{
|
||||
//QScrollBar *bar = ui->text->verticalScrollBar();
|
||||
if (isVisible())
|
||||
{
|
||||
if(!isActiveWindow())
|
||||
@@ -177,26 +175,11 @@ void ConsoleWindow::toggleConsole()
|
||||
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();
|
||||
/*
|
||||
if (m_scroll_active)
|
||||
{
|
||||
bar->setValue(bar->maximum());
|
||||
}
|
||||
else
|
||||
{
|
||||
bar->setValue(m_last_scroll_value);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +188,7 @@ void ConsoleWindow::closeEvent(QCloseEvent *event)
|
||||
if (!m_mayclose)
|
||||
{
|
||||
toggleConsole();
|
||||
event->ignore();
|
||||
}
|
||||
else if(m_container->requestClose(event))
|
||||
{
|
||||
@@ -213,7 +197,7 @@ void ConsoleWindow::closeEvent(QCloseEvent *event)
|
||||
|
||||
emit isClosing();
|
||||
m_trayIcon->hide();
|
||||
QMainWindow::closeEvent(event);
|
||||
event->accept();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,26 +210,24 @@ void ConsoleWindow::on_btnKillMinecraft_clicked()
|
||||
"is frozen for some reason"),
|
||||
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
|
||||
if (response == QMessageBox::Yes)
|
||||
m_proc->killMinecraft();
|
||||
m_proc->abort();
|
||||
else
|
||||
m_killButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void ConsoleWindow::onEnded(InstancePtr instance, int code, QProcess::ExitStatus status)
|
||||
void ConsoleWindow::onSucceeded()
|
||||
{
|
||||
bool peacefulExit = code == 0 && status != QProcess::CrashExit;
|
||||
m_killButton->setEnabled(false);
|
||||
setMayClose(true);
|
||||
if (instance->settings().get("AutoCloseConsole").toBool())
|
||||
if (m_proc->instance()->settings()->get("AutoCloseConsole").toBool() && m_container->requestClose(nullptr))
|
||||
{
|
||||
if (peacefulExit)
|
||||
{
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
this->close();
|
||||
return;
|
||||
}
|
||||
if (!isVisible())
|
||||
{
|
||||
show();
|
||||
}
|
||||
// Raise Window
|
||||
if (MMC->settings()->get("RaiseConsole").toBool())
|
||||
{
|
||||
@@ -254,12 +236,25 @@ void ConsoleWindow::onEnded(InstancePtr instance, int code, QProcess::ExitStatus
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWindow::onLaunchFailed(InstancePtr instance)
|
||||
void ConsoleWindow::onFailed(QString reason)
|
||||
{
|
||||
m_killButton->setEnabled(false);
|
||||
|
||||
setMayClose(true);
|
||||
|
||||
if (!isVisible())
|
||||
{
|
||||
show();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleWindow::onProgressRequested(Task* task)
|
||||
{
|
||||
ProgressDialog progDialog(this);
|
||||
m_proc->proceed();
|
||||
progDialog.execWithTask(task);
|
||||
}
|
||||
|
||||
|
||||
ConsoleWindow::~ConsoleWindow()
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QSystemTrayIcon>
|
||||
#include "logic/MinecraftProcess.h"
|
||||
#include "launch/LaunchTask.h"
|
||||
|
||||
class QPushButton;
|
||||
class PageContainer;
|
||||
@@ -26,8 +26,8 @@ class ConsoleWindow : public QMainWindow
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0);
|
||||
virtual ~ConsoleWindow() {};
|
||||
explicit ConsoleWindow(std::shared_ptr<LaunchTask> proc, QWidget *parent = 0);
|
||||
virtual ~ConsoleWindow();
|
||||
|
||||
/**
|
||||
* @brief specify if the window is allowed to close
|
||||
@@ -44,10 +44,11 @@ slots:
|
||||
void on_closeButton_clicked();
|
||||
void on_btnKillMinecraft_clicked();
|
||||
|
||||
void onEnded(InstancePtr instance, int code, QProcess::ExitStatus status);
|
||||
void onLaunchFailed(InstancePtr instance);
|
||||
void onSucceeded();
|
||||
void onFailed(QString reason);
|
||||
void onProgressRequested(Task *task);
|
||||
|
||||
// FIXME: add handlers for the other MinecraftProcess signals (pre/post launch command
|
||||
// FIXME: add handlers for the other MinecraftLauncher signals (pre/post launch command
|
||||
// failures)
|
||||
|
||||
void iconActivated(QSystemTrayIcon::ActivationReason);
|
||||
@@ -56,7 +57,7 @@ protected:
|
||||
void closeEvent(QCloseEvent *);
|
||||
|
||||
private:
|
||||
MinecraftProcess *m_proc = nullptr;
|
||||
std::shared_ptr<LaunchTask> m_proc;
|
||||
bool m_mayclose = true;
|
||||
QSystemTrayIcon *m_trayIcon = nullptr;
|
||||
PageContainer *m_container = nullptr;
|
||||
119
application/GuiUtil.cpp
Normal file
119
application/GuiUtil.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "GuiUtil.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include "dialogs/ProgressDialog.h"
|
||||
#include "net/PasteUpload.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include <settings/SettingsObject.h>
|
||||
#include <DesktopServices.h>
|
||||
#include <BuildConfig.h>
|
||||
|
||||
QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
|
||||
{
|
||||
ProgressDialog dialog(parentWidget);
|
||||
auto APIKeySetting = MMC->settings()->get("PasteEEAPIKey").toString();
|
||||
if(APIKeySetting == "multimc")
|
||||
{
|
||||
APIKeySetting = BuildConfig.PASTE_EE_KEY;
|
||||
}
|
||||
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text, APIKeySetting));
|
||||
|
||||
if (!paste->validateText())
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parentWidget, QObject::tr("Upload failed"),
|
||||
QObject::tr("The log file is too big. You'll have to upload it manually."),
|
||||
QMessageBox::Warning)->exec();
|
||||
return QString();
|
||||
}
|
||||
|
||||
dialog.execWithTask(paste.get());
|
||||
if (!paste->successful())
|
||||
{
|
||||
CustomMessageBox::selectable(parentWidget, QObject::tr("Upload failed"),
|
||||
paste->failReason(), QMessageBox::Critical)->exec();
|
||||
return QString();
|
||||
}
|
||||
else
|
||||
{
|
||||
const QString link = paste->pasteLink();
|
||||
setClipboardText(link);
|
||||
DesktopServices::openUrl(link);
|
||||
CustomMessageBox::selectable(
|
||||
parentWidget, QObject::tr("Upload finished"),
|
||||
QObject::tr("The <a href=\"%1\">link to the uploaded log</a> has been opened in "
|
||||
"the default "
|
||||
"browser and placed in your clipboard.").arg(link),
|
||||
QMessageBox::Information)->exec();
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiUtil::setClipboardText(const QString &text)
|
||||
{
|
||||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
|
||||
QStringList GuiUtil::BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget)
|
||||
{
|
||||
static QMap<QString, QString> savedPaths;
|
||||
|
||||
QFileDialog w(parentWidget, caption);
|
||||
QSet<QString> locations;
|
||||
auto f = [&](QStandardPaths::StandardLocation l)
|
||||
{
|
||||
QString location = QStandardPaths::writableLocation(l);
|
||||
QFileInfo finfo(location);
|
||||
if (!finfo.exists())
|
||||
return;
|
||||
locations.insert(location);
|
||||
};
|
||||
f(QStandardPaths::DesktopLocation);
|
||||
f(QStandardPaths::DocumentsLocation);
|
||||
f(QStandardPaths::DownloadLocation);
|
||||
f(QStandardPaths::HomeLocation);
|
||||
QList<QUrl> urls;
|
||||
for (auto location : locations)
|
||||
{
|
||||
urls.append(QUrl::fromLocalFile(location));
|
||||
}
|
||||
urls.append(QUrl::fromLocalFile(defaultPath));
|
||||
|
||||
w.setFileMode(QFileDialog::ExistingFiles);
|
||||
w.setAcceptMode(QFileDialog::AcceptOpen);
|
||||
w.setNameFilter(filter);
|
||||
|
||||
QString pathToOpen;
|
||||
if(savedPaths.contains(context))
|
||||
{
|
||||
pathToOpen = savedPaths[context];
|
||||
}
|
||||
else
|
||||
{
|
||||
pathToOpen = defaultPath;
|
||||
}
|
||||
if(!pathToOpen.isEmpty())
|
||||
{
|
||||
QFileInfo finfo(pathToOpen);
|
||||
if(finfo.exists() && finfo.isDir())
|
||||
{
|
||||
w.setDirectory(finfo.absoluteFilePath());
|
||||
}
|
||||
}
|
||||
|
||||
w.setSidebarUrls(urls);
|
||||
|
||||
if (w.exec())
|
||||
{
|
||||
savedPaths[context] = w.directory().absolutePath();
|
||||
return w.selectedFiles();
|
||||
}
|
||||
savedPaths[context] = w.directory().absolutePath();
|
||||
return {};
|
||||
}
|
||||
10
application/GuiUtil.h
Normal file
10
application/GuiUtil.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace GuiUtil
|
||||
{
|
||||
QString uploadPaste(const QString &text, QWidget *parentWidget);
|
||||
void setClipboardText(const QString &text);
|
||||
QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);
|
||||
}
|
||||
0
application/InstancePageProvider.cpp
Normal file
0
application/InstancePageProvider.cpp
Normal file
77
application/InstancePageProvider.h
Normal file
77
application/InstancePageProvider.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#pragma once
|
||||
#include "minecraft/onesix/OneSixInstance.h"
|
||||
#include "minecraft/legacy/LegacyInstance.h"
|
||||
#include <FileSystem.h>
|
||||
#include "pages/BasePage.h"
|
||||
#include "pages/VersionPage.h"
|
||||
#include "pages/ModFolderPage.h"
|
||||
#include "pages/ResourcePackPage.h"
|
||||
#include "pages/TexturePackPage.h"
|
||||
#include "pages/NotesPage.h"
|
||||
#include "pages/ScreenshotsPage.h"
|
||||
#include "pages/InstanceSettingsPage.h"
|
||||
#include "pages/OtherLogsPage.h"
|
||||
#include "pages/BasePageProvider.h"
|
||||
#include "pages/LegacyJarModPage.h"
|
||||
#include "pages/WorldListPage.h"
|
||||
|
||||
|
||||
class InstancePageProvider : public QObject, public BasePageProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstancePageProvider(InstancePtr parent)
|
||||
{
|
||||
inst = parent;
|
||||
}
|
||||
|
||||
virtual ~InstancePageProvider() {};
|
||||
virtual QList<BasePage *> getPages() override
|
||||
{
|
||||
QList<BasePage *> values;
|
||||
std::shared_ptr<OneSixInstance> onesix = std::dynamic_pointer_cast<OneSixInstance>(inst);
|
||||
if(onesix)
|
||||
{
|
||||
values.append(new VersionPage(onesix.get()));
|
||||
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
|
||||
modsPage->setFilter(tr("%1 (*.zip *.jar *.litemod)"));
|
||||
values.append(modsPage);
|
||||
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
|
||||
values.append(new ResourcePackPage(onesix.get()));
|
||||
values.append(new TexturePackPage(onesix.get()));
|
||||
values.append(new NotesPage(onesix.get()));
|
||||
values.append(new WorldListPage(onesix.get(), onesix->worldList(), "worlds", "worlds", tr("Worlds"), "Worlds"));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(onesix->minecraftRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(onesix.get()));
|
||||
}
|
||||
std::shared_ptr<LegacyInstance> legacy = std::dynamic_pointer_cast<LegacyInstance>(inst);
|
||||
if(legacy)
|
||||
{
|
||||
// FIXME: actually implement the legacy instance upgrade, then enable this.
|
||||
//values.append(new LegacyUpgradePage(this));
|
||||
values.append(new LegacyJarModPage(legacy.get()));
|
||||
auto modsPage = new ModFolderPage(legacy.get(), legacy->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
|
||||
modsPage->setFilter(tr("%1 (*.zip *.jar *.litemod)"));
|
||||
values.append(modsPage);
|
||||
values.append(new ModFolderPage(legacy.get(), legacy->coreModList(), "coremods", "coremods", tr("Core mods"), "Loader-mods"));
|
||||
values.append(new TexturePackPage(legacy.get()));
|
||||
values.append(new NotesPage(legacy.get()));
|
||||
values.append(new WorldListPage(legacy.get(), legacy->worldList(), "worlds", "worlds", tr("Worlds"), "Worlds"));
|
||||
values.append(new ScreenshotsPage(FS::PathCombine(legacy->minecraftRoot(), "screenshots")));
|
||||
values.append(new InstanceSettingsPage(legacy.get()));
|
||||
}
|
||||
auto logMatcher = inst->getLogFileMatcher();
|
||||
if(logMatcher)
|
||||
{
|
||||
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
virtual QString dialogTitle() override
|
||||
{
|
||||
return tr("Edit Instance (%1)").arg(inst->name());
|
||||
}
|
||||
protected:
|
||||
InstancePtr inst;
|
||||
};
|
||||
23
application/InstanceProxyModel.cpp
Normal file
23
application/InstanceProxyModel.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#include "InstanceProxyModel.h"
|
||||
#include "MultiMC.h"
|
||||
#include <BaseInstance.h>
|
||||
|
||||
InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
|
||||
const QModelIndex &right) const
|
||||
{
|
||||
BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
|
||||
BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
|
||||
QString sortMode = MMC->settings()->get("InstSortMode").toString();
|
||||
if (sortMode == "LastLaunch")
|
||||
{
|
||||
return pdataLeft->lastLaunch() > pdataRight->lastLaunch();
|
||||
}
|
||||
else
|
||||
{
|
||||
return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
|
||||
}
|
||||
}
|
||||
15
application/InstanceProxyModel.h
Normal file
15
application/InstanceProxyModel.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "groupview/GroupedProxyModel.h"
|
||||
|
||||
/**
|
||||
* A proxy model that is responsible for sorting instances into groups
|
||||
*/
|
||||
class InstanceProxyModel : public GroupedProxyModel
|
||||
{
|
||||
public:
|
||||
explicit InstanceProxyModel(QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const;
|
||||
};
|
||||
107
application/JavaCommon.cpp
Normal file
107
application/JavaCommon.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "JavaCommon.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
#include <MMCStrings.h>
|
||||
|
||||
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
|
||||
{
|
||||
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]")))
|
||||
{
|
||||
CustomMessageBox::selectable(
|
||||
parent, QObject::tr("JVM arguments warning"),
|
||||
QObject::tr("You tried to manually set a JVM memory option (using "
|
||||
" \"-XX:PermSize\", \"-Xmx\" or \"-Xms\") - there"
|
||||
" are dedicated boxes for these in the settings (Java"
|
||||
" tab, in the Memory group at the top).\n"
|
||||
"Your manual settings will be overridden by the"
|
||||
" dedicated options.\n"
|
||||
"This message will be displayed until you remove them"
|
||||
" from the JVM arguments."),
|
||||
QMessageBox::Warning)->exec();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::javaWasOk(JavaCheckResult result)
|
||||
{
|
||||
QString text;
|
||||
text += tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
|
||||
"reported: %2<br />").arg(result.realPlatform, result.javaVersion.toString());
|
||||
if (result.errorLog.size())
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
htmlError.replace('\n', "<br />");
|
||||
text += tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError);
|
||||
}
|
||||
CustomMessageBox::selectable(m_parent, tr("Java test success"), text,
|
||||
QMessageBox::Information)->show();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::javaArgsWereBad(JavaCheckResult result)
|
||||
{
|
||||
auto htmlError = result.errorLog;
|
||||
QString text;
|
||||
htmlError.replace('\n', "<br />");
|
||||
text += tr("The specified java binary didn't work with the arguments you provided:<br />");
|
||||
text += tr("<font color=\"red\">%1</font>").arg(htmlError);
|
||||
CustomMessageBox::selectable(m_parent, tr("Java test failure"), text, QMessageBox::Warning)
|
||||
->show();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::javaBinaryWasBad(JavaCheckResult result)
|
||||
{
|
||||
QString text;
|
||||
text += tr(
|
||||
"The specified java binary didn't work.<br />You should use the auto-detect feature, "
|
||||
"or set the path to the java executable.<br />");
|
||||
CustomMessageBox::selectable(m_parent, tr("Java test failure"), text, QMessageBox::Warning)
|
||||
->show();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::run()
|
||||
{
|
||||
if (!JavaCommon::checkJVMArgs(m_args, m_parent))
|
||||
{
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||
SLOT(checkFinished(JavaCheckResult)));
|
||||
checker->m_path = m_path;
|
||||
checker->performCheck();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
|
||||
{
|
||||
if (!result.valid)
|
||||
{
|
||||
javaBinaryWasBad(result);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
checker.reset(new JavaChecker());
|
||||
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
|
||||
SLOT(checkFinishedWithArgs(JavaCheckResult)));
|
||||
checker->m_path = m_path;
|
||||
checker->m_args = m_args;
|
||||
checker->m_minMem = m_minMem;
|
||||
checker->m_maxMem = m_maxMem;
|
||||
if (result.javaVersion.requiresPermGen())
|
||||
{
|
||||
checker->m_permGen = m_permGen;
|
||||
}
|
||||
checker->performCheck();
|
||||
}
|
||||
|
||||
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
|
||||
{
|
||||
if (result.valid)
|
||||
{
|
||||
javaWasOk(result);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
javaArgsWereBad(result);
|
||||
emit finished();
|
||||
}
|
||||
46
application/JavaCommon.h
Normal file
46
application/JavaCommon.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
#include <java/JavaChecker.h>
|
||||
|
||||
class QWidget;
|
||||
|
||||
/**
|
||||
* Common UI bits for the java pages to use.
|
||||
*/
|
||||
namespace JavaCommon
|
||||
{
|
||||
bool checkJVMArgs(QString args, QWidget *parent);
|
||||
|
||||
class TestCheck : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen)
|
||||
:m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
|
||||
{
|
||||
}
|
||||
virtual ~TestCheck() {};
|
||||
|
||||
void run();
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
void javaBinaryWasBad(JavaCheckResult result);
|
||||
void javaArgsWereBad(JavaCheckResult result);
|
||||
void javaWasOk(JavaCheckResult result);
|
||||
|
||||
private slots:
|
||||
void checkFinished(JavaCheckResult result);
|
||||
void checkFinishedWithArgs(JavaCheckResult result);
|
||||
|
||||
private:
|
||||
std::shared_ptr<JavaChecker> checker;
|
||||
QWidget *m_parent = nullptr;
|
||||
QString m_path;
|
||||
QString m_args;
|
||||
int m_minMem = 0;
|
||||
int m_maxMem = 0;
|
||||
int m_permGen = 64;
|
||||
};
|
||||
}
|
||||
271
application/LaunchInteraction.cpp
Normal file
271
application/LaunchInteraction.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
#include "LaunchInteraction.h"
|
||||
#include <minecraft/auth/MojangAccountList.h>
|
||||
#include "MultiMC.h"
|
||||
#include "dialogs/CustomMessageBox.h"
|
||||
#include "dialogs/AccountSelectDialog.h"
|
||||
#include "dialogs/ProgressDialog.h"
|
||||
#include "dialogs/EditAccountDialog.h"
|
||||
#include "ConsoleWindow.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "JavaCommon.h"
|
||||
#include "SettingsUI.h"
|
||||
#include <QLineEdit>
|
||||
#include <QInputDialog>
|
||||
#include <tasks/Task.h>
|
||||
#include <minecraft/auth/YggdrasilTask.h>
|
||||
#include <launch/steps/TextPrint.h>
|
||||
#include <QStringList>
|
||||
|
||||
LaunchController::LaunchController(QObject *parent) : Task(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void LaunchController::executeTask()
|
||||
{
|
||||
login();
|
||||
}
|
||||
|
||||
// FIXME: minecraft specific
|
||||
void LaunchController::login()
|
||||
{
|
||||
if (!m_instance)
|
||||
{
|
||||
emitFailed(tr("No instance specified"));
|
||||
return;
|
||||
}
|
||||
|
||||
JavaCommon::checkJVMArgs(m_instance->settings()->get("JvmArgs").toString(), m_parentWidget);
|
||||
|
||||
// Find an account to use.
|
||||
std::shared_ptr<MojangAccountList> accounts = MMC->accounts();
|
||||
MojangAccountPtr account = accounts->activeAccount();
|
||||
if (accounts->count() <= 0)
|
||||
{
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
// Open the account manager.
|
||||
SettingsUI::ShowPageDialog(MMC->globalSettingsPages(), m_parentWidget, "accounts");
|
||||
}
|
||||
}
|
||||
else if (account.get() == nullptr)
|
||||
{
|
||||
// If no default account is set, ask the user which one to use.
|
||||
AccountSelectDialog selectDialog(tr("Which account would you like to use?"),
|
||||
AccountSelectDialog::GlobalDefaultCheckbox, m_parentWidget);
|
||||
|
||||
selectDialog.exec();
|
||||
|
||||
// Launch the instance with the selected account.
|
||||
account = selectDialog.selectedAccount();
|
||||
|
||||
// If the user said to use the account as default, do that.
|
||||
if (selectDialog.useAsGlobalDefault() && account.get() != nullptr)
|
||||
accounts->setActiveAccount(account->username());
|
||||
}
|
||||
|
||||
// if no account is selected, we bail
|
||||
if (!account.get())
|
||||
{
|
||||
emitFailed(tr("No account selected for launch"));
|
||||
return;
|
||||
}
|
||||
|
||||
// we try empty password first :)
|
||||
QString password;
|
||||
// we loop until the user succeeds in logging in or gives up
|
||||
bool tryagain = true;
|
||||
// the failure. the default failure.
|
||||
const QString needLoginAgain = tr("Your account is currently not logged in. Please enter "
|
||||
"your password to log in again.");
|
||||
QString failReason = needLoginAgain;
|
||||
|
||||
while (tryagain)
|
||||
{
|
||||
m_session = std::make_shared<AuthSession>();
|
||||
m_session->wants_online = m_online;
|
||||
auto task = account->login(m_session, password);
|
||||
if (task)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account
|
||||
// is still logged in.
|
||||
ProgressDialog progDialog(m_parentWidget);
|
||||
if (m_online)
|
||||
{
|
||||
progDialog.setSkipButton(true, tr("Play Offline"));
|
||||
}
|
||||
progDialog.execWithTask(task.get());
|
||||
if (!task->successful())
|
||||
{
|
||||
auto failReasonNew = task->failReason();
|
||||
if(failReasonNew == "Invalid token.")
|
||||
{
|
||||
failReason = needLoginAgain;
|
||||
}
|
||||
else failReason = failReasonNew;
|
||||
}
|
||||
}
|
||||
switch (m_session->status)
|
||||
{
|
||||
case AuthSession::Undetermined:
|
||||
{
|
||||
qCritical() << "Received undetermined session status during login. Bye.";
|
||||
tryagain = false;
|
||||
emitFailed(tr("Received undetermined session status during login."));
|
||||
break;
|
||||
}
|
||||
case AuthSession::RequiresPassword:
|
||||
{
|
||||
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
|
||||
auto username = m_session->username;
|
||||
auto chopN = [](QString toChop, int N) -> QString
|
||||
{
|
||||
if(toChop.size() > N)
|
||||
{
|
||||
auto left = toChop.left(N);
|
||||
left += QString("\u25CF").repeated(toChop.size() - N);
|
||||
return left;
|
||||
}
|
||||
return toChop;
|
||||
};
|
||||
|
||||
if(username.contains('@'))
|
||||
{
|
||||
auto parts = username.split('@');
|
||||
auto mailbox = chopN(parts[0],3);
|
||||
QString domain = chopN(parts[1], 3);
|
||||
username = mailbox + '@' + domain;
|
||||
}
|
||||
passDialog.setUsername(username);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
password = passDialog.password();
|
||||
}
|
||||
else
|
||||
{
|
||||
tryagain = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthSession::PlayableOffline:
|
||||
{
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString usedname = m_session->player_name;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal, m_session->player_name, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length())
|
||||
{
|
||||
usedname = name;
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
case AuthSession::PlayableOnline:
|
||||
{
|
||||
launchInstance();
|
||||
tryagain = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
emitFailed(tr("Failed to launch."));
|
||||
}
|
||||
|
||||
void LaunchController::launchInstance()
|
||||
{
|
||||
Q_ASSERT_X(m_instance != NULL, "launchInstance", "instance is NULL");
|
||||
Q_ASSERT_X(m_session.get() != nullptr, "launchInstance", "session is NULL");
|
||||
|
||||
if(!m_instance->reload())
|
||||
{
|
||||
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't load the instance profile."));
|
||||
emitFailed(tr("Couldn't load the instance profile."));
|
||||
return;
|
||||
}
|
||||
|
||||
m_launcher = m_instance->createLaunchTask(m_session);
|
||||
if (!m_launcher)
|
||||
{
|
||||
emitFailed(tr("Couldn't instantiate a launcher."));
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_parentWidget)
|
||||
{
|
||||
m_parentWidget->hide();
|
||||
}
|
||||
|
||||
m_console = new ConsoleWindow(m_launcher);
|
||||
connect(m_console, &ConsoleWindow::isClosing, this, &LaunchController::instanceEnded);
|
||||
connect(m_launcher.get(), &LaunchTask::readyForLaunch, this, &LaunchController::readyForLaunch);
|
||||
|
||||
m_launcher->prependStep(std::make_shared<TextPrint>(m_launcher.get(), "MultiMC version: " + BuildConfig.printableVersionString() + "\n\n", MessageLevel::MultiMC));
|
||||
m_launcher->start();
|
||||
}
|
||||
|
||||
void LaunchController::readyForLaunch()
|
||||
{
|
||||
if (!m_profiler)
|
||||
{
|
||||
m_launcher->proceed();
|
||||
return;
|
||||
}
|
||||
|
||||
QString error;
|
||||
if (!m_profiler->check(&error))
|
||||
{
|
||||
m_launcher->abort();
|
||||
QMessageBox::critical(m_parentWidget, tr("Error"), tr("Couldn't start profiler: %1").arg(error));
|
||||
return;
|
||||
}
|
||||
BaseProfiler *profilerInstance = m_profiler->createProfiler(m_launcher->instance(), this);
|
||||
|
||||
connect(profilerInstance, &BaseProfiler::readyToLaunch, [this](const QString & message)
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setText(tr("The game launch is delayed until you press the "
|
||||
"button. This is the right time to setup the profiler, as the "
|
||||
"profiler server is running now.\n\n%1").arg(message));
|
||||
msg.setWindowTitle(tr("Waiting"));
|
||||
msg.setIcon(QMessageBox::Information);
|
||||
msg.addButton(tr("Launch"), QMessageBox::AcceptRole);
|
||||
msg.setModal(true);
|
||||
msg.exec();
|
||||
m_launcher->proceed();
|
||||
});
|
||||
connect(profilerInstance, &BaseProfiler::abortLaunch, [this](const QString & message)
|
||||
{
|
||||
QMessageBox msg;
|
||||
msg.setText(tr("Couldn't start the profiler: %1").arg(message));
|
||||
msg.setWindowTitle(tr("Error"));
|
||||
msg.setIcon(QMessageBox::Critical);
|
||||
msg.addButton(QMessageBox::Ok);
|
||||
msg.setModal(true);
|
||||
msg.exec();
|
||||
m_launcher->abort();
|
||||
});
|
||||
profilerInstance->beginProfiling(m_launcher);
|
||||
}
|
||||
|
||||
void LaunchController::instanceEnded()
|
||||
{
|
||||
if(m_parentWidget)
|
||||
{
|
||||
m_parentWidget->show();
|
||||
}
|
||||
emitSucceeded();
|
||||
}
|
||||
49
application/LaunchInteraction.h
Normal file
49
application/LaunchInteraction.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <BaseInstance.h>
|
||||
#include <tools/BaseProfiler.h>
|
||||
|
||||
class ConsoleWindow;
|
||||
class LaunchController: public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual void executeTask();
|
||||
|
||||
LaunchController(QObject * parent = nullptr);
|
||||
virtual ~LaunchController(){};
|
||||
|
||||
void setInstance(InstancePtr instance)
|
||||
{
|
||||
m_instance = instance;
|
||||
}
|
||||
void setOnline(bool online)
|
||||
{
|
||||
m_online = online;
|
||||
}
|
||||
void setProfiler(BaseProfilerFactory *profiler)
|
||||
{
|
||||
m_profiler = profiler;
|
||||
}
|
||||
void setParentWidget(QWidget * widget)
|
||||
{
|
||||
m_parentWidget = widget;
|
||||
}
|
||||
|
||||
private:
|
||||
void login();
|
||||
void launchInstance();
|
||||
|
||||
private slots:
|
||||
void readyForLaunch();
|
||||
void instanceEnded();
|
||||
|
||||
private:
|
||||
BaseProfilerFactory *m_profiler = nullptr;
|
||||
bool m_online = true;
|
||||
InstancePtr m_instance;
|
||||
QWidget * m_parentWidget = nullptr;
|
||||
ConsoleWindow *m_console = nullptr;
|
||||
AuthSessionPtr m_session;
|
||||
std::shared_ptr <LaunchTask> m_launcher;
|
||||
};
|
||||
1712
application/MainWindow.cpp
Normal file
1712
application/MainWindow.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,51 +15,56 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#include "logic/InstanceList.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
#include "logic/auth/MojangAccount.h"
|
||||
#include "logic/net/NetJob.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "minecraft/auth/MojangAccount.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "updater/GoUpdate.h"
|
||||
|
||||
class LaunchController;
|
||||
class NewsChecker;
|
||||
class NotificationChecker;
|
||||
class QToolButton;
|
||||
class InstanceProxyModel;
|
||||
class LabeledToolButton;
|
||||
class QLabel;
|
||||
class MinecraftProcess;
|
||||
class ConsoleWindow;
|
||||
class MinecraftLauncher;
|
||||
class BaseProfilerFactory;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class MainWindow;
|
||||
}
|
||||
class GroupView;
|
||||
class ServerStatus;
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class Ui;
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget *parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
void closeEvent(QCloseEvent *event);
|
||||
|
||||
// Browser Dialog
|
||||
void openWebPage(QUrl url);
|
||||
virtual bool eventFilter(QObject *obj, QEvent *ev) override;
|
||||
virtual void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
void checkSetDefaultJava();
|
||||
void checkMigrateLegacyAssets();
|
||||
void checkInstancePathForProblems();
|
||||
|
||||
private
|
||||
slots:
|
||||
private slots:
|
||||
void onCatToggled(bool);
|
||||
|
||||
void on_actionAbout_triggered();
|
||||
|
||||
void on_actionAddInstance_triggered();
|
||||
|
||||
void on_actionREDDIT_triggered();
|
||||
|
||||
void on_actionDISCORD_triggered();
|
||||
|
||||
void on_actionCopyInstance_triggered();
|
||||
|
||||
void on_actionChangeInstGroup_triggered();
|
||||
@@ -94,14 +99,14 @@ slots:
|
||||
|
||||
void on_mainToolBar_visibilityChanged(bool);
|
||||
|
||||
// void on_instanceView_customContextMenuRequested(const QPoint &pos);
|
||||
|
||||
void on_actionLaunchInstance_triggered();
|
||||
|
||||
void on_actionLaunchInstanceOffline_triggered();
|
||||
|
||||
void on_actionDeleteInstance_triggered();
|
||||
|
||||
void on_actionExportInstance_triggered();
|
||||
|
||||
void on_actionRenameInstance_triggered();
|
||||
|
||||
void on_actionEditInstance_triggered();
|
||||
@@ -110,49 +115,30 @@ slots:
|
||||
|
||||
void on_actionScreenshots_triggered();
|
||||
|
||||
/*!
|
||||
* Launches the currently selected instance with the default account.
|
||||
* If no default account is selected, prompts the user to pick an account.
|
||||
*/
|
||||
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(InstancePtr instance, AuthSessionPtr session, BaseProfilerFactory *profiler = 0);
|
||||
|
||||
/*!
|
||||
* Prepares the given instance for launch with the given account.
|
||||
*/
|
||||
void updateInstance(InstancePtr instance, AuthSessionPtr account, BaseProfilerFactory *profiler = 0);
|
||||
|
||||
void onGameUpdateError(QString error);
|
||||
|
||||
void taskStart();
|
||||
void taskEnd();
|
||||
|
||||
void instanceEnded();
|
||||
|
||||
// called when an icon is changed in the icon model.
|
||||
/**
|
||||
* called when an icon is changed in the icon model.
|
||||
*/
|
||||
void iconUpdated(QString);
|
||||
|
||||
void showInstanceContextMenu(const QPoint &);
|
||||
|
||||
void updateToolsMenu();
|
||||
|
||||
void skinJobFinished();
|
||||
public
|
||||
slots:
|
||||
void skinJobFinished();
|
||||
|
||||
void instanceActivated(QModelIndex);
|
||||
|
||||
void instanceChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
void instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
|
||||
|
||||
void selectionBad();
|
||||
|
||||
void startTask(Task *task);
|
||||
|
||||
void updateAvailable(QString repo, QString versionName, int versionId);
|
||||
void updateAvailable(GoUpdate::Status status);
|
||||
|
||||
void updateNotAvailable();
|
||||
|
||||
@@ -167,37 +153,44 @@ slots:
|
||||
void updateNewsLabel();
|
||||
|
||||
/*!
|
||||
* Runs the DownloadUpdateTask and installs updates.
|
||||
* Runs the DownloadTask and installs updates.
|
||||
*/
|
||||
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);
|
||||
void downloadUpdates(GoUpdate::Status status);
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
class GroupView *view;
|
||||
void setCatBackground(bool enabled);
|
||||
void updateInstanceToolIcon(QString new_icon);
|
||||
void setSelectedInstanceById(const QString &id);
|
||||
|
||||
void waitForMinecraftVersions();
|
||||
InstancePtr instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version);
|
||||
InstancePtr instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url);
|
||||
void finalizeInstance(InstancePtr inst);
|
||||
void launch(InstancePtr instance, bool online = true, BaseProfilerFactory *profiler = nullptr);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui> ui;
|
||||
|
||||
// these are managed by Qt's memory management model!
|
||||
GroupView *view;
|
||||
InstanceProxyModel *proxymodel;
|
||||
NetJobPtr skin_download_job;
|
||||
MinecraftProcess *proc;
|
||||
ConsoleWindow *console;
|
||||
LabeledToolButton *renameButton;
|
||||
QToolButton *changeIconButton;
|
||||
QToolButton *newsLabel;
|
||||
QLabel *m_statusLeft;
|
||||
ServerStatus *m_statusRight;
|
||||
QMenu *accountMenu;
|
||||
QToolButton *accountMenuButton;
|
||||
QAction *manageAccountsAction;
|
||||
|
||||
unique_qobject_ptr<NetJob> skin_download_job;
|
||||
unique_qobject_ptr<NewsChecker> m_newsChecker;
|
||||
unique_qobject_ptr<NotificationChecker> m_notificationChecker;
|
||||
unique_qobject_ptr<LaunchController> m_launchController;
|
||||
|
||||
InstancePtr m_selectedInstance;
|
||||
QString m_currentInstIcon;
|
||||
|
||||
// managed by the application object
|
||||
Task *m_versionLoadTask;
|
||||
|
||||
QLabel *m_statusLeft;
|
||||
class ServerStatus *m_statusRight;
|
||||
|
||||
QMenu *accountMenu;
|
||||
QToolButton *accountMenuButton;
|
||||
QAction *manageAccountsAction;
|
||||
};
|
||||
951
application/MultiMC.cpp
Normal file
951
application/MultiMC.cpp
Normal file
@@ -0,0 +1,951 @@
|
||||
#include "MultiMC.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "pages/BasePageProvider.h"
|
||||
#include "pages/global/MultiMCPage.h"
|
||||
#include "pages/global/MinecraftPage.h"
|
||||
#include "pages/global/JavaPage.h"
|
||||
#include "pages/global/ProxyPage.h"
|
||||
#include "pages/global/ExternalToolsPage.h"
|
||||
#include "pages/global/AccountListPage.h"
|
||||
#include "pages/global/PasteEEPage.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTranslator>
|
||||
#include <QLibraryInfo>
|
||||
#include <QMessageBox>
|
||||
#include <QStringList>
|
||||
#include <QDebug>
|
||||
|
||||
#include "InstanceList.h"
|
||||
#include <minecraft/auth/MojangAccountList.h>
|
||||
#include "icons/IconList.h"
|
||||
//FIXME: get rid of this
|
||||
#include "minecraft/legacy/LwjglVersionList.h"
|
||||
#include "minecraft/MinecraftVersionList.h"
|
||||
#include "minecraft/liteloader/LiteLoaderVersionList.h"
|
||||
#include "minecraft/forge/ForgeVersionList.h"
|
||||
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/URLConstants.h"
|
||||
#include "Env.h"
|
||||
|
||||
#include "java/JavaUtils.h"
|
||||
|
||||
#include "updater/UpdateChecker.h"
|
||||
|
||||
#include "tools/JProfiler.h"
|
||||
#include "tools/JVisualVM.h"
|
||||
#include "tools/MCEditTool.h"
|
||||
|
||||
#include <xdgicon.h>
|
||||
#include "settings/INISettingsObject.h"
|
||||
#include "settings/Setting.h"
|
||||
|
||||
#include "trans/TranslationDownloader.h"
|
||||
#include "resources/Resource.h"
|
||||
#include "handlers/IconResourceHandler.h"
|
||||
#include "handlers/WebResourceHandler.h"
|
||||
|
||||
#include "minecraft/ftb/FTBPlugin.h"
|
||||
|
||||
#include <Commandline.h>
|
||||
#include <FileSystem.h>
|
||||
#include <DesktopServices.h>
|
||||
|
||||
using namespace Commandline;
|
||||
|
||||
MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, argv)
|
||||
{
|
||||
setOrganizationName("MultiMC");
|
||||
setApplicationName("MultiMC5");
|
||||
|
||||
startTime = QDateTime::currentDateTime();
|
||||
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
// Don't quit on hiding the last window
|
||||
this->setQuitOnLastWindowClosed(false);
|
||||
|
||||
// Commandline parsing
|
||||
QHash<QString, QVariant> args;
|
||||
{
|
||||
Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals);
|
||||
|
||||
// --help
|
||||
parser.addSwitch("help");
|
||||
parser.addShortOpt("help", 'h');
|
||||
parser.addDocumentation("help", "display this help and exit.");
|
||||
// --version
|
||||
parser.addSwitch("version");
|
||||
parser.addShortOpt("version", 'V');
|
||||
parser.addDocumentation("version", "display program version and exit.");
|
||||
// --dir
|
||||
parser.addOption("dir", applicationDirPath());
|
||||
parser.addShortOpt("dir", 'd');
|
||||
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of "
|
||||
"the binary location (use '.' for current)");
|
||||
// --launch
|
||||
parser.addOption("launch");
|
||||
parser.addShortOpt("launch", 'l');
|
||||
parser.addDocumentation("launch", "launch the specified instance (by instance ID)");
|
||||
|
||||
// parse the arguments
|
||||
try
|
||||
{
|
||||
args = parser.parse(arguments());
|
||||
}
|
||||
catch (ParsingError e)
|
||||
{
|
||||
std::cerr << "CommandLineError: " << e.what() << std::endl;
|
||||
std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters."
|
||||
<< std::endl;
|
||||
m_status = MultiMC::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// display help and exit
|
||||
if (args["help"].toBool())
|
||||
{
|
||||
std::cout << qPrintable(parser.compileHelp(arguments()[0]));
|
||||
m_status = MultiMC::Succeeded;
|
||||
return;
|
||||
}
|
||||
|
||||
// display version and exit
|
||||
if (args["version"].toBool())
|
||||
{
|
||||
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
|
||||
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
|
||||
m_status = MultiMC::Succeeded;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString origcwdPath = QDir::currentPath();
|
||||
QString binPath = applicationDirPath();
|
||||
QString adjustedBy;
|
||||
QString dataPath;
|
||||
// change directory
|
||||
QString dirParam = args["dir"].toString();
|
||||
if (!dirParam.isEmpty())
|
||||
{
|
||||
// the dir param. it makes multimc data path point to whatever the user specified
|
||||
// on command line
|
||||
adjustedBy += "Command line " + dirParam;
|
||||
dataPath = dirParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataPath = applicationDirPath();
|
||||
adjustedBy += "Fallback to binary path " + dataPath;
|
||||
}
|
||||
|
||||
launchId = args["launch"].toString();
|
||||
|
||||
if (!FS::ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
|
||||
{
|
||||
// BAD STUFF. WHAT DO?
|
||||
initLogger();
|
||||
qCritical() << "Failed to set work path. Will exit. NOW.";
|
||||
m_status = MultiMC::Failed;
|
||||
return;
|
||||
}
|
||||
|
||||
// in test mode, root path is the same as the binary path.
|
||||
if (test_mode)
|
||||
{
|
||||
rootPath = binPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
QDir foo(FS::PathCombine(binPath, ".."));
|
||||
rootPath = foo.absolutePath();
|
||||
#elif defined(Q_OS_WIN32)
|
||||
rootPath = binPath;
|
||||
#elif defined(Q_OS_MAC)
|
||||
QDir foo(FS::PathCombine(binPath, "../.."));
|
||||
rootPath = foo.absolutePath();
|
||||
#endif
|
||||
}
|
||||
|
||||
// init the logger
|
||||
initLogger();
|
||||
|
||||
qDebug() << "MultiMC 5, (c) 2013-2015 MultiMC Contributors";
|
||||
qDebug() << "Version : " << BuildConfig.printableVersionString();
|
||||
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
|
||||
qDebug() << "Git refspec : " << BuildConfig.GIT_REFSPEC;
|
||||
if (adjustedBy.size())
|
||||
{
|
||||
qDebug() << "Work dir before adjustment : " << origcwdPath;
|
||||
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
|
||||
qDebug() << "Adjusted by : " << adjustedBy;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Work dir : " << QDir::currentPath();
|
||||
}
|
||||
qDebug() << "Binary path : " << binPath;
|
||||
qDebug() << "Application root path : " << rootPath;
|
||||
|
||||
// load settings
|
||||
initGlobalSettings(test_mode);
|
||||
|
||||
// load translations
|
||||
initTranslations();
|
||||
|
||||
// initialize the updater
|
||||
if(BuildConfig.UPDATER_ENABLED)
|
||||
{
|
||||
m_updateChecker.reset(new UpdateChecker(BuildConfig.CHANLIST_URL, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
|
||||
}
|
||||
|
||||
m_translationChecker.reset(new TranslationDownloader());
|
||||
|
||||
// load icons
|
||||
initIcons();
|
||||
|
||||
// and instances
|
||||
auto InstDirSetting = m_settings->getSetting("InstanceDir");
|
||||
// instance path: check for problems with '!' in instance path and warn the user in the log
|
||||
// and rememer that we have to show him a dialog when the gui starts (if it does so)
|
||||
QString instDir = m_settings->get("InstanceDir").toString();
|
||||
qDebug() << "Instance path : " << instDir;
|
||||
if (FS::checkProblemticPathJava(QDir(instDir)))
|
||||
{
|
||||
qWarning()
|
||||
<< "Your instance path contains \'!\' and this is known to cause java problems";
|
||||
}
|
||||
m_instances.reset(new InstanceList(m_settings, InstDirSetting->get().toString(), this));
|
||||
qDebug() << "Loading Instances...";
|
||||
m_instances->loadList();
|
||||
connect(InstDirSetting.get(), SIGNAL(SettingChanged(const Setting &, QVariant)),
|
||||
m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
|
||||
|
||||
// and accounts
|
||||
m_accounts.reset(new MojangAccountList(this));
|
||||
qDebug() << "Loading accounts...";
|
||||
m_accounts->setListFilePath("accounts.json", true);
|
||||
m_accounts->loadList();
|
||||
|
||||
// init the http meta cache
|
||||
ENV.initHttpMetaCache();
|
||||
|
||||
// create the global network manager
|
||||
ENV.m_qnam.reset(new QNetworkAccessManager(this));
|
||||
|
||||
// init proxy settings
|
||||
{
|
||||
QString proxyTypeStr = settings()->get("ProxyType").toString();
|
||||
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();
|
||||
ENV.updateProxySettings(proxyTypeStr, addr, port, user, pass);
|
||||
}
|
||||
|
||||
initSSL();
|
||||
|
||||
m_translationChecker->downloadTranslations();
|
||||
|
||||
//FIXME: what to do with these?
|
||||
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);
|
||||
}
|
||||
|
||||
//FIXME: what to do with these?
|
||||
m_tools.insert("mcedit", std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
|
||||
for (auto tool : m_tools.values())
|
||||
{
|
||||
tool->registerSettings(m_settings);
|
||||
}
|
||||
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(onExit()));
|
||||
m_status = MultiMC::Initialized;
|
||||
}
|
||||
|
||||
MultiMC::~MultiMC()
|
||||
{
|
||||
if (m_mmc_translator)
|
||||
{
|
||||
removeTranslator(m_mmc_translator.get());
|
||||
}
|
||||
if (m_qt_translator)
|
||||
{
|
||||
removeTranslator(m_qt_translator.get());
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "CertWorkaround.h"
|
||||
#endif
|
||||
|
||||
void MultiMC::initSSL()
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
Q_INIT_RESOURCE(certs);
|
||||
RebuildQtCertificates();
|
||||
QFile equifaxFile(":/certs/Equifax_Secure_Certificate_Authority.pem");
|
||||
equifaxFile.open(QIODevice::ReadOnly);
|
||||
QSslCertificate equifaxCert(equifaxFile.readAll(), QSsl::Pem);
|
||||
QSslSocket::addDefaultCaCertificate(equifaxCert);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiMC::initTranslations()
|
||||
{
|
||||
QLocale locale(m_settings->get("Language").toString());
|
||||
QLocale::setDefault(locale);
|
||||
qDebug() << "Your language is" << locale.bcp47Name();
|
||||
m_qt_translator.reset(new QTranslator());
|
||||
if (m_qt_translator->load("qt_" + locale.bcp47Name(),
|
||||
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
|
||||
{
|
||||
qDebug() << "Loading Qt Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_qt_translator.get()))
|
||||
{
|
||||
qCritical() << "Loading Qt Language File failed.";
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_qt_translator.reset();
|
||||
}
|
||||
|
||||
m_mmc_translator.reset(new QTranslator());
|
||||
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), FS::PathCombine(QDir::currentPath(), "translations")))
|
||||
{
|
||||
qDebug() << "Loading MMC Language File for"
|
||||
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
|
||||
if (!installTranslator(m_mmc_translator.get()))
|
||||
{
|
||||
qCritical() << "Loading MMC Language File failed.";
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mmc_translator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void MultiMC::initIcons()
|
||||
{
|
||||
auto setting = MMC->settings()->getSetting("IconsDir");
|
||||
ENV.m_icons.reset(new IconList(QString(":/icons/instances/"), setting->get().toString()));
|
||||
connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value)
|
||||
{
|
||||
ENV.m_icons->directoryChanged(value.toString());
|
||||
});
|
||||
|
||||
//FIXME: none of this should be here.
|
||||
Resource::registerHandler<WebResourceHandler>("web");
|
||||
Resource::registerHandler<IconResourceHandler>("icon");
|
||||
|
||||
Resource::registerTransformer([](const QByteArray &data) -> QPixmap
|
||||
{
|
||||
return QPixmap::fromImage(QImage::fromData(data));
|
||||
});
|
||||
Resource::registerTransformer([](const QByteArray &data) -> QIcon
|
||||
{
|
||||
return QIcon(QPixmap::fromImage(QImage::fromData(data)));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void moveFile(const QString &oldName, const QString &newName)
|
||||
{
|
||||
QFile::remove(newName);
|
||||
QFile::copy(oldName, newName);
|
||||
QFile::remove(oldName);
|
||||
}
|
||||
|
||||
|
||||
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
{
|
||||
const char *levels = "DWCF";
|
||||
const QString format("%1 %2 %3\n");
|
||||
|
||||
qint64 msecstotal = MMC->timeSinceStart();
|
||||
qint64 seconds = msecstotal / 1000;
|
||||
qint64 msecs = msecstotal % 1000;
|
||||
QString foo;
|
||||
char buf[1025] = {0};
|
||||
::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
|
||||
|
||||
QString out = format.arg(buf).arg(levels[type]).arg(msg);
|
||||
|
||||
MMC->logFile->write(out.toUtf8());
|
||||
MMC->logFile->flush();
|
||||
QTextStream(stderr) << out.toLocal8Bit();
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
void MultiMC::initLogger()
|
||||
{
|
||||
static const QString logBase = "MultiMC-%0.log";
|
||||
|
||||
moveFile(logBase.arg(3), logBase.arg(4));
|
||||
moveFile(logBase.arg(2), logBase.arg(3));
|
||||
moveFile(logBase.arg(1), logBase.arg(2));
|
||||
moveFile(logBase.arg(0), logBase.arg(1));
|
||||
|
||||
qInstallMessageHandler(appDebugOutput);
|
||||
|
||||
logFile = std::make_shared<QFile>(logBase.arg(0));
|
||||
logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
|
||||
}
|
||||
|
||||
void MultiMC::initGlobalSettings(bool test_mode)
|
||||
{
|
||||
m_settings.reset(new INISettingsObject("multimc.cfg", this));
|
||||
// Updates
|
||||
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
|
||||
m_settings->registerSetting("AutoUpdate", true);
|
||||
m_settings->registerSetting("IconTheme", QString("multimc"));
|
||||
|
||||
// Notifications
|
||||
m_settings->registerSetting("ShownNotifications", QString());
|
||||
|
||||
// Remembered state
|
||||
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
|
||||
|
||||
QString defaultMonospace;
|
||||
int defaultSize = 11;
|
||||
#ifdef Q_OS_WIN32
|
||||
defaultMonospace = "Courier";
|
||||
defaultSize = 10;
|
||||
#elif defined(Q_OS_MAC)
|
||||
defaultMonospace = "Menlo";
|
||||
#else
|
||||
defaultMonospace = "Monospace";
|
||||
#endif
|
||||
if(!test_mode)
|
||||
{
|
||||
// resolve the font so the default actually matches
|
||||
QFont consoleFont;
|
||||
consoleFont.setFamily(defaultMonospace);
|
||||
consoleFont.setStyleHint(QFont::Monospace);
|
||||
consoleFont.setFixedPitch(true);
|
||||
QFontInfo consoleFontInfo(consoleFont);
|
||||
QString resolvedDefaultMonospace = consoleFontInfo.family();
|
||||
QFont resolvedFont(resolvedDefaultMonospace);
|
||||
qDebug() << "Detected default console font:" << resolvedDefaultMonospace
|
||||
<< ", substitutions:" << resolvedFont.substitutions().join(',');
|
||||
m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in test mode, we don't have UI, so we don't do any font resolving
|
||||
m_settings->registerSetting("ConsoleFont", defaultMonospace);
|
||||
}
|
||||
m_settings->registerSetting("ConsoleFontSize", defaultSize);
|
||||
m_settings->registerSetting("ConsoleMaxLines", 100000);
|
||||
m_settings->registerSetting("ConsoleOverflowStop", true);
|
||||
|
||||
FTBPlugin::initialize(m_settings);
|
||||
|
||||
// Folders
|
||||
m_settings->registerSetting("InstanceDir", "instances");
|
||||
m_settings->registerSetting({"CentralModsDir", "ModsDir"}, "mods");
|
||||
m_settings->registerSetting({"LWJGLDir", "LwjglDir"}, "lwjgl");
|
||||
m_settings->registerSetting("IconsDir", "icons");
|
||||
|
||||
// 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));
|
||||
// m_settings->registerSetting("StdOutColor", QColor(Qt::black));
|
||||
// m_settings->registerSetting("StdErrColor", QColor(Qt::red));
|
||||
|
||||
// Window Size
|
||||
m_settings->registerSetting({"LaunchMaximized", "MCWindowMaximize"}, false);
|
||||
m_settings->registerSetting({"MinecraftWinWidth", "MCWindowWidth"}, 854);
|
||||
m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
|
||||
|
||||
// Proxy Settings
|
||||
m_settings->registerSetting("ProxyType", "None");
|
||||
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);
|
||||
m_settings->registerSetting("PermGen", 128);
|
||||
|
||||
// Java Settings
|
||||
m_settings->registerSetting("JavaPath", "");
|
||||
m_settings->registerSetting("JavaTimestamp", 0);
|
||||
m_settings->registerSetting("JavaVersion", "");
|
||||
m_settings->registerSetting("LastHostname", "");
|
||||
m_settings->registerSetting("JavaDetectionHack", "");
|
||||
m_settings->registerSetting("JvmArgs", "");
|
||||
|
||||
// Wrapper command for launch
|
||||
m_settings->registerSetting("WrapperCommand", "");
|
||||
|
||||
// Custom Commands
|
||||
m_settings->registerSetting({"PreLaunchCommand", "PreLaunchCmd"}, "");
|
||||
m_settings->registerSetting({"PostExitCommand", "PostExitCmd"}, "");
|
||||
|
||||
// The cat
|
||||
m_settings->registerSetting("TheCat", false);
|
||||
|
||||
m_settings->registerSetting("InstSortMode", "Name");
|
||||
m_settings->registerSetting("SelectedInstance", QString());
|
||||
|
||||
// Window state and geometry
|
||||
m_settings->registerSetting("MainWindowState", "");
|
||||
m_settings->registerSetting("MainWindowGeometry", "");
|
||||
|
||||
m_settings->registerSetting("ConsoleWindowState", "");
|
||||
m_settings->registerSetting("ConsoleWindowGeometry", "");
|
||||
|
||||
m_settings->registerSetting("SettingsGeometry", "");
|
||||
|
||||
m_settings->registerSetting("PagedGeometry", "");
|
||||
|
||||
// Jar mod nag dialog in version page
|
||||
m_settings->registerSetting("JarModNagSeen", false);
|
||||
|
||||
// paste.ee API key
|
||||
m_settings->registerSetting("PasteEEAPIKey", "multimc");
|
||||
|
||||
// Init page provider
|
||||
{
|
||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||
m_globalSettingsProvider->addPage<MultiMCPage>();
|
||||
m_globalSettingsProvider->addPage<MinecraftPage>();
|
||||
m_globalSettingsProvider->addPage<JavaPage>();
|
||||
m_globalSettingsProvider->addPage<ProxyPage>();
|
||||
m_globalSettingsProvider->addPage<ExternalToolsPage>();
|
||||
m_globalSettingsProvider->addPage<AccountListPage>();
|
||||
m_globalSettingsProvider->addPage<PasteEEPage>();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
|
||||
{
|
||||
if (!m_lwjgllist)
|
||||
{
|
||||
m_lwjgllist.reset(new LWJGLVersionList());
|
||||
ENV.registerVersionList("org.lwjgl.legacy", m_lwjgllist);
|
||||
}
|
||||
return m_lwjgllist;
|
||||
}
|
||||
|
||||
std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
|
||||
{
|
||||
if (!m_forgelist)
|
||||
{
|
||||
m_forgelist.reset(new ForgeVersionList());
|
||||
ENV.registerVersionList("net.minecraftforge", m_forgelist);
|
||||
}
|
||||
return m_forgelist;
|
||||
}
|
||||
|
||||
std::shared_ptr<LiteLoaderVersionList> MultiMC::liteloaderlist()
|
||||
{
|
||||
if (!m_liteloaderlist)
|
||||
{
|
||||
m_liteloaderlist.reset(new LiteLoaderVersionList());
|
||||
ENV.registerVersionList("com.mumfrey.liteloader", m_liteloaderlist);
|
||||
}
|
||||
return m_liteloaderlist;
|
||||
}
|
||||
|
||||
std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
|
||||
{
|
||||
if (!m_minecraftlist)
|
||||
{
|
||||
m_minecraftlist.reset(new MinecraftVersionList());
|
||||
ENV.registerVersionList("net.minecraft", m_minecraftlist);
|
||||
}
|
||||
return m_minecraftlist;
|
||||
}
|
||||
|
||||
std::shared_ptr<JavaInstallList> MultiMC::javalist()
|
||||
{
|
||||
if (!m_javalist)
|
||||
{
|
||||
m_javalist.reset(new JavaInstallList());
|
||||
ENV.registerVersionList("com.java", m_javalist);
|
||||
}
|
||||
return m_javalist;
|
||||
}
|
||||
|
||||
// from <sys/stat.h>
|
||||
#ifndef S_IRUSR
|
||||
#define __S_IREAD 0400 /* Read by owner. */
|
||||
#define __S_IWRITE 0200 /* Write by owner. */
|
||||
#define __S_IEXEC 0100 /* Execute by owner. */
|
||||
#define S_IRUSR __S_IREAD /* Read by owner. */
|
||||
#define S_IWUSR __S_IWRITE /* Write by owner. */
|
||||
#define S_IXUSR __S_IEXEC /* Execute by owner. */
|
||||
|
||||
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
|
||||
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
|
||||
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
|
||||
|
||||
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
|
||||
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
|
||||
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
|
||||
#endif
|
||||
static QFile::Permissions unixModeToPermissions(const int mode)
|
||||
{
|
||||
QFile::Permissions perms;
|
||||
|
||||
if (mode & S_IRUSR)
|
||||
{
|
||||
perms |= QFile::ReadUser;
|
||||
}
|
||||
if (mode & S_IWUSR)
|
||||
{
|
||||
perms |= QFile::WriteUser;
|
||||
}
|
||||
if (mode & S_IXUSR)
|
||||
{
|
||||
perms |= QFile::ExeUser;
|
||||
}
|
||||
|
||||
if (mode & S_IRGRP)
|
||||
{
|
||||
perms |= QFile::ReadGroup;
|
||||
}
|
||||
if (mode & S_IWGRP)
|
||||
{
|
||||
perms |= QFile::WriteGroup;
|
||||
}
|
||||
if (mode & S_IXGRP)
|
||||
{
|
||||
perms |= QFile::ExeGroup;
|
||||
}
|
||||
|
||||
if (mode & S_IROTH)
|
||||
{
|
||||
perms |= QFile::ReadOther;
|
||||
}
|
||||
if (mode & S_IWOTH)
|
||||
{
|
||||
perms |= QFile::WriteOther;
|
||||
}
|
||||
if (mode & S_IXOTH)
|
||||
{
|
||||
perms |= QFile::ExeOther;
|
||||
}
|
||||
return perms;
|
||||
}
|
||||
|
||||
void MultiMC::installUpdates(const QString updateFilesDir, GoUpdate::OperationList operations)
|
||||
{
|
||||
qint64 pid = -1;
|
||||
QStringList args;
|
||||
bool started = false;
|
||||
|
||||
qDebug() << "Installing updates.";
|
||||
#ifdef Q_OS_WIN
|
||||
QString finishCmd = applicationFilePath();
|
||||
#elif defined Q_OS_LINUX
|
||||
QString finishCmd = FS::PathCombine(root(), "MultiMC");
|
||||
#elif defined Q_OS_MAC
|
||||
QString finishCmd = applicationFilePath();
|
||||
#else
|
||||
#error Unsupported operating system.
|
||||
#endif
|
||||
|
||||
QString backupPath = FS::PathCombine(root(), "update", "backup");
|
||||
QDir origin(root());
|
||||
|
||||
// clean up the backup folder. it should be empty before we start
|
||||
if(!FS::deletePath(backupPath))
|
||||
{
|
||||
qWarning() << "couldn't remove previous backup folder" << backupPath;
|
||||
}
|
||||
// and it should exist.
|
||||
if(!FS::ensureFolderPathExists(backupPath))
|
||||
{
|
||||
qWarning() << "couldn't create folder" << backupPath;
|
||||
return;
|
||||
}
|
||||
|
||||
struct BackupEntry
|
||||
{
|
||||
QString orig;
|
||||
QString backup;
|
||||
};
|
||||
enum Failure
|
||||
{
|
||||
Replace,
|
||||
Delete,
|
||||
Start,
|
||||
Nothing
|
||||
} failedOperationType = Nothing;
|
||||
QString failedFile;
|
||||
|
||||
QList <BackupEntry> backups;
|
||||
QList <BackupEntry> trashcan;
|
||||
|
||||
bool useXPHack = false;
|
||||
QString exePath;
|
||||
QString exeOrigin;
|
||||
QString exeBackup;
|
||||
|
||||
// perform the update operations
|
||||
for(auto op: operations)
|
||||
{
|
||||
switch(op.type)
|
||||
{
|
||||
// replace = move original out to backup, if it exists, move the new file in its place
|
||||
case GoUpdate::Operation::OP_REPLACE:
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
// hack for people renaming the .exe because ... reasons :)
|
||||
if(op.dest == "MultiMC.exe")
|
||||
{
|
||||
op.dest = QFileInfo(applicationFilePath()).fileName();
|
||||
}
|
||||
#endif
|
||||
QFileInfo replaced (FS::PathCombine(root(), op.dest));
|
||||
#ifdef Q_OS_WIN32
|
||||
if(QSysInfo::windowsVersion() < QSysInfo::WV_VISTA)
|
||||
{
|
||||
if(replaced.fileName() == "MultiMC.exe")
|
||||
{
|
||||
QDir rootDir(root());
|
||||
exeOrigin = rootDir.relativeFilePath(op.file);
|
||||
exePath = rootDir.relativeFilePath(op.dest);
|
||||
exeBackup = rootDir.relativeFilePath(FS::PathCombine(backupPath, replaced.fileName()));
|
||||
useXPHack = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(replaced.exists())
|
||||
{
|
||||
QString backupName = op.dest;
|
||||
backupName.replace('/', '_');
|
||||
QString backupFilePath = FS::PathCombine(backupPath, backupName);
|
||||
if(!QFile::rename(replaced.absoluteFilePath(), backupFilePath))
|
||||
{
|
||||
qWarning() << "Couldn't move:" << replaced.absoluteFilePath() << "to" << backupFilePath;
|
||||
failedOperationType = Replace;
|
||||
failedFile = op.dest;
|
||||
goto FAILED;
|
||||
}
|
||||
BackupEntry be;
|
||||
be.orig = replaced.absoluteFilePath();
|
||||
be.backup = backupFilePath;
|
||||
backups.append(be);
|
||||
}
|
||||
// make sure the folder we are putting this into exists
|
||||
if(!FS::ensureFilePathExists(replaced.absoluteFilePath()))
|
||||
{
|
||||
qWarning() << "REPLACE: Couldn't create folder:" << replaced.absoluteFilePath();
|
||||
failedOperationType = Replace;
|
||||
failedFile = op.dest;
|
||||
goto FAILED;
|
||||
}
|
||||
// now move the new file in
|
||||
if(!QFile::rename(op.file, replaced.absoluteFilePath()))
|
||||
{
|
||||
qWarning() << "REPLACE: Couldn't move:" << op.file << "to" << replaced.absoluteFilePath();
|
||||
failedOperationType = Replace;
|
||||
failedFile = op.dest;
|
||||
goto FAILED;
|
||||
}
|
||||
QFile::setPermissions(replaced.absoluteFilePath(), unixModeToPermissions(op.mode));
|
||||
}
|
||||
break;
|
||||
// delete = move original to backup
|
||||
case GoUpdate::Operation::OP_DELETE:
|
||||
{
|
||||
QString origFilePath = FS::PathCombine(root(), op.file);
|
||||
if(QFile::exists(origFilePath))
|
||||
{
|
||||
QString backupName = op.file;
|
||||
backupName.replace('/', '_');
|
||||
QString trashFilePath = FS::PathCombine(backupPath, backupName);
|
||||
|
||||
if(!QFile::rename(origFilePath, trashFilePath))
|
||||
{
|
||||
qWarning() << "DELETE: Couldn't move:" << op.file << "to" << trashFilePath;
|
||||
failedFile = op.file;
|
||||
failedOperationType = Delete;
|
||||
goto FAILED;
|
||||
}
|
||||
BackupEntry be;
|
||||
be.orig = origFilePath;
|
||||
be.backup = trashFilePath;
|
||||
trashcan.append(be);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// try to start the new binary
|
||||
args = qApp->arguments();
|
||||
args.removeFirst();
|
||||
|
||||
// on old Windows, do insane things... no error checking here, this is just to have something.
|
||||
if(useXPHack)
|
||||
{
|
||||
QString script;
|
||||
auto nativePath = QDir::toNativeSeparators(exePath);
|
||||
auto nativeOriginPath = QDir::toNativeSeparators(exeOrigin);
|
||||
auto nativeBackupPath = QDir::toNativeSeparators(exeBackup);
|
||||
|
||||
// so we write this vbscript thing...
|
||||
QTextStream out(&script);
|
||||
out << "WScript.Sleep 1000\n";
|
||||
out << "Set fso=CreateObject(\"Scripting.FileSystemObject\")\n";
|
||||
out << "Set shell=CreateObject(\"WScript.Shell\")\n";
|
||||
out << "fso.MoveFile \"" << nativePath << "\", \"" << nativeBackupPath << "\"\n";
|
||||
out << "fso.MoveFile \"" << nativeOriginPath << "\", \"" << nativePath << "\"\n";
|
||||
out << "shell.Run \"" << nativePath << "\"\n";
|
||||
|
||||
QString scriptPath = FS::PathCombine(root(), "update", "update.vbs");
|
||||
|
||||
// we save it
|
||||
QFile scriptFile(scriptPath);
|
||||
scriptFile.open(QIODevice::WriteOnly);
|
||||
scriptFile.write(script.toLocal8Bit().replace("\n", "\r\n"));
|
||||
scriptFile.close();
|
||||
|
||||
// we run it
|
||||
started = QProcess::startDetached("wscript", {scriptPath}, root());
|
||||
|
||||
// and we quit. conscious thought.
|
||||
qApp->quit();
|
||||
return;
|
||||
}
|
||||
started = QProcess::startDetached(finishCmd, args, QDir::currentPath(), &pid);
|
||||
// failed to start... ?
|
||||
if(!started || pid == -1)
|
||||
{
|
||||
qWarning() << "Couldn't start new process properly!";
|
||||
failedOperationType = Start;
|
||||
goto FAILED;
|
||||
}
|
||||
origin.rmdir(updateFilesDir);
|
||||
qApp->quit();
|
||||
return;
|
||||
|
||||
FAILED:
|
||||
qWarning() << "Update failed!";
|
||||
bool revertOK = true;
|
||||
// if the above failed, roll back changes
|
||||
for(auto backup:backups)
|
||||
{
|
||||
qWarning() << "restoring" << backup.orig << "from" << backup.backup;
|
||||
if(!QFile::remove(backup.orig))
|
||||
{
|
||||
revertOK = false;
|
||||
qWarning() << "removing new" << backup.orig << "failed!";
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!QFile::rename(backup.backup, backup.orig))
|
||||
{
|
||||
revertOK = false;
|
||||
qWarning() << "restoring" << backup.orig << "failed!";
|
||||
}
|
||||
}
|
||||
for(auto backup:trashcan)
|
||||
{
|
||||
qWarning() << "restoring" << backup.orig << "from" << backup.backup;
|
||||
if(!QFile::rename(backup.backup, backup.orig))
|
||||
{
|
||||
revertOK = false;
|
||||
qWarning() << "restoring" << backup.orig << "failed!";
|
||||
}
|
||||
}
|
||||
QString msg;
|
||||
if(!revertOK)
|
||||
{
|
||||
msg = tr("The update failed and then the update revert failed too.\n"
|
||||
"You will have to repair MultiMC manually.\n"
|
||||
"Please let us know why and how this happened.").arg(failedFile);
|
||||
}
|
||||
else switch (failedOperationType)
|
||||
{
|
||||
case Replace:
|
||||
msg = tr("Couldn't replace file %1. Changes were reverted.\n"
|
||||
"See the MultiMC log file for details.").arg(failedFile);
|
||||
break;
|
||||
case Delete:
|
||||
msg = tr("Couldn't remove file %1. Changes were reverted.\n"
|
||||
"See the MultiMC log file for details.").arg(failedFile);
|
||||
break;
|
||||
case Start:
|
||||
msg = tr("The new version didn't start and the update was rolled back.");
|
||||
break;
|
||||
case Nothing:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
QMessageBox::critical(nullptr, tr("Update failed!"), msg);
|
||||
}
|
||||
|
||||
void MultiMC::setIconTheme(const QString& name)
|
||||
{
|
||||
XdgIcon::setThemeName(name);
|
||||
IconResourceHandler::setTheme(name);
|
||||
}
|
||||
|
||||
QIcon MultiMC::getThemedIcon(const QString& name)
|
||||
{
|
||||
return XdgIcon::fromTheme(name);
|
||||
}
|
||||
|
||||
void MultiMC::onExit()
|
||||
{
|
||||
if(m_instances)
|
||||
{
|
||||
m_instances->saveGroupList();
|
||||
}
|
||||
ENV.destroy();
|
||||
if(logFile)
|
||||
{
|
||||
logFile->flush();
|
||||
logFile->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool MultiMC::openJsonEditor(const QString &filename)
|
||||
{
|
||||
const QString file = QDir::current().absoluteFilePath(filename);
|
||||
if (m_settings->get("JsonEditor").toString().isEmpty())
|
||||
{
|
||||
return DesktopServices::openUrl(QUrl::fromLocalFile(file));
|
||||
}
|
||||
else
|
||||
{
|
||||
//return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file);
|
||||
return DesktopServices::run(m_settings->get("JsonEditor").toString(), {});
|
||||
}
|
||||
}
|
||||
|
||||
#include "MultiMC.moc"
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
#include <QApplication>
|
||||
#include <memory>
|
||||
#include "logger/QsLog.h"
|
||||
#include "logger/QsLogDest.h"
|
||||
#include <QDebug>
|
||||
#include <QFlag>
|
||||
#include <QIcon>
|
||||
#include <QDateTime>
|
||||
#include <updater/GoUpdate.h>
|
||||
|
||||
class GenericPageProvider;
|
||||
class QFile;
|
||||
class MinecraftVersionList;
|
||||
class LWJGLVersionList;
|
||||
class HttpMetaCache;
|
||||
@@ -16,35 +20,22 @@ class IconList;
|
||||
class QNetworkAccessManager;
|
||||
class ForgeVersionList;
|
||||
class LiteLoaderVersionList;
|
||||
class JavaVersionList;
|
||||
class JavaInstallList;
|
||||
class UpdateChecker;
|
||||
class NotificationChecker;
|
||||
class NewsChecker;
|
||||
class StatusChecker;
|
||||
class BaseProfilerFactory;
|
||||
class BaseDetachedToolFactory;
|
||||
class URNResolver;
|
||||
class TranslationDownloader;
|
||||
|
||||
#if defined(MMC)
|
||||
#undef MMC
|
||||
#endif
|
||||
#define MMC (static_cast<MultiMC *>(QCoreApplication::instance()))
|
||||
|
||||
enum UpdateFlag
|
||||
{
|
||||
None = 0x0,
|
||||
RestartOnFinish = 0x1,
|
||||
DryRun = 0x2,
|
||||
OnExit = 0x4
|
||||
};
|
||||
Q_DECLARE_FLAGS(UpdateFlags, UpdateFlag);
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateFlags);
|
||||
|
||||
// Global var used by the crash handling system to determine if a log file should be included in a crash report.
|
||||
extern bool loggerInitialized;
|
||||
|
||||
class MultiMC : public QApplication
|
||||
{
|
||||
// friends for the purpose of limiting access to deprecated stuff
|
||||
friend class MultiMCPage;
|
||||
friend class MainWindow;
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Status
|
||||
@@ -55,88 +46,76 @@ public:
|
||||
};
|
||||
|
||||
public:
|
||||
MultiMC(int &argc, char **argv, bool root_override = false);
|
||||
MultiMC(int &argc, char **argv, bool test_mode = false);
|
||||
virtual ~MultiMC();
|
||||
|
||||
// InstanceList, IconList, OneSixFTBInstance, LegacyUpdate, LegacyInstance, MCEditTool, JVisualVM, MinecraftInstance, JProfiler, BaseInstance
|
||||
std::shared_ptr<SettingsObject> settings()
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
std::shared_ptr<InstanceList> instances()
|
||||
std::shared_ptr<GenericPageProvider> globalSettingsPages()
|
||||
{
|
||||
return m_instances;
|
||||
return m_globalSettingsProvider;
|
||||
}
|
||||
|
||||
std::shared_ptr<MojangAccountList> accounts()
|
||||
qint64 timeSinceStart() const
|
||||
{
|
||||
return m_accounts;
|
||||
return startTime.msecsTo(QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
std::shared_ptr<IconList> icons();
|
||||
QIcon getThemedIcon(const QString& name);
|
||||
|
||||
Status status()
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
std::shared_ptr<QNetworkAccessManager> qnam()
|
||||
{
|
||||
return m_qnam;
|
||||
}
|
||||
|
||||
std::shared_ptr<HttpMetaCache> metacache()
|
||||
{
|
||||
return m_metacache;
|
||||
}
|
||||
void setIconTheme(const QString& name);
|
||||
|
||||
// DownloadUpdateTask
|
||||
std::shared_ptr<UpdateChecker> updateChecker()
|
||||
{
|
||||
return m_updateChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<NotificationChecker> notificationChecker()
|
||||
{
|
||||
return m_notificationChecker;
|
||||
}
|
||||
|
||||
std::shared_ptr<NewsChecker> newsChecker()
|
||||
{
|
||||
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<LWJGLVersionList> lwjgllist();
|
||||
std::shared_ptr<ForgeVersionList> forgelist();
|
||||
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
|
||||
std::shared_ptr<JavaInstallList> javalist();
|
||||
|
||||
std::shared_ptr<JavaVersionList> javalist();
|
||||
// APPLICATION ONLY
|
||||
std::shared_ptr<InstanceList> instances()
|
||||
{
|
||||
return m_instances;
|
||||
}
|
||||
|
||||
std::shared_ptr<URNResolver> resolver();
|
||||
// APPLICATION ONLY
|
||||
std::shared_ptr<MojangAccountList> accounts()
|
||||
{
|
||||
return m_accounts;
|
||||
}
|
||||
|
||||
// APPLICATION ONLY
|
||||
Status status()
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
// APPLICATION ONLY
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers()
|
||||
{
|
||||
return m_profilers;
|
||||
}
|
||||
|
||||
// APPLICATION ONLY
|
||||
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();
|
||||
// APPLICATION ONLY
|
||||
QString getFinishCmd();
|
||||
void installUpdates(const QString updateFilesDir, GoUpdate::OperationList operations);
|
||||
void updateXP(const QString updateFilesDir, GoUpdate::OperationList operations);
|
||||
void updateModern(const QString updateFilesDir, GoUpdate::OperationList operations);
|
||||
|
||||
/*!
|
||||
* Opens a json file using either a system default editor, or, if note empty, the editor
|
||||
@@ -144,33 +123,13 @@ public:
|
||||
*/
|
||||
bool openJsonEditor(const QString &filename);
|
||||
|
||||
/// this is the static data. it stores things that don't move.
|
||||
const QString &staticData()
|
||||
{
|
||||
return staticDataPath;
|
||||
}
|
||||
protected: /* to be removed! */
|
||||
// FIXME: remove. used by MainWindow to create application update tasks
|
||||
/// this is the root of the 'installation'. Used for automatic updates
|
||||
const QString &root()
|
||||
{
|
||||
return rootPath;
|
||||
}
|
||||
/// this is the where the binary files reside
|
||||
const QString &bin()
|
||||
{
|
||||
return binPath;
|
||||
}
|
||||
/// this is the work/data path. All user data is here.
|
||||
const QString &data()
|
||||
{
|
||||
return dataPath;
|
||||
}
|
||||
/**
|
||||
* this is the original work path before it was changed by the adjustment mechanism
|
||||
*/
|
||||
const QString &origcwd()
|
||||
{
|
||||
return origcwdPath;
|
||||
}
|
||||
|
||||
private slots:
|
||||
/**
|
||||
@@ -180,50 +139,38 @@ private slots:
|
||||
|
||||
private:
|
||||
void initLogger();
|
||||
|
||||
void initGlobalSettings();
|
||||
|
||||
void initHttpMetaCache();
|
||||
|
||||
void initIcons();
|
||||
void initGlobalSettings(bool test_mode);
|
||||
void initTranslations();
|
||||
void initSSL();
|
||||
|
||||
private:
|
||||
friend class UpdateCheckerTest;
|
||||
friend class DownloadUpdateTaskTest;
|
||||
friend class DownloadTaskTest;
|
||||
|
||||
QDateTime startTime;
|
||||
|
||||
std::shared_ptr<QTranslator> m_qt_translator;
|
||||
std::shared_ptr<QTranslator> m_mmc_translator;
|
||||
std::shared_ptr<SettingsObject> m_settings;
|
||||
std::shared_ptr<InstanceList> m_instances;
|
||||
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;
|
||||
std::shared_ptr<URNResolver> m_resolver;
|
||||
std::shared_ptr<JavaInstallList> m_javalist;
|
||||
std::shared_ptr<TranslationDownloader> m_translationChecker;
|
||||
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
|
||||
|
||||
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
|
||||
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
|
||||
|
||||
QsLogging::DestinationPtr m_fileDestination;
|
||||
QsLogging::DestinationPtr m_debugDestination;
|
||||
|
||||
QString m_updateOnExitPath;
|
||||
UpdateFlags m_updateOnExitFlags = None;
|
||||
|
||||
QString rootPath;
|
||||
QString staticDataPath;
|
||||
QString binPath;
|
||||
QString dataPath;
|
||||
QString origcwdPath;
|
||||
|
||||
Status m_status = MultiMC::Failed;
|
||||
public:
|
||||
QString launchId;
|
||||
std::shared_ptr<QFile> logFile;
|
||||
};
|
||||
9
application/SettingsUI.cpp
Normal file
9
application/SettingsUI.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "SettingsUI.h"
|
||||
namespace SettingsUI
|
||||
{
|
||||
void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page)
|
||||
{
|
||||
auto provider = std::make_shared<InstancePageProvider>(instance);
|
||||
ShowPageDialog(provider, parent, open_page);
|
||||
}
|
||||
}
|
||||
28
application/SettingsUI.h
Normal file
28
application/SettingsUI.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "pages/BasePageProvider.h"
|
||||
#include "MultiMC.h"
|
||||
#include "pagedialog/PageDialog.h"
|
||||
#include "InstancePageProvider.h"
|
||||
#include <settings/SettingsObject.h>
|
||||
#include <BaseInstance.h>
|
||||
|
||||
/*
|
||||
* FIXME: this is a fragment. find a better place for it.
|
||||
*/
|
||||
namespace SettingsUI
|
||||
{
|
||||
template <typename T>
|
||||
void ShowPageDialog(T raw_provider, QWidget * parent, QString open_page = QString())
|
||||
{
|
||||
auto provider = std::dynamic_pointer_cast<BasePageProvider>(raw_provider);
|
||||
if(!provider)
|
||||
return;
|
||||
{
|
||||
SettingsObject::Lock lock(MMC->settings());
|
||||
PageDialog dlg(provider, open_page, parent);
|
||||
dlg.exec();
|
||||
}
|
||||
}
|
||||
|
||||
void ShowInstancePageDialog(InstancePtr instance, QWidget * parent, QString open_page = QString());
|
||||
}
|
||||
413
application/VersionProxyModel.cpp
Normal file
413
application/VersionProxyModel.cpp
Normal file
@@ -0,0 +1,413 @@
|
||||
#include "VersionProxyModel.h"
|
||||
#include "MultiMC.h"
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QPixmapCache>
|
||||
#include <Version.h>
|
||||
|
||||
class VersionFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
VersionFilterModel(VersionProxyModel *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
|
||||
{
|
||||
const auto &filters = m_parent->filters();
|
||||
for (auto it = filters.begin(); it != filters.end(); ++it)
|
||||
{
|
||||
auto role = it.key();
|
||||
auto idx = sourceModel()->index(source_row, 0, source_parent);
|
||||
auto data = sourceModel()->data(idx, role);
|
||||
|
||||
switch(role)
|
||||
{
|
||||
case BaseVersionList::ParentGameVersionRole:
|
||||
case BaseVersionList::VersionIdRole:
|
||||
{
|
||||
auto versionString = data.toString();
|
||||
if(it.value().exact)
|
||||
{
|
||||
if (versionString != it.value().string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!versionIsInInterval(versionString, it.value().string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
auto match = data.toString();
|
||||
if(it.value().exact)
|
||||
{
|
||||
if (match != it.value().string)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (match.contains(it.value().string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
VersionProxyModel *m_parent;
|
||||
};
|
||||
|
||||
VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(parent)
|
||||
{
|
||||
filterModel = new VersionFilterModel(this);
|
||||
connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged);
|
||||
// FIXME: implement when needed
|
||||
/*
|
||||
connect(replacing, &QAbstractItemModel::rowsAboutToBeInserted, this, &VersionProxyModel::sourceRowsAboutToBeInserted);
|
||||
connect(replacing, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted);
|
||||
connect(replacing, &QAbstractItemModel::rowsAboutToBeRemoved, this, &VersionProxyModel::sourceRowsAboutToBeRemoved);
|
||||
connect(replacing, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved);
|
||||
connect(replacing, &QAbstractItemModel::rowsAboutToBeMoved, this, &VersionProxyModel::sourceRowsAboutToBeMoved);
|
||||
connect(replacing, &QAbstractItemModel::rowsMoved, this, &VersionProxyModel::sourceRowsMoved);
|
||||
connect(replacing, &QAbstractItemModel::layoutAboutToBeChanged, this, &VersionProxyModel::sourceLayoutAboutToBeChanged);
|
||||
connect(replacing, &QAbstractItemModel::layoutChanged, this, &VersionProxyModel::sourceLayoutChanged);
|
||||
*/
|
||||
connect(filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset);
|
||||
connect(filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset);
|
||||
|
||||
QAbstractProxyModel::setSourceModel(filterModel);
|
||||
}
|
||||
|
||||
QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if(section < 0 || section >= m_columns.size())
|
||||
return QVariant();
|
||||
if(orientation != Qt::Horizontal)
|
||||
return QVariant();
|
||||
auto column = m_columns[section];
|
||||
if(role == Qt::DisplayRole)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Name:
|
||||
return tr("Version");
|
||||
case ParentVersion:
|
||||
return tr("Minecraft"); //FIXME: this should come from metadata
|
||||
case Branch:
|
||||
return tr("Branch");
|
||||
case Type:
|
||||
return tr("Type");
|
||||
case Architecture:
|
||||
return tr("Architecture");
|
||||
case Path:
|
||||
return tr("Path");
|
||||
}
|
||||
}
|
||||
else if(role == Qt::ToolTipRole)
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Name:
|
||||
return tr("The name of the version.");
|
||||
case ParentVersion:
|
||||
return tr("Minecraft version"); //FIXME: this should come from metadata
|
||||
case Branch:
|
||||
return tr("The version's branch");
|
||||
case Type:
|
||||
return tr("The version's type");
|
||||
case Architecture:
|
||||
return tr("CPU Architecture");
|
||||
case Path:
|
||||
return tr("Filesystem path to this version");
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if(!index.isValid())
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
auto column = m_columns[index.column()];
|
||||
auto parentIndex = mapToSource(index);
|
||||
switch(role)
|
||||
{
|
||||
case Qt::DisplayRole:
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Name:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::VersionRole);
|
||||
case ParentVersion:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::ParentGameVersionRole);
|
||||
case Branch:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
|
||||
case Type:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
|
||||
case Architecture:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole);
|
||||
case Path:
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Name:
|
||||
{
|
||||
if(hasRecommended)
|
||||
{
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if(value.toBool())
|
||||
{
|
||||
return tr("Recommended");
|
||||
}
|
||||
else if(hasLatest)
|
||||
{
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if(value.toBool())
|
||||
{
|
||||
return tr("Latest");
|
||||
}
|
||||
}
|
||||
else if(index.row() == 0)
|
||||
{
|
||||
return tr("Latest");
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
case Qt::DecorationRole:
|
||||
{
|
||||
switch(column)
|
||||
{
|
||||
case Name:
|
||||
{
|
||||
if(hasRecommended)
|
||||
{
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if(value.toBool())
|
||||
{
|
||||
return MMC->getThemedIcon("star");
|
||||
}
|
||||
else if(hasLatest)
|
||||
{
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if(value.toBool())
|
||||
{
|
||||
return MMC->getThemedIcon("bug");
|
||||
}
|
||||
}
|
||||
else if(index.row() == 0)
|
||||
{
|
||||
return MMC->getThemedIcon("bug");
|
||||
}
|
||||
auto pixmap = QPixmapCache::find("placeholder");
|
||||
if(!pixmap)
|
||||
{
|
||||
QPixmap px(16,16);
|
||||
px.fill(Qt::transparent);
|
||||
QPixmapCache::insert("placeholder", px);
|
||||
return px;
|
||||
}
|
||||
return *pixmap;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
if(roles.contains((BaseVersionList::ModelRoles)role))
|
||||
{
|
||||
return sourceModel()->data(parentIndex, role);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QModelIndex VersionProxyModel::parent(const QModelIndex &child) const
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
|
||||
{
|
||||
if(sourceIndex.isValid())
|
||||
{
|
||||
return index(sourceIndex.row(), 0);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
|
||||
{
|
||||
if(proxyIndex.isValid())
|
||||
{
|
||||
return sourceModel()->index(proxyIndex.row(), 0);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
// no trees here... shoo
|
||||
if(parent.isValid())
|
||||
{
|
||||
return QModelIndex();
|
||||
}
|
||||
if(row < 0 || row >= sourceModel()->rowCount())
|
||||
return QModelIndex();
|
||||
if(column < 0 || column >= columnCount())
|
||||
return QModelIndex();
|
||||
return QAbstractItemModel::createIndex(row, column);
|
||||
}
|
||||
|
||||
int VersionProxyModel::columnCount(const QModelIndex &parent) const
|
||||
{
|
||||
return m_columns.size();
|
||||
}
|
||||
|
||||
int VersionProxyModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
if(sourceModel())
|
||||
{
|
||||
return sourceModel()->rowCount();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left,
|
||||
const QModelIndex &source_bottom_right)
|
||||
{
|
||||
if(source_top_left.parent() != source_bottom_right.parent())
|
||||
return;
|
||||
|
||||
// whole row is getting changed
|
||||
auto topLeft = createIndex(source_top_left.row(), 0);
|
||||
auto bottomRight = createIndex(source_bottom_right.row(), columnCount() - 1);
|
||||
emit dataChanged(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
|
||||
{
|
||||
auto replacing = dynamic_cast<BaseVersionList *>(replacingRaw);
|
||||
beginResetModel();
|
||||
|
||||
if(!replacing)
|
||||
{
|
||||
m_columns.clear();
|
||||
roles.clear();
|
||||
filterModel->setSourceModel(replacing);
|
||||
return;
|
||||
}
|
||||
|
||||
roles = replacing->providesRoles();
|
||||
if(roles.contains(BaseVersionList::VersionRole))
|
||||
{
|
||||
m_columns.push_back(Name);
|
||||
}
|
||||
/*
|
||||
if(roles.contains(BaseVersionList::ParentGameVersionRole))
|
||||
{
|
||||
m_columns.push_back(ParentVersion);
|
||||
}
|
||||
*/
|
||||
if(roles.contains(BaseVersionList::ArchitectureRole))
|
||||
{
|
||||
m_columns.push_back(Architecture);
|
||||
}
|
||||
if(roles.contains(BaseVersionList::PathRole))
|
||||
{
|
||||
m_columns.push_back(Path);
|
||||
}
|
||||
if(roles.contains(BaseVersionList::BranchRole))
|
||||
{
|
||||
m_columns.push_back(Branch);
|
||||
}
|
||||
if(roles.contains(BaseVersionList::TypeRole))
|
||||
{
|
||||
m_columns.push_back(Type);
|
||||
}
|
||||
if(roles.contains(BaseVersionList::RecommendedRole))
|
||||
{
|
||||
hasRecommended = true;
|
||||
}
|
||||
if(roles.contains(BaseVersionList::LatestRole))
|
||||
{
|
||||
hasLatest = true;
|
||||
}
|
||||
filterModel->setSourceModel(replacing);
|
||||
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::getRecommended() const
|
||||
{
|
||||
if(!roles.contains(BaseVersionList::RecommendedRole))
|
||||
{
|
||||
return index(0, 0);
|
||||
}
|
||||
int recommended = 0;
|
||||
for (int i = 0; i < rowCount(); i++)
|
||||
{
|
||||
auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole);
|
||||
if (value.toBool())
|
||||
{
|
||||
recommended = i;
|
||||
}
|
||||
}
|
||||
return index(recommended, 0);
|
||||
}
|
||||
|
||||
void VersionProxyModel::clearFilters()
|
||||
{
|
||||
m_filters.clear();
|
||||
filterModel->invalidate();
|
||||
}
|
||||
|
||||
void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, const QString &filter, const bool exact)
|
||||
{
|
||||
Filter f;
|
||||
f.string = filter;
|
||||
f.exact = exact;
|
||||
m_filters[column] = f;
|
||||
filterModel->invalidate();
|
||||
}
|
||||
|
||||
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
|
||||
{
|
||||
return m_filters;
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceAboutToBeReset()
|
||||
{
|
||||
beginResetModel();
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceReset()
|
||||
{
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
#include "VersionProxyModel.moc"
|
||||
58
application/VersionProxyModel.h
Normal file
58
application/VersionProxyModel.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
#include <QAbstractProxyModel>
|
||||
#include "BaseVersionList.h"
|
||||
|
||||
class VersionFilterModel;
|
||||
|
||||
class VersionProxyModel: public QAbstractProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Filter
|
||||
{
|
||||
QString string;
|
||||
bool exact = false;
|
||||
};
|
||||
enum Column
|
||||
{
|
||||
Name,
|
||||
ParentVersion,
|
||||
Branch,
|
||||
Type,
|
||||
Architecture,
|
||||
Path
|
||||
};
|
||||
typedef QHash<BaseVersionList::ModelRoles, Filter> FilterMap;
|
||||
|
||||
public:
|
||||
VersionProxyModel ( QObject* parent = 0 );
|
||||
virtual ~VersionProxyModel() {};
|
||||
|
||||
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
|
||||
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
|
||||
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
virtual QModelIndex parent(const QModelIndex &child) const override;
|
||||
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
|
||||
|
||||
const FilterMap &filters() const;
|
||||
void setFilter(const BaseVersionList::ModelRoles column, const QString &filter, const bool exact);
|
||||
void clearFilters();
|
||||
QModelIndex getRecommended() const;
|
||||
private slots:
|
||||
|
||||
void sourceDataChanged(const QModelIndex &source_top_left,const QModelIndex &source_bottom_right);
|
||||
void sourceAboutToBeReset();
|
||||
void sourceReset();
|
||||
|
||||
private:
|
||||
QList<Column> m_columns;
|
||||
FilterMap m_filters;
|
||||
BaseVersionList::RoleList roles;
|
||||
VersionFilterModel * filterModel;
|
||||
bool hasRecommended = false;
|
||||
bool hasLatest = false;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,16 +17,15 @@
|
||||
#include "ui_AboutDialog.h"
|
||||
#include <QIcon>
|
||||
#include "MultiMC.h"
|
||||
#include "gui/Platform.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include <logic/net/NetJob.h>
|
||||
#include <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 =
|
||||
QString creditsHtml = QObject::tr(
|
||||
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN' 'http://www.w3.org/TR/REC-html40/strict.dtd'>"
|
||||
"<html>"
|
||||
""
|
||||
@@ -56,9 +55,9 @@ QString getCreditsHtml(QStringList patrons)
|
||||
"%1"
|
||||
""
|
||||
"</body>"
|
||||
"</html>";
|
||||
"</html>");
|
||||
if (patrons.isEmpty())
|
||||
return creditsHtml.arg("<p>Loading...</p>");
|
||||
return creditsHtml.arg(QObject::tr("<p>Loading...</p>"));
|
||||
else
|
||||
{
|
||||
QString patronsStr;
|
||||
@@ -73,7 +72,6 @@ QString getCreditsHtml(QStringList patrons)
|
||||
|
||||
AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDialog)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
|
||||
QString chtml = getCreditsHtml(QStringList());
|
||||
@@ -81,11 +79,10 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
|
||||
|
||||
ui->urlLabel->setOpenExternalLinks(true);
|
||||
|
||||
ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
|
||||
ui->title->setText("MultiMC 5 " + BuildConfig.printableVersionString());
|
||||
ui->icon->setPixmap(MMC->getThemedIcon("multimc").pixmap(64));
|
||||
ui->title->setText("MultiMC 5");
|
||||
|
||||
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)
|
||||
@@ -100,7 +97,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
|
||||
|
||||
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
|
||||
|
||||
MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt()));
|
||||
connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt);
|
||||
|
||||
loadPatronList();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include <logic/net/ByteArrayDownload.h>
|
||||
#include <net/ByteArrayDownload.h>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
543
application/dialogs/AboutDialog.ui
Normal file
543
application/dialogs/AboutDialog.ui
Normal file
@@ -0,0 +1,543 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AboutDialog</class>
|
||||
<widget class="QDialog" name="AboutDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>707</width>
|
||||
<height>593</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>450</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About MultiMC</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<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="QLabel" name="icon">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="baseSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="title">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>15</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">MultiMC 5</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="aboutTab">
|
||||
<attribute name="title">
|
||||
<string>About</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<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="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">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.</p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="copyLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
<kerning>true</kerning>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>© 2013-2015 MultiMC Contributors</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="urlLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>10</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<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>212</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="creditsTab">
|
||||
<attribute name="title">
|
||||
<string>Credits</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="creditsText">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="html">
|
||||
<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:'Oxygen-Sans'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="translationInfo">
|
||||
<property name="text">
|
||||
<string extracomment="Hey, Translator, feel free to put credit to you here">No Language file loaded.</string>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="licenseTab">
|
||||
<attribute name="title">
|
||||
<string>License</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="licenseText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>DejaVu Sans Mono</family>
|
||||
</font>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string notr="true"><!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:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">MultiMC</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright 2012-2015 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-family:'Courier New,courier';">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</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-family:'Courier New,courier';">you may not use this file except in compliance with the License.</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-family:'Courier New,courier';">You may obtain a copy of the License at</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:'Courier New,courier';"><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:'Courier New,courier';"> http://www.apache.org/licenses/LICENSE-2.0</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:'Courier New,courier';"><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:'Courier New,courier';">Unless required by applicable law or agreed to in writing, software</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-family:'Courier New,courier';">distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</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-family:'Courier New,courier';">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 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-family:'Courier New,courier';">See the License for the specific language governing permissions 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-family:'Courier New,courier';">limitations under the License.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">MinGW runtime (Windows)</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (c) 2012 MinGW.org project</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:'Courier New,courier';"><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:'Courier New,courier';">Permission is hereby granted, free of charge, to any person obtaining a</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-family:'Courier New,courier';">copy of this software and associated documentation files (the &quot;Software&quot;),</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-family:'Courier New,courier';">to deal in the Software without restriction, including without limitation</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-family:'Courier New,courier';">the rights to use, copy, modify, merge, publish, distribute, sublicense,</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-family:'Courier New,courier';">and/or sell copies of the Software, and to permit persons to whom 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-family:'Courier New,courier';">Software is furnished to do so, subject to the following conditions:</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:'Courier New,courier';"><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:'Courier New,courier';">The above copyright notice, this permission notice and the below 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-family:'Courier New,courier';">shall be included in all copies or substantial portions of the Software.</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:'Courier New,courier';"><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:'Courier New,courier';">THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR</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-family:'Courier New,courier';">IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,</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-family:'Courier New,courier';">FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 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-family:'Courier New,courier';">AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER</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-family:'Courier New,courier';">LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING</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-family:'Courier New,courier';">FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER</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-family:'Courier New,courier';">DEALINGS IN THE SOFTWARE.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Qt 5</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).</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-family:'Courier New,courier';">Contact: http://www.qt-project.org/legal</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:'Courier New,courier';"><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:'Courier New,courier';">Licensed under LGPL v2.1</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">libnbt++</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">libnbt++ - A library for the Minecraft Named Binary Tag format.</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-family:'Courier New,courier';">Copyright (C) 2013, 2015 ljfa-ag</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:'Courier New,courier';"><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:'Courier New,courier';">libnbt++ is free software: you can redistribute it and/or modify</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-family:'Courier New,courier';">it under the terms of the GNU Lesser General Public License as published by</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-family:'Courier New,courier';">the Free Software Foundation, either version 3 of the License, or</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-family:'Courier New,courier';">(at your option) any later version.</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:'Courier New,courier';"><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:'Courier New,courier';">libnbt++ is distributed in the hope that it will be useful,</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-family:'Courier New,courier';">but WITHOUT ANY WARRANTY; without even the implied warranty of</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-family:'Courier New,courier';">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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-family:'Courier New,courier';">GNU Lesser General Public License for more details.</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:'Courier New,courier';"><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:'Courier New,courier';">You should have received a copy of the GNU Lesser General Public License</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-family:'Courier New,courier';">along with libnbt++. If not, see &lt;http://www.gnu.org/licenses/&gt;.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Group View</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (C) 2007 Rafael Fern\u00E1ndez L\u00F3pez &lt;ereslibre@kde.org&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-family:'Courier New,courier';">Copyright (C) 2007 John Tapsell &lt;tapsell@kde.org&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-family:'Courier New,courier';"><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:'Courier New,courier';">This library is free software; you can redistribute it and/or</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-family:'Courier New,courier';">modify it under the terms of the GNU Library General Public</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-family:'Courier New,courier';">License as published by the Free Software Foundation; either</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-family:'Courier New,courier';">version 2 of the License, or (at your option) any later version.</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:'Courier New,courier';"><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:'Courier New,courier';">This library is distributed in the hope that it will be useful,</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-family:'Courier New,courier';">but WITHOUT ANY WARRANTY; without even the implied warranty of</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-family:'Courier New,courier';">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</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-family:'Courier New,courier';">Library General Public License for more details.</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:'Courier New,courier';"><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:'Courier New,courier';">You should have received a copy of the GNU Library General Public License</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-family:'Courier New,courier';">along with this library; see the file COPYING.LIB. If not, write 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-family:'Courier New,courier';">the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,</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-family:'Courier New,courier';">Boston, MA 02110-1301, USA.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">rainbow (KGuiAddons)</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (C) 2007 Matthew Woehlke &lt;mw_triad@users.sourceforge.net&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-family:'Courier New,courier';">Copyright (C) 2007 Olaf Schmidt &lt;ojschmidt@kde.org&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-family:'Courier New,courier';">Copyright (C) 2007 Thomas Zander &lt;zander@kde.org&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-family:'Courier New,courier';">Copyright (C) 2007 Zack Rusin &lt;zack@kde.org&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-family:'Courier New,courier';">Copyright (C) 2015 Petr Mrazek &lt;peterix@gmail.com&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-family:'Courier New,courier';"><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:'Courier New,courier';">This library is free software; you can redistribute it and/or</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-family:'Courier New,courier';">modify it under the terms of the GNU Library General Public</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-family:'Courier New,courier';">License as published by the Free Software Foundation; either</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-family:'Courier New,courier';">version 2 of the License, or (at your option) any later version.</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:'Courier New,courier';"><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:'Courier New,courier';">This library is distributed in the hope that it will be useful,</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-family:'Courier New,courier';">but WITHOUT ANY WARRANTY; without even the implied warranty of</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-family:'Courier New,courier';">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</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-family:'Courier New,courier';">Library General Public License for more details.</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:'Courier New,courier';"><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:'Courier New,courier';">You should have received a copy of the GNU Library General Public License</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-family:'Courier New,courier';">along with this library; see the file COPYING.LIB. If not, write 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-family:'Courier New,courier';">the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,</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-family:'Courier New,courier';">Boston, MA 02110-1301, USA.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Hoedown</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (c) 2008, Natacha Port\u00E9</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-family:'Courier New,courier';">Copyright (c) 2011, Vicent Mart\u00ED</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-family:'Courier New,courier';">Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors</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:'Courier New,courier';"><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:'Courier New,courier';">Permission to use, copy, modify, and distribute this software 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-family:'Courier New,courier';">purpose with or without fee is hereby granted, provided that the above</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-family:'Courier New,courier';">copyright notice and this permission notice appear in all copies.</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:'Courier New,courier';"><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:'Courier New,courier';">THE SOFTWARE IS PROVIDED &quot;AS IS&quot; AND THE AUTHOR DISCLAIMS ALL WARRANTIES</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-family:'Courier New,courier';">WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF</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-family:'Courier New,courier';">MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR</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-family:'Courier New,courier';">ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY 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-family:'Courier New,courier';">WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN</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-family:'Courier New,courier';">ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF</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-family:'Courier New,courier';">OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Batch icon set</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">You are free to use Batch (the &quot;icon set&quot;) or any part thereof (the &quot;icons&quot;)</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-family:'Courier New,courier';">in any personal, open-source or commercial work without obligation of payment</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-family:'Courier New,courier';">(monetary or otherwise) or attribution. Do not sell the icon set, host</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-family:'Courier New,courier';">the icon set or rent the icon set (either in existing or modified form).</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:'Courier New,courier';"><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:'Courier New,courier';">While attribution is optional, it is always appreciated.</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:'Courier New,courier';"><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:'Courier New,courier';">Intellectual property rights are not transferred with the download of the icons.</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:'Courier New,courier';"><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:'Courier New,courier';">EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT</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-family:'Courier New,courier';">BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,</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-family:'Courier New,courier';">PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,</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-family:'Courier New,courier';">EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Pack200</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">The GNU General Public License (GPL)</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:'Courier New,courier';"><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:'Courier New,courier';">Version 2, June 1991</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:'Courier New,courier';"><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:'Courier New,courier';">+ &quot;CLASSPATH&quot; EXCEPTION TO THE GPL</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:'Courier New,courier';"><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:'Courier New,courier';">Certain source files distributed by Oracle America and/or its affiliates 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-family:'Courier New,courier';">subject to the following clarification and special exception to the GPL, but</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-family:'Courier New,courier';">only where Oracle has expressly included in the particular source file's header</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-family:'Courier New,courier';">the words &quot;Oracle designates this particular file as subject to the &quot;Classpath&quot;</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-family:'Courier New,courier';">exception as provided by Oracle in the LICENSE file that accompanied this code.&quot;</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:'Courier New,courier';"><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:'Courier New,courier';">Linking this library statically or dynamically with other modules is making</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-family:'Courier New,courier';">a combined work based on this library. Thus, the terms and conditions of</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-family:'Courier New,courier';">the GNU General Public License cover the whole combination.</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:'Courier New,courier';"><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:'Courier New,courier';">As a special exception, the copyright holders of this library give you</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-family:'Courier New,courier';">permission to link this library with independent modules to produce an</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-family:'Courier New,courier';">executable, regardless of the license terms of these independent modules,</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-family:'Courier New,courier';">and to copy and distribute the resulting executable under terms of your</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-family:'Courier New,courier';">choice, provided that you also meet, for each linked independent module,</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-family:'Courier New,courier';">the terms and conditions of the license of that module. An independent</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-family:'Courier New,courier';">module is a module which is not derived from or based on this library. If</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-family:'Courier New,courier';">you modify this library, you may extend this exception to your version of</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-family:'Courier New,courier';">the library, but you are not obligated to do so. If you do not wish to do</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-family:'Courier New,courier';">so, delete this exception statement from your version.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">Quazip</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright (C) 2005-2011 Sergey A. Tachenov</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:'Courier New,courier';"><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:'Courier New,courier';">This program is free software; you can redistribute it and/or modify it</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-family:'Courier New,courier';">under the terms of the GNU Lesser General Public License as published by</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-family:'Courier New,courier';">the Free Software Foundation; either version 2 of the License, or (at</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-family:'Courier New,courier';">your option) any later version.</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:'Courier New,courier';"><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:'Courier New,courier';">This program is distributed in the hope that it will be useful, but</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-family:'Courier New,courier';">WITHOUT ANY WARRANTY; without even the implied warranty of</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-family:'Courier New,courier';">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser</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-family:'Courier New,courier';">General Public License for more details.</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:'Courier New,courier';"><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:'Courier New,courier';">You should have received a copy of the GNU Lesser General Public License</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-family:'Courier New,courier';">along with this program; if not, write to the Free Software Foundation,</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-family:'Courier New,courier';">Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</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:'Courier New,courier';"><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:'Courier New,courier';">See COPYING file for the full LGPL text.</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:'Courier New,courier';"><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:'Courier New,courier';">Original ZIP package is copyrighted by Gilles Vollant, see</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-family:'Courier New,courier';">quazip/(un)zip.h files for details, basically it's zlib license.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">xz-minidec</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">XZ decompressor</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:'Courier New,courier';"><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:'Courier New,courier';">Authors: Lasse Collin &lt;lasse.collin@tukaani.org&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-family:'Courier New,courier';"> Igor Pavlov &lt;http://7-zip.org/&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-family:'Courier New,courier';"><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:'Courier New,courier';">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-family:'Courier New,courier';">You can do whatever you want with this file.</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p>
|
||||
<p style=" margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:xx-large; font-weight:600;">ColumnResizer</span> </p>
|
||||
<p style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';">Copyright 2011 Aur\u00E9lien G\u00E2teau &lt;agateau@kde.org&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-family:'Courier New,courier';">License: LGPL v2.1 or later (see COPYING)</span></p>
|
||||
<p style=" margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Courier New,courier';"> </span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::TextBrowserInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="forkingTab">
|
||||
<attribute name="title">
|
||||
<string>Forking/Redistribution</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="html">
|
||||
<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:'Oxygen-Sans'; font-size:11pt; 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';">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';"><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';">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>
|
||||
<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';"><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';">The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork </span><span style=" font-family:'Bitstream Vera Sans'; font-weight:600;">without</span><span style=" font-family:'Bitstream Vera Sans';"> implying that you have our blessing.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="aboutQt">
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About Qt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<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="closeButton">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>tabWidget</tabstop>
|
||||
<tabstop>creditsText</tabstop>
|
||||
<tabstop>translationInfo</tabstop>
|
||||
<tabstop>licenseText</tabstop>
|
||||
<tabstop>textEdit</tabstop>
|
||||
<tabstop>aboutQt</tabstop>
|
||||
<tabstop>closeButton</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../resources/multimc/multimc.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
#include <QItemSelectionModel>
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
#include <QDebug>
|
||||
|
||||
#include <gui/dialogs/ProgressDialog.h>
|
||||
#include <dialogs/ProgressDialog.h>
|
||||
|
||||
#include <MultiMC.h>
|
||||
|
||||
@@ -40,10 +40,12 @@ AccountSelectDialog::AccountSelectDialog(const QString &message, int flags, QWid
|
||||
// Flags...
|
||||
ui->globalDefaultCheck->setVisible(flags & GlobalDefaultCheckbox);
|
||||
ui->instDefaultCheck->setVisible(flags & InstanceDefaultCheckbox);
|
||||
QLOG_DEBUG() << flags;
|
||||
qDebug() << flags;
|
||||
|
||||
// Select the first entry in the list.
|
||||
ui->listView->setCurrentIndex(ui->listView->model()->index(0, 0));
|
||||
|
||||
connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(on_buttonBox_accepted()));
|
||||
}
|
||||
|
||||
AccountSelectDialog::~AccountSelectDialog()
|
||||
@@ -72,8 +74,7 @@ void AccountSelectDialog::on_buttonBox_accepted()
|
||||
if (selection.size() > 0)
|
||||
{
|
||||
QModelIndex selected = selection.first();
|
||||
MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
m_selected = account;
|
||||
m_selected = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
|
||||
}
|
||||
close();
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "logic/auth/MojangAccountList.h"
|
||||
#include "minecraft/auth/MojangAccountList.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,29 +20,39 @@
|
||||
#include "CopyInstanceDialog.h"
|
||||
#include "ui_CopyInstanceDialog.h"
|
||||
|
||||
#include "gui/Platform.h"
|
||||
#include "gui/dialogs/VersionSelectDialog.h"
|
||||
#include "gui/dialogs/ProgressDialog.h"
|
||||
#include "gui/dialogs/IconPickerDialog.h"
|
||||
#include "dialogs/IconPickerDialog.h"
|
||||
|
||||
#include "logic/InstanceFactory.h"
|
||||
#include "logic/BaseVersion.h"
|
||||
#include "logic/icons/IconList.h"
|
||||
#include "logic/tasks/Task.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
#include "BaseVersion.h"
|
||||
#include "icons/IconList.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceList.h"
|
||||
|
||||
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
|
||||
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
resize(minimumSizeHint());
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
InstIconKey = original->iconKey();
|
||||
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
|
||||
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
|
||||
ui->instNameTextBox->setText(original->name());
|
||||
ui->instNameTextBox->setFocus();
|
||||
auto groups = MMC->instances()->getGroups().toSet();
|
||||
auto groupList = QStringList(groups.toList());
|
||||
groupList.sort(Qt::CaseInsensitive);
|
||||
groupList.removeOne("");
|
||||
groupList.push_front("");
|
||||
ui->groupBox->addItems(groupList);
|
||||
int index = groupList.indexOf(m_original->group());
|
||||
if(index == -1)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
ui->groupBox->setCurrentIndex(index);
|
||||
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
|
||||
ui->copySavesCheckbox->setChecked(m_copySaves);
|
||||
}
|
||||
|
||||
CopyInstanceDialog::~CopyInstanceDialog()
|
||||
@@ -65,15 +75,20 @@ QString CopyInstanceDialog::iconKey() const
|
||||
return InstIconKey;
|
||||
}
|
||||
|
||||
QString CopyInstanceDialog::instGroup() const
|
||||
{
|
||||
return ui->groupBox->currentText();
|
||||
}
|
||||
|
||||
void CopyInstanceDialog::on_iconButton_clicked()
|
||||
{
|
||||
IconPickerDialog dlg(this);
|
||||
dlg.exec(InstIconKey);
|
||||
dlg.execWithSelection(InstIconKey);
|
||||
|
||||
if (dlg.result() == QDialog::Accepted)
|
||||
{
|
||||
InstIconKey = dlg.selectedIconKey;
|
||||
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
|
||||
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,3 +96,20 @@ void CopyInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
|
||||
{
|
||||
updateDialogState();
|
||||
}
|
||||
|
||||
bool CopyInstanceDialog::shouldCopySaves() const
|
||||
{
|
||||
return m_copySaves;
|
||||
}
|
||||
|
||||
void CopyInstanceDialog::on_copySavesCheckbox_stateChanged(int state)
|
||||
{
|
||||
if(state == Qt::Unchecked)
|
||||
{
|
||||
m_copySaves = false;
|
||||
}
|
||||
else if(state == Qt::Checked)
|
||||
{
|
||||
m_copySaves = true;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "logic/BaseVersion.h"
|
||||
#include <logic/BaseInstance.h>
|
||||
#include "BaseVersion.h"
|
||||
#include <BaseInstance.h>
|
||||
|
||||
class BaseInstance;
|
||||
|
||||
@@ -37,15 +37,19 @@ public:
|
||||
void updateDialogState();
|
||||
|
||||
QString instName() const;
|
||||
QString instGroup() const;
|
||||
QString iconKey() const;
|
||||
bool shouldCopySaves() const;
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_iconButton_clicked();
|
||||
void on_instNameTextBox_textChanged(const QString &arg1);
|
||||
void on_copySavesCheckbox_stateChanged(int state);
|
||||
|
||||
private:
|
||||
Ui::CopyInstanceDialog *ui;
|
||||
QString InstIconKey;
|
||||
InstancePtr m_original;
|
||||
bool m_copySaves = true;
|
||||
};
|
||||
@@ -10,14 +10,14 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>345</width>
|
||||
<height>205</height>
|
||||
<height>240</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Copy Instance</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../../graphics.qrc">
|
||||
<iconset>
|
||||
<normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset>
|
||||
</property>
|
||||
<property name="modal">
|
||||
@@ -42,7 +42,7 @@
|
||||
<item>
|
||||
<widget class="QToolButton" name="iconButton">
|
||||
<property name="icon">
|
||||
<iconset resource="../../graphics.qrc">
|
||||
<iconset>
|
||||
<normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -82,6 +82,40 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="labelVersion_3">
|
||||
<property name="text">
|
||||
<string>&Group</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>groupBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="copySavesCheckbox">
|
||||
<property name="text">
|
||||
<string>Copy saves</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
#include "EditAccountDialog.h"
|
||||
#include "ui_EditAccountDialog.h"
|
||||
#include <QDesktopServices>
|
||||
#include <DesktopServices.h>
|
||||
#include <QUrl>
|
||||
|
||||
EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int flags)
|
||||
@@ -26,8 +26,8 @@ EditAccountDialog::EditAccountDialog(const QString &text, QWidget *parent, int f
|
||||
ui->label->setText(text);
|
||||
ui->label->setVisible(!text.isEmpty());
|
||||
|
||||
ui->userTextBox->setVisible(flags & UsernameField);
|
||||
ui->passTextBox->setVisible(flags & PasswordField);
|
||||
ui->userTextBox->setEnabled(flags & UsernameField);
|
||||
ui->passTextBox->setEnabled(flags & PasswordField);
|
||||
}
|
||||
|
||||
EditAccountDialog::~EditAccountDialog()
|
||||
@@ -37,7 +37,12 @@ EditAccountDialog::~EditAccountDialog()
|
||||
|
||||
void EditAccountDialog::on_label_linkActivated(const QString &link)
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(link));
|
||||
DesktopServices::openUrl(QUrl(link));
|
||||
}
|
||||
|
||||
void EditAccountDialog::setUsername(const QString & user) const
|
||||
{
|
||||
ui->userTextBox->setText(user);
|
||||
}
|
||||
|
||||
QString EditAccountDialog::username() const
|
||||
@@ -45,6 +50,11 @@ QString EditAccountDialog::username() const
|
||||
return ui->userTextBox->text();
|
||||
}
|
||||
|
||||
void EditAccountDialog::setPassword(const QString & pass) const
|
||||
{
|
||||
ui->passTextBox->setText(pass);
|
||||
}
|
||||
|
||||
QString EditAccountDialog::password() const
|
||||
{
|
||||
return ui->passTextBox->text();
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -31,14 +31,10 @@ public:
|
||||
int flags = UsernameField | PasswordField);
|
||||
~EditAccountDialog();
|
||||
|
||||
/*!
|
||||
* Gets the text entered in the dialog's username field.
|
||||
*/
|
||||
QString username() const;
|
||||
void setUsername(const QString & user) const;
|
||||
void setPassword(const QString & pass) const;
|
||||
|
||||
/*!
|
||||
* Gets the text entered in the dialog's password field.
|
||||
*/
|
||||
QString username() const;
|
||||
QString password() const;
|
||||
|
||||
enum Flags
|
||||
@@ -11,13 +11,13 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Edit Account</string>
|
||||
<string>Login</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Message label placeholder.</string>
|
||||
<string notr="true">Message label placeholder.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
484
application/dialogs/ExportInstanceDialog.cpp
Normal file
484
application/dialogs/ExportInstanceDialog.cpp
Normal file
@@ -0,0 +1,484 @@
|
||||
/* Copyright 2013-2015 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 "ExportInstanceDialog.h"
|
||||
#include "ui_ExportInstanceDialog.h"
|
||||
#include <BaseInstance.h>
|
||||
#include <MMCZip.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <qfilesystemmodel.h>
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QDebug>
|
||||
#include <qstack.h>
|
||||
#include <QSaveFile>
|
||||
#include "MMCStrings.h"
|
||||
#include "SeparatorPrefixTree.h"
|
||||
#include "Env.h"
|
||||
#include <icons/IconList.h>
|
||||
#include <FileSystem.h>
|
||||
|
||||
class PackIgnoreProxy : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PackIgnoreProxy(InstancePtr instance, QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
m_instance = instance;
|
||||
}
|
||||
// NOTE: Sadly, we have to do sorting ourselves.
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
|
||||
if (!fsm)
|
||||
{
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
|
||||
|
||||
QFileInfo leftFileInfo = fsm->fileInfo(left);
|
||||
QFileInfo rightFileInfo = fsm->fileInfo(right);
|
||||
|
||||
if (!leftFileInfo.isDir() && rightFileInfo.isDir())
|
||||
{
|
||||
return !asc;
|
||||
}
|
||||
if (leftFileInfo.isDir() && !rightFileInfo.isDir())
|
||||
{
|
||||
return asc;
|
||||
}
|
||||
|
||||
// sort and proxy model breaks the original model...
|
||||
if (sortColumn() == 0)
|
||||
{
|
||||
return Strings::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),
|
||||
Qt::CaseInsensitive) < 0;
|
||||
}
|
||||
if (sortColumn() == 1)
|
||||
{
|
||||
auto leftSize = leftFileInfo.size();
|
||||
auto rightSize = rightFileInfo.size();
|
||||
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))
|
||||
{
|
||||
return Strings::naturalCompare(leftFileInfo.fileName(),
|
||||
rightFileInfo.fileName(),
|
||||
Qt::CaseInsensitive) < 0
|
||||
? asc
|
||||
: !asc;
|
||||
}
|
||||
return leftSize < rightSize;
|
||||
}
|
||||
return QSortFilterProxyModel::lessThan(left, right);
|
||||
}
|
||||
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::NoItemFlags;
|
||||
|
||||
auto sourceIndex = mapToSource(index);
|
||||
Qt::ItemFlags flags = sourceIndex.flags();
|
||||
if (index.column() == 0)
|
||||
{
|
||||
flags |= Qt::ItemIsUserCheckable;
|
||||
if (sourceIndex.model()->hasChildren(sourceIndex))
|
||||
{
|
||||
flags |= Qt::ItemIsTristate;
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
|
||||
{
|
||||
QModelIndex sourceIndex = mapToSource(index);
|
||||
|
||||
if (index.column() == 0 && role == Qt::CheckStateRole)
|
||||
{
|
||||
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
|
||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||
auto cover = blocked.cover(blockedPath);
|
||||
if (!cover.isNull())
|
||||
{
|
||||
return QVariant(Qt::Unchecked);
|
||||
}
|
||||
else if (blocked.exists(blockedPath))
|
||||
{
|
||||
return QVariant(Qt::PartiallyChecked);
|
||||
}
|
||||
else
|
||||
{
|
||||
return QVariant(Qt::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
return sourceIndex.data(role);
|
||||
}
|
||||
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value,
|
||||
int role = Qt::EditRole)
|
||||
{
|
||||
if (index.column() == 0 && role == Qt::CheckStateRole)
|
||||
{
|
||||
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
|
||||
return setFilterState(index, state);
|
||||
}
|
||||
|
||||
QModelIndex sourceIndex = mapToSource(index);
|
||||
return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role);
|
||||
}
|
||||
|
||||
QString relPath(const QString &path) const
|
||||
{
|
||||
QString prefix = QDir().absoluteFilePath(m_instance->instanceRoot());
|
||||
prefix += '/';
|
||||
if (!path.startsWith(prefix))
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
return path.mid(prefix.size());
|
||||
}
|
||||
|
||||
bool setFilterState(QModelIndex index, Qt::CheckState state)
|
||||
{
|
||||
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
|
||||
|
||||
if (!fsm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QModelIndex sourceIndex = mapToSource(index);
|
||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||
bool changed = false;
|
||||
if (state == Qt::Unchecked)
|
||||
{
|
||||
// blocking a path
|
||||
auto &node = blocked.insert(blockedPath);
|
||||
// get rid of all blocked nodes below
|
||||
node.clear();
|
||||
changed = true;
|
||||
}
|
||||
else if (state == Qt::Checked || state == Qt::PartiallyChecked)
|
||||
{
|
||||
if (!blocked.remove(blockedPath))
|
||||
{
|
||||
auto cover = blocked.cover(blockedPath);
|
||||
qDebug() << "Blocked by cover" << cover;
|
||||
// uncover
|
||||
blocked.remove(cover);
|
||||
// block all contents, except for any cover
|
||||
QModelIndex rootIndex =
|
||||
fsm->index(FS::PathCombine(m_instance->instanceRoot(), cover));
|
||||
QModelIndex doing = rootIndex;
|
||||
int row = 0;
|
||||
QStack<QModelIndex> todo;
|
||||
while (1)
|
||||
{
|
||||
auto node = doing.child(row, 0);
|
||||
if (!node.isValid())
|
||||
{
|
||||
if (!todo.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
doing = todo.pop();
|
||||
row = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto relpath = relPath(fsm->filePath(node));
|
||||
if (blockedPath.startsWith(relpath)) // cover found?
|
||||
{
|
||||
// continue processing cover later
|
||||
todo.push(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
// or just block this one.
|
||||
blocked.insert(relpath);
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
// update the thing
|
||||
emit dataChanged(index, index, {Qt::CheckStateRole});
|
||||
// update everything above index
|
||||
QModelIndex up = index.parent();
|
||||
while (1)
|
||||
{
|
||||
if (!up.isValid())
|
||||
break;
|
||||
emit dataChanged(up, up, {Qt::CheckStateRole});
|
||||
up = up.parent();
|
||||
}
|
||||
// and everything below the index
|
||||
QModelIndex doing = index;
|
||||
int row = 0;
|
||||
QStack<QModelIndex> todo;
|
||||
while (1)
|
||||
{
|
||||
auto node = doing.child(row, 0);
|
||||
if (!node.isValid())
|
||||
{
|
||||
if (!todo.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
doing = todo.pop();
|
||||
row = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
emit dataChanged(node, node, {Qt::CheckStateRole});
|
||||
todo.push(node);
|
||||
row++;
|
||||
}
|
||||
// siblings and unrelated nodes are ignored
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool shouldExpand(QModelIndex index)
|
||||
{
|
||||
QModelIndex sourceIndex = mapToSource(index);
|
||||
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
|
||||
if (!fsm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto blockedPath = relPath(fsm->filePath(sourceIndex));
|
||||
auto found = blocked.find(blockedPath);
|
||||
if(found)
|
||||
{
|
||||
return !found->leaf();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setBlockedPaths(QStringList paths)
|
||||
{
|
||||
beginResetModel();
|
||||
blocked.clear();
|
||||
blocked.insert(paths);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
const SeparatorPrefixTree<'/'> & blockedPaths() const
|
||||
{
|
||||
return blocked;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
|
||||
{
|
||||
Q_UNUSED(source_parent)
|
||||
|
||||
// adjust the columns you want to filter out here
|
||||
// return false for those that will be hidden
|
||||
if (source_column == 2 || source_column == 3)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
InstancePtr m_instance;
|
||||
SeparatorPrefixTree<'/'> blocked;
|
||||
};
|
||||
|
||||
ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::ExportInstanceDialog), m_instance(instance)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
auto model = new QFileSystemModel(this);
|
||||
proxyModel = new PackIgnoreProxy(m_instance, this);
|
||||
loadPackIgnore();
|
||||
proxyModel->setSourceModel(model);
|
||||
auto root = instance->instanceRoot();
|
||||
ui->treeView->setModel(proxyModel);
|
||||
ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root)));
|
||||
|
||||
connect(proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int)));
|
||||
|
||||
model->setRootPath(root);
|
||||
auto headerView = ui->treeView->header();
|
||||
headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
}
|
||||
|
||||
ExportInstanceDialog::~ExportInstanceDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
/// Save icon to instance's folder is needed
|
||||
void SaveIcon(InstancePtr m_instance)
|
||||
{
|
||||
auto iconKey = m_instance->iconKey();
|
||||
auto iconList = ENV.icons();
|
||||
auto mmcIcon = iconList->icon(iconKey);
|
||||
if(mmcIcon)
|
||||
{
|
||||
bool saveIcon = false;
|
||||
switch(mmcIcon->type())
|
||||
{
|
||||
case MMCIcon::FileBased:
|
||||
case MMCIcon::Transient:
|
||||
saveIcon = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if(saveIcon)
|
||||
{
|
||||
auto & image = mmcIcon->m_images[mmcIcon->type()];
|
||||
auto & icon = image.icon;
|
||||
auto sizes = icon.availableSizes();
|
||||
if(sizes.size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto areaOf = [](QSize size)
|
||||
{
|
||||
return size.width() * size.height();
|
||||
};
|
||||
QSize largest = sizes[0];
|
||||
// find variant with largest area
|
||||
for(auto size: sizes)
|
||||
{
|
||||
if(areaOf(largest) < areaOf(size))
|
||||
{
|
||||
largest = size;
|
||||
}
|
||||
}
|
||||
auto pixmap = icon.pixmap(largest);
|
||||
pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ExportInstanceDialog::doExport()
|
||||
{
|
||||
auto name = FS::RemoveInvalidFilenameChars(m_instance->name());
|
||||
|
||||
const QString output = QFileDialog::getSaveFileName(
|
||||
this, tr("Export %1").arg(m_instance->name()),
|
||||
FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)", nullptr, QFileDialog::DontConfirmOverwrite);
|
||||
if (output.isNull())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (QFile::exists(output))
|
||||
{
|
||||
int ret =
|
||||
QMessageBox::question(this, tr("Overwrite?"),
|
||||
tr("This file already exists. Do you want to overwrite it?"),
|
||||
QMessageBox::No, QMessageBox::Yes);
|
||||
if (ret == QMessageBox::No)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SaveIcon(m_instance);
|
||||
|
||||
if (!MMCZip::compressDir(output, m_instance->instanceRoot(), name, &proxyModel->blockedPaths()))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExportInstanceDialog::done(int result)
|
||||
{
|
||||
savePackIgnore();
|
||||
if (result == QDialog::Accepted)
|
||||
{
|
||||
if (doExport())
|
||||
{
|
||||
QDialog::done(QDialog::Accepted);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
QDialog::done(result);
|
||||
}
|
||||
|
||||
void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom)
|
||||
{
|
||||
//WARNING: possible off-by-one?
|
||||
for(int i = top; i < bottom; i++)
|
||||
{
|
||||
auto node = parent.child(i, 0);
|
||||
if(proxyModel->shouldExpand(node))
|
||||
{
|
||||
auto expNode = node.parent();
|
||||
if(!expNode.isValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ui->treeView->expand(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ExportInstanceDialog::ignoreFileName()
|
||||
{
|
||||
return FS::PathCombine(m_instance->instanceRoot(), ".packignore");
|
||||
}
|
||||
|
||||
void ExportInstanceDialog::loadPackIgnore()
|
||||
{
|
||||
auto filename = ignoreFileName();
|
||||
QFile ignoreFile(filename);
|
||||
if(!ignoreFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto data = ignoreFile.readAll();
|
||||
auto string = QString::fromUtf8(data);
|
||||
proxyModel->setBlockedPaths(string.split('\n', QString::SkipEmptyParts));
|
||||
}
|
||||
|
||||
void ExportInstanceDialog::savePackIgnore()
|
||||
{
|
||||
auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8();
|
||||
auto filename = ignoreFileName();
|
||||
try
|
||||
{
|
||||
FS::write(filename, data);
|
||||
}
|
||||
catch (Exception & e)
|
||||
{
|
||||
qWarning() << e.cause();
|
||||
}
|
||||
}
|
||||
|
||||
#include "ExportInstanceDialog.moc"
|
||||
54
application/dialogs/ExportInstanceDialog.h
Normal file
54
application/dialogs/ExportInstanceDialog.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* Copyright 2013-2015 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 <QDialog>
|
||||
#include <QModelIndex>
|
||||
#include <memory>
|
||||
|
||||
class BaseInstance;
|
||||
class PackIgnoreProxy;
|
||||
typedef std::shared_ptr<BaseInstance> InstancePtr;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class ExportInstanceDialog;
|
||||
}
|
||||
|
||||
class ExportInstanceDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ExportInstanceDialog(InstancePtr instance, QWidget *parent = 0);
|
||||
~ExportInstanceDialog();
|
||||
|
||||
virtual void done(int result);
|
||||
|
||||
private:
|
||||
bool doExport();
|
||||
void loadPackIgnore();
|
||||
void savePackIgnore();
|
||||
QString ignoreFileName();
|
||||
|
||||
private:
|
||||
Ui::ExportInstanceDialog *ui;
|
||||
InstancePtr m_instance;
|
||||
PackIgnoreProxy * proxyModel;
|
||||
|
||||
private slots:
|
||||
void rowsInserted(QModelIndex parent, int top, int bottom);
|
||||
};
|
||||
83
application/dialogs/ExportInstanceDialog.ui
Normal file
83
application/dialogs/ExportInstanceDialog.ui
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ExportInstanceDialog</class>
|
||||
<widget class="QDialog" name="ExportInstanceDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>625</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Export Instance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTreeView" name="treeView">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</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>
|
||||
<tabstops>
|
||||
<tabstop>treeView</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ExportInstanceDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ExportInstanceDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,15 +22,13 @@
|
||||
#include "IconPickerDialog.h"
|
||||
#include "ui_IconPickerDialog.h"
|
||||
|
||||
#include "gui/Platform.h"
|
||||
#include "gui/groupview/InstanceDelegate.h"
|
||||
#include "groupview/InstanceDelegate.h"
|
||||
|
||||
#include "logic/icons/IconList.h"
|
||||
#include "icons/IconList.h"
|
||||
|
||||
IconPickerDialog::IconPickerDialog(QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::IconPickerDialog)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
@@ -59,7 +57,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
|
||||
|
||||
contentsWidget->installEventFilter(this);
|
||||
|
||||
contentsWidget->setModel(MMC->icons().get());
|
||||
contentsWidget->setModel(ENV.icons().get());
|
||||
|
||||
auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole);
|
||||
auto buttonRemove =
|
||||
@@ -104,12 +102,12 @@ void IconPickerDialog::addNewIcon()
|
||||
//: The type of icon files
|
||||
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
|
||||
tr("Icons") + "(*.png *.jpg *.jpeg *.ico)");
|
||||
MMC->icons()->installIcons(fileNames);
|
||||
ENV.icons()->installIcons(fileNames);
|
||||
}
|
||||
|
||||
void IconPickerDialog::removeSelectedIcon()
|
||||
{
|
||||
MMC->icons()->deleteIcon(selectedIconKey);
|
||||
ENV.icons()->deleteIcon(selectedIconKey);
|
||||
}
|
||||
|
||||
void IconPickerDialog::activated(QModelIndex index)
|
||||
@@ -128,9 +126,9 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection
|
||||
selectedIconKey = key;
|
||||
}
|
||||
|
||||
int IconPickerDialog::exec(QString selection)
|
||||
int IconPickerDialog::execWithSelection(QString selection)
|
||||
{
|
||||
auto list = MMC->icons();
|
||||
auto list = ENV.icons();
|
||||
auto contentsWidget = ui->iconView;
|
||||
selectedIconKey = selection;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,10 +29,7 @@ class IconPickerDialog : public QDialog
|
||||
public:
|
||||
explicit IconPickerDialog(QWidget *parent = 0);
|
||||
~IconPickerDialog();
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-virtual"
|
||||
int exec(QString selection);
|
||||
#pragma clang diagnostic pop
|
||||
int execWithSelection(QString selection);
|
||||
QString selectedIconKey;
|
||||
|
||||
protected:
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2014 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,7 @@
|
||||
#include "LoginDialog.h"
|
||||
#include "ui_LoginDialog.h"
|
||||
|
||||
#include "logic/auth/YggdrasilTask.h"
|
||||
#include "minecraft/auth/YggdrasilTask.h"
|
||||
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
@@ -44,11 +44,11 @@ void LoginDialog::accept()
|
||||
// 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,
|
||||
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
|
||||
connect(m_loginTask.get(), &Task::succeeded, this,
|
||||
&LoginDialog::onTaskSucceeded);
|
||||
connect(m_loginTask.get(), &ProgressProvider::status, this, &LoginDialog::onTaskStatus);
|
||||
connect(m_loginTask.get(), &ProgressProvider::progress, this, &LoginDialog::onTaskProgress);
|
||||
connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus);
|
||||
connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress);
|
||||
m_loginTask->start();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2014 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +18,7 @@
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtCore/QEventLoop>
|
||||
|
||||
#include "logic/auth/MojangAccount.h"
|
||||
#include "minecraft/auth/MojangAccount.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -23,7 +23,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Message label placeholder.</string>
|
||||
<string notr="true">Message label placeholder.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
bool lastfirst(QModelIndexList &list, int &first, int &last)
|
||||
{
|
||||
if (!list.size())
|
||||
if (list.isEmpty())
|
||||
return false;
|
||||
first = last = list[0].row();
|
||||
for (auto item : list)
|
||||
@@ -28,7 +28,7 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
|
||||
{
|
||||
url = "http://" + url;
|
||||
}
|
||||
QDesktopServices::openUrl(url);
|
||||
DesktopServices::openUrl(url);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -37,4 +37,4 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
|
||||
QObject::tr("The mod author didn't provide a website link for this mod."),
|
||||
QMessageBox::Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
#include <QModelIndex>
|
||||
#include <QDesktopServices>
|
||||
#include <DesktopServices.h>
|
||||
#include <QWidget>
|
||||
#include <logic/Mod.h>
|
||||
#include <minecraft/Mod.h>
|
||||
|
||||
bool lastfirst(QModelIndexList &list, int &first, int &last);
|
||||
|
||||
238
application/dialogs/NewInstanceDialog.cpp
Normal file
238
application/dialogs/NewInstanceDialog.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
/* Copyright 2013-2015 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 "MultiMC.h"
|
||||
#include "NewInstanceDialog.h"
|
||||
#include "ui_NewInstanceDialog.h"
|
||||
|
||||
#include <BaseVersion.h>
|
||||
#include <icons/IconList.h>
|
||||
#include <minecraft/MinecraftVersionList.h>
|
||||
#include <tasks/Task.h>
|
||||
#include <InstanceList.h>
|
||||
|
||||
#include "VersionSelectDialog.h"
|
||||
#include "ProgressDialog.h"
|
||||
#include "IconPickerDialog.h"
|
||||
|
||||
#include <QLayout>
|
||||
#include <QPushButton>
|
||||
#include <QFileDialog>
|
||||
#include <QValidator>
|
||||
|
||||
class UrlValidator : public QValidator
|
||||
{
|
||||
public:
|
||||
using QValidator::QValidator;
|
||||
|
||||
State validate(QString &in, int &pos) const
|
||||
{
|
||||
const QUrl url(in);
|
||||
if (url.isValid() && !url.isRelative() && !url.isEmpty())
|
||||
{
|
||||
return Acceptable;
|
||||
}
|
||||
else if (QFile::exists(in))
|
||||
{
|
||||
return Acceptable;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Intermediate;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NewInstanceDialog::NewInstanceDialog(QWidget *parent)
|
||||
: QDialog(parent), ui(new Ui::NewInstanceDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
resize(minimumSizeHint());
|
||||
layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
setSelectedVersion(MMC->minecraftlist()->getRecommended());
|
||||
InstIconKey = "default";
|
||||
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
|
||||
|
||||
ui->modpackEdit->setValidator(new UrlValidator(ui->modpackEdit));
|
||||
|
||||
ui->instNameTextBox->setAlignment(Qt::AlignHCenter);
|
||||
|
||||
connect(ui->modpackEdit, &QLineEdit::textChanged, this, &NewInstanceDialog::updateDialogState);
|
||||
connect(ui->modpackBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState);
|
||||
connect(ui->versionBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState);
|
||||
connect(ui->versionTextBox, &QLineEdit::textChanged, this, &NewInstanceDialog::updateDialogState);
|
||||
|
||||
auto groups = MMC->instances()->getGroups().toSet();
|
||||
auto groupList = QStringList(groups.toList());
|
||||
groupList.sort(Qt::CaseInsensitive);
|
||||
groupList.removeOne("");
|
||||
QString oldValue = MMC->settings()->get("LastUsedGroupForNewInstance").toString();
|
||||
groupList.push_front(oldValue);
|
||||
groupList.push_front("");
|
||||
ui->groupBox->addItems(groupList);
|
||||
int index = groupList.indexOf(oldValue);
|
||||
if(index == -1)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
ui->groupBox->setCurrentIndex(index);
|
||||
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
|
||||
ui->buttonBox->setFocus();
|
||||
|
||||
originalPlaceholderText = ui->instNameTextBox->placeholderText();
|
||||
updateDialogState();
|
||||
}
|
||||
|
||||
NewInstanceDialog::~NewInstanceDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void NewInstanceDialog::updateDialogState()
|
||||
{
|
||||
QString suggestedName;
|
||||
if(ui->versionBox->isChecked())
|
||||
{
|
||||
suggestedName = ui->versionTextBox->text();
|
||||
}
|
||||
else if (ui->modpackBox->isChecked())
|
||||
{
|
||||
auto url = QUrl::fromUserInput(ui->modpackEdit->text());
|
||||
QFileInfo fi(url.fileName());
|
||||
suggestedName = fi.completeBaseName();
|
||||
}
|
||||
if(suggestedName.isEmpty())
|
||||
{
|
||||
ui->instNameTextBox->setPlaceholderText(originalPlaceholderText);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->instNameTextBox->setPlaceholderText(suggestedName);
|
||||
}
|
||||
bool allowOK = !instName().isEmpty() && (
|
||||
(ui->versionBox->isChecked() && m_selectedVersion) ||
|
||||
(ui->modpackBox->isChecked() && ui->modpackEdit->hasAcceptableInput())
|
||||
);
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(allowOK);
|
||||
}
|
||||
|
||||
void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version)
|
||||
{
|
||||
m_selectedVersion = version;
|
||||
|
||||
if (m_selectedVersion)
|
||||
{
|
||||
ui->versionTextBox->setText(version->name());
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->versionTextBox->setText("");
|
||||
}
|
||||
}
|
||||
|
||||
QString NewInstanceDialog::instName() const
|
||||
{
|
||||
auto result = ui->instNameTextBox->text();
|
||||
if(result.size())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result = ui->instNameTextBox->placeholderText();
|
||||
if(result.size() && result != originalPlaceholderText)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString NewInstanceDialog::instGroup() const
|
||||
{
|
||||
return ui->groupBox->currentText();
|
||||
}
|
||||
QString NewInstanceDialog::iconKey() const
|
||||
{
|
||||
return InstIconKey;
|
||||
}
|
||||
QUrl NewInstanceDialog::modpackUrl() const
|
||||
{
|
||||
if (ui->modpackBox->isChecked())
|
||||
{
|
||||
const QUrl url(ui->modpackEdit->text());
|
||||
if (url.isValid() && !url.isRelative() && !url.host().isEmpty())
|
||||
{
|
||||
return url;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QUrl::fromLocalFile(ui->modpackEdit->text());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return QUrl();
|
||||
}
|
||||
}
|
||||
|
||||
BaseVersionPtr NewInstanceDialog::selectedVersion() const
|
||||
{
|
||||
return m_selectedVersion;
|
||||
}
|
||||
|
||||
void NewInstanceDialog::on_btnChangeVersion_clicked()
|
||||
{
|
||||
VersionSelectDialog vselect(MMC->minecraftlist().get(), tr("Change Minecraft version"),
|
||||
this);
|
||||
vselect.exec();
|
||||
if (vselect.result() == QDialog::Accepted)
|
||||
{
|
||||
BaseVersionPtr version = vselect.selectedVersion();
|
||||
if (version)
|
||||
setSelectedVersion(version);
|
||||
}
|
||||
}
|
||||
|
||||
void NewInstanceDialog::on_iconButton_clicked()
|
||||
{
|
||||
IconPickerDialog dlg(this);
|
||||
dlg.execWithSelection(InstIconKey);
|
||||
|
||||
if (dlg.result() == QDialog::Accepted)
|
||||
{
|
||||
InstIconKey = dlg.selectedIconKey;
|
||||
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
|
||||
}
|
||||
}
|
||||
|
||||
void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
|
||||
{
|
||||
updateDialogState();
|
||||
}
|
||||
|
||||
void NewInstanceDialog::on_modpackBtn_clicked()
|
||||
{
|
||||
const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), tr("Zip (*.zip)"));
|
||||
if (url.isValid())
|
||||
{
|
||||
if (url.isLocalFile())
|
||||
{
|
||||
ui->modpackEdit->setText(url.toLocalFile());
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->modpackEdit->setText(url.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "logic/BaseVersion.h"
|
||||
|
||||
#include "BaseVersion.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -33,18 +34,21 @@ public:
|
||||
|
||||
void updateDialogState();
|
||||
|
||||
void setSelectedVersion(BaseVersionPtr version, bool initial = false);
|
||||
void setSelectedVersion(BaseVersionPtr version);
|
||||
|
||||
void loadVersionList();
|
||||
|
||||
QString instName() const;
|
||||
QString instGroup() const;
|
||||
QString iconKey() const;
|
||||
QUrl modpackUrl() const;
|
||||
BaseVersionPtr selectedVersion() const;
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_btnChangeVersion_clicked();
|
||||
void on_iconButton_clicked();
|
||||
void on_modpackBtn_clicked();
|
||||
void on_instNameTextBox_textChanged(const QString &arg1);
|
||||
|
||||
private:
|
||||
@@ -52,4 +56,5 @@ private:
|
||||
|
||||
BaseVersionPtr m_selectedVersion;
|
||||
QString InstIconKey;
|
||||
QString originalPlaceholderText;
|
||||
};
|
||||
312
application/dialogs/NewInstanceDialog.ui
Normal file
312
application/dialogs/NewInstanceDialog.ui
Normal file
@@ -0,0 +1,312 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NewInstanceDialog</class>
|
||||
<widget class="QDialog" name="NewInstanceDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>281</width>
|
||||
<height>404</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>New Instance</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset>
|
||||
<normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="iconBtnLayout">
|
||||
<item>
|
||||
<spacer name="iconBtnLeftSpacer">
|
||||
<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="QToolButton" name="iconButton">
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="iconBtnRightSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="instNameTextBox">
|
||||
<property name="placeholderText">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelVersion_3">
|
||||
<property name="text">
|
||||
<string>&Group:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>groupBox</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="4" column="2">
|
||||
<widget class="QToolButton" name="modpackBtn">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="FocusLineEdit" name="modpackEdit">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">http://</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLineEdit" name="versionTextBox">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QToolButton" name="btnChangeVersion">
|
||||
<property name="text">
|
||||
<string notr="true">...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="modpackBox">
|
||||
<property name="text">
|
||||
<string>Impor&t Modpack (local file or link):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="versionBox">
|
||||
<property name="text">
|
||||
<string>Vani&lla Minecraft (select version):</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>FocusLineEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>widgets/FocusLineEdit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>instNameTextBox</tabstop>
|
||||
<tabstop>groupBox</tabstop>
|
||||
<tabstop>versionBox</tabstop>
|
||||
<tabstop>versionTextBox</tabstop>
|
||||
<tabstop>btnChangeVersion</tabstop>
|
||||
<tabstop>modpackBox</tabstop>
|
||||
<tabstop>modpackEdit</tabstop>
|
||||
<tabstop>modpackBtn</tabstop>
|
||||
<tabstop>iconButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>NewInstanceDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>257</x>
|
||||
<y>333</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>NewInstanceDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>333</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>modpackBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>modpackEdit</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>81</x>
|
||||
<y>229</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>236</x>
|
||||
<y>221</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>modpackBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>modpackBtn</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>129</x>
|
||||
<y>225</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>328</x>
|
||||
<y>229</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>versionBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>versionTextBox</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>93</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>213</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>versionBox</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>btnChangeVersion</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>104</x>
|
||||
<y>198</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>322</x>
|
||||
<y>192</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "logic/updater/NotificationChecker.h"
|
||||
#include "notifications/NotificationChecker.h"
|
||||
|
||||
namespace Ui {
|
||||
class NotificationDialog;
|
||||
@@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
<string>Notification</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||
<item>
|
||||
@@ -19,14 +19,14 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="iconLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="messageLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
<string notr="true">TextLabel</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,12 +18,10 @@
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "logic/tasks/Task.h"
|
||||
#include "gui/Platform.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ProgressDialog)
|
||||
{
|
||||
MultiMCPlatform::fixWM_CLASS(this);
|
||||
ui->setupUi(this);
|
||||
this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setSkipButton(false);
|
||||
@@ -54,9 +52,15 @@ void ProgressDialog::updateSize()
|
||||
resize(QSize(480, minimumSizeHint().height()));
|
||||
}
|
||||
|
||||
int ProgressDialog::exec(ProgressProvider *task)
|
||||
int ProgressDialog::execWithTask(Task *task)
|
||||
{
|
||||
this->task = task;
|
||||
QDialog::DialogCode result;
|
||||
|
||||
if(handleImmediateResult(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// Connect signals.
|
||||
connect(task, SIGNAL(started()), SLOT(onTaskStarted()));
|
||||
@@ -67,14 +71,43 @@ int ProgressDialog::exec(ProgressProvider *task)
|
||||
|
||||
// if this didn't connect to an already running task, invoke start
|
||||
if(!task->isRunning())
|
||||
{
|
||||
task->start();
|
||||
}
|
||||
if(task->isRunning())
|
||||
{
|
||||
changeProgress(task->getProgress(), task->getTotalProgress());
|
||||
changeStatus(task->getStatus());
|
||||
return QDialog::exec();
|
||||
}
|
||||
else if(handleImmediateResult(result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
{
|
||||
return QDialog::Rejected;
|
||||
}
|
||||
}
|
||||
|
||||
ProgressProvider *ProgressDialog::getTask()
|
||||
bool ProgressDialog::handleImmediateResult(QDialog::DialogCode &result)
|
||||
{
|
||||
if(task->isFinished())
|
||||
{
|
||||
if(task->successful())
|
||||
{
|
||||
result = QDialog::Accepted;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = QDialog::Rejected;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Task *ProgressDialog::getTask()
|
||||
{
|
||||
return task;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class ProgressProvider;
|
||||
class Task;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -34,14 +34,11 @@ public:
|
||||
|
||||
void updateSize();
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Woverloaded-virtual"
|
||||
int exec(ProgressProvider *task);
|
||||
#pragma clang diagnostic pop
|
||||
int execWithTask(Task *task);
|
||||
|
||||
void setSkipButton(bool present, QString label = QString());
|
||||
|
||||
ProgressProvider *getTask();
|
||||
Task *getTask();
|
||||
|
||||
public
|
||||
slots:
|
||||
@@ -52,7 +49,7 @@ slots:
|
||||
void changeStatus(const QString &status);
|
||||
void changeProgress(qint64 current, qint64 total);
|
||||
|
||||
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_skipButton_clicked(bool checked);
|
||||
@@ -61,8 +58,11 @@ protected:
|
||||
virtual void keyPressEvent(QKeyEvent *e);
|
||||
virtual void closeEvent(QCloseEvent *e);
|
||||
|
||||
private:
|
||||
bool handleImmediateResult(QDialog::DialogCode &result);
|
||||
|
||||
private:
|
||||
Ui::ProgressDialog *ui;
|
||||
|
||||
ProgressProvider *task;
|
||||
Task *task;
|
||||
};
|
||||
226
application/dialogs/UpdateDialog.cpp
Normal file
226
application/dialogs/UpdateDialog.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
#include "UpdateDialog.h"
|
||||
#include "ui_UpdateDialog.h"
|
||||
#include <QDebug>
|
||||
#include "MultiMC.h"
|
||||
#include <settings/SettingsObject.h>
|
||||
#include <Json.h>
|
||||
|
||||
#include <hoedown/html.h>
|
||||
#include <hoedown/document.h>
|
||||
|
||||
#include "BuildConfig.h"
|
||||
|
||||
UpdateDialog::UpdateDialog(bool hasUpdate, QWidget *parent) : QDialog(parent), ui(new Ui::UpdateDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
auto channel = MMC->settings()->get("UpdateChannel").toString();
|
||||
if(hasUpdate)
|
||||
{
|
||||
ui->label->setText(tr("A new %1 update is available!").arg(channel));
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->label->setText(tr("No %1 updates found. You are running the latest version.").arg(channel));
|
||||
ui->btnUpdateNow->setHidden(true);
|
||||
ui->btnUpdateLater->setText(tr("Close"));
|
||||
}
|
||||
loadChangelog();
|
||||
}
|
||||
|
||||
UpdateDialog::~UpdateDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateDialog::loadChangelog()
|
||||
{
|
||||
auto channel = MMC->settings()->get("UpdateChannel").toString();
|
||||
dljob.reset(new NetJob("Changelog"));
|
||||
QString url;
|
||||
if(channel == "stable")
|
||||
{
|
||||
url = QString("https://raw.githubusercontent.com/MultiMC/MultiMC5/%1/changelog.md").arg(channel);
|
||||
m_changelogType = CHANGELOG_MARKDOWN;
|
||||
}
|
||||
else
|
||||
{
|
||||
url = QString("https://api.github.com/repos/MultiMC/MultiMC5/compare/%1...%2").arg(BuildConfig.GIT_COMMIT, channel);
|
||||
m_changelogType = CHANGELOG_COMMITS;
|
||||
}
|
||||
changelogDownload = ByteArrayDownload::make(QUrl(url));
|
||||
dljob->addNetAction(changelogDownload);
|
||||
connect(dljob.get(), &NetJob::succeeded, this, &UpdateDialog::changelogLoaded);
|
||||
connect(dljob.get(), &NetJob::failed, this, &UpdateDialog::changelogFailed);
|
||||
dljob->start();
|
||||
}
|
||||
|
||||
/**
|
||||
* hoedown wrapper, because dealing with resource lifetime in C is stupid
|
||||
*/
|
||||
class HoeDown
|
||||
{
|
||||
public:
|
||||
class buffer
|
||||
{
|
||||
public:
|
||||
buffer(size_t unit = 4096)
|
||||
{
|
||||
buf = hoedown_buffer_new(unit);
|
||||
}
|
||||
~buffer()
|
||||
{
|
||||
hoedown_buffer_free(buf);
|
||||
}
|
||||
const char * cstr()
|
||||
{
|
||||
return hoedown_buffer_cstr(buf);
|
||||
}
|
||||
void put(QByteArray input)
|
||||
{
|
||||
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
|
||||
}
|
||||
const uint8_t * data() const
|
||||
{
|
||||
return buf->data;
|
||||
}
|
||||
size_t size() const
|
||||
{
|
||||
return buf->size;
|
||||
}
|
||||
hoedown_buffer * buf;
|
||||
} ib, ob;
|
||||
HoeDown()
|
||||
{
|
||||
renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0);
|
||||
document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8);
|
||||
}
|
||||
~HoeDown()
|
||||
{
|
||||
hoedown_document_free(document);
|
||||
hoedown_html_renderer_free(renderer);
|
||||
}
|
||||
QString process(QByteArray input)
|
||||
{
|
||||
ib.put(input);
|
||||
hoedown_document_render(document, ob.buf, ib.data(), ib.size());
|
||||
return ob.cstr();
|
||||
}
|
||||
private:
|
||||
hoedown_document * document;
|
||||
hoedown_renderer * renderer;
|
||||
};
|
||||
|
||||
QString reprocessMarkdown(QByteArray markdown)
|
||||
{
|
||||
HoeDown hoedown;
|
||||
QString output = hoedown.process(markdown);
|
||||
|
||||
// HACK: easier than customizing hoedown
|
||||
output.replace(QRegExp("GH-([0-9]+)"), "<a href=\"https://github.com/MultiMC/MultiMC5/issues/\\1\">GH-\\1</a>");
|
||||
qDebug() << output;
|
||||
return output;
|
||||
}
|
||||
|
||||
QString reprocessCommits(QByteArray json)
|
||||
{
|
||||
auto channel = MMC->settings()->get("UpdateChannel").toString();
|
||||
try
|
||||
{
|
||||
QString result;
|
||||
auto document = Json::requireDocument(json);
|
||||
auto rootobject = Json::requireObject(document);
|
||||
auto status = Json::requireString(rootobject, "status");
|
||||
auto diff_url = Json::requireString(rootobject, "html_url");
|
||||
|
||||
auto print_commits = [&]()
|
||||
{
|
||||
result += "<table cellspacing=0 cellpadding=2 style='border-width: 1px; border-style: solid'>";
|
||||
auto commitarray = Json::requireArray(rootobject, "commits");
|
||||
for(int i = commitarray.size() - 1; i >= 0; i--)
|
||||
{
|
||||
const auto & commitval = commitarray[i];
|
||||
auto commitobj = Json::requireObject(commitval);
|
||||
auto commit_url = Json::requireString(commitobj, "html_url");
|
||||
auto commit_info = Json::requireObject(commitobj, "commit");
|
||||
auto commit_message = Json::requireString(commit_info, "message");
|
||||
auto lines = commit_message.split('\n');
|
||||
QRegularExpression regexp("(?<prefix>(GH-(?<issuenr>[0-9]+))|(NOISSUE)|(SCRATCH))? *(?<rest>.*) *");
|
||||
auto match = regexp.match(lines.takeFirst(), 0, QRegularExpression::NormalMatch);
|
||||
auto issuenr = match.captured("issuenr");
|
||||
auto prefix = match.captured("prefix");
|
||||
auto rest = match.captured("rest");
|
||||
result += "<tr><td>";
|
||||
if(issuenr.length())
|
||||
{
|
||||
result += QString("<a href=\"https://github.com/MultiMC/MultiMC5/issues/%1\">GH-%2</a>").arg(issuenr, issuenr);
|
||||
}
|
||||
else if(prefix.length())
|
||||
{
|
||||
result += QString("<a href=\"%1\">%2</a>").arg(commit_url, prefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
result += QString("<a href=\"%1\">NOISSUE</a>").arg(commit_url);
|
||||
}
|
||||
result += "</td>";
|
||||
lines.prepend(rest);
|
||||
result += "<td><p>" + lines.join("<br />") + "</p></td></tr>";
|
||||
}
|
||||
result += "</table>";
|
||||
};
|
||||
|
||||
if(status == "identical")
|
||||
{
|
||||
return QObject::tr("<p>There are no code changes between your current version and latest %1.</p>").arg(channel);
|
||||
}
|
||||
else if(status == "ahead")
|
||||
{
|
||||
result += QObject::tr("<p>Following commits were added since last update:</p>");
|
||||
print_commits();
|
||||
}
|
||||
else if(status == "diverged")
|
||||
{
|
||||
auto commit_ahead = Json::requireInteger(rootobject, "ahead_by");
|
||||
auto commit_behind = Json::requireInteger(rootobject, "behind_by");
|
||||
result += QObject::tr("<p>The update removes %1 commits and adds the following %2:</p>").arg(commit_behind).arg(commit_ahead);
|
||||
print_commits();
|
||||
}
|
||||
result += QObject::tr("<p>You can <a href=\"%1\">look at the changes on github</a>.</p>").arg(diff_url);
|
||||
return result;
|
||||
}
|
||||
catch (JSONValidationError & e)
|
||||
{
|
||||
qWarning() << "Got an unparseable commit log from github:" << e.what();
|
||||
qDebug() << json;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void UpdateDialog::changelogLoaded()
|
||||
{
|
||||
QString result;
|
||||
switch(m_changelogType)
|
||||
{
|
||||
case CHANGELOG_COMMITS:
|
||||
result = reprocessCommits(changelogDownload->m_data);
|
||||
break;
|
||||
case CHANGELOG_MARKDOWN:
|
||||
result = reprocessMarkdown(changelogDownload->m_data);
|
||||
break;
|
||||
}
|
||||
ui->changelogBrowser->setHtml(result);
|
||||
}
|
||||
|
||||
void UpdateDialog::changelogFailed(QString reason)
|
||||
{
|
||||
ui->changelogBrowser->setHtml(tr("<p align=\"center\" <span style=\"font-size:22pt;\">Failed to fetch changelog... Error: %1</span></p>").arg(reason));
|
||||
}
|
||||
|
||||
void UpdateDialog::on_btnUpdateLater_clicked()
|
||||
{
|
||||
reject();
|
||||
}
|
||||
|
||||
void UpdateDialog::on_btnUpdateNow_clicked()
|
||||
{
|
||||
done(UPDATE_NOW);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,8 +16,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include "logic/net/ByteArrayDownload.h"
|
||||
#include "logic/net/NetJob.h"
|
||||
#include "net/ByteArrayDownload.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
@@ -28,7 +28,12 @@ enum UpdateAction
|
||||
{
|
||||
UPDATE_LATER = QDialog::Rejected,
|
||||
UPDATE_NOW = QDialog::Accepted,
|
||||
UPDATE_ONEXIT = 2
|
||||
};
|
||||
|
||||
enum ChangelogType
|
||||
{
|
||||
CHANGELOG_MARKDOWN,
|
||||
CHANGELOG_COMMITS
|
||||
};
|
||||
|
||||
class UpdateDialog : public QDialog
|
||||
@@ -43,19 +48,19 @@ private:
|
||||
Ui::UpdateDialog *ui;
|
||||
public slots:
|
||||
void on_btnUpdateNow_clicked();
|
||||
void on_btnUpdateOnExit_clicked();
|
||||
void on_btnUpdateLater_clicked();
|
||||
|
||||
/// Starts loading the changelog
|
||||
void loadChangelog();
|
||||
|
||||
|
||||
/// Slot for when the chengelog loads successfully.
|
||||
void changelogLoaded();
|
||||
|
||||
|
||||
/// Slot for when the chengelog fails to load...
|
||||
void changelogFailed();
|
||||
|
||||
void changelogFailed(QString reason);
|
||||
|
||||
private:
|
||||
ByteArrayDownloadPtr changelogDownload;
|
||||
NetJobPtr dljob;
|
||||
ChangelogType m_changelogType = CHANGELOG_MARKDOWN;
|
||||
};
|
||||
@@ -46,8 +46,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:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:22pt;">Loading changelog...</span></p></body></html></string>
|
||||
</style></head><body style=" font-family:'Noto Sans'; font-size:12pt; font-weight:400; font-style:normal;">
|
||||
<p align="center" style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:22pt;">Loading changelog...</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
@@ -55,8 +55,8 @@ p, li { white-space: pre-wrap; }
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QPushButton" name="btnUpdateNow">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
@@ -69,14 +69,7 @@ p, li { white-space: pre-wrap; }
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnUpdateOnExit">
|
||||
<property name="text">
|
||||
<string>Update after MultiMC closes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QPushButton" name="btnUpdateLater">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
@@ -96,7 +89,6 @@ p, li { white-space: pre-wrap; }
|
||||
<tabstops>
|
||||
<tabstop>changelogBrowser</tabstop>
|
||||
<tabstop>btnUpdateNow</tabstop>
|
||||
<tabstop>btnUpdateOnExit</tabstop>
|
||||
<tabstop>btnUpdateLater</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
191
application/dialogs/VersionSelectDialog.cpp
Normal file
191
application/dialogs/VersionSelectDialog.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/* Copyright 2013-2015 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 "VersionSelectDialog.h"
|
||||
#include "ui_VersionSelectDialog.h"
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
#include <dialogs/ProgressDialog.h>
|
||||
#include "CustomMessageBox.h"
|
||||
|
||||
#include <BaseVersion.h>
|
||||
#include <BaseVersionList.h>
|
||||
#include <tasks/Task.h>
|
||||
#include <QDebug>
|
||||
#include "MultiMC.h"
|
||||
#include <VersionProxyModel.h>
|
||||
|
||||
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent,
|
||||
bool cancelable)
|
||||
: QDialog(parent), ui(new Ui::VersionSelectDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowModality(Qt::WindowModal);
|
||||
setWindowTitle(title);
|
||||
|
||||
m_vlist = vlist;
|
||||
|
||||
m_proxyModel = new VersionProxyModel(this);
|
||||
m_proxyModel->setSourceModel(vlist);
|
||||
|
||||
ui->listView->setModel(m_proxyModel);
|
||||
ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
ui->sneakyProgressBar->setHidden(true);
|
||||
|
||||
if (!cancelable)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setEmptyString(QString emptyString)
|
||||
{
|
||||
ui->listView->setEmptyString(emptyString);
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setEmptyErrorString(QString emptyErrorString)
|
||||
{
|
||||
ui->listView->setEmptyErrorString(emptyErrorString);
|
||||
}
|
||||
|
||||
VersionSelectDialog::~VersionSelectDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setResizeOn(int column)
|
||||
{
|
||||
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents);
|
||||
resizeOnColumn = column;
|
||||
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
|
||||
}
|
||||
|
||||
int VersionSelectDialog::exec()
|
||||
{
|
||||
QDialog::open();
|
||||
if (!m_vlist->isLoaded())
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_proxyModel->rowCount() == 0)
|
||||
{
|
||||
ui->listView->setEmptyMode(VersionListView::String);
|
||||
}
|
||||
preselect();
|
||||
}
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::closeEvent(QCloseEvent * event)
|
||||
{
|
||||
if(loadTask)
|
||||
{
|
||||
loadTask->abort();
|
||||
loadTask->deleteLater();
|
||||
loadTask = nullptr;
|
||||
}
|
||||
QDialog::closeEvent(event);
|
||||
}
|
||||
|
||||
void VersionSelectDialog::loadList()
|
||||
{
|
||||
if(loadTask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
loadTask = m_vlist->getLoadTask();
|
||||
if (!loadTask)
|
||||
{
|
||||
return;
|
||||
}
|
||||
connect(loadTask, &Task::finished, this, &VersionSelectDialog::onTaskFinished);
|
||||
connect(loadTask, &Task::progress, this, &VersionSelectDialog::changeProgress);
|
||||
loadTask->start();
|
||||
ui->sneakyProgressBar->setHidden(false);
|
||||
}
|
||||
|
||||
void VersionSelectDialog::onTaskFinished()
|
||||
{
|
||||
if (!loadTask->successful())
|
||||
{
|
||||
CustomMessageBox::selectable(this, tr("Error"),
|
||||
tr("List update failed:\n%1").arg(loadTask->failReason()),
|
||||
QMessageBox::Warning)->show();
|
||||
if (m_proxyModel->rowCount() == 0)
|
||||
{
|
||||
ui->listView->setEmptyMode(VersionListView::ErrorString);
|
||||
}
|
||||
}
|
||||
else if (m_proxyModel->rowCount() == 0)
|
||||
{
|
||||
ui->listView->setEmptyMode(VersionListView::String);
|
||||
}
|
||||
ui->sneakyProgressBar->setHidden(true);
|
||||
loadTask->deleteLater();
|
||||
loadTask = nullptr;
|
||||
preselect();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::changeProgress(qint64 current, qint64 total)
|
||||
{
|
||||
ui->sneakyProgressBar->setMaximum(total);
|
||||
ui->sneakyProgressBar->setValue(current);
|
||||
}
|
||||
|
||||
void VersionSelectDialog::preselect()
|
||||
{
|
||||
if(preselectedAlready)
|
||||
return;
|
||||
preselectedAlready = true;
|
||||
selectRecommended();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::selectRecommended()
|
||||
{
|
||||
auto idx = m_proxyModel->getRecommended();
|
||||
if(idx.isValid())
|
||||
{
|
||||
ui->listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
|
||||
ui->listView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
|
||||
}
|
||||
}
|
||||
|
||||
BaseVersionPtr VersionSelectDialog::selectedVersion() const
|
||||
{
|
||||
auto currentIndex = ui->listView->selectionModel()->currentIndex();
|
||||
auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole);
|
||||
return variant.value<BaseVersionPtr>();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::on_refreshButton_clicked()
|
||||
{
|
||||
loadList();
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilter(role, filter, true);
|
||||
}
|
||||
|
||||
void VersionSelectDialog::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter)
|
||||
{
|
||||
m_proxyModel->setFilter(role, filter, false);
|
||||
}
|
||||
|
||||
#include "VersionSelectDialog.moc"
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,15 +18,15 @@
|
||||
#include <QDialog>
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include "logic/BaseVersion.h"
|
||||
|
||||
class BaseVersionList;
|
||||
#include "BaseVersionList.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class VersionSelectDialog;
|
||||
}
|
||||
|
||||
class VersionProxyModel;
|
||||
|
||||
class VersionSelectDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
@@ -43,21 +43,37 @@ public:
|
||||
|
||||
BaseVersionPtr selectedVersion() const;
|
||||
|
||||
void setFuzzyFilter(int column, QString filter);
|
||||
void setExactFilter(int column, QString filter);
|
||||
void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter);
|
||||
void setExactFilter(BaseVersionList::ModelRoles role, QString filter);
|
||||
void setEmptyString(QString emptyString);
|
||||
void setEmptyErrorString(QString emptyErrorString);
|
||||
void setResizeOn(int column);
|
||||
void setUseLatest(const bool useLatest);
|
||||
|
||||
protected:
|
||||
virtual void closeEvent ( QCloseEvent* );
|
||||
|
||||
private
|
||||
slots:
|
||||
void on_refreshButton_clicked();
|
||||
|
||||
void onTaskFinished();
|
||||
void changeProgress(qint64 current, qint64 total);
|
||||
|
||||
private:
|
||||
Ui::VersionSelectDialog *ui;
|
||||
void preselect();
|
||||
void selectRecommended();
|
||||
|
||||
BaseVersionList *m_vlist;
|
||||
private:
|
||||
Ui::VersionSelectDialog *ui = nullptr;
|
||||
|
||||
QSortFilterProxyModel *m_proxyModel;
|
||||
BaseVersionList *m_vlist = nullptr;
|
||||
|
||||
VersionProxyModel *m_proxyModel = nullptr;
|
||||
|
||||
int resizeOnColumn = 0;
|
||||
|
||||
Task * loadTask = nullptr;
|
||||
|
||||
bool preselectedAlready = false;
|
||||
};
|
||||
@@ -39,6 +39,16 @@
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="sneakyProgressBar">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string notr="true">%p%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
@@ -69,7 +79,7 @@
|
||||
<customwidget>
|
||||
<class>VersionListView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>gui/widgets/VersionListView.h</header>
|
||||
<header>widgets/VersionListView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "VisualGroup.h"
|
||||
#include "logger/QsLog.h"
|
||||
#include <QDebug>
|
||||
|
||||
template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2)
|
||||
{
|
||||
@@ -62,12 +62,28 @@ void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
|
||||
scheduleDelayedItemsLayout();
|
||||
}
|
||||
|
||||
class LocaleString : public QString
|
||||
{
|
||||
public:
|
||||
LocaleString(const char *s) : QString(s)
|
||||
{
|
||||
}
|
||||
LocaleString(const QString &s) : QString(s)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline bool operator<(const LocaleString &lhs, const LocaleString &rhs)
|
||||
{
|
||||
return (QString::localeAwareCompare(lhs, rhs) < 0);
|
||||
}
|
||||
|
||||
void GroupView::updateGeometries()
|
||||
{
|
||||
geometryCache.clear();
|
||||
int previousScroll = verticalScrollBar()->value();
|
||||
|
||||
QMap<QString, VisualGroup *> cats;
|
||||
QMap<LocaleString, VisualGroup *> cats;
|
||||
|
||||
for (int i = 0; i < model()->rowCount(); ++i)
|
||||
{
|
||||
@@ -205,7 +221,7 @@ void GroupView::mousePressEvent(QMouseEvent *event)
|
||||
|
||||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
|
||||
|
||||
QPersistentModelIndex index = indexAt(visualPos);
|
||||
|
||||
m_pressedIndex = index;
|
||||
@@ -681,95 +697,6 @@ bool GroupView::isDragEventAccepted(QDropEvent *event)
|
||||
QPair<VisualGroup *, int> GroupView::rowDropPos(const QPoint &pos)
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
// FIXME: PIXIE DUST.
|
||||
/*
|
||||
// check that we aren't on a category header and calculate which category we're in
|
||||
VisualGroup *category = 0;
|
||||
{
|
||||
int y = 0;
|
||||
for (auto cat : m_groups)
|
||||
{
|
||||
if (pos.y() > y && pos.y() < (y + cat->headerHeight()))
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
}
|
||||
y += cat->totalHeight() + m_categoryMargin;
|
||||
if (pos.y() < y)
|
||||
{
|
||||
category = cat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (category == 0)
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QModelIndex> indices = category->items();
|
||||
|
||||
// calculate the internal column
|
||||
int internalColumn = -1;
|
||||
{
|
||||
const int itemWidth = this->itemWidth();
|
||||
if (pos.x() >= (itemWidth * itemsPerRow()))
|
||||
{
|
||||
internalColumn = itemsPerRow();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 , ++c)
|
||||
{
|
||||
if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2))
|
||||
{
|
||||
internalColumn = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (internalColumn == -1)
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate the internal row
|
||||
int internalRow = -1;
|
||||
{
|
||||
// FIXME rework the drag and drop code
|
||||
const int top = category->verticalPosition();
|
||||
for (int r = 0, h = top; r < category->numRows();
|
||||
h += itemHeightForCategoryRow(category, r), ++r)
|
||||
{
|
||||
if (pos.y() > h && pos.y() < (h + itemHeightForCategoryRow(category, r)))
|
||||
{
|
||||
internalRow = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (internalRow == -1)
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
}
|
||||
// this happens if we're in the margin between a one category and another
|
||||
// categories header
|
||||
if (internalRow > (indices.size() / itemsPerRow()))
|
||||
{
|
||||
return qMakePair<VisualGroup*, int>(nullptr, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// flaten the internalColumn/internalRow to one row
|
||||
int categoryRow = internalRow * itemsPerRow() + internalColumn;
|
||||
|
||||
// this is used if we're past the last item
|
||||
if (categoryRow >= indices.size())
|
||||
{
|
||||
return qMakePair(category, indices.last().row() + 1);
|
||||
}
|
||||
|
||||
return qMakePair(category, indices.at(categoryRow).row());
|
||||
*/
|
||||
}
|
||||
|
||||
QPoint GroupView::offset() const
|
||||
@@ -50,13 +50,15 @@ public:
|
||||
{
|
||||
return m_spacing;
|
||||
};
|
||||
protected
|
||||
slots:
|
||||
|
||||
public slots:
|
||||
virtual void updateGeometries() override;
|
||||
|
||||
protected slots:
|
||||
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
|
||||
const QVector<int> &roles) override;
|
||||
virtual void rowsInserted(const QModelIndex &parent, int start, int end) override;
|
||||
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override;
|
||||
virtual void updateGeometries() override;
|
||||
void modelReset();
|
||||
|
||||
protected:
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "GroupedProxyModel.h"
|
||||
|
||||
#include "GroupView.h"
|
||||
#include <QDebug>
|
||||
|
||||
GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
|
||||
{
|
||||
@@ -16,7 +17,13 @@ bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
|
||||
}
|
||||
else
|
||||
{
|
||||
return leftCategory < rightCategory;
|
||||
// FIXME: real group sorting happens in GroupView::updateGeometries(), see LocaleString
|
||||
auto result = leftCategory.localeAwareCompare(rightCategory);
|
||||
if(result == 0)
|
||||
{
|
||||
return subSortLessThan(left, right);
|
||||
}
|
||||
return result < 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,8 +21,8 @@
|
||||
#include <QtMath>
|
||||
|
||||
#include "GroupView.h"
|
||||
#include "logic/BaseInstance.h"
|
||||
#include "logic/InstanceList.h"
|
||||
#include "BaseInstance.h"
|
||||
#include "InstanceList.h"
|
||||
|
||||
QCache<QString, QPixmap> ListViewDelegate::m_pixmapCache;
|
||||
|
||||
@@ -113,16 +113,14 @@ void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option
|
||||
void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseInstance *instance)
|
||||
{
|
||||
QList<QString> pixmaps;
|
||||
for (auto flag : instance->flags())
|
||||
const BaseInstance::InstanceFlags flags = instance->flags();
|
||||
if (flags & BaseInstance::VersionBrokenFlag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case BaseInstance::VersionBrokenFlag:
|
||||
pixmaps.append("broken");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pixmaps.append("broken");
|
||||
}
|
||||
if (flags & BaseInstance::UpdateAvailable)
|
||||
{
|
||||
pixmaps.append("updateavailable");
|
||||
}
|
||||
|
||||
// begin easter eggs
|
||||
@@ -160,7 +158,7 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseIns
|
||||
{
|
||||
return;
|
||||
}
|
||||
const QPixmap pixmap = ListViewDelegate::requestPixmap(it.next()).scaled(
|
||||
const QPixmap pixmap = ListViewDelegate::requestBadgePixmap(it.next()).scaled(
|
||||
itemSide, itemSide, Qt::KeepAspectRatio, Qt::FastTransformation);
|
||||
painter->drawPixmap(option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide,
|
||||
y * itemSide + qMax(y - 1, 0) * spacing, itemSide, itemSide,
|
||||
@@ -354,7 +352,7 @@ QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
return sz;
|
||||
}
|
||||
|
||||
QPixmap ListViewDelegate::requestPixmap(const QString &key)
|
||||
QPixmap ListViewDelegate::requestBadgePixmap(const QString &key)
|
||||
{
|
||||
if (!m_pixmapCache.contains(key))
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
/* Copyright 2013 MultiMC Contributors
|
||||
/* Copyright 2013-2015 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,7 +23,7 @@ class ListViewDelegate : public QStyledItemDelegate
|
||||
public:
|
||||
explicit ListViewDelegate(QObject *parent = 0);
|
||||
|
||||
static QPixmap requestPixmap(const QString &key);
|
||||
static QPixmap requestBadgePixmap(const QString &key);
|
||||
|
||||
protected:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
37
application/handlers/IconResourceHandler.cpp
Normal file
37
application/handlers/IconResourceHandler.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "IconResourceHandler.h"
|
||||
#include <xdgicon.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
QList<std::weak_ptr<IconResourceHandler>> IconResourceHandler::m_iconHandlers;
|
||||
|
||||
IconResourceHandler::IconResourceHandler(const QString &key)
|
||||
: m_key(key)
|
||||
{
|
||||
}
|
||||
|
||||
void IconResourceHandler::setTheme(const QString &theme)
|
||||
{
|
||||
// notify everyone
|
||||
for (auto handler : m_iconHandlers)
|
||||
{
|
||||
std::shared_ptr<IconResourceHandler> ptr = handler.lock();
|
||||
if (ptr)
|
||||
{
|
||||
ptr->setResult(ptr->get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IconResourceHandler::init(std::shared_ptr<ResourceHandler> &ptr)
|
||||
{
|
||||
m_iconHandlers.append(std::dynamic_pointer_cast<IconResourceHandler>(ptr));
|
||||
// we always have a result, so lets report it now!
|
||||
setResult(get());
|
||||
}
|
||||
|
||||
QVariant IconResourceHandler::get() const
|
||||
{
|
||||
return XdgIcon::fromTheme(m_key);
|
||||
}
|
||||
23
application/handlers/IconResourceHandler.h
Normal file
23
application/handlers/IconResourceHandler.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <resources/ResourceHandler.h>
|
||||
|
||||
class IconResourceHandler : public ResourceHandler
|
||||
{
|
||||
public:
|
||||
explicit IconResourceHandler(const QString &key);
|
||||
|
||||
/// Sets the current theme and notifies all IconResourceHandlers of the change
|
||||
static void setTheme(const QString &theme);
|
||||
|
||||
private:
|
||||
// we need to keep track of all IconResourceHandlers so that we can update them if the theme changes
|
||||
void init(std::shared_ptr<ResourceHandler> &ptr) override;
|
||||
static QList<std::weak_ptr<IconResourceHandler>> m_iconHandlers;
|
||||
|
||||
QString m_key;
|
||||
|
||||
// the workhorse, returns QVariantMap (filename => size) for m_key/m_theme
|
||||
QVariant get() const;
|
||||
};
|
||||
68
application/handlers/WebResourceHandler.cpp
Normal file
68
application/handlers/WebResourceHandler.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "WebResourceHandler.h"
|
||||
|
||||
#include "net/CacheDownload.h"
|
||||
#include "net/HttpMetaCache.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Env.h"
|
||||
|
||||
//FIXME: wrong. needs to be done elsewhere.
|
||||
QMap<QString, NetJob *> WebResourceHandler::m_activeDownloads;
|
||||
|
||||
WebResourceHandler::WebResourceHandler(const QString &url)
|
||||
: QObject(), m_url(url)
|
||||
{
|
||||
MetaEntryPtr entry = ENV.metacache()->resolveEntry("icons", url);
|
||||
if (!entry->stale)
|
||||
{
|
||||
setResultFromFile(entry->getFullPath());
|
||||
}
|
||||
else if (m_activeDownloads.contains(url))
|
||||
{
|
||||
NetJob *job = m_activeDownloads.value(url);
|
||||
connect(job, &NetJob::succeeded, this, &WebResourceHandler::succeeded);
|
||||
connect(job, &NetJob::failed, this, [job, this]() {setFailure(job->failReason());});
|
||||
connect(job, &NetJob::progress, this, &WebResourceHandler::progress);
|
||||
}
|
||||
else
|
||||
{
|
||||
NetJob *job = new NetJob("Icon download");
|
||||
job->addNetAction(CacheDownload::make(QUrl(url), entry));
|
||||
connect(job, &NetJob::succeeded, this, &WebResourceHandler::succeeded);
|
||||
connect(job, &NetJob::failed, this, [job, this]() {setFailure(job->failReason());});
|
||||
connect(job, &NetJob::progress, this, &WebResourceHandler::progress);
|
||||
connect(job, &NetJob::finished, job, [job](){m_activeDownloads.remove(m_activeDownloads.key(job));job->deleteLater();});
|
||||
m_activeDownloads.insert(url, job);
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
void WebResourceHandler::succeeded()
|
||||
{
|
||||
MetaEntryPtr entry = ENV.metacache()->resolveEntry("icons", m_url);
|
||||
setResultFromFile(entry->getFullPath());
|
||||
m_activeDownloads.remove(m_activeDownloads.key(qobject_cast<NetJob *>(sender())));
|
||||
}
|
||||
void WebResourceHandler::progress(qint64 current, qint64 total)
|
||||
{
|
||||
if (total == 0)
|
||||
{
|
||||
setProgress(101);
|
||||
}
|
||||
else
|
||||
{
|
||||
setProgress(current / total);
|
||||
}
|
||||
}
|
||||
|
||||
void WebResourceHandler::setResultFromFile(const QString &file)
|
||||
{
|
||||
try
|
||||
{
|
||||
setResult(FS::read(file));
|
||||
}
|
||||
catch (Exception &e)
|
||||
{
|
||||
setFailure(e.cause());
|
||||
}
|
||||
}
|
||||
23
application/handlers/WebResourceHandler.h
Normal file
23
application/handlers/WebResourceHandler.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <resources/ResourceHandler.h>
|
||||
|
||||
class NetJob;
|
||||
|
||||
class WebResourceHandler : public QObject, public ResourceHandler
|
||||
{
|
||||
public:
|
||||
explicit WebResourceHandler(const QString &url);
|
||||
|
||||
private slots:
|
||||
void succeeded();
|
||||
void progress(qint64 current, qint64 total);
|
||||
|
||||
private:
|
||||
static QMap<QString, NetJob *> m_activeDownloads;
|
||||
|
||||
QString m_url;
|
||||
|
||||
void setResultFromFile(const QString &file);
|
||||
};
|
||||
@@ -2,14 +2,15 @@ set(CMAKE_MODULE_PATH "@CMAKE_MODULE_PATH@")
|
||||
|
||||
file(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@")
|
||||
function(gp_resolved_file_type_override resolved_file type_var)
|
||||
if(resolved_file MATCHES "^/usr/lib/libQt")
|
||||
message("resolving ${resolved_file} as other")
|
||||
if(resolved_file MATCHES "^/(usr/)?lib/libQt")
|
||||
set(${type_var} other PARENT_SCOPE)
|
||||
elseif(resolved_file MATCHES "^/usr/lib(.+)?/libxcb")
|
||||
message("resolving ${resolved_file} as other")
|
||||
elseif(resolved_file MATCHES "^/(usr/)?lib(.+)?/libxcb")
|
||||
set(${type_var} other PARENT_SCOPE)
|
||||
elseif(resolved_file MATCHES "^/usr/lib(.+)?/libicu")
|
||||
message("resolving ${resolved_file} as other")
|
||||
elseif(resolved_file MATCHES "^/(usr/)?lib(.+)?/libicu")
|
||||
set(${type_var} other PARENT_SCOPE)
|
||||
elseif(resolved_file MATCHES "^/(usr/)?lib(.+)?/libpng")
|
||||
set(${type_var} other PARENT_SCOPE)
|
||||
elseif((resolved_file MATCHES "^/(usr/)?lib(.+)?/libstdc\\+\\+") AND (UNIX AND NOT APPLE))
|
||||
set(${type_var} other PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user