Compare commits

..

467 Commits

Author SHA1 Message Date
Petr Mrázek
a3cd3d5ff1 NOISSUE update changelog 2016-02-28 20:08:59 +01:00
Petr Mrázek
1a9793197f GH-1502 move launch script generation to the Minecraft launch step 2016-02-28 19:33:05 +01:00
Petr Mrázek
9497b7e96c NOISSUE even more version file refactors
There is no end to them in sight
2016-02-28 19:01:54 +01:00
Petr Mrázek
a0b47aee5b NOISSUE move version file reading and writing to dedicated namespaces 2016-02-27 22:02:56 +01:00
Petr Mrázek
17ad1e64f8 NOISSUE move files into paths that make more sense 2016-02-27 19:58:40 +01:00
Petr Mrázek
71e4b147ec NOISSUE remove OneSixLibrary 2016-02-26 02:04:21 +01:00
Petr Mrázek
f6b2ccb110 NOISSUE remove old unused version file features 2016-02-25 00:29:50 +01:00
Petr Mrázek
c943019ab5 NOISSUE fix a benign leak in FTB implementation 2016-02-25 00:29:08 +01:00
Spencer Burris
fc43fd1105 NOISSUE Add issue template 2016-02-21 14:53:53 -08:00
Petr Mrázek
401d5b698f GH-1453 handle certain version loading corner cases better, clean up FTB 2016-02-21 05:51:36 +01:00
Petr Mrázek
1a0bbdd9ac GH-1453 report version file problems in the version page 2016-02-21 01:44:27 +01:00
Petr Mrázek
495d320ce2 Revert "Update libnbtplusplus with magical changes"
This reverts commit 6e6e2bf262.
2016-02-20 09:11:10 +01:00
Petr Mrázek
5e737f42bf GH-1410 use libc++ on OSX 2016-02-19 00:57:46 +01:00
Petr Mrázek
6e6e2bf262 Update libnbtplusplus with magical changes 2016-02-19 00:02:57 +01:00
Petr Mrázek
163a3095b1 GH-1453 separate out Mojang version reading, use version file URLs 2016-02-17 08:23:57 +01:00
Petr Mrázek
a20e2590da GH-1453 React to the minimum version change - 18 2016-02-13 17:34:27 +01:00
Petr Mrázek
1978078662 NOISSUE remove dead code from MC version list 2016-02-13 17:32:14 +01:00
Petr Mrázek
ea08ede4c3 GH-1483 Use the new version index URL 2016-02-13 16:41:26 +01:00
Petr Mrázek
b7f75637fa GH-1451 add make install step to OSX build instructions 2016-02-11 21:04:10 +01:00
Petr Mrázek
4ee1900201 Merge branch 'patch-1' of git://github.com/iarspider/MultiMC5 into develop 2016-02-03 19:24:51 +01:00
Petr Mrázek
ab67d763f4 NOISSUE bump release number 2016-02-03 19:22:55 +01:00
Petr Mrázek
7ca9f92343 GH-1422 bump version to 0.4.10 and update changelog 2016-01-22 02:00:20 +01:00
Petr Mrázek
cbd4b88e91 GH-1422 remove bearer plugins again. 2016-01-21 20:16:57 +01:00
Petr Mrázek
0958bb2fcc NOISSUE bump version number to 0.4.9 2016-01-21 01:28:39 +01:00
iarspider
fbec48080b NOISSUE Do not ask to overwrite existing file twice when exporting instances 2016-01-20 21:11:58 +03:00
Petr Mrázek
33b6222f9f NOISSUE update changelog 2016-01-20 04:08:29 +01:00
Petr Mrázek
5ecaed21b5 GH-1411 fail if pre-launch and post-exit commands return non-zero values 2016-01-20 03:23:19 +01:00
Petr Mrázek
9fd66b3bb1 GH-1362 Put timestamps into the log when it is copied or uploaded 2016-01-20 03:10:02 +01:00
Petr Mrázek
47ea2a71c0 GH-1408 add bearer plugins
This should fix wifi issues on Windows
2016-01-20 02:44:57 +01:00
Petr Mrázek
2c2b960ab4 GH-1055 fix more types in changelog 2016-01-17 14:43:51 +01:00
Petr Mrázek
ce326ccdff GH-1055 fix typo and missing description 2016-01-17 13:24:59 +01:00
Petr Mrázek
1ed86bfe1a GH-1055 update changelog for 0.4.8 2016-01-17 13:04:07 +01:00
Petr Mrázek
781e53cccb GH-1402 add a hack for updater to respect renamed binary on Windows 2016-01-12 06:52:29 +01:00
Petr Mrázek
25991c36af GH-1280 modpack import: make the progress dialog say what is being downloaded 2016-01-12 05:30:03 +01:00
Petr Mrázek
5ccfbba435 NOISSUE select modpack field content on focus in new instance dialog 2016-01-12 04:37:30 +01:00
Petr Mrázek
1f0e76a3c1 GH-1397 add discord icon to MultiMC toolbar 2016-01-09 01:39:51 +01:00
Petr Mrázek
d8b1ae38fb GH-1379 fix build on win32 2016-01-07 06:47:07 +01:00
Petr Mrázek
59e6b4ed55 GH-1379 fix build with Qt 5.2.1 on ubuntu64 2016-01-07 06:40:26 +01:00
Petr Mrázek
5ff9f90ce9 Merge pull request #1393 from MultiMC/feature/travis-older-qt
GH-1393 Add more qt versions to travis
2016-01-06 11:28:44 +01:00
Jan Dalheimer
34bf4ccdc7 NOISSUE Add more qt versions to travis
5.0, 5.1 and 5.2 are currently marked as "allow failure". If they can be made
to pass they should be removed from this list, if not they should be removed
entirely.
2016-01-06 10:33:47 +01:00
Petr Mrázek
aa8103adf2 GH-1390 improve linux runner script more
* use `-q` while calling dnf to avoid garbage in output
2016-01-05 08:50:47 +01:00
Petr Mrázek
b300c4956c GH-1390 improve linux runner script
* include fedora dnf package manager
* output of the missing dependency lookup is now passed through `sort -u`
2016-01-05 08:40:30 +01:00
Petr Mrázek
4d0caf6254 GH-1389 wrap QDesktopServices and QProcess::startDetached
Essentially do not pass some environment variables to subprocesses:
* LD_PRELOAD
* LD_LIBRARY_PATH
* LD_DEBUG
* QT_PLUGIN_PATH
* QT_FONTPATH
2016-01-05 07:32:52 +01:00
Petr Mrázek
d1e344f28f GH-1389 Revert "GH-1389 remove use of LD_LIBRARY_PATH for mmc libs"
This reverts commit 6f92ca843e.
2016-01-04 02:00:24 +01:00
Petr Mrázek
e0a9970d59 NOISSUE Revert "NOISSUE remove old BundleUtilities. 2014 called and wants its cmake scripts back."
This reverts commit 8cf23bd5aa.
2016-01-04 01:42:48 +01:00
Petr Mrázek
6f92ca843e GH-1389 remove use of LD_LIBRARY_PATH for mmc libs 2016-01-04 01:37:46 +01:00
Petr Mrázek
8cf23bd5aa NOISSUE remove old BundleUtilities. 2014 called and wants its cmake scripts back. 2016-01-03 22:49:31 +01:00
Petr Mrázek
c6afa7d73e GH-1378 fix libpng path matcher for bundle fixup 2016-01-03 16:04:26 +01:00
Petr Mrázek
dcb4e0fa6f GH-1313 tweak the window title to be easier to translate 2016-01-03 16:03:49 +01:00
Petr Mrázek
15aaded80b GH-1313 remove or disable some translateable strings 2016-01-03 03:15:12 +01:00
Petr Mrázek
b9b5a82c2e GH-1313 remove translatable strings from MCModInfoFrame 2016-01-02 04:01:00 +01:00
Petr Mrázek
da2af5e449 GH-1365 fix java detection build on windows 2016-01-02 01:16:44 +01:00
Petr Mrázek
1363b1d364 GH-1365 fix java detection OS ifdefs 2016-01-02 01:13:42 +01:00
Petr Mrázek
a008efd24e GH-1365 rework java version parsing and sorting 2016-01-02 00:35:54 +01:00
Petr Mrázek
5f57df8110 GH-1365 Revert "GH-1365 use the first part of the java version number as the major version - native extraction"
This reverts commit b437988d7b.
2015-12-30 14:24:56 +01:00
Petr Mrázek
b437988d7b GH-1365 use the first part of the java version number as the major version - native extraction 2015-12-28 19:06:03 +01:00
Petr Mrázek
07449e514a GH-1360 fix some typos 2015-12-28 06:00:36 +01:00
Petr Mrázek
bd2843952a NOISSUE export more stuff from Json 2015-12-28 05:47:11 +01:00
Petr Mrázek
5402acb3c6 GH-1360 add basic changelog based on github API, fix update dialog buttons 2015-12-28 05:36:17 +01:00
Petr Mrázek
e8063d193d GH-1308 remove use of static data (translations) and root (notifications.json) paths. 2015-12-27 03:34:03 +01:00
Petr Mrázek
7670d72bd9 GH-1178 sanitize mod paths while installing folder mods 2015-12-26 03:20:41 +01:00
Petr Mrázek
c64a7940c1 GH-1178 add failing test 2015-12-26 03:20:19 +01:00
Petr Mrázek
243c5d1cfb NOISSUE add a basic test for FS::copy 2015-12-26 02:44:33 +01:00
Petr Mrázek
478ff11485 GH-1355 do not allow setting LD_LIBRARY_PATh for Minecraft to the MultiMC bin folder 2015-12-18 05:43:44 +01:00
Soni L
2db4a595dd GH-1355 Fix MMC env propagating to MC after update
This closes GH-1355
2015-12-17 19:38:14 -02:00
Petr Mrázek
2da3162206 GH-1320 do not fail when launcher part can't change sys_paths 2015-12-03 23:27:26 +01:00
Petr Mrázek
a5b8f22eab GH-1339 do not destroy console window while screenshot upload is active 2015-12-03 23:00:51 +01:00
Petr Mrázek
b5902b739e GH-1338: mark asset index as stale during instance update
This forces an update for assets changing on mojang servers
2015-11-19 22:20:40 +01:00
Petr Mrázek
11afc61426 GH-1322 fix mod description ... button popping up multiple dialogs 2015-11-13 00:50:38 +01:00
Petr Mrázek
384c03c9c5 NOISSUE travis: just force it to work. 2015-11-13 00:32:23 +01:00
Petr Mrázek
21490a42bd Revert "NOISSUE travis: use a ppa for cmake 3.2.3"
This reverts commit d855f4905d.
2015-11-13 00:30:40 +01:00
Petr Mrázek
d855f4905d NOISSUE travis: use a ppa for cmake 3.2.3 2015-11-13 00:24:07 +01:00
Petr Mrázek
12c7b11fe6 NOISSUE fix bad patch.exe link and formatting in BUILD document 2015-11-13 00:14:12 +01:00
Petr Mrázek
055774dd58 NOISSUE reword and reformat the README 2015-11-09 00:27:57 +01:00
Petr Mrázek
fdbe431d6b NOISSUE update versions in build how-to 2015-11-09 00:27:24 +01:00
Petr Mrázek
9c01b2294f GH-1319 add patch.exe mention to the build how-to 2015-11-09 00:24:43 +01:00
Petr Mrázek
895d8ab469 GH-1300 call application quit when direct launch instance quits 2015-10-24 00:57:54 +02:00
Petr Mrázek
8a4fd8c468 GH-1301 fix linux runner script argument escaping 2015-10-23 20:01:33 +02:00
Petr Mrázek
61ad480588 NOISSUE update travis to use Qt 5.5.1 2015-10-20 17:42:22 +02:00
Petr Mrázek
79ae4ef2f7 NOISSUE reformat MainWindow with new clang_format settings 2015-10-20 17:22:13 +02:00
Petr Mrázek
125abf5027 NOISSUE rename QObjectPtr to shared_qobject_ptr, introduce unique_qobject_ptr, refactor MainWindow to match 2015-10-20 17:18:53 +02:00
Petr Mrázek
9ad99ac481 NOISSUE raise formatting column limit to 160 2015-10-20 17:02:12 +02:00
Petr Mrázek
69989ab54e GH-1274 update the licenses, the dirty way 2015-10-20 01:50:29 +02:00
Petr Mrázek
7ef1f88de7 NOISSUE fix non-zero exit code from minecraft not being a 'crash' 2015-10-18 02:35:47 +02:00
Petr Mrázek
432e812da1 GH-1275 create server-resource-packs folder on launch
This is a workaround for Minecraft bug MCL-3732
2015-10-12 17:55:10 +02:00
Petr Mrázek
d5aee5fd23 NOISSUE maybe less evil... 2015-10-11 20:06:46 +02:00
Petr Mrázek
457dd2e94e NOISSUE make the compiler more evil 2015-10-11 19:56:31 +02:00
Petr Mrázek
44db72ead5 GH-93 add an option to not copy saves on instance copy 2015-10-10 05:55:55 +02:00
Petr Mrázek
4fbcb3efb9 GH-1268 ... and catch the inevitable errors 2015-10-05 23:52:23 +02:00
Petr Mrázek
439c6b43a3 GH-1268 simplify and fix instance group file IO 2015-10-05 23:48:38 +02:00
Petr Mrázek
db926a546e NOISSUE there is too much ifdef involved... 2015-10-05 02:00:03 +02:00
Petr Mrázek
4a900a58d4 NOISSUE missed a few places... 2015-10-05 01:53:09 +02:00
Petr Mrázek
f93f867c3d NOISSUE dissolve util library 2015-10-05 01:47:27 +02:00
Petr Mrázek
7459eb627c GH-1266 fix ubuntu run script 2015-10-02 23:06:22 +02:00
Petr Mrázek
ae4216de61 GH-719 implement paste.ee API keys 2015-10-02 00:12:53 +02:00
Petr Mrázek
ac8ff88061 GH-729 use mod mmc_id as name when name is empty 2015-09-30 23:30:55 +02:00
Petr Mrázek
477a1a88c6 GH-1262 fix relative paths for java binaries 2015-09-30 22:52:55 +02:00
Petr Mrázek
cf0308c970 GH-1263 update build instructions 2015-09-30 22:00:31 +02:00
Petr Mrázek
e2fd299fc5 GH-253 implement launching instances from command line 2015-09-30 00:11:00 +02:00
Petr Mrázek
e993b1152d GH-1202 rebuild SSL certs on start on OSX - part 4 2015-09-29 01:42:45 +02:00
Petr Mrázek
877240524d GH-1202 rebuild SSL certs on start on OSX - part 3 2015-09-29 01:21:04 +02:00
Petr Mrázek
b7ff8a4c1c GH-1202 rebuild SSL certs on start on OSX - part 2 2015-09-29 01:06:26 +02:00
Petr Mrázek
22c0d5cf46 GH-1202 rebuild SSL certs on start on OSX 2015-09-29 00:49:54 +02:00
Petr Mrázek
12b14c3400 GH-1179 incorporate fixes for comments from linked changeset
Use SSL for downloads
Use XDG dirs for storage
Do not run from deploy function
2015-09-28 23:28:40 +02:00
Petr Mrázek
00994a425e GH-1238 add reddit button 2015-09-28 22:47:02 +02:00
Petr Mrázek
ccb5fc6f4a GH-1072 use crafatar for grabbing the user skin 2015-09-28 22:43:57 +02:00
Petr Mrázek
1cbe543b39 NOISSUE and even more: forgot to remove export from IconResourceHandler 2015-09-28 21:28:46 +02:00
Petr Mrázek
260a2cea59 NOISSUE more cmake tweakery 2015-09-28 21:20:27 +02:00
Petr Mrázek
143e24fa04 NOISSUE clean up some old cmake messes 2015-09-28 20:53:46 +02:00
Petr Mrázek
33c3850b40 NOISSUE add missing files 2015-09-27 22:31:52 +02:00
Petr Mrázek
271ad9e4fd GH-1072 split resource system to UI and logic parts 2015-09-26 13:45:29 +02:00
Petr Mrázek
cca6700134 NOISSUE fix all clang warnings 2015-09-26 04:04:09 +02:00
Petr Mrázek
0af04dc060 NOISSUE attempt to fix NBT lib options not applying 2015-09-26 01:22:37 +02:00
Petr Mrázek
e4df8165f7 NOISSUE and even more 2015-09-26 00:44:52 +02:00
Petr Mrázek
1ab26ef2ad NOISSUE fix travis not notifying IRC, again 2015-09-26 00:39:24 +02:00
Petr Mrázek
5d5bee4992 GH-1238 re-integrate nbt library 2015-09-23 01:21:19 +02:00
Petr Mrázek
e60a652b78 GH-1217 reset time played on zip pack import 2015-09-22 01:25:34 +02:00
Petr Mrázek
9ba1cd15e7 GH-1217 add simple instance play time tracking
Not reliable in the face of crashes... but what is?
2015-09-22 01:06:45 +02:00
Petr Mrázek
b107617112 GH-1121 select instance after creating it 2015-09-16 00:21:50 +02:00
Petr Mrázek
0a187d0ad3 GH-1227 protect world from corruption 2015-09-15 22:51:10 +02:00
Petr Mrázek
8d3f13c447 GH-1227 add world copy and rename 2015-09-14 23:49:32 +02:00
Petr Mrázek
dd8eacee1b GH-1227 renam GZip functions to not collide with zlib macros 2015-09-14 02:36:03 +02:00
Petr Mrázek
e38cc1d480 GH-1227 add GZip compress function and a unit test fo GZip 2015-09-14 02:25:47 +02:00
Petr Mrázek
cfd5976471 GH-1227 display some messages when MCEdit isn't setup or fails 2015-09-14 02:23:40 +02:00
Petr Mrázek
8ef07ec634 GH-1227 allow structured world zip import and drag and drop out of MultiMC 2015-09-13 04:21:26 +02:00
Petr Mrázek
2315f463a8 GH-1237 make mod info clickable and selectable 2015-09-12 03:08:36 +02:00
Petr Mrázek
38901ed21d NOISSUE use whole zip base filename for default instance name 2015-09-12 03:07:41 +02:00
Petr Mrázek
ec3472f21d GH-1232 try to export the primitive tag template instantiations explicitly 2015-09-10 08:39:41 +02:00
Petr Mrázek
bd96a25f7a Revert "GH-1232 attempt to fix reading NBT longs on OSX"
This reverts commit 877fc94f50.
2015-09-10 00:41:12 +02:00
Petr Mrázek
877fc94f50 GH-1232 attempt to fix reading NBT longs on OSX
EXPORT ALL THE THINGS
2015-09-10 00:16:33 +02:00
Petr Mrázek
db5816b0a2 GH-1227 fix zlib nonsense on Windows 2015-09-10 00:02:02 +02:00
Petr Mrázek
a1fd50e920 GH-1227: World import using drag and drop - zip files and folders 2015-09-09 23:53:33 +02:00
Petr Mrázek
51070a13f7 GH-1231 add libpng to packages 2015-09-08 22:09:08 +02:00
Petr Mrázek
36dbf1fb43 GH-1233 do not load worlds when not needed 2015-09-08 21:22:23 +02:00
Petr Mrázek
1ca9fc8961 NOISSUE catch more errors from nbt lib 2015-09-08 09:28:14 +02:00
Petr Mrázek
b8cdcdb96b GH-1047 build fixes 2015-09-06 23:46:38 +02:00
Petr Mrázek
38693e1d6c GH-1047 parse world files and integrate MCEdit with world page 2015-09-06 23:35:58 +02:00
Petr Mrázek
40b233448c Use default Yes/No buttons for confirmation 2015-09-06 16:00:07 +02:00
Petr Mrázek
7d8c71aad8 Just use an oxygen icon for the worlds folder... 2015-09-06 16:00:07 +02:00
Alex
498dc8fc03 Add confirmation dialog for world deletion 2015-09-06 16:00:07 +02:00
Petr Mrázek
c3480d6fe4 Icon 2015-09-06 16:00:07 +02:00
Alex
b5d5490714 Copyright fixes for new code 2015-09-06 16:00:07 +02:00
Alex
83434a9be5 Comment changes and general cleanup 2015-09-06 16:00:07 +02:00
Alex
583e5946f4 GH-1047 World management for instances. Removal only currently. 2015-09-06 16:00:07 +02:00
Petr Mrázek
16df6c16f3 Merge pull request #1225 from iambob314/patch-1
Fixed overwriting of wrapper command
2015-09-06 07:26:11 +02:00
iambob314
6148023ad6 Fixed overwriting of wrapper command
Fixed "WrapperCommand" setting being overwritten by the value of "PreLaunchCommand".
2015-09-05 19:39:13 -04:00
Petr Mrázek
6496c65285 SCRATCH do not export the pure template 2015-09-05 19:23:46 +02:00
Petr Mrázek
db5e55e026 SCRATCH NetAction fix 2015-09-05 19:10:03 +02:00
Petr Mrázek
23d0bd8edd NOISSUE make shared logic library ... shared 2015-09-05 18:46:57 +02:00
Petr Mrázek
cd108fd029 GH-1223 fix override settings
They now work more like passthrough settings, except not passing through set and reset
2015-09-04 02:10:29 +02:00
Petr Mrázek
151a0ca11e GH-1124 tell user which java path is wrong. 2015-08-24 09:38:05 +02:00
Petr Mrázek
bc917668ff GH-1133 fix bad java path detection some more 2015-08-24 00:56:45 +02:00
Petr Mrázek
961c1c61b8 GH-1206 fix deleting files during update
Backups weren't created properly which led to failure to update
2015-08-23 22:33:59 +02:00
Petr Mrázek
0d15247247 NOISSUE remoce the old WM_CLASS workaround 2015-08-23 16:05:53 +02:00
Petr Mrázek
b6ec2ac4b0 GH-1140 mark cached modpack dls as stale so it checks the server 2015-08-22 00:52:50 +02:00
Petr Mrázek
c5bb33c716 GH-1158 generate client uuid for yggdrasil auth 2015-08-22 00:42:40 +02:00
Petr Mrázek
40ed2654c7 GH-1148 New instance name is either version or modpack 'base name'
Default names now use the placeholder text in the new instance dialog.
2015-08-21 09:00:35 +02:00
Petr Mrázek
875c707358 NOISSUE fix librainbow some more 2015-08-21 07:40:39 +02:00
Mrazek, Petr
e5f7676622 NOISSUE fix librainbow 2015-08-20 15:27:51 +02:00
Mrazek, Petr
cc4d0a0a8e GH-1149 add irc notification to travis 2015-08-20 12:29:33 +02:00
Petr Mrázek
d0e88011dc GH-1197 finish color stuff 2015-08-20 01:49:03 +02:00
Petr Mrázek
6858f1dd62 GH-1197 add console log color adaptation
rainbow library was part of KDE - KGuiAddons
2015-08-19 23:52:53 +02:00
Petr Mrázek
9681f724e5 NOISSUE revamp the minecraft log exception detection 2015-08-19 23:50:36 +02:00
Petr Mrázek
5bc29b06a9 NOISSUE fix log-related legacy instance crash and show hidden log files 2015-08-19 02:04:56 +02:00
Petr Mrázek
96fdaebb5c GH-926 implement log cleaning functionality
Also adds gzip compressed log support
2015-08-18 08:51:12 +02:00
Petr Mrázek
4e3af265da GH-1164 make sure the censor filter never contains empty keys 2015-08-16 02:17:50 +02:00
Petr Mrázek
d7b3887fe1 Revert "NOISSUE redo the launcher part"
This reverts commit c1f7dda8fe.
2015-08-14 23:30:12 +02:00
Petr Mrázek
0adb572a07 NOISSUE improve account UI 2015-08-14 02:27:01 +02:00
Petr Mrázek
8ed10c5b81 NOISSUE fix build on windows - pid -> processId 2015-08-14 01:01:50 +02:00
Petr Mrázek
d8caab515a GH-1053 add back update progress dialog 2015-07-26 17:55:29 +02:00
Petr Mrázek
6310f6569c GH-1053 move guessLevel to instances 2015-07-22 09:01:04 +02:00
Petr Mrázek
2fc18921b0 GH-1053 add launch step creation to legacy instances 2015-07-21 09:21:59 +02:00
Petr Mrázek
61c5a67777 GH-1053 explode launch task into many small steps, each a Task 2015-07-21 02:38:15 +02:00
Petr Mrázek
8e7caf4e25 GH-1053 move launch related things and rename them 2015-07-10 01:11:06 +02:00
Petr Mrázek
5dd48e89f5 GH-1034 do jar modding separate from update 2015-07-10 00:06:05 +02:00
Petr Mrázek
5133b0f34f GH-1053 cleanup 2015-07-05 02:47:22 +02:00
Petr Mrázek
5f41886d76 GH-1053 split settings dialog creation to its own namespace 2015-07-05 02:29:41 +02:00
Petr Mrázek
7f1320390c GH-1053 move launch process UI to a separate class 2015-07-05 01:54:30 +02:00
Petr Mrázek
526a511f45 GH-1053 move instance update into the launch task (BaseLauncher) 2015-07-04 20:02:43 +02:00
Petr Mrázek
5628d3d379 SCRATCH squash MinecraftLauncher into BaseLauncher
needs to be split differently
needs to be squashed together with the logic from MainWindow
2015-06-30 07:16:20 +02:00
Petr Mrázek
f86a39c21c SCRATCH fix BaseLauncher 2015-06-30 07:16:20 +02:00
Petr Mrázek
34ddfc7ecc GH-1053 base process and launch refactor, part 1 2015-06-30 07:16:20 +02:00
Jan Dalheimer
d14a61b0df GH-1100 Fix issues with LD_* variables when restarting on updates 2015-06-29 20:22:04 +02:00
Petr Mrázek
bbba63eca5 GH-1096 make ubuntu package work on both 32bit and 64bit machines 2015-06-28 22:44:53 +02:00
Petr Mrázek
d403d12d6a GH-1096 simple debian/ubuntu wrapper package
Built with `fakeroot dpkg-deb --build multimc_1.0-1`
2015-06-28 11:25:32 +02:00
Petr Mrázek
b343434f99 GH-1068 update changelog 2015-06-13 02:22:12 +02:00
Petr Mrázek
f723721bd0 GH-1069 env hack/passthhrough for LD_PRELOAD and LD_LIBRARY_PATH 2015-06-12 09:40:41 +02:00
Petr Mrázek
b427a652ad GH-1009 use .sh instead of .pyc - .pyc doesn't work at all 2015-06-11 02:33:02 +02:00
Petr Mrázek
9684d3b0a0 GH-1008 implement log window max line count
Defaults to 100k lines
2015-06-11 01:50:20 +02:00
Petr Mrázek
1feb4bb387 GH-1009 add mcedit.pyc as a valid mcedit 'executable' 2015-06-10 23:51:05 +02:00
Petr Mrázek
dd97ea8029 GH-1060 ugly XP hack is ugly 2015-06-10 03:06:29 +02:00
Petr Mrázek
88f5c8d347 GH-1060 create and delete update dir 2015-06-10 00:46:45 +02:00
Petr Mrázek
15b7c3039a GH-1060 update tweaks
* download to multimc folder hierarchy
* use rename, not copy
* keep backup after update
* clean previous backup before update
* it's not 'copy', it's 'replace'
2015-06-09 23:30:28 +02:00
Petr Mrázek
22c5ced5dc GH-1060 add a lot of error checking and reporting to the inner updater 2015-06-09 20:58:19 +02:00
Petr Mrázek
64b70acac1 GH-1060 tweaks to new update mechanism - logging 2015-06-09 00:48:25 +02:00
Petr Mrázek
82e05661d2 GH-1060 implement very basic updater (only linux and maybe osx right now) 2015-06-09 00:03:42 +02:00
Petr Mrázek
166813cb91 GH-1060 remove some old updater bits and pieces 2015-06-09 00:03:42 +02:00
Petr Mrázek
38e42ad794 GH-1049 fix bad ifdefs that prevent linux-specific env blacklisting 2015-06-08 23:54:30 +02:00
Petr Mrázek
6d7bff2476 GH-1060 remove updater code 2015-06-07 21:10:18 +02:00
Petr Mrázek
977e11ef8d GH-1051 ignore CDPATH 2015-06-06 23:40:13 +02:00
Jan Dalheimer
634bdcdbcb GH-1033 Set up travis for C++14 setup
No clang, no OSX, nothing
2015-06-06 23:27:58 +02:00
Jan Dalheimer
1e51b62c88 NOISSUE Comment and bugfix the Resource system 2015-06-06 21:23:05 +02:00
Petr Mrázek
24db645167 NOISSUE sanitize Json
Removes magical parameter madness.
All require* can throw
All ensure* need a default value and never throw
2015-06-06 21:23:05 +02:00
Petr Mrázek
dde35a0eb8 NOISSUE remove StandardTask 2015-06-06 21:23:05 +02:00
Petr Mrázek
57b75dfcf7 NOISSUE document exceptions a bit more 2015-06-06 21:23:05 +02:00
Petr Mrázek
06a67fbd38 NOISSUE use FS a bit more 2015-06-06 21:23:05 +02:00
Jan Dalheimer
3a8b238052 NOISSUE Various changes from multiauth that are unrelated to it 2015-06-06 21:23:05 +02:00
Petr Mrázek
161dc66c2c GH-1052 try using c++1y on OSX 2015-06-06 21:00:37 +02:00
Petr Mrázek
678da0b639 GH-1052 switch over to C++14 everywhere 2015-06-06 19:56:51 +02:00
Jan Dalheimer
db69a3dacd NOISSUE Don't attempt to package the accessible plugins on Qt >= 5.4 2015-06-06 15:10:19 +02:00
Petr Mrázek
47f919173e NOISSUE begin working on 0.4.8 and update changelog 2015-06-02 01:11:31 +02:00
Alex
c1f7dda8fe NOISSUE redo the launcher part 2015-06-02 00:11:09 +02:00
Petr Mrázek
678c4793f9 GH-980 finalize changelog 2015-06-01 01:29:09 +02:00
Petr Mrázek
405cea1778 GH-1031 include icon in exported instance if it is custom 2015-06-01 01:19:12 +02:00
Petr Mrázek
96c497f654 GH-980 more changelog tweakery 2015-06-01 00:18:52 +02:00
Petr Mrázek
10b3906b53 GH-980 Update changelog for 0.4.7 2015-06-01 00:12:46 +02:00
Petr Mrázek
6fd18a5cce GH-1016 print list of mods, coremods and jarmods
Includes a change to jar mods, where they gain an 'originalName' attribute used only for display
2015-05-31 21:50:01 +02:00
Petr Mrázek
9920062003 GH-1016 print mods, jar mods and core mods on start
Needs some work - jar mods just have the uuid name
2015-05-31 21:50:01 +02:00
Petr Mrázek
99f248ecd4 GH-1015 catch exceptions when doing profile reapply
This is a temporary solution.
2015-05-31 20:00:15 +02:00
Petr Mrázek
b9e06b5da0 GH-1021 make builtin versions not customizable
They use attributes not defined in the OneSix format.
2015-05-31 19:24:39 +02:00
Petr Mrázek
ff64b6cf1d GH-1020 use plain strings for library URLs
Because the URLs can contain {}, which are percent encoded in URLs and this breaks variable substitution
2015-05-31 17:51:20 +02:00
Petr Mrázek
84757f485b GH-1015 fix crash when version is incomplete and adding jar mods 2015-05-29 08:32:05 +02:00
Petr Mrázek
b7f8241968 GH-994 hopefully fix issue with people still using jar mods
Added an 'add mods' button to the version page
Add jar mods now has a very angry nag dialog until it's used successfully
Buttons on version page are rearranged to deemphasize jar mods
2015-05-29 02:22:02 +02:00
Petr Mrázek
a98e1df10c GH-1011 fetch missing versions when customizing/reverting Minecraft patches 2015-05-28 09:36:58 +02:00
Petr Mrázek
f9e186ab70 GH-967 make libraries handle their own path prefix
Makes it possible to mix libraries managed by FTB and MultiMC
Backport from unstable
2015-05-27 01:30:18 +02:00
Petr Mrázek
50a4a1e19e NOISSUE use -fPIC builds when the system Qt is derpy 2015-05-26 22:38:01 +02:00
Petr Mrázek
2f087b55b9 GH-997 fix saving of settings values with special characters
Values are now escaped properly
2015-05-26 08:33:10 +02:00
Petr Mrázek
2dcedcfde3 GH-997 add unit test for ini file save/load passthrough 2015-05-26 08:29:43 +02:00
Petr Mrázek
c1c23e47a7 GH-1003 Fix settings dialog delays 2015-05-26 08:14:33 +02:00
Petr Mrázek
8fb5d4add3 GH-1003 add some save locking for dialog pages that deal with settings 2015-05-25 08:21:35 +02:00
Petr Mrázek
185ff238c2 GH-992 GH-997 Do not rewrite values when loading FTB packs
name, icon and notes won't be overwritten when loading FTB packs
this also eliminates the file saving delay from setting the values
2015-05-25 07:35:43 +02:00
Petr Mrázek
09673cc16e GH-977 Initialize FTB icon properly (iconKey vs logo), remove debug prints 2015-05-24 19:48:22 +02:00
Petr Mrázek
dfb0a3b724 GH-991 implement wrapper commands 2015-05-24 14:49:54 +02:00
Petr Mrázek
ce99fabe13 GH-992 Add a transaction/locking mechanism to settings objects
This can cut the FTB loading by ~66% - worth it, but not ideal.
Real solution will have to be implemented later.
2015-05-23 16:07:47 +02:00
Petr Mrázek
0e0ddf5494 GH-977 Improve FTB loading and instance creation 2015-05-22 23:06:51 +02:00
Petr Mrázek
12b9a90c4c GH-980 update chengelog 2015-05-22 02:06:25 +02:00
Petr Mrázek
8715746774 GH-977 this isn't funny anymore... 2015-05-22 01:27:59 +02:00
Petr Mrázek
cfdfd0e811 GH-977 possibly fix FTB on windows. Maybe. Partially. Now maybe for real. 2015-05-22 01:08:37 +02:00
Petr Mrázek
81b37dae18 GH-977 possibly fix FTB on windows. Maybe. Partially. 2015-05-21 23:25:16 +02:00
Petr Mrázek
29ce36c7bc GH-983 use 'minecraft.jar' for ancient jar-modded versions
Fixes NEI in MC 1.4.7 and probably other obscure issues
2015-05-21 22:38:31 +02:00
Petr Mrázek
22a0294a33 GH-985 fix jar mods 2015-05-21 20:47:47 +02:00
Petr Mrázek
5334d88c1d GH-970 fix help page links some more 2015-05-21 20:33:15 +02:00
Petr Mrázek
06080108f3 GH-980 update version number and changelog for 0.4.7 2015-05-21 01:13:05 +02:00
Petr Mrázek
08898c7c63 GH-970 fix help page links 2015-05-21 01:12:18 +02:00
Petr Mrázek
1bc2fbef11 GH-794 add libstdc++ to build on linux. hopefully? 2015-05-21 00:28:30 +02:00
Benjamin Hoffmeyer
a296ec914b NOISSUE Link wiki feature list in readme 2015-05-20 04:00:14 +02:00
Petr Mrázek
53d8c9169d GH-972 fix 'temporaty' typo 2015-05-20 03:14:02 +02:00
Petr Mrázek
4c11ce8063 GH-932 Icon themes actually do not need a restart to be applied 2015-05-20 01:44:01 +02:00
Petr Mrázek
cff2e4823a GH-960 tweak changelog for the OSX SSL fix 2015-05-19 23:12:39 +02:00
Petr Mrázek
d0b31da4b5 GH-960 possible fix for missing OSX ca certs 2015-05-19 22:28:51 +02:00
Petr Mrázek
2ad9e6393f GH-952 Update changelog for hardcore version page tweakery 2015-05-18 00:51:47 +02:00
Petr Mrázek
743af4769e GH-952 Hardcore version page tweakery
Version patches get a lot of new flags that determine which actions are allowed
Version page respects the flags
Customize, revert and edit for version patches
Builting patches can be customized
2015-05-17 23:38:28 +02:00
Petr Mrázek
6ab6a450f6 GH-952 fix legacy edit instance 2015-05-16 23:52:11 +02:00
Petr Mrázek
3ed467e1fa NOISSUE do not dump minecraft version files into the log 2015-05-16 23:33:42 +02:00
Petr Mrázek
44d76f5d82 NOISSUE change travis to Qt 5.3 2015-05-16 23:19:33 +02:00
Petr Mrázek
ff715f7785 NOISSUE replace derpy merkdown thing with hoedown 2015-05-16 23:04:00 +02:00
Petr Mrázek
43c777f386 NOISSUE add some functionality to the derpy markdown changelog thing 2015-05-16 19:33:53 +02:00
Petr Mrázek
a39fb1ef17 GH-958 print PID when starting Minecraft 2015-05-16 18:42:17 +02:00
Petr Mrázek
c75cac684e GH-952 rearrange 0.4.6 changelog into sections so it's actually readable 2015-05-16 18:42:17 +02:00
Petr Mrázek
f2026df597 GH-952 do not remove {version,custom}.json files, rename them 2015-05-16 18:42:14 +02:00
Petr Mrázek
416e08f741 GH-952 flesh out {version,custom}.json upgrade step 2015-05-15 01:37:15 +02:00
Petr Mrázek
5bbe1c7132 GH-951 add .litemod to mod browse dialog 2015-05-12 23:43:11 +02:00
Petr Mrázek
bd1a28d863 GH-932 fix some changelog typos 2015-05-12 09:40:56 +02:00
Petr Mrázek
ffcb5ab1ef GH-932 update version and changelog for 0.4.6 2015-05-12 09:17:04 +02:00
Petr Mrázek
88f975eff7 NOISSUE only watch mod folders when the user is looking at them 2015-05-11 22:50:35 +02:00
Petr Mrázek
11c376f6f1 NOISSUE Remove PermGemn warning ignoring 2015-05-07 08:42:35 +02:00
Petr Mrázek
757b4e260b NOISSUE more logging 2015-05-06 22:16:52 +02:00
Petr Mrázek
2a4647125d GH-942 fix vanilla version list
Latest release gets the star
Latest snapshot, if it's newer than latest release gets the bug
2015-05-06 09:00:21 +02:00
Petr Mrázek
9598f80335 NOISSUE do not show file browse dialog twice 2015-05-06 07:22:24 +02:00
Petr Mrázek
34a5e59007 GH-835 show errors reported by the update download task to the user 2015-05-05 08:15:56 +02:00
Sky
1271188019 Fixed some Forge typos in dialogs. Fixes #940 2015-05-05 00:33:34 +01:00
Petr Mrázek
4c6edc9fd4 GH-907 fix location/java override for java detection 2015-05-05 01:09:28 +02:00
Petr Mrázek
49d3705d16 GH-899 clean up mod browse buttons and dead legacy forge 2015-05-05 00:42:04 +02:00
Petr Mrázek
c09dc85090 GH-899 fix add mod button not opening the central mods folder 2015-05-04 22:17:05 +02:00
Petr Mrázek
c10a4a54d9 NOISSUE windows hates me 2015-05-04 01:28:16 +02:00
Petr Mrázek
1b884d0a9d GH-907 improve Java testing and PermGen deprecation handling 2015-05-04 01:20:48 +02:00
Petr Mrázek
8e9d5f56b5 GH-933 map exit code -1 to 'crashed' 2015-05-02 23:48:18 +02:00
Petr Mrázek
5779ffd664 GH-922 improve version select dialogs 2015-05-02 23:42:33 +02:00
Petr Mrázek
4fc4a17256 NOISSUE handle recommended versions better
Moved constants to the version data file
Use recommended Minecraft instead of latest stable for new instances by default
2015-05-02 12:44:37 +02:00
Petr Mrázek
bb01c91469 NOISSUE do not propagate instance change events when nothing actually changed 2015-05-02 12:11:33 +02:00
Petr Mrázek
fb3c9efc8a NOISSUE launcher part should exit when MultiMC 'hangs up' 2015-05-02 12:08:18 +02:00
Petr Mrázek
55f9117ce3 NOISSUE do not remake instance tools menu, refill it instead 2015-05-02 12:07:18 +02:00
Petr Mrázek
994c815bb9 NOISSUE show errors for instance updates in edit instance window 2015-05-02 01:43:04 +02:00
Petr Mrázek
32f45578fd NOISSUE fix build issues
Hopefully all
2015-05-02 01:43:00 +02:00
Petr Mrázek
2af03ba0d9 GH-930 Improve wording of instance delete dialog 2015-05-01 21:27:16 +02:00
Petr Mrázek
aea51a0876 GH-328 overhaul all relevant version lists 2015-05-01 20:50:24 +02:00
Petr Mrázek
75dfbc61fc GH-925 add scroll to bottom button to LogPage 2015-04-29 01:28:58 +02:00
Petr Mrázek
f8650e3965 NOISSUE eliminate ProgressProvider 2015-04-26 23:04:50 +02:00
Petr Mrázek
84549ed807 GH-849 Further NetJob related fixes 2015-04-26 18:33:29 +02:00
Petr Mrázek
d5c79db12c GH-849 Fix failure signals not getting delivered from NetJob properly 2015-04-26 04:09:09 +02:00
Petr Mrázek
f623dc54ef GH-909 warn about MultiMC running from temporary folders 2015-04-26 00:01:41 +02:00
Petr Mrázek
dc279fbfdc Revert "NOISSUE Cleanup onesix launcher"
This reverts commit 07bebddac9.
2015-04-25 15:32:38 +02:00
Petr Mrázek
8fa58dc244 NOISSUE save group file after copying instance 2015-04-19 21:02:34 +02:00
robotbrain
07bebddac9 NOISSUE Cleanup onesix launcher 2015-04-19 19:07:39 +02:00
Petr Mrázek
4f417d527e GH-894 link server status widgets to help.mojang.com 2015-04-19 17:58:53 +02:00
Petr Mrázek
c7c81463fd GH-885 export dialog for filtering exported files
Includes implementation of a separator based prefix tree and some related bits
2015-04-19 16:14:32 +02:00
Petr Mrázek
6cfac115b1 NOISSUE add commented callgrind startup to linux script 2015-04-15 03:13:57 +02:00
Petr Mrázek
3507ccaf50 GH-866 load instance profile on launch and from version page 2015-04-15 03:12:57 +02:00
Petr Mrázek
28aa8f342e GH-887 fix patch file removal 2015-04-13 23:26:52 +02:00
Petr Mrázek
4d8f068f9c NOISSUE refactor and rearrange zip file utils 2015-04-13 00:53:59 +02:00
Petr Mrázek
1f9dd45e49 GH-329 update description text in MainWindow when instance Minecraft version changes 2015-04-13 00:25:55 +02:00
Petr Mrázek
f061bf7a27 NOISSUE use QObjectPtr for translations and screenshots 2015-04-13 00:21:55 +02:00
Petr Mrázek
d8ea3501eb NOISSUE add waffle.io badge to README
DONTBUILD
2015-04-13 00:20:45 +02:00
Petr Mrázek
9df2f1fa5c NOISSUE fix legacy edit instance 2015-04-13 00:15:23 +02:00
Petr Mrázek
fe540e5dda NOISSUE do not fail when updates don't have MultiMC.app prefix on OSX 2015-04-13 00:11:59 +02:00
Petr Mrázek
c7398dfdc5 GH-228 do not recurse into reparse points when deleting instances 2015-04-13 00:06:31 +02:00
Petr Mrázek
0220fe4f9d GH-228 do not follow symlinks during instance copy on unix
Windows will need a more complex solution.
2015-04-13 00:06:31 +02:00
Petr Mrázek
58840ac10c NOISSUE fix profilers 2015-04-13 00:04:08 +02:00
Petr Mrázek
3d3725f088 SCRATCH small cmake tweaks 2015-04-12 20:57:18 +02:00
Petr Mrázek
47bbc349eb SCRATCH remove more obsolete asset logic 2015-04-12 20:57:18 +02:00
Petr Mrázek
c8687a8d05 NOISSUE get rid of the obsolete version builder 2015-04-12 20:57:18 +02:00
Petr Mrázek
234f57b8e6 NOISSUE Add NullInstance for instances that can't be loaded 2015-04-12 20:57:18 +02:00
Petr Mrázek
d4d8cb4891 NOISSUE remove group sorting log spam 2015-04-12 20:57:18 +02:00
Petr Mrázek
d1ba972c59 SCRATCH move some cmake bits 2015-04-12 20:57:18 +02:00
Petr Mrázek
db877ba121 NOISSUE move everything. 2015-04-12 20:57:18 +02:00
Petr Mrázek
4730f54df7 SCRATCH separate the generic updater logic from the application 2015-04-12 20:57:17 +02:00
Petr Mrázek
7a71ecd8af NOISSUE fix notification checker 2015-04-12 20:57:17 +02:00
Petr Mrázek
4e94de413b SCRATCH no more gui includes in logic 2015-04-12 20:57:17 +02:00
Petr Mrázek
141e0a02a0 SCRATCH move things to the right places 2015-04-12 20:57:17 +02:00
Petr Mrázek
473971b6e7 NOISSUE fix overlap in instance settings registration 2015-04-12 20:57:17 +02:00
Petr Mrázek
b47e196b32 SCRATCH fix version file loading 2015-04-12 20:57:17 +02:00
Petr Mrázek
cd9d37aac4 SCRATCH nuke the overcomplicated logger, use a simple one. 2015-04-12 20:57:17 +02:00
Petr Mrázek
28a39ef7ac NOISSUE fix segfault when version list is null 2015-04-12 20:57:17 +02:00
Petr Mrázek
d313e9ab09 SCRATCH remove remaining references to MultiMC.h and fix legacy LWJGL 2015-04-12 20:57:17 +02:00
Petr Mrázek
382ae78a0b Fix NagUtils and hack GroupView to work 2015-04-12 20:57:17 +02:00
Petr Mrázek
aa70ed2244 SCRATCH move icons over to Env, instance proxy model to gui 2015-04-12 20:57:16 +02:00
Petr Mrázek
154d19bb74 SCRATCH eliminate InstanceFactory 2015-04-12 20:57:16 +02:00
Petr Mrázek
c088d3bef0 GH-881 Include a top-level directory in exported instances 2015-04-12 20:50:45 +02:00
Petr Mrázek
6775e3e72b NOISSUE Improve new instance dialog
Better layout, showing more of the modpack URL
Fixed logic for enabling OK button
2015-04-11 12:30:18 +02:00
Petr Mrázek
8b4e22bbb8 NOISSUE Move FTB logic out of generic code 2015-04-04 15:46:15 +02:00
Petr Mrázek
c7b39fe116 NOISSUE Remove special FTB logic from generic version patch code 2015-04-04 02:01:52 +02:00
Petr Mrázek
865b200571 GH-856 add profile strategy for FTB packs 2015-04-03 11:55:16 +02:00
Petr Mrázek
dc84ac3682 NOISSUE make slightly more compatible with current unstable
Recognize MinecraftVersion as IntendedVersion
2015-04-02 22:14:54 +02:00
Petr Mrázek
695bfd5f7c NOISSUE insert blatant self-promotion 2015-04-02 21:56:25 +02:00
Petr Mrázek
5ff2681da6 NOISSUE use QSaveFile for saving patch order 2015-04-02 21:56:25 +02:00
Petr Mrázek
5359f4499a NOISSUE remove obsolete EnabledItemFilter model 2015-04-02 20:22:52 +02:00
Petr Mrázek
04b45f3629 NOISSUE remove obsolete LWJGL selection dialog 2015-04-02 20:13:26 +02:00
Petr Mrázek
9249768db5 NOISSUE Make tests no longer use the MultiMC object
They do not require the application part anymore
2015-04-02 11:30:38 +02:00
Petr Mrázek
6f3aa65bd6 NOISSUE Split MultiMC app object into MultiMC and Env 2015-04-02 11:30:24 +02:00
Petr Mrázek
e508728246 NOISSUE remove obsolete assets migration task 2015-04-02 00:37:52 +02:00
Petr Mrázek
360ec557b2 NOISSUE remove notification checker form application object 2015-04-02 00:14:06 +02:00
Petr Mrázek
7334b8e520 NOISSUE remove status checker from application object 2015-04-02 00:14:06 +02:00
Petr Mrázek
791221e923 NOISSUE Refactors and moving of things 2015-04-02 00:14:06 +02:00
Petr Mrázek
593111b144 GH-813 Add 'mcedit2.exe' to the list of things the MCEdit tool looks for 2015-04-01 22:43:18 +02:00
Petr Mrázek
3b6574181e GH-853 evict asset index files from cache when they don't parse 2015-04-01 00:23:17 +02:00
Petr Mrázek
eae544f0eb GH-841 fix for modpack downloads on windows 2015-03-27 02:03:14 +01:00
Petr Mrázek
2eb3ec39bf NOISSUE tweak version display in the application to reflect recent changes 2015-03-24 22:40:49 +01:00
Petr Mrázek
a27c341781 SCRATCH more 2015-03-16 22:33:41 +01:00
Petr Mrázek
02f82e9694 NOISSUE Z_PREFIX for QuaZip 2015-03-16 21:56:27 +01:00
Petr Mrázek
405833bbbe NOISSUE eliminate qt5_use_modules from build 2015-03-16 08:43:00 +01:00
Petr Mrázek
8be865fb2a SCRATCH more experiments 2015-03-16 02:11:02 +01:00
Petr Mrázek
568a79e7b1 SCRATCH more quazip experiments 2015-03-16 01:53:19 +01:00
Petr Mrázek
06c9a64a87 NOISSUE fix bad export in iconfix 2015-03-16 00:27:06 +01:00
Petr Mrázek
ceec70e014 GH-796 Icon theme loading workaround
Replacing the Qt machinery with other Qt machinery under our control
2015-03-01 22:20:57 +01:00
Jan Dalheimer
ef34cafe17 Fix QuaZIP target dependency 2015-02-21 21:23:23 +01:00
Petr Mrázek
93b247592d NOISSUE actually make INI file saving work again... oops :P 2015-02-21 08:59:38 +01:00
Petr Mrázek
b8a8b09796 NOISSUE make sure saving config files is atomic 2015-02-21 00:21:19 +01:00
Jan Dalheimer
a53f8d506e GH-366: Plain and simple modpack export/import/download
Also removed the in-source QuaZIP in favour of upstream version
2015-02-19 21:04:27 +01:00
Jan Dalheimer
f9a17eb9de Fix the updater on OS X 2015-02-16 22:05:39 +01:00
Greenphlem
c6c5134398 Change copyright dates to 2015 2015-02-06 01:18:02 +01:00
Petr Mrázek
49ff31f131 NOISSUE do not use proxy by default 2015-02-06 00:41:36 +01:00
Petr Mrázek
e25e076d2e NOISSUE ignore PermGen warnings in log 2015-02-02 21:42:01 +01:00
Greenphlem
125ddc5f93 Change default PermGen from 64 to 128 mb
This will help users who run mods. Too many issues have been made over the lack of more permgen being assigned.
2015-02-02 00:42:36 -08:00
Petr Mrázek
d03dbea1b7 NOISSUE change icon themes without restart 2015-01-28 20:56:23 +01:00
Petr Mrázek
c6427caa9e GH-734: block more java env variable holes. 2015-01-17 23:05:34 +01:00
Petr Mrázek
0be0e822e4 GH-720 Add timestamps (since mmc start) to log 2015-01-12 22:18:26 +01:00
Petr Mrázek
55e5322fbe GH-721 Log errors in asset and MMC update downloads. 2015-01-11 22:30:54 +01:00
Petr Mrázek
0886786bb5 GH-721 Redo internal NetJob implementation.
NetJob is now using its own task queue and does not start more than 6 actions at the same time
2015-01-11 22:04:31 +01:00
Petr Mrázek
1151037f96 GH-719 Fix paste upload encoding and do not try to upload over limit 2015-01-11 03:08:41 +01:00
robotbrain
acb3346409 NOISSUE Update and sort modlist after adding mods 2015-01-06 21:23:02 +01:00
Alex Sieberer
85756d0e78 Remove IRC in favor of ElrosGem 2014-12-29 12:38:02 -05:00
Alex Sieberer
5c599d8658 Add translation badge to README 2014-12-29 10:44:23 -05:00
Petr Mrázek
4db31aacd6 NOISSUE Treat any forge downloads <= 4KB as stale. 2014-12-27 22:45:49 +01:00
Petr Mrázek
a30a9559c7 NOISSUE Fix jar mods for OnesSix 2014-12-27 20:50:33 +01:00
Petr Mrázek
01f44e0f39 NOISSUE Set minimum Minecraft window size to 1x1 2014-12-12 21:52:24 +01:00
Petr Mrázek
bbcd44a657 NOISSUE Always follow redirects for NetAction based downloads 2014-12-12 00:44:55 +01:00
Petr Mrázek
a060d79c12 GH-631, GH-658 Implement yellow status icon, refresh status icons in themes. 2014-12-07 22:55:29 +01:00
Petr Mrázek
28140b1db6 NOISSUE Change default font on Windows to Courier size 10 2014-12-03 21:48:27 +01:00
Petr Mrázek
5af1f0cf50 GH-640 Make legacy forge downloads follow redirects 2014-11-29 13:03:20 +01:00
Petr Mrázek
7778c84121 NOISSUE No more symlinks for LWJGL. 2014-11-29 13:03:08 +01:00
Petr Mrázek
4ae0d8e0af NOISSUE Update changelog for version 0.4.5 2014-11-21 11:20:18 +01:00
Petr Mrázek
9f14b319df NOISSUE Remove obsolete ReleaseCandidate logic from the build system 2014-11-21 10:53:59 +01:00
Petr Mrázek
a9af17eebb NOISSUE Remove remnant of crash handler 2014-11-21 10:34:38 +01:00
Petr Mrázek
d3c2230a24 NOISSUE fix OSX version number and (C) year 2014-11-20 19:07:10 +01:00
Petr Mrázek
f8bd687994 NOISSUE Fix console window hiding 2014-11-17 22:43:10 +01:00
Petr Mrázek
fa8d3c564d GH-618 Add updated light and dark simple icons 2014-11-17 22:10:58 +01:00
Petr Mrázek
80d3f734c6 GH-619 Add libraries missing in copies of 1.7.10 FTB packs 2014-11-17 22:01:32 +01:00
Petr Mrázek
9ad9826d08 GH-608 Re-detect java when the binary goes missing 2014-11-16 12:56:33 +01:00
Petr Mrázek
6a09fd2898 Set versions back to develop 2014-11-16 04:04:24 +01:00
Petr Mrázek
03a25c9a5d Do not do symlink hackery for java < 8 2014-11-16 04:04:24 +01:00
Petr Mrázek
83b90d8bfb Fix typo in changelog 2014-11-16 02:12:52 +01:00
Petr Mrázek
1d5c09051c VersionSelectDialog is for SELECTING versions.
Not blinking in and out of existence. That is not the job of a version SELECTION dialog.
2014-11-16 01:56:07 +01:00
Petr Mrázek
940f160091 Set version to 0.4.4 for release 2014-11-15 23:37:12 +01:00
Petr Mrázek
3d2de6add8 Add Batch icon set to the about dialog. 2014-11-15 23:37:12 +01:00
Petr Mrázek
b7c4284019 Remove crash handler 2014-11-15 23:35:24 +01:00
Jan Dalheimer
2315bf7bc5 Fix a OS X build error (missing a defined()) 2014-11-15 19:45:49 +01:00
Petr Mrázek
41bd2a6634 Add console font size setting and a preview\
Also moves the console settings from the minecraft page.
2014-11-15 19:45:49 +01:00
Petr Mrázek
5711b1be95 'Fix' instance group sorting 2014-11-10 08:51:24 +01:00
Petr Mrázek
90eea4f05c More pixels. 2014-11-10 07:37:30 +01:00
Petr Mrázek
cbb1e0ea45 HACK for scalable icons in QListView on Windows 2014-11-10 07:33:49 +01:00
Petr Mrázek
ec4805cce8 Default console font tweaks
* Lucida Console on Windows
* Menlo on OSX
* Monospace (resolved to whatever Monospace means) on linux
* Added ability to select proportional fonts in settings
2014-11-10 06:26:17 +01:00
Petr Mrázek
a2ac9c5a3a Fix coloring and processing of console output
* Removing \r
* Adding missing break statements for coloring
2014-11-10 05:10:58 +01:00
Petr Mrázek
551e101146 Follow redirects for skins 2014-11-10 05:10:23 +01:00
Petr Mrázek
1dd8978f8c Do not use QFont without Xorg 2014-11-09 20:49:23 +01:00
Petr Mrázek
24a0635b62 Allow changing the console font family 2014-11-09 19:48:35 +01:00
Petr Mrázek
2e9284951c Improve log formatting
* Updated log level detection for current Minecraft versions
* Better formatting: using a monospaced font and raw text instead of HTML (fixes #111)
2014-11-09 14:53:08 +01:00
Jan Dalheimer
f9a7c1cf21 Fix #208: Allow double clicking an account in the account selection dialog 2014-11-09 02:50:30 +01:00
Petr Mrázek
28eebc09fc Give paste upload a nice status message
Fixes #364
2014-11-09 02:09:01 +01:00
Petr Mrázek
cc19159f4d Document pre/post commands better
Fixes #398
2014-11-09 01:20:09 +01:00
Petr Mrázek
587fedce84 Implement search and logging pause in minecraft log
Fixes #47
2014-11-09 00:59:22 +01:00
Petr Mrázek
fa42a27525 Workaround for QTBUG-42500
Process has to have LD_LIBRARY_PATH set to empty string to not inherit it by default
2014-11-09 00:19:54 +01:00
Petr Mrázek
84723add8f Fix #537
Core Mods help now goes to Loader Mods
Fixed Minecraft Log -> Minecraft Logs problem
2014-11-08 21:47:51 +01:00
Petr Mrázek
992ba0c3f8 Implement #545
* Instance group can be selected when creating and copying instances
* Original group is pre-selected when copying
* Last used group is pre-selected when creating new instances
2014-11-08 21:17:28 +01:00
Jan Dalheimer
7d1dd2a32f Fix #474: Bad jvisualvm check 2014-11-02 20:29:09 +01:00
Jan Dalheimer
add23a9a0b Fix #220: Use .exe suffix on windows for jprofiler 2014-11-02 20:16:29 +01:00
Jan Dalheimer
d9b2f0ed42 Fix another bunch of copyright years, including fixing #397 2014-11-02 20:08:26 +01:00
Jan Dalheimer
9217d9263e Update copyright year (finally...) 2014-11-02 19:49:58 +01:00
Jan Dalheimer
a3a5afe119 Fix #231: Enable translation for more strings 2014-11-02 19:25:11 +01:00
Petr Mrázek
7fb6cafe9e Add LWJGL 2.9.2-nightly-20140822 2014-11-02 12:47:39 +01:00
Petr Mrázek
c1b6f42551 Also block other java-related env vars, for good measure
"JAVA_ARGS"
"CLASSPATH"
"CONFIGPATH"
"JAVA_HOME"
"JRE_HOME"
2014-11-02 11:13:18 +01:00
Petr Mrázek
3d1426b559 Filter env variables passed to Minecraft
QT_* and LD_* are not passed through
env variables are logged on launch
2014-11-01 14:11:20 +01:00
Petr Mrázek
095640ed01 Fix Java 8 issues with LWJGL native libs on OSX? 2014-11-01 10:39:32 +01:00
Petr Mrázek
9e8a74cc89 Fix some issues in the newly added themes 2014-10-27 00:41:40 +01:00
Petr Mrázek
547f6f77d0 Add iOS and OSX icon themes by pe 2014-10-27 00:15:52 +01:00
Petr Mrázek
8f7aec032b Add dark, light, blue and colored theme from pe.
Replaces the old dark and light themes
2014-10-26 23:44:20 +01:00
TakSuyu
92560bf0cd Merge pull request #561 from max96at/patch-1
One line change that was already attempted. Easy enough to revert if in the off chance it regresses.
2014-10-19 10:28:05 -07:00
max96at
2dd2b7a291 Finally with Qt5.3 this should work. :D 2014-10-19 18:19:37 +02:00
robotbrain
b4122cff89 Fix translation downloading 2014-10-05 11:37:49 -04:00
Petr Mrázek
43b9706b5c Fix translations path for the meta cache 2014-10-01 00:24:56 +02:00
robotbrain
bbdf5c1395 Translation downloading! 2014-09-30 16:22:39 -04:00
Petr Mrázek
382e167d64 Do not choke on large files when showing them in the 'other logs' page. 2014-09-21 00:05:48 +02:00
Sky
de2bb0c6f3 README tweaks 2014-09-15 15:53:20 +01:00
Petr Mrázek
2083ac8cc1 Add a dot. 2014-09-14 11:31:43 +02:00
Petr Mrázek
16955c6188 Delete old translations 2014-09-09 07:57:27 +02:00
Petr Mrázek
b00e63dbe8 More sync from quickmods
Also a small VersionSelectDialog refactor
2014-09-06 21:01:23 +02:00
Petr Mrázek
20cb97a35a Sync from quickmods 2014-09-06 19:03:05 +02:00
Petr Mrázek
36efcf8d3c Back to develop 2014-09-06 13:05:17 +02:00
Petr Mrázek
a59e83cd12 Update changelog
Conflicts:
	changelog.md
2014-08-21 00:48:14 +02:00
Petr Mrázek
febf3645d0 Fix version file problems, fix console window not being destroyed 2014-08-21 00:32:32 +02:00
Petr Mrázek
01ca3d6aad Add some logging to places where version updates might not trigger. 2014-08-21 00:32:32 +02:00
Mrazek, Petr
6deb41e32d Travis build: take negative test results as build errors 2014-08-21 00:32:32 +02:00
Petr Mrázek
74f7bd9a1c Add changelog pieces, version 2014-08-16 11:33:01 +02:00
Petr Mrázek
7d8c5ac9b5 Revert "Do not include jutils in LWJGL versions."
This reverts commit 376467740b.
2014-08-15 00:02:44 +02:00
Petr Mrázek
d172daf3a9 Include QtXml, we use it.
Fixes KDevelop semantic analysis magic.
2014-08-15 00:01:55 +02:00
Petr Mrázek
56b16320dd Print the Minecraft window size on launch. 2014-08-13 04:44:19 +02:00
Petr Mrázek
376467740b Do not include jutils in LWJGL versions. 2014-08-13 04:44:01 +02:00
Petr Mrázek
814d5d3315 Properly detect if the instance is vanilla and don't treat it as custom. 2014-08-11 02:17:48 +02:00
Petr Mrázek
fd6706391b Increase the java checker timeout from 5s to 15s 2014-08-10 15:13:35 +02:00
Jan Dalheimer
21597da33d Merge branch 'Loetkolben-pr_feature_warnProblematicInstPath' into develop
Closes #400
2014-07-30 21:45:12 +02:00
Loetkolben
c0254d9a75 Show a warning if the instance path contains a '!'
The checks and warnings happen the time MMC loads (via QLOG_INFO), the time the GUI starts (via a dialog) and when the user changes the instance path via the settings window.
2014-07-30 21:40:18 +02:00
Mrazek, Petr
151fbde8d0 Fix loading of Minecraft versions from FTB packs 2014-07-30 06:25:17 -04:00
Petr Mrázek
03b13b0b3f Rearrange RawLibrary and OneSixLibrary heavily.
Fix #396
2014-07-26 23:00:35 +02:00
Petr Mrázek
9b82c87c92 Tweak windows rc file description (Minecraft Launcher -> MultiMC Launcher) 2014-07-23 22:54:03 +02:00
Petr Mrázek
75cb329f17 Check if the java binary can be found before launch.
Fix #386
2014-07-23 00:16:31 +02:00
Petr Mrázek
bef869ff76 Add a note about MultiMC not working in system folders to build instructions.
Fix #185
2014-07-22 23:29:10 +02:00
Petr Mrázek
0a64579401 Merge branch 'pullrequest2' of https://github.com/p-schneider/MultiMC5 into develop 2014-07-22 23:21:17 +02:00
Petr Mrázek
d71697efb3 Do not deploy debug version of the svg icon lib in release builds. 2014-07-22 23:19:53 +02:00
Mrazek, Petr
e5b393318f Include the SVG icon engine. 2014-07-22 12:27:00 +02:00
Petr Mrázek
1ed90293ac Unify look of all pages.
Now they have a QTabWidget with no tabs as a background.
2014-07-21 00:10:13 +02:00
Petr Mrázek
bc05ad30aa Rework the settings dialog. Rework all of it. Thoroughly.
Also introduces the ColumnResizer from:
https://github.com/agateau/columnresizer/
2014-07-20 23:47:46 +02:00
Jan Dalheimer
e178284172 Merge global settings and accounts into a pagedialog
Also split external tools into it's own page
2014-07-20 15:01:02 +02:00
Petr Mrázek
c91adfb3d1 Merge branch 'master' into develop
Conflicts:
	CMakeLists.txt
	changelog.md
2014-07-20 14:23:14 +02:00
Petr Mrázek
c767707c95 Make forge work.
Using classifiers FTW.
2014-07-19 23:16:02 +02:00
p-schneider
66fa241257 Show a warning in the log if a library is missing 2014-07-19 15:46:55 +02:00
Petr Mrázek
8a56ab6780 Implement gradle spec reader/writer 2014-07-16 02:03:52 +02:00
Petr Mrázek
71575a5022 Fix #367 2014-07-14 20:48:51 +02:00
Petr Mrázek
222c26f9cf Bump dev version 2014-07-14 02:43:10 +02:00
1192 changed files with 50098 additions and 82030 deletions

View File

@@ -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
View File

@@ -0,0 +1 @@
*.pem -crlf

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "depends/libnbtplusplus"]
path = depends/libnbtplusplus
url = https://github.com/MultiMC/libnbtplusplus.git

View File

@@ -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
View File

@@ -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))!**

View File

@@ -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;
}

View File

@@ -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
View 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)

View File

@@ -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
}
// }}}

View File

@@ -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
View 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
View 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
View 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!

View File

@@ -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;
};

View File

@@ -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"

View File

@@ -1,17 +1,33 @@
![MultiMC](http://i.imgur.com/QJXbz.png)
MultiMC 5 [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](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: [![Stories in Ready](https://badge.waffle.io/MultiMC/MultiMC5.svg?label=ready&title=Ready)](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 [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](https://travis-ci.org/MultiMC/MultiMC5)
### Code formatting
We use [Clang Format](http://clang.llvm.org/docs/ClangFormat.html) to format the project. We highly recommend setting it up so the project stays well formatted.
## Translations
Translations can be done 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 [![Translation Status](http://translate.multimc.org/widgets/multimc/-/shields-badge.svg)](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 &copy; 2013 MultiMC Contributors
Copyright &copy; 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).

View File

@@ -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;
}

View File

@@ -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);

View 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;
}

View File

@@ -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
View 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)

View 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);
}
}
}

View File

@@ -0,0 +1,3 @@
#pragma once
void RebuildQtCertificates();

View 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
View 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);
}
};

View 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

View 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;
};

View File

@@ -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()
{
}

View File

@@ -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
View 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
View 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);
}

View File

View 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;
};

View 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;
}
}

View 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
View 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
View 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;
};
}

View 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();
}

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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 &current, 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
View 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"

View File

@@ -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;
};

View 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
View 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());
}

View 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"

View 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;
};

View File

@@ -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();
}

View File

@@ -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
{

View 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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;MultiMC is a custom launcher that makes managing Minecraft easier by allowing you to have multiple instances of Minecraft at once.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://github.com/MultiMC/MultiMC5&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;http://github.com/MultiMC/MultiMC5&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Oxygen-Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="translationInfo">
<property name="text">
<string extracomment="Hey, Translator, feel free to put credit to you here">No Language file loaded.</string>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="licenseTab">
<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">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans Mono'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;MultiMC&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright 2012-2015 MultiMC Contributors&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;);&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;you may not use this file except in compliance with the License.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You may obtain a copy of the License at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Unless required by applicable law or agreed to in writing, software&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;distributed under the License is distributed on an &amp;quot;AS IS&amp;quot; BASIS,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;See the License for the specific language governing permissions and&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;limitations under the License.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;MinGW runtime (Windows)&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (c) 2012 MinGW.org project&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Permission is hereby granted, free of charge, to any person obtaining a&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;copy of this software and associated documentation files (the &amp;quot;Software&amp;quot;),&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;to deal in the Software without restriction, including without limitation&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the rights to use, copy, modify, merge, publish, distribute, sublicense,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;and/or sell copies of the Software, and to permit persons to whom the&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Software is furnished to do so, subject to the following conditions:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;The above copyright notice, this permission notice and the below disclaimer&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;shall be included in all copies or substantial portions of the Software.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;DEALINGS IN THE SOFTWARE.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Qt 5&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Contact: http://www.qt-project.org/legal&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Licensed under LGPL v2.1&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;libnbt++&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;libnbt++ - A library for the Minecraft Named Binary Tag format.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2013, 2015 ljfa-ag&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;libnbt++ is free software: you can redistribute it and/or modify&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;it under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the Free Software Foundation, either version 3 of the License, or&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;(at your option) any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;libnbt++ is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;GNU Lesser General Public License for more details.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;along with libnbt++. If not, see &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Group View&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 Rafael Fern\u00E1ndez L\u00F3pez &amp;lt;ereslibre@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 John Tapsell &amp;lt;tapsell@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;rainbow (KGuiAddons)&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 Matthew Woehlke &amp;lt;mw_triad@users.sourceforge.net&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 Olaf Schmidt &amp;lt;ojschmidt@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 Thomas Zander &amp;lt;zander@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2007 Zack Rusin &amp;lt;zack@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2015 Petr Mrazek &amp;lt;peterix@gmail.com&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This library is free software; you can redistribute it and/or&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;modify it under the terms of the GNU Library General Public&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;License as published by the Free Software Foundation; either&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;version 2 of the License, or (at your option) any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This library is distributed in the hope that it will be useful,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Library General Public License for more details.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You should have received a copy of the GNU Library General Public License&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;along with this library; see the file COPYING.LIB. If not, write to&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Boston, MA 02110-1301, USA.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Hoedown&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (c) 2008, Natacha Port\u00E9&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (c) 2011, Vicent Mart\u00ED&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (c) 2014, Xavier Mendez, Devin Torres and the Hoedown authors&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Permission to use, copy, modify, and distribute this software for any&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;purpose with or without fee is hereby granted, provided that the above&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;copyright notice and this permission notice appear in all copies.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;THE SOFTWARE IS PROVIDED &amp;quot;AS IS&amp;quot; AND THE AUTHOR DISCLAIMS ALL WARRANTIES&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Batch icon set&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You are free to use Batch (the &amp;quot;icon set&amp;quot;) or any part thereof (the &amp;quot;icons&amp;quot;)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;in any personal, open-source or commercial work without obligation of payment&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;(monetary or otherwise) or attribution. Do not sell the icon set, host&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the icon set or rent the icon set (either in existing or modified form).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;While attribution is optional, it is always appreciated.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Intellectual property rights are not transferred with the download of the icons.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL ADAM WHITCROFT&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THE USE OF THE ICONS,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Pack200&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;The GNU General Public License (GPL)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Version 2, June 1991&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;+ &amp;quot;CLASSPATH&amp;quot; EXCEPTION TO THE GPL&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Certain source files distributed by Oracle America and/or its affiliates are&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;subject to the following clarification and special exception to the GPL, but&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;only where Oracle has expressly included in the particular source file's header&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the words &amp;quot;Oracle designates this particular file as subject to the &amp;quot;Classpath&amp;quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;exception as provided by Oracle in the LICENSE file that accompanied this code.&amp;quot;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Linking this library statically or dynamically with other modules is making&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;a combined work based on this library. Thus, the terms and conditions of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the GNU General Public License cover the whole combination.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;As a special exception, the copyright holders of this library give you&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;permission to link this library with independent modules to produce an&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;executable, regardless of the license terms of these independent modules,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;and to copy and distribute the resulting executable under terms of your&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;choice, provided that you also meet, for each linked independent module,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the terms and conditions of the license of that module. An independent&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;module is a module which is not derived from or based on this library. If&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;you modify this library, you may extend this exception to your version of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the library, but you are not obligated to do so. If you do not wish to do&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;so, delete this exception statement from your version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;Quazip&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright (C) 2005-2011 Sergey A. Tachenov&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This program is free software; you can redistribute it and/or modify it&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;the Free Software Foundation; either version 2 of the License, or (at&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;your option) any later version.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This program is distributed in the hope that it will be useful, but&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;WITHOUT ANY WARRANTY; without even the implied warranty of&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;General Public License for more details.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You should have received a copy of the GNU Lesser General Public License&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;along with this program; if not, write to the Free Software Foundation,&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;See COPYING file for the full LGPL text.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Original ZIP package is copyrighted by Gilles Vollant, see&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;quazip/(un)zip.h files for details, basically it's zlib license.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;xz-minidec&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;XZ decompressor&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Authors: Lasse Collin &amp;lt;lasse.collin@tukaani.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; Igor Pavlov &amp;lt;http://7-zip.org/&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Courier New,courier';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;This file has been put into the public domain.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;You can do whatever you want with this file.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:xx-large; font-weight:600;&quot;&gt;ColumnResizer&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;Copyright 2011 Aur\u00E9lien G\u00E2teau &amp;lt;agateau@kde.org&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;License: LGPL v2.1 or later (see COPYING)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt; &lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Oxygen-Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;Part of the reason for using the Apache license is we don't want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</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>

View File

@@ -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();
}

View File

@@ -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
{

View File

@@ -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;
}
}

View File

@@ -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;
};

View File

@@ -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>&amp;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">

View File

@@ -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.

View File

@@ -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.

View File

@@ -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();

View File

@@ -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

View File

@@ -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>

View 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"

View 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);
};

View 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>

View File

@@ -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;

View File

@@ -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:

View File

@@ -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();
}

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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);

View 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());
}
}
}

View File

@@ -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;
};

View 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>&amp;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&amp;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&amp;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>

View File

@@ -3,7 +3,7 @@
#include <QDialog>
#include "logic/updater/NotificationChecker.h"
#include "notifications/NotificationChecker.h"
namespace Ui {
class NotificationDialog;

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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;
};

View 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);
}

View File

@@ -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;
};

View File

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

View 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"

View File

@@ -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;
};

View File

@@ -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/>

View File

@@ -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

View File

@@ -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:

View File

@@ -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;
}
}

View File

@@ -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))
{

View File

@@ -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,

View 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);
}

View 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;
};

View 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());
}
}

View 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);
};

View 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