Compare commits

..

190 Commits

Author SHA1 Message Date
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
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
499 changed files with 16272 additions and 49225 deletions

View File

@@ -10,7 +10,7 @@ NamespaceIndentation: None
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 96
ColumnLimit: 160
MaxEmptyLinesToKeep: 1
Standard: Cpp11

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,23 +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-qt532
- 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 qt53base qt53svg qt53tools qt53x11extras qt53webkit
- 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/qt53/lib/cmake ..
- cmake -DCMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH ..
script:
- make -j4
- make test ARGS="-V"
notifications:
email: false
- make -j4 && make test ARGS="-V"

135
BUILD.md
View File

@@ -2,7 +2,9 @@ Build Instructions
==================
# Contents
* [Note](#note)
* [Getting the source](#source)
* [Linux](#linux)
* [Windows](#windows)
* [OS X](#os-x)
@@ -14,60 +16,69 @@ That would be anything outside your home folder. Before runing `make install`, m
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
@@ -75,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,
@@ -84,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,
@@ -100,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.
@@ -108,26 +119,34 @@ 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
```
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 ..
make
```
*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,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)
@@ -10,7 +10,7 @@ if(WIN32)
cmake_policy(SET CMP0020 OLD)
endif()
project(Megatron)
project(MultiMC)
enable_testing()
######## Set CMake options ########
@@ -19,12 +19,13 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
######## 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()
@@ -32,9 +33,13 @@ 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}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
# cmake code needed for the coverity scan upload
@@ -43,21 +48,12 @@ include(Coverity)
################################ 3rd Party Libs ################################
# Find the required Qt parts
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Concurrent REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Test REQUIRED)
find_package(Qt5Xml REQUIRED)
include_directories(
${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS}
${Qt5Concurrent_INCLUDE_DIRS}
${Qt5Test_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${Qt5Xml_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)
@@ -96,45 +92,27 @@ else()
set(QUAZIP_LIBRARIES -L"${CMAKE_BINARY_DIR}/External/Install/QuaZIP/lib" quazip)
endif()
# Add the markdown parser thing
add_subdirectory(depends/hoedown)
include_directories(${HOEDOWN_INCLUDE_DIR})
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 the java launcher and checker
add_subdirectory(depends/launcher)
add_subdirectory(depends/javacheck)
# 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 GUI -> Logic connection header
add_subdirectory(depends/LogicalGui)
include_directories(${LOGICALGUI_INCLUDE_DIR})
# Add the GUI -> Logic connection header
add_subdirectory(depends/iconfix)
include_directories(${ICONFIX_INCLUDE_DIR})
add_subdirectory(depends/LogicalGui) # GUI -> Logic connection
add_subdirectory(depends/iconfix) # fork of Qt's QIcon loader
include(Coverity)
############################### Built Artifacts ###############################
add_subdirectory(tests)
add_subdirectory(logic)
add_subdirectory(application)
add_subdirectory(mmc_updater)

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)

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,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) [![Translation Status](http://weblate.robotbrain.info/widgets/multimc/-/shields-badge.svg)](http://weblate.robotbrain.info/engage/multimc/?utm_source=widget) [![Stories in Ready](https://badge.waffle.io/MultiMC/MultiMC5.svg?label=ready&title=Ready)](http://waffle.io/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. 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. 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.

View File

@@ -6,51 +6,54 @@ Config BuildConfig;
Config::Config()
{
// Version information
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
VERSION_MAJOR = @MultiMC_VERSION_MAJOR@;
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
VERSION_BUILD = @MultiMC_VERSION_BUILD@;
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_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));
QString vstr = QString("%1.%2").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR));
if (VERSION_HOTFIX > 0) vstr += "." + QString::number(VERSION_HOTFIX);
// if this is a hotfix release, append that
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(VERSION_CHANNEL == "stable")
// If the build is not a main release, append the channel
if(VERSION_CHANNEL != "stable")
{
return vstr;
vstr += "-" + VERSION_CHANNEL;
}
else if(VERSION_CHANNEL == "develop")
// if a build number is set, also add it to the end
if(VERSION_BUILD >= 0)
{
vstr += "-dev-" + QString::number(VERSION_BUILD);
}
else if(VERSION_CHANNEL == "unstable")
{
vstr += "-nuke-" + QString::number(VERSION_BUILD);
}
else if(VERSION_CHANNEL == "custom")
{
vstr += "-local";
}
else
{
vstr += "-" + VERSION_CHANNEL + "-" + QString::number(VERSION_BUILD);
vstr += "-" + QString::number(VERSION_BUILD);
}
return vstr;
}

View File

@@ -23,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;
@@ -35,15 +37,12 @@ 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;
/// The git refspec of this build
QString GIT_REFSPEC;
/// This is printed on start to standard output
QString VERSION_STR;
@@ -53,6 +52,11 @@ public:
*/
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).

View File

@@ -1,4 +1,4 @@
project(MultiMC-Application)
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.")
@@ -6,90 +6,36 @@ set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch
######## Set version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 4)
set(MultiMC_VERSION_HOTFIX 7)
set(MultiMC_VERSION_HOTFIX 10)
# 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.")
#### Check the current Git commit
include(GitFunctions)
git_run(COMMAND rev-parse HEAD DEFAULT "Unknown" OUTPUT_VAR MultiMC_GIT_COMMIT)
# 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()
# 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 "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}")
#### Custom target to just print the version.
add_custom_target(version echo "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()
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")
@@ -123,7 +69,7 @@ elseif(WIN32)
endif()
# directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR})
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR} ${CMAKE_LIBRARY_OUTPUT_DIRECTORY} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
################################ FILES ################################
@@ -136,6 +82,12 @@ SET(MULTIMC_SOURCES
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
@@ -145,6 +97,8 @@ SET(MULTIMC_SOURCES
InstanceProxyModel.cpp
VersionProxyModel.h
VersionProxyModel.cpp
ColorCache.h
ColorCache.cpp
# GUI - windows
MainWindow.h
@@ -152,6 +106,14 @@ SET(MULTIMC_SOURCES
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
@@ -183,6 +145,8 @@ SET(MULTIMC_SOURCES
pages/LegacyJarModPage.h
pages/LegacyUpgradePage.cpp
pages/LegacyUpgradePage.h
pages/WorldListPage.cpp
pages/WorldListPage.h
# GUI - global settings pages
pages/global/AccountListPage.cpp
@@ -197,6 +161,8 @@ SET(MULTIMC_SOURCES
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
@@ -234,6 +200,8 @@ SET(MULTIMC_SOURCES
# GUI - widgets
widgets/Common.cpp
widgets/Common.h
widgets/FocusLineEdit.cpp
widgets/FocusLineEdit.h
widgets/IconLabel.cpp
widgets/IconLabel.h
widgets/LabeledToolButton.cpp
@@ -251,6 +219,8 @@ SET(MULTIMC_SOURCES
widgets/ServerStatus.h
widgets/VersionListView.cpp
widgets/VersionListView.h
widgets/ProgressWidget.h
widgets/ProgressWidget.cpp
# GUI - instance group view
@@ -276,6 +246,7 @@ SET(MULTIMC_UIS
pages/OtherLogsPage.ui
pages/LegacyJarModPage.ui
pages/LegacyUpgradePage.ui
pages/WorldListPage.ui
# Global settings pages
pages/global/AccountListPage.ui
@@ -284,6 +255,7 @@ SET(MULTIMC_UIS
pages/global/MinecraftPage.ui
pages/global/MultiMCPage.ui
pages/global/ProxyPage.ui
pages/global/PasteEEPage.ui
# Dialogs
dialogs/CopyInstanceDialog.ui
@@ -317,38 +289,37 @@ set(MULTIMC_QRCS
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()
####### X11 Stuff #######
if(UNIX AND NOT APPLE)
find_package(Qt5X11Extras REQUIRED)
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} xcb Qt5::X11Extras)
list(APPEND MULTIMC_SOURCES Platform_X11.cpp)
else()
list(APPEND MULTIMC_SOURCES Platform_Other.cpp)
endif()
# Link additional libraries
if(WIN32)
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} Qt5::WinMain)
endif(WIN32)
include_directories(../logic)
# 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 libUtil LogicalGui
${QUAZIP_LIBRARIES} Qt5::Core Qt5::Xml Qt5::Widgets Qt5::Network Qt5::Concurrent
hoedown
${MultiMC_LINK_ADDITIONAL_LIBS})
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 ################################
@@ -433,15 +404,18 @@ else()
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
)
# 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()

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

@@ -24,7 +24,6 @@
#include <qlayoutitem.h>
#include <QCloseEvent>
#include <Platform.h>
#include <dialogs/CustomMessageBox.h>
#include <dialogs/ProgressDialog.h>
#include "widgets/PageContainer.h"
@@ -53,10 +52,9 @@ private:
BasePage * m_log_page;
};
ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent)
: QMainWindow(parent), m_proc(process)
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();
@@ -129,12 +127,9 @@ ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent)
}
// Set up signal connections
connect(m_proc, SIGNAL(ended(InstancePtr, int, QProcess::ExitStatus)), this,
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
connect(m_proc, SIGNAL(prelaunch_failed(InstancePtr, int, QProcess::ExitStatus)), this,
SLOT(onEnded(InstancePtr, int, QProcess::ExitStatus)));
connect(m_proc, 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);
@@ -215,23 +210,19 @@ 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->killProcess();
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())
{
@@ -245,15 +236,24 @@ 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

@@ -17,7 +17,7 @@
#include <QMainWindow>
#include <QSystemTrayIcon>
#include "BaseProcess.h"
#include "launch/LaunchTask.h"
class QPushButton;
class PageContainer;
@@ -26,7 +26,7 @@ class ConsoleWindow : public QMainWindow
Q_OBJECT
public:
explicit ConsoleWindow(BaseProcess *proc, QWidget *parent = 0);
explicit ConsoleWindow(std::shared_ptr<LaunchTask> proc, QWidget *parent = 0);
virtual ~ConsoleWindow();
/**
@@ -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:
BaseProcess *m_proc = nullptr;
std::shared_ptr<LaunchTask> m_proc;
bool m_mayclose = true;
QSystemTrayIcon *m_trayIcon = nullptr;
PageContainer *m_container = nullptr;

View File

@@ -1,7 +1,6 @@
#include "GuiUtil.h"
#include <QClipboard>
#include <QDesktopServices>
#include <QApplication>
#include <QFileDialog>
@@ -11,11 +10,18 @@
#include "MultiMC.h"
#include <settings/SettingsObject.h>
#include <DesktopServices.h>
#include <BuildConfig.h>
void GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
QString GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
{
ProgressDialog dialog(parentWidget);
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text));
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())
{
@@ -23,26 +29,28 @@ void GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
parentWidget, QObject::tr("Upload failed"),
QObject::tr("The log file is too big. You'll have to upload it manually."),
QMessageBox::Warning)->exec();
return;
return QString();
}
dialog.exec(paste.get());
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);
QDesktopServices::openUrl(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;
}
}
@@ -51,14 +59,13 @@ void GuiUtil::setClipboardText(const QString &text)
QApplication::clipboard()->setText(text);
}
QStringList GuiUtil::BrowseForMods(QString context, QString caption, QString filter,
QWidget *parentWidget)
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;
QString modsFolder = MMC->settings()->get("CentralModsDir").toString();
auto f = [&](QStandardPaths::StandardLocation l)
{
QString location = QStandardPaths::writableLocation(l);
@@ -76,19 +83,30 @@ QStringList GuiUtil::BrowseForMods(QString context, QString caption, QString fil
{
urls.append(QUrl::fromLocalFile(location));
}
urls.append(QUrl::fromLocalFile(modsFolder));
urls.append(QUrl::fromLocalFile(defaultPath));
w.setFileMode(QFileDialog::ExistingFiles);
w.setAcceptMode(QFileDialog::AcceptOpen);
w.setNameFilter(filter);
QString pathToOpen;
if(savedPaths.contains(context))
{
w.setDirectory(savedPaths[context]);
pathToOpen = savedPaths[context];
}
else
{
w.setDirectory(modsFolder);
pathToOpen = defaultPath;
}
if(!pathToOpen.isEmpty())
{
QFileInfo finfo(pathToOpen);
if(finfo.exists() && finfo.isDir())
{
w.setDirectory(finfo.absoluteFilePath());
}
}
w.setSidebarUrls(urls);
if (w.exec())

View File

@@ -4,7 +4,7 @@
namespace GuiUtil
{
void uploadPaste(const QString &text, QWidget *parentWidget);
QString uploadPaste(const QString &text, QWidget *parentWidget);
void setClipboardText(const QString &text);
QStringList BrowseForMods(QString context, QString caption, QString filter, QWidget *parentWidget);
QStringList BrowseForFiles(QString context, QString caption, QString filter, QString defaultPath, QWidget *parentWidget);
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "minecraft/OneSixInstance.h"
#include "minecraft/LegacyInstance.h"
#include <FileSystem.h>
#include "pages/BasePage.h"
#include "pages/VersionPage.h"
#include "pages/ModFolderPage.h"
@@ -12,7 +13,7 @@
#include "pages/OtherLogsPage.h"
#include "pages/BasePageProvider.h"
#include "pages/LegacyJarModPage.h"
#include <pathutils.h>
#include "pages/WorldListPage.h"
class InstancePageProvider : public QObject, public BasePageProvider
@@ -39,14 +40,13 @@ public:
values.append(new ResourcePackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get()));
values.append(new NotesPage(onesix.get()));
values.append(new ScreenshotsPage(PathCombine(onesix->minecraftRoot(), "screenshots")));
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()));
values.append(new OtherLogsPage(onesix->minecraftRoot()));
}
std::shared_ptr<LegacyInstance> legacy = std::dynamic_pointer_cast<LegacyInstance>(inst);
if(legacy)
{
QList<BasePage *> values;
// FIXME: actually implement the legacy instance upgrade, then enable this.
//values.append(new LegacyUpgradePage(this));
values.append(new LegacyJarModPage(legacy.get()));
@@ -56,10 +56,14 @@ public:
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 ScreenshotsPage(PathCombine(legacy->minecraftRoot(), "screenshots")));
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()));
values.append(new OtherLogsPage(legacy->minecraftRoot()));
return values;
}
auto logMatcher = inst->getLogFileMatcher();
if(logMatcher)
{
values.append(new OtherLogsPage(inst->getLogFileRoot(), logMatcher));
}
return values;
}

View File

@@ -1,3 +1,5 @@
#pragma once
#include "groupview/GroupedProxyModel.h"
/**

View File

@@ -26,7 +26,7 @@ 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);
"reported: %2<br />").arg(result.realPlatform, result.javaVersion.toString());
if (result.errorLog.size())
{
auto htmlError = result.errorLog;
@@ -87,7 +87,7 @@ void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
checker->m_args = m_args;
checker->m_minMem = m_minMem;
checker->m_maxMem = m_maxMem;
if (Strings::naturalCompare(result.javaVersion, "1.8", Qt::CaseInsensitive) < 0)
if (result.javaVersion.requiresPermGen())
{
checker->m_permGen = m_permGen;
}

View File

@@ -0,0 +1,271 @@
#include "LaunchInteraction.h"
#include <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 <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;
};

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,8 @@
#pragma once
#include <memory>
#include <QMainWindow>
#include <QProcess>
#include <QTimer>
@@ -24,46 +26,45 @@
#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;
class GenericPageProvider;
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 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();
@@ -98,8 +99,6 @@ slots:
void on_mainToolBar_visibilityChanged(bool);
// void on_instanceView_customContextMenuRequested(const QPoint &pos);
void on_actionLaunchInstance_triggered();
void on_actionLaunchInstanceOffline_triggered();
@@ -116,40 +115,19 @@ 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);
@@ -177,44 +155,42 @@ slots:
/*!
* Runs the DownloadTask and installs updates.
*/
void downloadUpdates(GoUpdate::Status status, bool installOnExit = false);
void downloadUpdates(GoUpdate::Status status);
protected:
bool eventFilter(QObject *obj, QEvent *ev);
private:
void setCatBackground(bool enabled);
void updateInstanceToolIcon(QString new_icon);
void setSelectedInstanceById(const QString &id);
void waitForMinecraftVersions();
void instanceFromVersion(QString instName, QString instGroup, QString instIcon, BaseVersionPtr version);
void instanceFromZipPack(QString instName, QString instGroup, QString instIcon, QUrl url);
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:
Ui::MainWindow *ui;
class GroupView *view;
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;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::shared_ptr<NewsChecker> m_newsChecker;
std::shared_ptr<NotificationChecker> m_notificationChecker;
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;
};

View File

@@ -1,5 +1,13 @@
#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>
@@ -9,7 +17,6 @@
#include <QLibraryInfo>
#include <QMessageBox>
#include <QStringList>
#include <QDesktopServices>
#include <QDebug>
#include "InstanceList.h"
@@ -33,17 +40,22 @@
#include "tools/JVisualVM.h"
#include "tools/MCEditTool.h"
#include "pathutils.h"
#include "cmdutils.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 "ftb/FTBPlugin.h"
using namespace Util::Commandline;
#include <Commandline.h>
#include <FileSystem.h>
#include <DesktopServices.h>
using namespace Commandline;
MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, argv)
{
@@ -74,6 +86,10 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
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
@@ -100,7 +116,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
// display version and exit
if (args["version"].toBool())
{
std::cout << "Version " << BuildConfig.VERSION_STR.toStdString() << std::endl;
std::cout << "Version " << BuildConfig.printableVersionString().toStdString() << std::endl;
std::cout << "Git " << BuildConfig.GIT_COMMIT.toStdString() << std::endl;
m_status = MultiMC::Succeeded;
return;
@@ -110,6 +126,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
QString origcwdPath = QDir::currentPath();
QString binPath = applicationDirPath();
QString adjustedBy;
QString dataPath;
// change directory
QString dirParam = args["dir"].toString();
if (!dirParam.isEmpty())
@@ -125,7 +142,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
adjustedBy += "Fallback to binary path " + dataPath;
}
if (!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
launchId = args["launch"].toString();
if (!FS::ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
{
// BAD STUFF. WHAT DO?
initLogger();
@@ -142,33 +161,23 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
else
{
#ifdef Q_OS_LINUX
QDir foo(PathCombine(binPath, ".."));
QDir foo(FS::PathCombine(binPath, ".."));
rootPath = foo.absolutePath();
#elif defined(Q_OS_WIN32)
rootPath = binPath;
#elif defined(Q_OS_MAC)
QDir foo(PathCombine(binPath, "../.."));
QDir foo(FS::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();
qDebug() << "MultiMC 5, (c) 2013-2015 MultiMC Contributors";
qDebug() << "Version : " << BuildConfig.VERSION_STR;
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;
@@ -181,7 +190,6 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
}
qDebug() << "Binary path : " << binPath;
qDebug() << "Application root path : " << rootPath;
qDebug() << "Static data path : " << staticDataPath;
// load settings
initGlobalSettings(test_mode);
@@ -190,7 +198,10 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
initTranslations();
// initialize the updater
m_updateChecker.reset(new UpdateChecker(BuildConfig.CHANLIST_URL, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
if(BuildConfig.UPDATER_ENABLED)
{
m_updateChecker.reset(new UpdateChecker(BuildConfig.CHANLIST_URL, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
}
m_translationChecker.reset(new TranslationDownloader());
@@ -203,7 +214,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
// 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 (checkProblemticPathJava(QDir(instDir)))
if (FS::checkProblemticPathJava(QDir(instDir)))
{
qWarning()
<< "Your instance path contains \'!\' and this is known to cause java problems";
@@ -221,7 +232,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, ar
m_accounts->loadList();
// init the http meta cache
ENV.initHttpMetaCache(rootPath, staticDataPath);
ENV.initHttpMetaCache();
// create the global network manager
ENV.m_qnam.reset(new QNetworkAccessManager(this));
@@ -273,10 +284,15 @@ MultiMC::~MultiMC()
}
}
#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);
@@ -307,7 +323,7 @@ void MultiMC::initTranslations()
}
m_mmc_translator.reset(new QTranslator());
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), staticDataPath + "/translations"))
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), FS::PathCombine(QDir::currentPath(), "translations")))
{
qDebug() << "Loading MMC Language File for"
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
@@ -331,6 +347,19 @@ void MultiMC::initIcons()
{
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)));
});
}
@@ -421,6 +450,8 @@ void MultiMC::initGlobalSettings(bool test_mode)
m_settings->registerSetting("ConsoleFont", defaultMonospace);
}
m_settings->registerSetting("ConsoleFontSize", defaultSize);
m_settings->registerSetting("ConsoleMaxLines", 100000);
m_settings->registerSetting("ConsoleOverflowStop", true);
FTBPlugin::initialize(m_settings);
@@ -498,6 +529,21 @@ void MultiMC::initGlobalSettings(bool test_mode)
// 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()
@@ -540,76 +586,333 @@ std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
return m_minecraftlist;
}
std::shared_ptr<JavaVersionList> MultiMC::javalist()
std::shared_ptr<JavaInstallList> MultiMC::javalist()
{
if (!m_javalist)
{
m_javalist.reset(new JavaVersionList());
m_javalist.reset(new JavaInstallList());
ENV.registerVersionList("com.java", m_javalist);
}
return m_javalist;
}
void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
// 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)
{
// if we are going to update on exit, save the params now
if (flags & OnExit)
QFile::Permissions perms;
if (mode & S_IRUSR)
{
m_updateOnExitPath = updateFilesDir;
m_updateOnExitFlags = flags & ~OnExit;
return;
perms |= QFile::ReadUser;
}
// otherwise if there already were some params for on exit update, clear them and continue
else if (m_updateOnExitPath.size())
if (mode & S_IWUSR)
{
m_updateOnExitFlags = None;
m_updateOnExitPath.clear();
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 WINDOWS
#ifdef Q_OS_WIN
QString finishCmd = applicationFilePath();
QString updaterBinary = PathCombine(applicationDirPath(), "updater.exe");
#elif LINUX
QString finishCmd = PathCombine(root(), "MultiMC");
QString updaterBinary = PathCombine(applicationDirPath(), "updater");
#elif OSX
#elif defined Q_OS_LINUX
QString finishCmd = FS::PathCombine(root(), "MultiMC");
#elif defined Q_OS_MAC
QString finishCmd = applicationFilePath();
QString updaterBinary = PathCombine(applicationDirPath(), "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(applicationPid());
if (flags & DryRun)
args << "--dry-run";
if (flags & RestartOnFinish)
{
args << "--finish-cmd" << finishCmd;
args << "--finish-dir" << dataPath;
}
qDebug() << "Running updater with command" << updaterBinary << args.join(" ");
QFile::setPermissions(updaterBinary, (QFileDevice::Permission)0x7755);
QString backupPath = FS::PathCombine(root(), "update", "backup");
QDir origin(root());
if (!QProcess::startDetached(updaterBinary, args /*, root()*/))
// clean up the backup folder. it should be empty before we start
if(!FS::deletePath(backupPath))
{
qCritical() << "Failed to start the updater process!";
qWarning() << "couldn't remove previous backup folder" << backupPath;
}
// and it should exist.
if(!FS::ensureFolderPathExists(backupPath))
{
qWarning() << "couldn't create folder" << backupPath;
return;
}
ENV.destroy();
// Now that we've started the updater, quit MultiMC.
quit();
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)
@@ -623,10 +926,6 @@ void MultiMC::onExit()
{
m_instances->saveGroupList();
}
if (m_updateOnExitPath.size())
{
installUpdates(m_updateOnExitPath, m_updateOnExitFlags);
}
ENV.destroy();
if(logFile)
{
@@ -640,12 +939,12 @@ 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));
return DesktopServices::openUrl(QUrl::fromLocalFile(file));
}
else
{
return QProcess::startDetached(m_settings->get("JsonEditor").toString(), QStringList()
<< file);
//return DesktopServices::openFile(m_settings->get("JsonEditor").toString(), file);
return DesktopServices::run(m_settings->get("JsonEditor").toString(), {});
}
}

View File

@@ -6,7 +6,9 @@
#include <QFlag>
#include <QIcon>
#include <QDateTime>
#include <updater/GoUpdate.h>
class GenericPageProvider;
class QFile;
class MinecraftVersionList;
class LWJGLVersionList;
@@ -18,7 +20,7 @@ class IconList;
class QNetworkAccessManager;
class ForgeVersionList;
class LiteLoaderVersionList;
class JavaVersionList;
class JavaInstallList;
class UpdateChecker;
class BaseProfilerFactory;
class BaseDetachedToolFactory;
@@ -29,16 +31,6 @@ class TranslationDownloader;
#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);
class MultiMC : public QApplication
{
// friends for the purpose of limiting access to deprecated stuff
@@ -63,6 +55,11 @@ public:
return m_settings;
}
std::shared_ptr<GenericPageProvider> globalSettingsPages()
{
return m_globalSettingsProvider;
}
qint64 timeSinceStart() const
{
return startTime.msecsTo(QDateTime::currentDateTime());
@@ -82,7 +79,7 @@ public:
std::shared_ptr<LWJGLVersionList> lwjgllist();
std::shared_ptr<ForgeVersionList> forgelist();
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
std::shared_ptr<JavaVersionList> javalist();
std::shared_ptr<JavaInstallList> javalist();
// APPLICATION ONLY
std::shared_ptr<InstanceList> instances()
@@ -115,7 +112,10 @@ public:
}
// APPLICATION ONLY
void installUpdates(const QString updateFilesDir, UpdateFlags flags = None);
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
@@ -124,13 +124,6 @@ public:
bool openJsonEditor(const QString &filename);
protected: /* to be removed! */
// FIXME: remove. used by MultiMCPage to enumerate translations.
/// this is the static data. it stores things that don't move.
const QString &staticData()
{
return staticDataPath;
}
// FIXME: remove. used by MainWindow to create application update tasks
/// this is the root of the 'installation'. Used for automatic updates
const QString &root()
@@ -146,13 +139,10 @@ private slots:
private:
void initLogger();
void initIcons();
void initGlobalSettings(bool test_mode);
void initTranslations();
void initSSL();
void initSSL();
private:
friend class UpdateCheckerTest;
@@ -170,20 +160,17 @@ private:
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<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;
QString m_updateOnExitPath;
UpdateFlags m_updateOnExitFlags = None;
QString rootPath;
QString staticDataPath;
QString dataPath;
Status m_status = MultiMC::Failed;
public:
QString launchId;
std::shared_ptr<QFile> logFile;
};

View File

@@ -1,62 +0,0 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
* 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 <Platform.h>
#include <QtX11Extras/QX11Info>
#include <xcb/xcb.h>
static QByteArray WM_CLASS = "MultiMC5\0MultiMC5";
template <typename... ArgTypes, typename... ArgTypes2>
static inline unsigned int XcbCallVoid(xcb_void_cookie_t (*func)(xcb_connection_t *, ArgTypes...), ArgTypes2... args...)
{
return func(QX11Info::connection(), args...).sequence;
}
static void getAtoms(size_t n, xcb_atom_t *atoms, const char *const names[], bool create)
{
xcb_connection_t *conn = QX11Info::connection();
xcb_intern_atom_cookie_t *cookies = (xcb_intern_atom_cookie_t *)malloc(sizeof(xcb_intern_atom_cookie_t) * 2);
for (size_t i = 0; i < n; ++i)
cookies[i] = xcb_intern_atom(conn, create, strlen(names[i]), names[i]);
memset(atoms, 0, sizeof(xcb_atom_t) * n);
for (size_t i = 0; i < n; ++i)
{
xcb_intern_atom_reply_t *r = xcb_intern_atom_reply(conn, cookies[i], 0);
if (r)
{
atoms[i] = r->atom;
free(r);
}
}
free(cookies);
}
static inline xcb_atom_t getAtom(const char *name, bool create=false)
{
xcb_atom_t atom;
getAtoms(1, &atom, &name, create);
return atom;
}
void MultiMCPlatform::fixWM_CLASS(QWidget *widget)
{
static const xcb_atom_t atom = getAtom("WM_CLASS");
XcbCallVoid(xcb_change_property, XCB_PROP_MODE_REPLACE,
widget->winId(), atom, XCB_ATOM_STRING, 8, WM_CLASS.count(),
WM_CLASS.constData());
}

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

@@ -2,7 +2,7 @@
#include "MultiMC.h"
#include <QSortFilterProxyModel>
#include <QPixmapCache>
#include <modutils.h>
#include <Version.h>
class VersionFilterModel : public QSortFilterProxyModel
{
@@ -35,7 +35,7 @@ public:
return false;
}
}
else if (!Util::versionIsInInterval(versionString, it.value().string))
else if (!versionIsInInterval(versionString, it.value().string))
{
return false;
}
@@ -309,8 +309,9 @@ void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left,
emit dataChanged(topLeft, bottomRight);
}
void VersionProxyModel::setSourceModel(BaseVersionList *replacing)
void VersionProxyModel::setSourceModel(QAbstractItemModel *replacingRaw)
{
auto replacing = dynamic_cast<BaseVersionList *>(replacingRaw);
beginResetModel();
if(!replacing)

View File

@@ -36,7 +36,7 @@ public:
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;
void setSourceModel(BaseVersionList *sourceModel);
virtual void setSourceModel(QAbstractItemModel *sourceModel) override;
const FilterMap &filters() const;
void setFilter(const BaseVersionList::ModelRoles column, const QString &filter, const bool exact);

View File

@@ -17,7 +17,6 @@
#include "ui_AboutDialog.h"
#include <QIcon>
#include "MultiMC.h"
#include "Platform.h"
#include "BuildConfig.h"
#include <net/NetJob.h>
@@ -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());

View File

@@ -83,7 +83,7 @@
</font>
</property>
<property name="text">
<string>MultiMC 5</string>
<string notr="true">MultiMC 5</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -91,23 +91,15 @@
</widget>
</item>
<item>
<widget class="QToolBox" name="toolBox">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="aboutPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
<widget class="QWidget" name="aboutTab">
<attribute name="title">
<string>About</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QLabel" name="versionLabel">
<property name="text">
@@ -203,26 +195,18 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>212</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="creditsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
<widget class="QWidget" name="creditsTab">
<attribute name="title">
<string>Credits</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTextEdit" name="creditsText">
<property name="readOnly">
@@ -252,19 +236,11 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<widget class="QWidget" name="licensePage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
<widget class="QWidget" name="licenseTab">
<attribute name="title">
<string>License</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTextEdit" name="licenseText">
<property name="minimumSize">
@@ -282,182 +258,204 @@ p, li { white-space: pre-wrap; }
<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;
<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 align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;MultiMC&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-size:10pt;&quot;&gt;Copyright 2012-2014 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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&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-size:10pt;&quot;&gt;limitations under the 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-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;QSLog&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-size:8pt;&quot;&gt;Copyright (c) 2010, Razvan Petru&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-size:8pt;&quot;&gt;All rights reserved.&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-size:8pt;&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-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without modification,&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-size:8pt;&quot;&gt;are permitted provided that the following conditions are met:&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-size:8pt;&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-size:8pt;&quot;&gt;* Redistributions of source code must retain the above copyright notice, this&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-size:8pt;&quot;&gt; list of conditions and the following 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-size:8pt;&quot;&gt;* Redistributions in binary form must reproduce the above copyright notice, this&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-size:8pt;&quot;&gt; list of conditions and the following disclaimer in the documentation and/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-size:8pt;&quot;&gt; materials provided with the distribution.&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-size:8pt;&quot;&gt;* The name of the contributors may not be used to endorse or promote products&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-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&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-size:8pt;&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-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; 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-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 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-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.&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-size:8pt;&quot;&gt;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,&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-size:8pt;&quot;&gt;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,&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-size:8pt;&quot;&gt;BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,&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-size:8pt;&quot;&gt;DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 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-size:8pt;&quot;&gt;LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE&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-size:8pt;&quot;&gt;OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED&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-size:8pt;&quot;&gt;OF THE POSSIBILITY OF SUCH DAMAGE.&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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Group View (instance view)&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-size:8pt;&quot;&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-size:8pt;&quot;&gt; * Copyright (C) 2007 Rafael Fernández López &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-size:8pt;&quot;&gt; * Copyright (C) 2007 John Tapsell &amp;lt;tapsell@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-size:8pt;&quot;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt; * version 2 of the License, or (at your option) any later version.&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-size:8pt;&quot;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt; * Library General Public License for more details.&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-size:8pt;&quot;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt; * Boston, MA 02110-1301, USA.&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-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; 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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Batch icon set&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt;EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.&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-size:10pt;&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;a href=&quot;http://adamwhitcroft.com/batch/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#68a0df;&quot;&gt;http://adamwhitcroft.com/batch/&lt;/span&gt;&lt;/a&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-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Pack200&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt; so, delete this exception statement from your 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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Quazip&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&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-size:8pt;&quot;&gt;quazip/(un)zip.h files for details, basically it's zlib 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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;xz-minidec&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-size:8pt;&quot;&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-size:8pt;&quot;&gt; * XZ decompressor&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-size:8pt;&quot;&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-size:8pt;&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-size:8pt;&quot;&gt; * Igor Pavlov &amp;lt;http://7-zip.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-size:8pt;&quot;&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-size:8pt;&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-size:8pt;&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:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;Java IconLoader class&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;&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-size:8pt;&quot;&gt;Copyright (c) 2011, Chris Molini&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-size:8pt;&quot;&gt;All rights reserved.&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-size:8pt;&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-size:8pt;&quot;&gt;Redistribution and use in source and binary forms, with or without&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-size:8pt;&quot;&gt;modification, are permitted provided that the following conditions are met:&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-size:8pt;&quot;&gt; * Redistributions of source code must retain the above copyright&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-size:8pt;&quot;&gt; notice, this list of conditions and the following 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-size:8pt;&quot;&gt; * Redistributions in binary form must reproduce the above copyright&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-size:8pt;&quot;&gt; notice, this list of conditions and the following disclaimer in 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-size:8pt;&quot;&gt; documentation and/or other materials provided with the distribution.&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-size:8pt;&quot;&gt; * Neither the name of the &amp;lt;organization&amp;gt; nor 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-size:8pt;&quot;&gt; names of its contributors may be used to endorse or promote products&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-size:8pt;&quot;&gt; derived from this software without specific prior written permission.&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-size:8pt;&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-size:8pt;&quot;&gt;THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &amp;quot;AS IS&amp;quot; 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-size:8pt;&quot;&gt;ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 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-size:8pt;&quot;&gt;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 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-size:8pt;&quot;&gt;DISCLAIMED. IN NO EVENT SHALL &amp;lt;COPYRIGHT HOLDER&amp;gt; BE LIABLE 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-size:8pt;&quot;&gt;DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 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-size:8pt;&quot;&gt;(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;&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-size:8pt;&quot;&gt;LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 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-size:8pt;&quot;&gt;ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT&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-size:8pt;&quot;&gt;(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS&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-size:8pt;&quot;&gt;SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.&lt;/span&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; 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-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p align=&quot;center&quot; style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;&quot;&gt;ColumnResizer&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-size:8pt;&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-size:8pt;&quot;&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-size:8pt;&quot;&gt; * Copyright 2011 Aurélien Gâteau &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-size:8pt;&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:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt; */&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&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>
@@ -466,19 +464,11 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<widget class="QWidget" name="forkPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
<widget class="QWidget" name="forkingTab">
<attribute name="title">
<string>Forking/Redistribution</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_33">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTextEdit" name="textEdit">
<property name="html">
@@ -537,6 +527,15 @@ p, li { white-space: pre-wrap; }
</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>

View File

@@ -20,7 +20,6 @@
#include "CopyInstanceDialog.h"
#include "ui_CopyInstanceDialog.h"
#include "Platform.h"
#include "dialogs/IconPickerDialog.h"
#include "BaseVersion.h"
@@ -32,7 +31,6 @@
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);
@@ -54,6 +52,7 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
}
ui->groupBox->setCurrentIndex(index);
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
ui->copySavesCheckbox->setChecked(m_copySaves);
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -84,7 +83,7 @@ QString CopyInstanceDialog::instGroup() const
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
dlg.exec(InstIconKey);
dlg.execWithSelection(InstIconKey);
if (dlg.result() == QDialog::Accepted)
{
@@ -97,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

@@ -39,14 +39,17 @@ public:
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,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>345</width>
<height>205</height>
<height>240</height>
</rect>
</property>
<property name="windowTitle">
@@ -87,7 +87,7 @@
<item row="0" column="0">
<widget class="QLabel" name="labelVersion_3">
<property name="text">
<string>Group</string>
<string>&amp;Group</string>
</property>
<property name="buddy">
<cstring>groupBox</cstring>
@@ -109,6 +109,13 @@
</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

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

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

@@ -17,7 +17,6 @@
#include "ui_ExportInstanceDialog.h"
#include <BaseInstance.h>
#include <MMCZip.h>
#include <pathutils.h>
#include <QFileDialog>
#include <QMessageBox>
#include <qfilesystemmodel.h>
@@ -30,6 +29,7 @@
#include "SeparatorPrefixTree.h"
#include "Env.h"
#include <icons/IconList.h>
#include <FileSystem.h>
class PackIgnoreProxy : public QSortFilterProxyModel
{
@@ -184,7 +184,7 @@ public:
blocked.remove(cover);
// block all contents, except for any cover
QModelIndex rootIndex =
fsm->index(PathCombine(m_instance->instanceRoot(), cover));
fsm->index(FS::PathCombine(m_instance->instanceRoot(), cover));
QModelIndex doing = rootIndex;
int row = 0;
QStack<QModelIndex> todo;
@@ -375,18 +375,18 @@ void SaveIcon(InstancePtr m_instance)
}
}
auto pixmap = icon.pixmap(largest);
pixmap.save(PathCombine(m_instance->instanceRoot(), iconKey + ".png"));
pixmap.save(FS::PathCombine(m_instance->instanceRoot(), iconKey + ".png"));
}
}
}
bool ExportInstanceDialog::doExport()
{
auto name = RemoveInvalidFilenameChars(m_instance->name());
auto name = FS::RemoveInvalidFilenameChars(m_instance->name());
const QString output = QFileDialog::getSaveFileName(
this, tr("Export %1").arg(m_instance->name()),
PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)");
FS::PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)");
if (output.isNull())
{
return false;
@@ -451,7 +451,7 @@ void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom)
QString ExportInstanceDialog::ignoreFileName()
{
return PathCombine(m_instance->instanceRoot(), ".packignore");
return FS::PathCombine(m_instance->instanceRoot(), ".packignore");
}
void ExportInstanceDialog::loadPackIgnore()
@@ -469,15 +469,16 @@ void ExportInstanceDialog::loadPackIgnore()
void ExportInstanceDialog::savePackIgnore()
{
auto filename = ignoreFileName();
QSaveFile ignoreFile(filename);
if(!ignoreFile.open(QIODevice::WriteOnly))
{
ignoreFile.cancelWriting();
}
auto data = proxyModel->blockedPaths().toStringList().join('\n').toUtf8();
ignoreFile.write(data);
ignoreFile.commit();
auto filename = ignoreFileName();
try
{
FS::write(filename, data);
}
catch (Exception & e)
{
qWarning() << e.cause();
}
}
#include "ExportInstanceDialog.moc"

View File

@@ -22,7 +22,6 @@
#include "IconPickerDialog.h"
#include "ui_IconPickerDialog.h"
#include "Platform.h"
#include "groupview/InstanceDelegate.h"
#include "icons/IconList.h"
@@ -30,7 +29,6 @@
IconPickerDialog::IconPickerDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::IconPickerDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
setWindowModality(Qt::WindowModal);
@@ -128,7 +126,7 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection
selectedIconKey = key;
}
int IconPickerDialog::exec(QString selection)
int IconPickerDialog::execWithSelection(QString selection)
{
auto list = ENV.icons();
auto contentsWidget = ui->iconView;

View File

@@ -29,7 +29,7 @@ class IconPickerDialog : public QDialog
public:
explicit IconPickerDialog(QWidget *parent = 0);
~IconPickerDialog();
int exec(QString selection);
int execWithSelection(QString selection);
QString selectedIconKey;
protected:

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

@@ -28,7 +28,7 @@ void showWebsiteForMod(QWidget *parentDlg, Mod &m)
{
url = "http://" + url;
}
QDesktopServices::openUrl(url);
DesktopServices::openUrl(url);
}
else
{

View File

@@ -1,6 +1,6 @@
#pragma once
#include <QModelIndex>
#include <QDesktopServices>
#include <DesktopServices.h>
#include <QWidget>
#include <minecraft/Mod.h>

View File

@@ -23,7 +23,6 @@
#include <tasks/Task.h>
#include <InstanceList.h>
#include "Platform.h"
#include "VersionSelectDialog.h"
#include "ProgressDialog.h"
#include "IconPickerDialog.h"
@@ -59,19 +58,22 @@ public:
NewInstanceDialog::NewInstanceDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::NewInstanceDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
setSelectedVersion(MMC->minecraftlist()->getRecommended(), true);
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());
@@ -88,6 +90,10 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent)
}
ui->groupBox->setCurrentIndex(index);
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
ui->buttonBox->setFocus();
originalPlaceholderText = ui->instNameTextBox->placeholderText();
updateDialogState();
}
NewInstanceDialog::~NewInstanceDialog()
@@ -97,36 +103,61 @@ NewInstanceDialog::~NewInstanceDialog()
void NewInstanceDialog::updateDialogState()
{
bool allowOK = !instName().isEmpty() &&
(ui->versionBox->isChecked() && m_selectedVersion ||
(ui->modpackBox->isChecked() && ui->modpackEdit->hasAcceptableInput()));
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, bool initial)
void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version)
{
m_selectedVersion = version;
if (m_selectedVersion)
{
ui->versionTextBox->setText(version->name());
if(ui->instNameTextBox->text().isEmpty() && !initial)
{
ui->instNameTextBox->setText(version->name());
}
}
else
{
ui->versionTextBox->setText("");
}
updateDialogState();
}
QString NewInstanceDialog::instName() const
{
return ui->instNameTextBox->text();
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();
@@ -176,7 +207,7 @@ void NewInstanceDialog::on_btnChangeVersion_clicked()
void NewInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
dlg.exec(InstIconKey);
dlg.execWithSelection(InstIconKey);
if (dlg.result() == QDialog::Accepted)
{

View File

@@ -34,7 +34,7 @@ public:
void updateDialogState();
void setSelectedVersion(BaseVersionPtr version, bool initial = false);
void setSelectedVersion(BaseVersionPtr version);
void loadVersionList();
@@ -56,4 +56,5 @@ private:
BaseVersionPtr m_selectedVersion;
QString InstIconKey;
QString originalPlaceholderText;
};

View File

@@ -9,7 +9,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>277</width>
<width>281</width>
<height>404</height>
</rect>
</property>
@@ -113,17 +113,17 @@
<bool>false</bool>
</property>
<property name="text">
<string>...</string>
<string notr="true">...</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLineEdit" name="modpackEdit">
<widget class="FocusLineEdit" name="modpackEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>http://</string>
<string notr="true">http://</string>
</property>
</widget>
</item>
@@ -137,7 +137,7 @@
<item row="2" column="2">
<widget class="QToolButton" name="btnChangeVersion">
<property name="text">
<string>...</string>
<string notr="true">...</string>
</property>
</widget>
</item>
@@ -192,6 +192,13 @@
</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>

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

@@ -19,11 +19,9 @@
#include <QKeyEvent>
#include "tasks/Task.h"
#include "Platform.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(Task *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,11 +71,40 @@ int ProgressDialog::exec(Task *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 QDialog::Accepted;
{
return QDialog::Rejected;
}
}
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()

View File

@@ -34,7 +34,7 @@ public:
void updateSize();
int exec(Task *task);
int execWithTask(Task *task);
void setSkipButton(bool present, QString label = QString());
@@ -58,6 +58,9 @@ protected:
virtual void keyPressEvent(QKeyEvent *e);
virtual void closeEvent(QCloseEvent *e);
private:
bool handleImmediateResult(QDialog::DialogCode &result);
private:
Ui::ProgressDialog *ui;

View File

@@ -1,16 +1,17 @@
#include "UpdateDialog.h"
#include "ui_UpdateDialog.h"
#include "Platform.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)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
auto channel = MMC->settings()->get("UpdateChannel").toString();
if(hasUpdate)
@@ -20,8 +21,8 @@ UpdateDialog::UpdateDialog(bool hasUpdate, QWidget *parent) : QDialog(parent), u
else
{
ui->label->setText(tr("No %1 updates found. You are running the latest version.").arg(channel));
ui->btnUpdateNow->setDisabled(true);
ui->btnUpdateOnExit->setDisabled(true);
ui->btnUpdateNow->setHidden(true);
ui->btnUpdateLater->setText(tr("Close"));
}
loadChangelog();
}
@@ -34,7 +35,17 @@ void UpdateDialog::loadChangelog()
{
auto channel = MMC->settings()->get("UpdateChannel").toString();
dljob.reset(new NetJob("Changelog"));
auto url = QString("https://raw.githubusercontent.com/MultiMC/MultiMC5/%1/changelog.md").arg(channel);
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);
@@ -109,10 +120,94 @@ QString reprocessMarkdown(QByteArray markdown)
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()
{
auto html = reprocessMarkdown(changelogDownload->m_data);
ui->changelogBrowser->setHtml(html);
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)
@@ -129,8 +224,3 @@ void UpdateDialog::on_btnUpdateNow_clicked()
{
done(UPDATE_NOW);
}
void UpdateDialog::on_btnUpdateOnExit_clicked()
{
done(UPDATE_ONEXIT);
}

View File

@@ -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,7 +48,6 @@ private:
Ui::UpdateDialog *ui;
public slots:
void on_btnUpdateNow_clicked();
void on_btnUpdateOnExit_clicked();
void on_btnUpdateLater_clicked();
/// Starts loading the changelog
@@ -58,4 +62,5 @@ public slots:
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

@@ -20,12 +20,10 @@
#include <dialogs/ProgressDialog.h>
#include "CustomMessageBox.h"
#include "Platform.h"
#include <BaseVersion.h>
#include <BaseVersionList.h>
#include <tasks/Task.h>
#include <modutils.h>
#include <QDebug>
#include "MultiMC.h"
#include <VersionProxyModel.h>
@@ -34,7 +32,6 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title,
bool cancelable)
: QDialog(parent), ui(new Ui::VersionSelectDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
setWindowModality(Qt::WindowModal);
setWindowTitle(title);

View File

@@ -221,7 +221,7 @@ void GroupView::mousePressEvent(QMouseEvent *event)
QPoint visualPos = event->pos();
QPoint geometryPos = event->pos() + offset();
QPersistentModelIndex index = indexAt(visualPos);
m_pressedIndex = index;
@@ -697,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

@@ -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,17 +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(.+)?/libstdc\\+\\+") AND (UNIX AND NOT APPLE))
message("resolving ${resolved_file} as other")
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()

View File

@@ -1,10 +1,11 @@
#include "MultiMC.h"
#include "MainWindow.h"
#include "LaunchInteraction.h"
#include <InstanceList.h>
#include <QDebug>
int main_gui(MultiMC &app)
int launchMainWindow(MultiMC &app)
{
// show main window
app.setIconTheme(MMC->settings()->get("IconTheme").toString());
MainWindow mainWin;
mainWin.restoreState(QByteArray::fromBase64(MMC->settings()->get("MainWindowState").toByteArray()));
mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray()));
@@ -14,6 +15,32 @@ int main_gui(MultiMC &app)
return app.exec();
}
int launchInstance(MultiMC &app, InstancePtr inst)
{
app.minecraftlist();
LaunchController launchController;
launchController.setInstance(inst);
launchController.setOnline(true);
QMetaObject::invokeMethod(&launchController, "start", Qt::QueuedConnection);
app.connect(&launchController, &Task::finished, [&app]()
{
app.quit();
});
return app.exec();
}
int main_gui(MultiMC &app)
{
app.setIconTheme(MMC->settings()->get("IconTheme").toString());
// show main window
auto inst = app.instances()->getInstanceById(app.launchId);
if(inst)
{
return launchInstance(app, inst);
}
return launchMainWindow(app);
}
int main(int argc, char *argv[])
{
// initialize Qt

View File

@@ -17,13 +17,16 @@ fi
MMC_DIR="$(dirname "$(readlink -f "$0")")"
echo "MultiMC Dir: ${MMC_DIR}"
# Set up env
export LD_LIBRARY_PATH="${MMC_DIR}/bin":$LD_LIBRARY_PATH
# Set up env - filter out input LD_ variables but pass them in under different names
export GAME_LIBRARY_PATH=${GAME_LIBRARY_PATH-${LD_LIBRARY_PATH}}
export GAME_PRELOAD=${GAME_PRELOAD-${LD_PRELOAD}}
export LD_LIBRARY_PATH="${MMC_DIR}/bin":$MMC_LIBRARY_PATH
export LD_PRELOAD=$MMC_PRELOAD
export QT_PLUGIN_PATH="${MMC_DIR}/plugins"
export QT_FONTPATH="${MMC_DIR}/fonts"
# Detect missing dependencies...
DEPS_LIST=`ldd "${MMC_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | awk -vORS=", " '{ print $1 }'`
DEPS_LIST=`ldd "${MMC_DIR}"/plugins/*/*.so 2>/dev/null | grep "not found" | sort -u | awk -vORS=", " '{ print $1 }'`
if [ "x$DEPS_LIST" = "x" ]; then
# We have all our dependencies. Run MultiMC.
echo "No missing dependencies found."
@@ -32,13 +35,13 @@ if [ "x$DEPS_LIST" = "x" ]; then
chmod +x "${MMC_DIR}/bin/MultiMC"
# Run MultiMC
"${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@
"${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" "$@"
# Run MultiMC in valgrind
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@
# valgrind --log-file="valgrind.log" --leak-check=full --track-origins=yes "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" "$@"
# Run MultiMC with callgrind, delay instrumentation
# valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@
# valgrind --log-file="valgrind.log" --tool=callgrind --instr-atstart=no "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" "$@"
# use callgrind_control -i on/off to profile actions
# Exit with MultiMC's exit code.
@@ -46,38 +49,44 @@ if [ "x$DEPS_LIST" = "x" ]; then
else
# apt
if which apt-file &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do apt-file -l search $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo apt-get install $COMMAND_LIBS"
# pacman
elif which pkgfile &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pkgfile $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo pacman -S $COMMAND_LIBS"
# dnf
elif which dnf &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do dnf whatprovides -q $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | grep -v 'Repo' | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo dnf install $COMMAND_LIBS"
# yum
elif which yum &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do yum whatprovides $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo yum install $COMMAND_LIBS"
# zypper
elif which zypper &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do zypper wp $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo zypper install $COMMAND_LIBS"
# emerge
elif which pfl &>/dev/null; then
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*"`
LIBRARIES=`echo "$DEPS_LIST" | grep -oP "[^, ]*" | sort -u`
COMMAND_LIBS=`for LIBRARY in $LIBRARIES; do pfl $LIBRARY; done`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | awk -vORS=" " '{ print $1 }'`
COMMAND_LIBS=`echo "$COMMAND_LIBS" | sort -u | awk -vORS=" " '{ print $1 }'`
INSTALL_CMD="sudo emerge $COMMAND_LIBS"
fi
MESSAGE="Error: MultiMC is missing the following libraries that it needs to work correctly:\n\t${DEPS_LIST}\nPlease install them from your distribution's package manager."
MESSAGE="$MESSAGE\n\nHint: $INSTALL_CMD\n"
MESSAGE="$MESSAGE\n\nHint (please apply common sense): $INSTALL_CMD\n"
printerror "$MESSAGE"
exit 1

View File

@@ -0,0 +1,11 @@
Package: multimc
Version: 1.1-2
Architecture: all
Maintainer: Petr Mrázek <peterix@gmail.com>
Section: games
Priority: optional
Installed-Size: 75
Depends: zenity, default-jre
Homepage: http://multimc.org
Description: A local install wrapper for MultiMC

View File

@@ -0,0 +1,3 @@
#!/bin/sh
set -e
update-desktop-database

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -0,0 +1,33 @@
#!/bin/bash
INSTDIR="${XDG_DATA_HOME-$HOME/.local/share}/multimc"
if [ `getconf LONG_BIT` = "64" ]
then
PACKAGE="mmc-stable-lin64.tar.gz"
else
PACKAGE="mmc-stable-lin32.tar.gz"
fi
deploy() {
mkdir -p $INSTDIR
cd ${INSTDIR}
wget --progress=dot:force "https://files.multimc.org/downloads/${PACKAGE}" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# Downloading at \2\/s, ETA \3/' | zenity --progress --auto-close --auto-kill --title="Downloading MultiMC..."
tar -xzf ${PACKAGE} --transform='s,MultiMC/,,'
rm ${PACKAGE}
chmod +x MultiMC
}
runmmc() {
cd ${INSTDIR}
./MultiMC
}
if [[ ! -f ${INSTDIR}/MultiMC ]]; then
deploy
runmmc
else
runmmc
fi

View File

@@ -0,0 +1,16 @@
[Desktop Entry]
Categories=Game;
Exec=/opt/multimc/run.sh
Icon=/opt/multimc/icon.svg
Keywords=game;
MimeType=
Name=MultiMC 5
Path=
StartupNotify=true
Terminal=false
TerminalOptions=
Type=Application
X-DBUS-ServiceName=
X-DBUS-StartupType=
X-KDE-SubstituteUID=false
X-KDE-Username=

View File

@@ -0,0 +1,12 @@
# What is this?
A simple ubuntu package for MultiMC that wraps the contains a script that downloads and installs real MultiMC on ubuntu based systems.
It contains a `.dekstop` file, an icon, and a simple script that does the heavy lifting.
# How to build this?
You need dpkg utils and then run:
```
fakeroot dpkg-deb --build multimc_1.1-1
```
Replace the version with whatever is appropriate.

View File

@@ -22,14 +22,12 @@
#include "MultiMC.h"
#include "settings/SettingsObject.h"
#include "Platform.h"
#include "widgets/IconLabel.h"
#include "widgets/PageContainer.h"
PageDialog::PageDialog(BasePageProviderPtr pageProvider, QString defaultId, QWidget *parent)
: QDialog(parent)
{
MultiMCPlatform::fixWM_CLASS(this);
setWindowTitle(pageProvider->dialogTitle());
m_container = new PageContainer(pageProvider, defaultId, this);

View File

@@ -9,7 +9,8 @@
#include "JavaCommon.h"
#include "MultiMC.h"
#include <java/JavaVersionList.h>
#include <java/JavaInstallList.h>
#include <FileSystem.h>
InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
: QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst)
@@ -119,11 +120,13 @@ void InstanceSettingsPage::applySettings()
if (custcmd)
{
m_settings->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
m_settings->set("WrapperCommand", ui->wrapperCmdTextBox->text());
m_settings->set("PostExitCommand", ui->postExitCmdTextBox->text());
}
else
{
m_settings->reset("PreLaunchCommand");
m_settings->reset("WrapperCommand");
m_settings->reset("PostExitCommand");
}
}
@@ -161,12 +164,13 @@ void InstanceSettingsPage::loadSettings()
// Custom Commands
ui->customCommandsGroupBox->setChecked(m_settings->get("OverrideCommands").toBool());
ui->preLaunchCmdTextBox->setText(m_settings->get("PreLaunchCommand").toString());
ui->wrapperCmdTextBox->setText(m_settings->get("WrapperCommand").toString());
ui->postExitCmdTextBox->setText(m_settings->get("PostExitCommand").toString());
}
void InstanceSettingsPage::on_javaDetectBtn_clicked()
{
JavaVersionPtr java;
JavaInstallPtr java;
VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
vselect.setResizeOn(2);
@@ -174,18 +178,28 @@ void InstanceSettingsPage::on_javaDetectBtn_clicked()
if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
{
java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
java = std::dynamic_pointer_cast<JavaInstall>(vselect.selectedVersion());
ui->javaPathTextBox->setText(java->path);
}
}
void InstanceSettingsPage::on_javaBrowseBtn_clicked()
{
QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
if (!dir.isNull())
QString raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
QString cooked_path = FS::NormalizePath(raw_path);
// do not allow current dir - it's dirty. Do not allow dirs that don't exist
if(cooked_path.isEmpty())
{
ui->javaPathTextBox->setText(dir);
return;
}
QFileInfo javaInfo(cooked_path);;
if(!javaInfo.exists() || !javaInfo.isExecutable())
{
return;
}
ui->javaPathTextBox->setText(cooked_path);
}
void InstanceSettingsPage::on_javaTestBtn_clicked()

View File

@@ -49,12 +49,12 @@ public:
{
return "settings";
}
virtual bool apply();
virtual bool apply() override;
virtual QString helpPage() const override
{
return "Instance-settings";
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
private slots:
void on_javaDetectBtn_clicked();
@@ -70,5 +70,5 @@ private:
Ui::InstanceSettingsPage *ui;
BaseInstance *m_instance;
SettingsObjectPtr m_settings;
QObjectPtr<JavaCommon::TestCheck> checker;
unique_qobject_ptr<JavaCommon::TestCheck> checker;
};

View File

@@ -10,9 +10,6 @@
<height>426</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
@@ -36,7 +33,7 @@
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
<string>Java</string>
<string notr="true">Java</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
@@ -45,7 +42,7 @@
<bool>true</bool>
</property>
<property name="title">
<string>Java installation</string>
<string>Java ins&amp;tallation</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -87,7 +84,7 @@
<bool>true</bool>
</property>
<property name="title">
<string>Memory</string>
<string>Memor&amp;y</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -102,7 +99,7 @@
<string>The maximum amount of memory Minecraft is allowed to use.</string>
</property>
<property name="suffix">
<string> MB</string>
<string notr="true"> MB</string>
</property>
<property name="minimum">
<number>512</number>
@@ -138,7 +135,7 @@
<string>The amount of memory Minecraft is started with.</string>
</property>
<property name="suffix">
<string> MB</string>
<string notr="true"> MB</string>
</property>
<property name="minimum">
<number>256</number>
@@ -160,7 +157,7 @@
<string>The amount of memory available to store loaded Java classes.</string>
</property>
<property name="suffix">
<string> MB</string>
<string notr="true"> MB</string>
</property>
<property name="minimum">
<number>64</number>
@@ -179,7 +176,7 @@
<item row="2" column="0">
<widget class="QLabel" name="labelPermGen">
<property name="text">
<string>PermGen:</string>
<string notr="true">PermGen:</string>
</property>
</widget>
</item>
@@ -305,7 +302,7 @@
<bool>true</bool>
</property>
<property name="title">
<string>Console Settings</string>
<string>Conso&amp;le Settings</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -357,7 +354,7 @@
<bool>true</bool>
</property>
<property name="title">
<string>Custom Commands</string>
<string>Cus&amp;tom Commands</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -366,16 +363,16 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="1">
<widget class="QLineEdit" name="preLaunchCmdTextBox"/>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QLabel" name="labelPostExitCmd">
<property name="text">
<string>Post-exit command:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="preLaunchCmdTextBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelPreLaunchCmd">
<property name="text">
@@ -383,9 +380,19 @@
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QLineEdit" name="postExitCmdTextBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelWrapperCmd">
<property name="text">
<string>Wrapper command:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="wrapperCmdTextBox"/>
</item>
</layout>
</widget>
</item>
@@ -446,6 +453,7 @@
<tabstop>autoCloseConsoleCheck</tabstop>
<tabstop>customCommandsGroupBox</tabstop>
<tabstop>preLaunchCmdTextBox</tabstop>
<tabstop>wrapperCmdTextBox</tabstop>
<tabstop>postExitCmdTextBox</tabstop>
</tabstops>
<resources/>

View File

@@ -19,14 +19,13 @@
#include <QKeyEvent>
#include <QKeyEvent>
#include <pathutils.h>
#include "dialogs/VersionSelectDialog.h"
#include "dialogs/ProgressDialog.h"
#include "dialogs/ModEditDialogCommon.h"
#include "minecraft/ModList.h"
#include "minecraft/LegacyInstance.h"
#include "Env.h"
#include <DesktopServices.h>
#include "MultiMC.h"
#include <GuiUtil.h>
@@ -100,13 +99,13 @@ bool LegacyJarModPage::eventFilter(QObject *obj, QEvent *ev)
void LegacyJarModPage::on_addJarBtn_clicked()
{
auto list = GuiUtil::BrowseForMods("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), this->parentWidget());
auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), MMC->settings()->get("CentralModsDir").toString(), this->parentWidget());
if(!list.empty())
{
m_jarmods->stopWatching();
for (auto filename : list)
{
m_jarmods->installMod(QFileInfo(filename));
m_jarmods->installMod(filename);
}
m_jarmods->startWatching();
}
@@ -147,7 +146,7 @@ void LegacyJarModPage::on_rmJarBtn_clicked()
void LegacyJarModPage::on_viewJarBtn_clicked()
{
openDirInDefaultProgram(m_inst->jarModsDir(), true);
DesktopServices::openDirectory(m_inst->jarModsDir(), true);
}
void LegacyJarModPage::jarCurrent(QModelIndex current, QModelIndex previous)

View File

@@ -36,15 +36,15 @@ public:
explicit LegacyJarModPage(LegacyInstance *inst, QWidget *parent = 0);
virtual ~LegacyJarModPage();
virtual QString displayName() const
virtual QString displayName() const override
{
return tr("Jar Mods");
}
virtual QIcon icon() const
virtual QIcon icon() const override
{
return MMC->getThemedIcon("jarmods");
}
virtual QString id() const
virtual QString id() const override
{
return "jarmods";
}
@@ -52,7 +52,7 @@ public:
{
return "Legacy-jar-mods";
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
private
slots:

View File

@@ -10,9 +10,6 @@
<height>593</height>
</rect>
</property>
<property name="windowTitle">
<string>LegacyJarModPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View File

@@ -49,7 +49,7 @@ public:
{
return "Legacy-upgrade";
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
private
slots:
void on_upgradeButton_clicked();

View File

@@ -10,9 +10,6 @@
<height>405</height>
</rect>
</property>
<property name="windowTitle">
<string>Upgrade</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>

View File

@@ -7,15 +7,17 @@
#include <QScrollBar>
#include <QShortcut>
#include "BaseProcess.h"
#include "launch/LaunchTask.h"
#include <settings/Setting.h>
#include "GuiUtil.h"
#include <ColorCache.h>
LogPage::LogPage(BaseProcess *proc, QWidget *parent)
LogPage::LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent)
: QWidget(parent), ui(new Ui::LogPage), m_process(proc)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
connect(m_process, SIGNAL(log(QString, MessageLevel::Enum)), this,
connect(m_process.get(), SIGNAL(log(QString, MessageLevel::Enum)), this,
SLOT(write(QString, MessageLevel::Enum)));
// create the format and set its font
@@ -29,6 +31,22 @@ LogPage::LogPage(BaseProcess *proc, QWidget *parent)
}
defaultFormat->setFont(QFont(fontFamily, fontSize));
// ensure we don't eat all the RAM
auto lineSetting = MMC->settings()->getSetting("ConsoleMaxLines");
int maxLines = lineSetting->get().toInt(&conversionOk);
if(!conversionOk)
{
maxLines = lineSetting->defValue().toInt();
qWarning() << "ConsoleMaxLines has nonsensical value, defaulting to" << maxLines;
}
ui->text->setMaximumBlockCount(maxLines);
auto origForeground = ui->text->palette().color(ui->text->foregroundRole());
auto origBackground = ui->text->palette().color(ui->text->backgroundRole());
m_colors.reset(new LogColorCache(origForeground, origBackground));
m_stopOnOverflow = MMC->settings()->get("ConsoleOverflowStop").toBool();
auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this);
connect(findShortcut, SIGNAL(activated()), SLOT(findActivated()));
auto findNextShortcut = new QShortcut(QKeySequence(QKeySequence::FindNext), this);
@@ -56,11 +74,22 @@ bool LogPage::shouldDisplay() const
void LogPage::on_btnPaste_clicked()
{
GuiUtil::uploadPaste(ui->text->toPlainText(), this);
//FIXME: turn this into a proper task and move the upload logic out of GuiUtil!
write(tr("MultiMC: Log upload triggered at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)), MessageLevel::MultiMC);
auto url = GuiUtil::uploadPaste(ui->text->toPlainText(), this);
if(!url.isEmpty())
{
write(tr("MultiMC: Log uploaded to: %1").arg(url), MessageLevel::MultiMC);
}
else
{
write(tr("MultiMC: Log upload failed!"), MessageLevel::Error);
}
}
void LogPage::on_btnCopy_clicked()
{
write(QString("Clipboard copy at: %1").arg(QDateTime::currentDateTime().toString(Qt::RFC2822Date)), MessageLevel::MultiMC);
GuiUtil::setClipboardText(ui->text->toPlainText());
}
@@ -126,15 +155,40 @@ void LogPage::findPreviousActivated()
}
}
void LogPage::setParentContainer(BasePageContainer * container)
{
m_parentContainer = container;
}
void LogPage::write(QString data, MessageLevel::Enum mode)
{
if (!m_write_active)
{
if (mode != MessageLevel::PrePost && mode != MessageLevel::MultiMC)
if (mode != MessageLevel::MultiMC)
{
return;
}
}
if(m_stopOnOverflow && m_write_active)
{
if(mode != MessageLevel::MultiMC)
{
if(ui->text->blockCount() >= ui->text->maximumBlockCount())
{
m_write_active = false;
data = tr("MultiMC stopped watching the game log because the log length surpassed %1 lines.\n"
"You may have to fix your mods because the game is still loggging to files and"
" likely wasting harddrive space at an alarming rate!")
.arg(ui->text->maximumBlockCount());
mode = MessageLevel::Fatal;
ui->trackLogCheckbox->setCheckState(Qt::Unchecked);
if(!isVisible())
{
m_parentContainer->selectPage(id());
}
}
}
}
// save the cursor so it can be restored.
auto savedCursor = ui->text->cursor();
@@ -165,46 +219,8 @@ void LogPage::write(QString data, MessageLevel::Enum mode)
QListIterator<QString> iter(filtered);
QTextCharFormat format(*defaultFormat);
switch(mode)
{
case MessageLevel::MultiMC:
{
format.setForeground(QColor("blue"));
break;
}
case MessageLevel::Debug:
{
format.setForeground(QColor("green"));
break;
}
case MessageLevel::Warning:
{
format.setForeground(QColor("orange"));
break;
}
case MessageLevel::Error:
{
format.setForeground(QColor("red"));
break;
}
case MessageLevel::Fatal:
{
format.setForeground(QColor("red"));
format.setBackground(QColor("black"));
break;
}
case MessageLevel::PrePost:
{
format.setForeground(QColor("grey"));
break;
}
case MessageLevel::Info:
case MessageLevel::Message:
default:
{
// do nothing, keep original
}
}
format.setForeground(m_colors->getFront(mode));
format.setBackground(m_colors->getBack(mode));
while (iter.hasNext())
{

View File

@@ -18,9 +18,10 @@
#include <QWidget>
#include "BaseInstance.h"
#include "BaseProcess.h"
#include "launch/LaunchTask.h"
#include "BasePage.h"
#include <MultiMC.h>
#include <ColorCache.h>
namespace Ui
{
@@ -33,7 +34,7 @@ class LogPage : public QWidget, public BasePage
Q_OBJECT
public:
explicit LogPage(BaseProcess *proc, QWidget *parent = 0);
explicit LogPage(std::shared_ptr<LaunchTask> proc, QWidget *parent = 0);
virtual ~LogPage();
virtual QString displayName() const override
{
@@ -47,18 +48,19 @@ public:
{
return "console";
}
virtual bool apply();
virtual bool apply() override;
virtual QString helpPage() const override
{
return "Minecraft-Logs";
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
virtual void setParentContainer(BasePageContainer *) override;
private slots:
/**
* @brief write a string
* @param data the string
* @param mode the WriteMode
* @param level the @MessageLevel the string should be written under
* lines have to be put through this as a whole!
*/
void write(QString data, MessageLevel::Enum level = MessageLevel::MultiMC);
@@ -76,11 +78,14 @@ private slots:
private:
Ui::LogPage *ui;
BaseProcess *m_process;
std::shared_ptr<LaunchTask> m_process;
int m_last_scroll_value = 0;
bool m_scroll_active = true;
int m_saved_offset = 0;
bool m_write_active = true;
bool m_stopOnOverflow = true;
QTextCharFormat * defaultFormat;
BasePageContainer * m_parentContainer;
std::unique_ptr<LogColorCache> m_colors;
};

View File

@@ -10,9 +10,6 @@
<height>782</height>
</rect>
</property>
<property name="windowTitle">
<string>Log</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="5">

View File

@@ -19,11 +19,8 @@
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
#include <QDesktopServices>
#include <QAbstractItemModel>
#include <pathutils.h>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/ModEditDialogCommon.h"
@@ -31,6 +28,7 @@
#include "minecraft/ModList.h"
#include "minecraft/Mod.h"
#include "minecraft/VersionFilterData.h"
#include <DesktopServices.h>
ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id,
QString iconName, QString displayName, QString helpPage,
@@ -131,23 +129,24 @@ bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev)
void ModFolderPage::on_addModBtn_clicked()
{
auto list = GuiUtil::BrowseForMods(
auto list = GuiUtil::BrowseForFiles(
m_helpName,
tr("Select %1",
"Select whatever type of files the page contains. Example: 'Loader Mods'")
.arg(m_displayName),
m_filter.arg(m_displayName), this->parentWidget());
m_filter.arg(m_displayName), MMC->settings()->get("CentralModsDir").toString(),
this->parentWidget());
if (!list.empty())
{
m_mods->stopWatching();
for (auto filename : list)
{
m_mods->installMod(QFileInfo(filename));
m_mods->installMod(filename);
}
m_mods->startWatching();
}
}
void ModFolderPage::on_rmModBtn_clicked()
{
int first, last;
@@ -162,7 +161,7 @@ void ModFolderPage::on_rmModBtn_clicked()
void ModFolderPage::on_viewModBtn_clicked()
{
openDirInDefaultProgram(m_mods->dir().absolutePath(), true);
DesktopServices::openDirectory(m_mods->dir().absolutePath(), true);
}
void ModFolderPage::modCurrent(const QModelIndex &current, const QModelIndex &previous)

View File

@@ -58,12 +58,12 @@ public:
{
return m_helpName;
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
virtual void opened();
virtual void closed();
virtual void opened() override;
virtual void closed() override;
protected:
bool eventFilter(QObject *obj, QEvent *ev);
bool eventFilter(QObject *obj, QEvent *ev) override;
bool modListFilter(QKeyEvent *ev);
protected:

View File

@@ -10,9 +10,6 @@
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>Mods</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">

View File

@@ -48,7 +48,7 @@ public:
{
return "notes";
}
virtual bool apply();
virtual bool apply() override;
virtual QString helpPage() const override
{
return "Notes";

View File

@@ -10,9 +10,6 @@
<height>538</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View File

@@ -20,20 +20,20 @@
#include "GuiUtil.h"
#include "RecursiveFileSystemWatcher.h"
#include <pathutils.h>
#include <GZip.h>
#include <FileSystem.h>
OtherLogsPage::OtherLogsPage(QString path, QWidget *parent)
: QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path),
OtherLogsPage::OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget *parent)
: QWidget(parent), ui(new Ui::OtherLogsPage), m_path(path), m_fileFilter(fileFilter),
m_watcher(new RecursiveFileSystemWatcher(this))
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_watcher->setFileExpression("(.*\\.log(\\.[0-9]*)?$)|(crash-.*\\.txt)");
m_watcher->setMatcher(fileFilter);
m_watcher->setRootDir(QDir::current().absoluteFilePath(m_path));
connect(m_watcher, &RecursiveFileSystemWatcher::filesChanged, this,
&OtherLogsPage::populateSelectLogBox);
connect(m_watcher, &RecursiveFileSystemWatcher::filesChanged, this, &OtherLogsPage::populateSelectLogBox);
populateSelectLogBox();
}
@@ -55,15 +55,23 @@ void OtherLogsPage::populateSelectLogBox()
{
ui->selectLogBox->clear();
ui->selectLogBox->addItems(m_watcher->files());
if (m_currentFile.isNull())
if (m_currentFile.isEmpty())
{
setControlsEnabled(false);
ui->selectLogBox->setCurrentIndex(-1);
}
else
{
const int index = ui->selectLogBox->findText(m_currentFile);
if (index != -1)
{
ui->selectLogBox->setCurrentIndex(index);
setControlsEnabled(true);
}
else
{
setControlsEnabled(false);
}
}
}
@@ -75,7 +83,7 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index)
file = ui->selectLogBox->itemText(index);
}
if (file.isEmpty() || !QFile::exists(PathCombine(m_path, file)))
if (file.isEmpty() || !QFile::exists(FS::PathCombine(m_path, file)))
{
m_currentFile = QString();
ui->text->clear();
@@ -91,7 +99,12 @@ void OtherLogsPage::on_selectLogBox_currentIndexChanged(const int index)
void OtherLogsPage::on_btnReload_clicked()
{
QFile file(PathCombine(m_path, m_currentFile));
if(m_currentFile.isEmpty())
{
setControlsEnabled(false);
return;
}
QFile file(FS::PathCombine(m_path, m_currentFile));
if (!file.open(QFile::ReadOnly))
{
setControlsEnabled(false);
@@ -102,16 +115,39 @@ void OtherLogsPage::on_btnReload_clicked()
}
else
{
if (file.size() < 10000000ll)
{
ui->text->setPlainText(QString::fromUtf8(file.readAll()));
}
else
auto showTooBig = [&]()
{
ui->text->setPlainText(
tr("The file (%1) is too big. You may want to open it in a viewer optimized "
"for large files.").arg(file.fileName()));
};
if(file.size() > (1024ll * 1024ll * 12ll))
{
showTooBig();
return;
}
QString content;
if(file.fileName().endsWith(".gz"))
{
QByteArray temp;
if(!GZip::unzip(file.readAll(), temp))
{
ui->text->setPlainText(
tr("The file (%1) is not readable.").arg(file.fileName()));
return;
}
content = QString::fromUtf8(temp);
}
else
{
content = QString::fromUtf8(file.readAll());
}
if (content.size() >= 50000000ll)
{
showTooBig();
return;
}
ui->text->setPlainText(content);
}
}
@@ -119,19 +155,26 @@ void OtherLogsPage::on_btnPaste_clicked()
{
GuiUtil::uploadPaste(ui->text->toPlainText(), this);
}
void OtherLogsPage::on_btnCopy_clicked()
{
GuiUtil::setClipboardText(ui->text->toPlainText());
}
void OtherLogsPage::on_btnDelete_clicked()
{
if(m_currentFile.isEmpty())
{
setControlsEnabled(false);
return;
}
if (QMessageBox::question(this, tr("Delete"),
tr("Do you really want to delete %1?").arg(m_currentFile),
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No)
{
return;
}
QFile file(PathCombine(m_path, m_currentFile));
QFile file(FS::PathCombine(m_path, m_currentFile));
if (!file.remove())
{
QMessageBox::critical(this, tr("Error"), tr("Unable to delete %1: %2")
@@ -139,6 +182,68 @@ void OtherLogsPage::on_btnDelete_clicked()
}
}
void OtherLogsPage::on_btnClean_clicked()
{
auto toDelete = m_watcher->files();
if(toDelete.isEmpty())
{
return;
}
QMessageBox *messageBox = new QMessageBox(this);
messageBox->setWindowTitle(tr("Clean up"));
if(toDelete.size() > 5)
{
messageBox->setText(tr("Do you really want to delete all log files?"));
messageBox->setDetailedText(toDelete.join('\n'));
}
else
{
messageBox->setText(tr("Do you really want to these files?\n%1").arg(toDelete.join('\n')));
}
messageBox->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
messageBox->setDefaultButton(QMessageBox::Ok);
messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse);
messageBox->setIcon(QMessageBox::Question);
messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
if (messageBox->exec() != QMessageBox::Ok)
{
return;
}
QStringList failed;
for(auto item: toDelete)
{
QFile file(FS::PathCombine(m_path, item));
if (!file.remove())
{
failed.push_back(item);
}
}
if(!failed.empty())
{
QMessageBox *messageBox = new QMessageBox(this);
messageBox->setWindowTitle(tr("Error"));
if(failed.size() > 5)
{
messageBox->setText(tr("Couldn't delete some files!"));
messageBox->setDetailedText(failed.join('\n'));
}
else
{
messageBox->setText(tr("Couldn't delete some files:\n%1").arg(failed.join('\n')));
}
messageBox->setStandardButtons(QMessageBox::Ok);
messageBox->setDefaultButton(QMessageBox::Ok);
messageBox->setTextInteractionFlags(Qt::TextSelectableByMouse);
messageBox->setIcon(QMessageBox::Critical);
messageBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
messageBox->exec();
}
}
void OtherLogsPage::setControlsEnabled(const bool enabled)
{
ui->btnReload->setEnabled(enabled);
@@ -146,4 +251,5 @@ void OtherLogsPage::setControlsEnabled(const bool enabled)
ui->btnCopy->setEnabled(enabled);
ui->btnPaste->setEnabled(enabled);
ui->text->setEnabled(enabled);
ui->btnClean->setEnabled(enabled);
}

View File

@@ -19,6 +19,7 @@
#include "BasePage.h"
#include <MultiMC.h>
#include <pathmatcher/IPathMatcher.h>
namespace Ui
{
@@ -32,7 +33,7 @@ class OtherLogsPage : public QWidget, public BasePage
Q_OBJECT
public:
explicit OtherLogsPage(QString path, QWidget *parent = 0);
explicit OtherLogsPage(QString path, IPathMatcher::Ptr fileFilter, QWidget *parent = 0);
~OtherLogsPage();
QString id() const override
@@ -61,12 +62,15 @@ private slots:
void on_btnPaste_clicked();
void on_btnCopy_clicked();
void on_btnDelete_clicked();
void on_btnClean_clicked();
private:
void setControlsEnabled(const bool enabled);
private:
Ui::OtherLogsPage *ui;
QString m_path;
RecursiveFileSystemWatcher *m_watcher;
QString m_currentFile;
void setControlsEnabled(const bool enabled);
IPathMatcher::Ptr m_fileFilter;
RecursiveFileSystemWatcher *m_watcher;
};

View File

@@ -10,9 +10,6 @@
<height>538</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
@@ -33,29 +30,12 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="selectLogBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnReload">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="3" column="1">
<widget class="QPushButton" name="btnCopy">
<property name="toolTip">
<string>Copy the whole log into the clipboard</string>
@@ -65,7 +45,17 @@
</property>
</widget>
</item>
<item>
<item row="3" column="3">
<widget class="QPushButton" name="btnDelete">
<property name="toolTip">
<string>Clear the log</string>
</property>
<property name="text">
<string>Delete</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="btnPaste">
<property name="toolTip">
<string>Upload the log to paste.ee - it will stay online for a month</string>
@@ -75,13 +65,30 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnDelete">
<item row="3" column="0">
<widget class="QPushButton" name="btnReload">
<property name="text">
<string>Reload</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<widget class="QComboBox" name="selectLogBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="3" column="4">
<widget class="QPushButton" name="btnClean">
<property name="toolTip">
<string>Clear the log</string>
</property>
<property name="text">
<string>Delete</string>
<string>Clean</string>
</property>
</widget>
</item>

View File

@@ -12,10 +12,8 @@
#include <QEvent>
#include <QPainter>
#include <QClipboard>
#include <QDesktopServices>
#include <QKeyEvent>
#include <pathutils.h>
#include <MultiMC.h>
#include "dialogs/ProgressDialog.h"
@@ -26,6 +24,8 @@
#include "tasks/SequentialTask.h"
#include "RWStorage.h"
#include <FileSystem.h>
#include <DesktopServices.h>
typedef RWStorage<QString, QIcon> SharedIconCache;
typedef std::shared_ptr<SharedIconCache> SharedIconCachePtr;
@@ -73,7 +73,6 @@ public:
small = image.scaledToWidth(512).scaledToWidth(256, Qt::SmoothTransformation);
else
small = image.scaledToHeight(512).scaledToHeight(256, Qt::SmoothTransformation);
auto smallSize = small.size();
QPoint offset((256 - small.width()) / 2, (256 - small.height()) / 2);
QImage square(QSize(256, 256), QImage::Format_ARGB32);
square.fill(Qt::transparent);
@@ -220,7 +219,7 @@ ScreenshotsPage::ScreenshotsPage(QString path, QWidget *parent)
m_model->setNameFilters({"*.png"});
m_model->setNameFilterDisables(false);
m_folder = path;
m_valid = ensureFolderPathExists(m_folder);
m_valid = FS::ensureFolderPathExists(m_folder);
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
@@ -272,12 +271,12 @@ void ScreenshotsPage::onItemActivated(QModelIndex index)
return;
auto info = m_model->fileInfo(index);
QString fileName = info.absoluteFilePath();
openFileInDefaultProgram(info.absoluteFilePath());
DesktopServices::openFile(info.absoluteFilePath());
}
void ScreenshotsPage::on_viewFolderBtn_clicked()
{
openDirInDefaultProgram(m_folder, true);
DesktopServices::openDirectory(m_folder, true);
}
void ScreenshotsPage::on_uploadBtn_clicked()
@@ -301,8 +300,9 @@ void ScreenshotsPage::on_uploadBtn_clicked()
albumTask->addNetAction(imgurAlbum);
task.addTask(job.unwrap());
task.addTask(albumTask.unwrap());
m_uploadActive = true;
ProgressDialog prog(this);
if (prog.exec(&task) != QDialog::Accepted)
if (prog.execWithTask(&task) != QDialog::Accepted)
{
CustomMessageBox::selectable(this, tr("Failed to upload screenshots!"),
tr("Unknown error"), QMessageBox::Warning)->exec();
@@ -312,7 +312,7 @@ void ScreenshotsPage::on_uploadBtn_clicked()
auto link = QString("https://imgur.com/a/%1").arg(imgurAlbum->id());
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(link);
QDesktopServices::openUrl(link);
DesktopServices::openUrl(link);
CustomMessageBox::selectable(
this, tr("Upload finished"),
tr("The <a href=\"%1\">link to the uploaded album</a> has been opened in the "
@@ -321,6 +321,7 @@ void ScreenshotsPage::on_uploadBtn_clicked()
.arg(link, imgurAlbum->deleteHash()),
QMessageBox::Information)->exec();
}
m_uploadActive = false;
}
void ScreenshotsPage::on_deleteBtn_clicked()

View File

@@ -46,7 +46,7 @@ public:
NothingDone = 0x42
};
virtual bool eventFilter(QObject *, QEvent *);
virtual bool eventFilter(QObject *, QEvent *) override;
virtual QString displayName() const override
{
return tr("Screenshots");
@@ -63,6 +63,10 @@ public:
{
return "Screenshots-management";
}
virtual bool apply() override
{
return !m_uploadActive;
}
private slots:
void on_uploadBtn_clicked();
void on_deleteBtn_clicked();
@@ -76,4 +80,5 @@ private:
std::shared_ptr<QIdentityProxyModel> m_filterModel;
QString m_folder;
bool m_valid = false;
bool m_uploadActive = false;
};

View File

@@ -10,9 +10,6 @@
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>Mods</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

View File

@@ -15,7 +15,6 @@
#include "MultiMC.h"
#include <pathutils.h>
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
@@ -23,7 +22,6 @@
#include "VersionPage.h"
#include "ui_VersionPage.h"
#include "Platform.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/VersionSelectDialog.h"
#include "dialogs/ModEditDialogCommon.h"
@@ -47,7 +45,7 @@
#include <minecraft/MinecraftVersion.h>
#include <minecraft/MinecraftVersionList.h>
#include "icons/IconList.h"
#include "Exception.h"
QIcon VersionPage::icon() const
{
@@ -118,7 +116,7 @@ bool VersionPage::reloadMinecraftProfile()
m_inst->reloadProfile();
return true;
}
catch (MMCError &e)
catch (Exception &e)
{
QMessageBox::critical(this, tr("Error"), e.cause());
return false;
@@ -181,7 +179,7 @@ void VersionPage::on_jarmodBtn_clicked()
nagShown = true;
}
}
auto list = GuiUtil::BrowseForMods("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), this->parentWidget());
auto list = GuiUtil::BrowseForFiles("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), MMC->settings()->get("CentralModsDir").toString(), this->parentWidget());
if(!list.empty())
{
m_version->installJarMods(list);
@@ -199,7 +197,7 @@ void VersionPage::on_resetOrderBtn_clicked()
{
m_version->resetOrder();
}
catch (MMCError &e)
catch (Exception &e)
{
QMessageBox::critical(this, tr("Error"), e.cause());
}
@@ -212,7 +210,7 @@ void VersionPage::on_moveUpBtn_clicked()
{
m_version->move(currentRow(), MinecraftProfile::MoveUp);
}
catch (MMCError &e)
catch (Exception &e)
{
QMessageBox::critical(this, tr("Error"), e.cause());
}
@@ -225,7 +223,7 @@ void VersionPage::on_moveDownBtn_clicked()
{
m_version->move(currentRow(), MinecraftProfile::MoveDown);
}
catch (MMCError &e)
catch (Exception &e)
{
QMessageBox::critical(this, tr("Error"), e.cause());
}
@@ -269,14 +267,14 @@ void VersionPage::on_changeVersionBtn_clicked()
int VersionPage::doUpdate()
{
auto updateTask = m_inst->doUpdate();
auto updateTask = m_inst->createUpdateTask();
if (!updateTask)
{
return 1;
}
ProgressDialog tDialog(this);
connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString)));
int ret = tDialog.exec(updateTask.get());
int ret = tDialog.execWithTask(updateTask.get());
updateButtons();
return ret;
}
@@ -291,7 +289,7 @@ void VersionPage::on_forgeBtn_clicked()
if (vselect.exec() && vselect.selectedVersion())
{
ProgressDialog dialog(this);
dialog.exec(
dialog.execWithTask(
ForgeInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
preselect(m_version->rowCount(QModelIndex())-1);
}
@@ -308,7 +306,7 @@ void VersionPage::on_liteloaderBtn_clicked()
if (vselect.exec() && vselect.selectedVersion())
{
ProgressDialog dialog(this);
dialog.exec(
dialog.execWithTask(
LiteLoaderInstaller().createInstallTask(m_inst, vselect.selectedVersion(), this));
preselect(m_version->rowCount(QModelIndex())-1);
}
@@ -448,7 +446,7 @@ void VersionPage::on_revertBtn_clicked()
return;
}
}
if(!m_version->revert(version))
if(!m_version->revertToBase(version))
{
// TODO: some error box here
}

View File

@@ -45,9 +45,9 @@ public:
{
return "Instance-Versions";
}
virtual bool shouldDisplay() const;
virtual bool shouldDisplay() const override;
virtual void setParentContainer(BasePageContainer *);
virtual void setParentContainer(BasePageContainer *) override;
private slots:
void on_forgeBtn_clicked();

View File

@@ -10,9 +10,6 @@
<height>575</height>
</rect>
</property>
<property name="windowTitle">
<string>Package Versions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
@@ -33,7 +30,7 @@
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
<string notr="true">Tab 1</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>

View File

@@ -0,0 +1,305 @@
/* Copyright 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 "WorldListPage.h"
#include "ui_WorldListPage.h"
#include "minecraft/WorldList.h"
#include <DesktopServices.h>
#include "dialogs/ModEditDialogCommon.h"
#include <QEvent>
#include <QKeyEvent>
#include <QClipboard>
#include <QMessageBox>
#include <QTreeView>
#include <QInputDialog>
#include "MultiMC.h"
#include <GuiUtil.h>
WorldListPage::WorldListPage(BaseInstance *inst, std::shared_ptr<WorldList> worlds, QString id,
QString iconName, QString displayName, QString helpPage,
QWidget *parent)
: QWidget(parent), m_inst(inst), ui(new Ui::WorldListPage), m_worlds(worlds), m_iconName(iconName), m_id(id), m_displayName(displayName), m_helpName(helpPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
QSortFilterProxyModel * proxy = new QSortFilterProxyModel(this);
proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
proxy->setSourceModel(m_worlds.get());
ui->worldTreeView->setSortingEnabled(true);
ui->worldTreeView->setModel(proxy);
ui->worldTreeView->installEventFilter(this);
auto head = ui->worldTreeView->header();
head->setSectionResizeMode(0, QHeaderView::Stretch);
head->setSectionResizeMode(1, QHeaderView::ResizeToContents);
connect(ui->worldTreeView->selectionModel(),
SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this,
SLOT(worldChanged(const QModelIndex &, const QModelIndex &)));
worldChanged(QModelIndex(), QModelIndex());
}
void WorldListPage::opened()
{
m_worlds->startWatching();
}
void WorldListPage::closed()
{
m_worlds->stopWatching();
}
WorldListPage::~WorldListPage()
{
m_worlds->stopWatching();
delete ui;
}
bool WorldListPage::shouldDisplay() const
{
return true;
}
bool WorldListPage::worldListFilter(QKeyEvent *keyEvent)
{
switch (keyEvent->key())
{
case Qt::Key_Delete:
on_rmWorldBtn_clicked();
return true;
default:
break;
}
return QWidget::eventFilter(ui->worldTreeView, keyEvent);
}
bool WorldListPage::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() != QEvent::KeyPress)
{
return QWidget::eventFilter(obj, ev);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (obj == ui->worldTreeView)
return worldListFilter(keyEvent);
return QWidget::eventFilter(obj, ev);
}
void WorldListPage::on_rmWorldBtn_clicked()
{
auto proxiedIndex = getSelectedWorld();
if(!proxiedIndex.isValid())
return;
auto result = QMessageBox::question(this,
tr("Are you sure?"),
tr("This will remove the selected world permenantly.\n"
"The world will be gone forever (A LONG TIME).\n"
"\n"
"Do you want to continue?"));
if(result != QMessageBox::Yes)
{
return;
}
m_worlds->stopWatching();
m_worlds->deleteWorld(proxiedIndex.row());
m_worlds->startWatching();
}
void WorldListPage::on_viewFolderBtn_clicked()
{
DesktopServices::openDirectory(m_worlds->dir().absolutePath(), true);
}
QModelIndex WorldListPage::getSelectedWorld()
{
auto index = ui->worldTreeView->selectionModel()->currentIndex();
auto proxy = (QSortFilterProxyModel *) ui->worldTreeView->model();
return proxy->mapToSource(index);
}
void WorldListPage::on_copySeedBtn_clicked()
{
QModelIndex index = getSelectedWorld();
if (!index.isValid())
{
return;
}
int64_t seed = m_worlds->data(index, WorldList::SeedRole).toLongLong();
MMC->clipboard()->setText(QString::number(seed));
}
void WorldListPage::on_mcEditBtn_clicked()
{
const QString mceditPath = MMC->settings()->get("MCEditPath").toString();
QModelIndex index = getSelectedWorld();
if (!index.isValid())
{
return;
}
if(!worldSafetyNagQuestion())
return;
auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString();
#ifdef Q_OS_OSX
QProcess *process = new QProcess();
connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), process, SLOT(deleteLater()));
process->setProgram(mceditPath);
process->setArguments(QStringList() << fullPath);
process->start();
#else
QDir mceditDir(mceditPath);
QString program;
#ifdef Q_OS_LINUX
if (mceditDir.exists("mcedit.py"))
{
program = mceditDir.absoluteFilePath("mcedit.py");
}
else if (mceditDir.exists("mcedit.sh"))
{
program = mceditDir.absoluteFilePath("mcedit.sh");
}
#elif defined(Q_OS_WIN32)
if (mceditDir.exists("mcedit.exe"))
{
program = mceditDir.absoluteFilePath("mcedit.exe");
}
else if (mceditDir.exists("mcedit2.exe"))
{
program = mceditDir.absoluteFilePath("mcedit2.exe");
}
#endif
if(program.size())
{
qint64 pid = 0;
DesktopServices::openFile(program, fullPath, mceditPath, &pid);
if(pid == 0)
{
QMessageBox::warning(this->parentWidget(), tr("MCEdit failed to start!"), tr("MCEdit failed to start.\nIt may be necessary to reinstall it."));
}
}
else
{
QMessageBox::warning(this->parentWidget(), tr("No MCEdit found or set up!"), tr("You do not have MCEdit set up or it was moved.\nYou can set it up in the global settings."));
}
#endif
}
void WorldListPage::worldChanged(const QModelIndex &current, const QModelIndex &previous)
{
QModelIndex index = getSelectedWorld();
bool enable = index.isValid();
ui->copySeedBtn->setEnabled(enable);
ui->mcEditBtn->setEnabled(enable);
ui->rmWorldBtn->setEnabled(enable);
ui->copyBtn->setEnabled(enable);
ui->renameBtn->setEnabled(enable);
}
void WorldListPage::on_addBtn_clicked()
{
auto list = GuiUtil::BrowseForFiles(
m_helpName,
tr("Select a Minecraft world zip"),
tr("Minecraft World Zip File (*.zip)"), QString(), this->parentWidget());
if (!list.empty())
{
m_worlds->stopWatching();
for (auto filename : list)
{
m_worlds->installWorld(QFileInfo(filename));
}
m_worlds->startWatching();
}
}
bool WorldListPage::isWorldSafe(QModelIndex)
{
return !m_inst->isRunning();
}
bool WorldListPage::worldSafetyNagQuestion()
{
if(!isWorldSafe(getSelectedWorld()))
{
auto result = QMessageBox::question(this, tr("Copy World"), tr("Changing a world while Minecraft is running is potentially unsafe.\nDo you wish to proceed?"));
if(result == QMessageBox::No)
{
return false;
}
}
return true;
}
void WorldListPage::on_copyBtn_clicked()
{
QModelIndex index = getSelectedWorld();
if (!index.isValid())
{
return;
}
if(!worldSafetyNagQuestion())
return;
auto worldVariant = m_worlds->data(index, WorldList::ObjectRole);
auto world = (World *) worldVariant.value<void *>();
bool ok = false;
QString name = QInputDialog::getText(this, tr("World name"), tr("Enter a new name for the copy."), QLineEdit::Normal, world->name(), &ok);
if (ok && name.length() > 0)
{
world->install(m_worlds->dir().absolutePath(), name);
}
}
void WorldListPage::on_renameBtn_clicked()
{
QModelIndex index = getSelectedWorld();
if (!index.isValid())
{
return;
}
if(!worldSafetyNagQuestion())
return;
auto worldVariant = m_worlds->data(index, WorldList::ObjectRole);
auto world = (World *) worldVariant.value<void *>();
bool ok = false;
QString name = QInputDialog::getText(this, tr("World name"), tr("Enter a new world name."), QLineEdit::Normal, world->name(), &ok);
if (ok && name.length() > 0)
{
world->rename(name);
}
}
void WorldListPage::on_refreshBtn_clicked()
{
m_worlds->update();
}

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