Compare commits

..

339 Commits
0.3.5 ... 0.4.6

Author SHA1 Message Date
Petr Mrázek
4c11ce8063 GH-932 Icon themes actually do not need a restart to be applied 2015-05-20 01:44:01 +02:00
Petr Mrázek
cff2e4823a GH-960 tweak changelog for the OSX SSL fix 2015-05-19 23:12:39 +02:00
Petr Mrázek
d0b31da4b5 GH-960 possible fix for missing OSX ca certs 2015-05-19 22:28:51 +02:00
Petr Mrázek
2ad9e6393f GH-952 Update changelog for hardcore version page tweakery 2015-05-18 00:51:47 +02:00
Petr Mrázek
743af4769e GH-952 Hardcore version page tweakery
Version patches get a lot of new flags that determine which actions are allowed
Version page respects the flags
Customize, revert and edit for version patches
Builting patches can be customized
2015-05-17 23:38:28 +02:00
Petr Mrázek
6ab6a450f6 GH-952 fix legacy edit instance 2015-05-16 23:52:11 +02:00
Petr Mrázek
3ed467e1fa NOISSUE do not dump minecraft version files into the log 2015-05-16 23:33:42 +02:00
Petr Mrázek
44d76f5d82 NOISSUE change travis to Qt 5.3 2015-05-16 23:19:33 +02:00
Petr Mrázek
ff715f7785 NOISSUE replace derpy merkdown thing with hoedown 2015-05-16 23:04:00 +02:00
Petr Mrázek
43c777f386 NOISSUE add some functionality to the derpy markdown changelog thing 2015-05-16 19:33:53 +02:00
Petr Mrázek
a39fb1ef17 GH-958 print PID when starting Minecraft 2015-05-16 18:42:17 +02:00
Petr Mrázek
c75cac684e GH-952 rearrange 0.4.6 changelog into sections so it's actually readable 2015-05-16 18:42:17 +02:00
Petr Mrázek
f2026df597 GH-952 do not remove {version,custom}.json files, rename them 2015-05-16 18:42:14 +02:00
Petr Mrázek
416e08f741 GH-952 flesh out {version,custom}.json upgrade step 2015-05-15 01:37:15 +02:00
Petr Mrázek
5bbe1c7132 GH-951 add .litemod to mod browse dialog 2015-05-12 23:43:11 +02:00
Petr Mrázek
bd1a28d863 GH-932 fix some changelog typos 2015-05-12 09:40:56 +02:00
Petr Mrázek
ffcb5ab1ef GH-932 update version and changelog for 0.4.6 2015-05-12 09:17:04 +02:00
Petr Mrázek
88f975eff7 NOISSUE only watch mod folders when the user is looking at them 2015-05-11 22:50:35 +02:00
Petr Mrázek
11c376f6f1 NOISSUE Remove PermGemn warning ignoring 2015-05-07 08:42:35 +02:00
Petr Mrázek
757b4e260b NOISSUE more logging 2015-05-06 22:16:52 +02:00
Petr Mrázek
2a4647125d GH-942 fix vanilla version list
Latest release gets the star
Latest snapshot, if it's newer than latest release gets the bug
2015-05-06 09:00:21 +02:00
Petr Mrázek
9598f80335 NOISSUE do not show file browse dialog twice 2015-05-06 07:22:24 +02:00
Petr Mrázek
34a5e59007 GH-835 show errors reported by the update download task to the user 2015-05-05 08:15:56 +02:00
Sky
1271188019 Fixed some Forge typos in dialogs. Fixes #940 2015-05-05 00:33:34 +01:00
Petr Mrázek
4c6edc9fd4 GH-907 fix location/java override for java detection 2015-05-05 01:09:28 +02:00
Petr Mrázek
49d3705d16 GH-899 clean up mod browse buttons and dead legacy forge 2015-05-05 00:42:04 +02:00
Petr Mrázek
c09dc85090 GH-899 fix add mod button not opening the central mods folder 2015-05-04 22:17:05 +02:00
Petr Mrázek
c10a4a54d9 NOISSUE windows hates me 2015-05-04 01:28:16 +02:00
Petr Mrázek
1b884d0a9d GH-907 improve Java testing and PermGen deprecation handling 2015-05-04 01:20:48 +02:00
Petr Mrázek
8e9d5f56b5 GH-933 map exit code -1 to 'crashed' 2015-05-02 23:48:18 +02:00
Petr Mrázek
5779ffd664 GH-922 improve version select dialogs 2015-05-02 23:42:33 +02:00
Petr Mrázek
4fc4a17256 NOISSUE handle recommended versions better
Moved constants to the version data file
Use recommended Minecraft instead of latest stable for new instances by default
2015-05-02 12:44:37 +02:00
Petr Mrázek
bb01c91469 NOISSUE do not propagate instance change events when nothing actually changed 2015-05-02 12:11:33 +02:00
Petr Mrázek
fb3c9efc8a NOISSUE launcher part should exit when MultiMC 'hangs up' 2015-05-02 12:08:18 +02:00
Petr Mrázek
55f9117ce3 NOISSUE do not remake instance tools menu, refill it instead 2015-05-02 12:07:18 +02:00
Petr Mrázek
994c815bb9 NOISSUE show errors for instance updates in edit instance window 2015-05-02 01:43:04 +02:00
Petr Mrázek
32f45578fd NOISSUE fix build issues
Hopefully all
2015-05-02 01:43:00 +02:00
Petr Mrázek
2af03ba0d9 GH-930 Improve wording of instance delete dialog 2015-05-01 21:27:16 +02:00
Petr Mrázek
aea51a0876 GH-328 overhaul all relevant version lists 2015-05-01 20:50:24 +02:00
Petr Mrázek
75dfbc61fc GH-925 add scroll to bottom button to LogPage 2015-04-29 01:28:58 +02:00
Petr Mrázek
f8650e3965 NOISSUE eliminate ProgressProvider 2015-04-26 23:04:50 +02:00
Petr Mrázek
84549ed807 GH-849 Further NetJob related fixes 2015-04-26 18:33:29 +02:00
Petr Mrázek
d5c79db12c GH-849 Fix failure signals not getting delivered from NetJob properly 2015-04-26 04:09:09 +02:00
Petr Mrázek
f623dc54ef GH-909 warn about MultiMC running from temporary folders 2015-04-26 00:01:41 +02:00
Petr Mrázek
dc279fbfdc Revert "NOISSUE Cleanup onesix launcher"
This reverts commit 07bebddac9.
2015-04-25 15:32:38 +02:00
Petr Mrázek
8fa58dc244 NOISSUE save group file after copying instance 2015-04-19 21:02:34 +02:00
robotbrain
07bebddac9 NOISSUE Cleanup onesix launcher 2015-04-19 19:07:39 +02:00
Petr Mrázek
4f417d527e GH-894 link server status widgets to help.mojang.com 2015-04-19 17:58:53 +02:00
Petr Mrázek
c7c81463fd GH-885 export dialog for filtering exported files
Includes implementation of a separator based prefix tree and some related bits
2015-04-19 16:14:32 +02:00
Petr Mrázek
6cfac115b1 NOISSUE add commented callgrind startup to linux script 2015-04-15 03:13:57 +02:00
Petr Mrázek
3507ccaf50 GH-866 load instance profile on launch and from version page 2015-04-15 03:12:57 +02:00
Petr Mrázek
28aa8f342e GH-887 fix patch file removal 2015-04-13 23:26:52 +02:00
Petr Mrázek
4d8f068f9c NOISSUE refactor and rearrange zip file utils 2015-04-13 00:53:59 +02:00
Petr Mrázek
1f9dd45e49 GH-329 update description text in MainWindow when instance Minecraft version changes 2015-04-13 00:25:55 +02:00
Petr Mrázek
f061bf7a27 NOISSUE use QObjectPtr for translations and screenshots 2015-04-13 00:21:55 +02:00
Petr Mrázek
d8ea3501eb NOISSUE add waffle.io badge to README
DONTBUILD
2015-04-13 00:20:45 +02:00
Petr Mrázek
9df2f1fa5c NOISSUE fix legacy edit instance 2015-04-13 00:15:23 +02:00
Petr Mrázek
fe540e5dda NOISSUE do not fail when updates don't have MultiMC.app prefix on OSX 2015-04-13 00:11:59 +02:00
Petr Mrázek
c7398dfdc5 GH-228 do not recurse into reparse points when deleting instances 2015-04-13 00:06:31 +02:00
Petr Mrázek
0220fe4f9d GH-228 do not follow symlinks during instance copy on unix
Windows will need a more complex solution.
2015-04-13 00:06:31 +02:00
Petr Mrázek
58840ac10c NOISSUE fix profilers 2015-04-13 00:04:08 +02:00
Petr Mrázek
3d3725f088 SCRATCH small cmake tweaks 2015-04-12 20:57:18 +02:00
Petr Mrázek
47bbc349eb SCRATCH remove more obsolete asset logic 2015-04-12 20:57:18 +02:00
Petr Mrázek
c8687a8d05 NOISSUE get rid of the obsolete version builder 2015-04-12 20:57:18 +02:00
Petr Mrázek
234f57b8e6 NOISSUE Add NullInstance for instances that can't be loaded 2015-04-12 20:57:18 +02:00
Petr Mrázek
d4d8cb4891 NOISSUE remove group sorting log spam 2015-04-12 20:57:18 +02:00
Petr Mrázek
d1ba972c59 SCRATCH move some cmake bits 2015-04-12 20:57:18 +02:00
Petr Mrázek
db877ba121 NOISSUE move everything. 2015-04-12 20:57:18 +02:00
Petr Mrázek
4730f54df7 SCRATCH separate the generic updater logic from the application 2015-04-12 20:57:17 +02:00
Petr Mrázek
7a71ecd8af NOISSUE fix notification checker 2015-04-12 20:57:17 +02:00
Petr Mrázek
4e94de413b SCRATCH no more gui includes in logic 2015-04-12 20:57:17 +02:00
Petr Mrázek
141e0a02a0 SCRATCH move things to the right places 2015-04-12 20:57:17 +02:00
Petr Mrázek
473971b6e7 NOISSUE fix overlap in instance settings registration 2015-04-12 20:57:17 +02:00
Petr Mrázek
b47e196b32 SCRATCH fix version file loading 2015-04-12 20:57:17 +02:00
Petr Mrázek
cd9d37aac4 SCRATCH nuke the overcomplicated logger, use a simple one. 2015-04-12 20:57:17 +02:00
Petr Mrázek
28a39ef7ac NOISSUE fix segfault when version list is null 2015-04-12 20:57:17 +02:00
Petr Mrázek
d313e9ab09 SCRATCH remove remaining references to MultiMC.h and fix legacy LWJGL 2015-04-12 20:57:17 +02:00
Petr Mrázek
382ae78a0b Fix NagUtils and hack GroupView to work 2015-04-12 20:57:17 +02:00
Petr Mrázek
aa70ed2244 SCRATCH move icons over to Env, instance proxy model to gui 2015-04-12 20:57:16 +02:00
Petr Mrázek
154d19bb74 SCRATCH eliminate InstanceFactory 2015-04-12 20:57:16 +02:00
Petr Mrázek
c088d3bef0 GH-881 Include a top-level directory in exported instances 2015-04-12 20:50:45 +02:00
Petr Mrázek
6775e3e72b NOISSUE Improve new instance dialog
Better layout, showing more of the modpack URL
Fixed logic for enabling OK button
2015-04-11 12:30:18 +02:00
Petr Mrázek
8b4e22bbb8 NOISSUE Move FTB logic out of generic code 2015-04-04 15:46:15 +02:00
Petr Mrázek
c7b39fe116 NOISSUE Remove special FTB logic from generic version patch code 2015-04-04 02:01:52 +02:00
Petr Mrázek
865b200571 GH-856 add profile strategy for FTB packs 2015-04-03 11:55:16 +02:00
Petr Mrázek
dc84ac3682 NOISSUE make slightly more compatible with current unstable
Recognize MinecraftVersion as IntendedVersion
2015-04-02 22:14:54 +02:00
Petr Mrázek
695bfd5f7c NOISSUE insert blatant self-promotion 2015-04-02 21:56:25 +02:00
Petr Mrázek
5ff2681da6 NOISSUE use QSaveFile for saving patch order 2015-04-02 21:56:25 +02:00
Petr Mrázek
5359f4499a NOISSUE remove obsolete EnabledItemFilter model 2015-04-02 20:22:52 +02:00
Petr Mrázek
04b45f3629 NOISSUE remove obsolete LWJGL selection dialog 2015-04-02 20:13:26 +02:00
Petr Mrázek
9249768db5 NOISSUE Make tests no longer use the MultiMC object
They do not require the application part anymore
2015-04-02 11:30:38 +02:00
Petr Mrázek
6f3aa65bd6 NOISSUE Split MultiMC app object into MultiMC and Env 2015-04-02 11:30:24 +02:00
Petr Mrázek
e508728246 NOISSUE remove obsolete assets migration task 2015-04-02 00:37:52 +02:00
Petr Mrázek
360ec557b2 NOISSUE remove notification checker form application object 2015-04-02 00:14:06 +02:00
Petr Mrázek
7334b8e520 NOISSUE remove status checker from application object 2015-04-02 00:14:06 +02:00
Petr Mrázek
791221e923 NOISSUE Refactors and moving of things 2015-04-02 00:14:06 +02:00
Petr Mrázek
593111b144 GH-813 Add 'mcedit2.exe' to the list of things the MCEdit tool looks for 2015-04-01 22:43:18 +02:00
Petr Mrázek
3b6574181e GH-853 evict asset index files from cache when they don't parse 2015-04-01 00:23:17 +02:00
Petr Mrázek
eae544f0eb GH-841 fix for modpack downloads on windows 2015-03-27 02:03:14 +01:00
Petr Mrázek
2eb3ec39bf NOISSUE tweak version display in the application to reflect recent changes 2015-03-24 22:40:49 +01:00
Petr Mrázek
a27c341781 SCRATCH more 2015-03-16 22:33:41 +01:00
Petr Mrázek
02f82e9694 NOISSUE Z_PREFIX for QuaZip 2015-03-16 21:56:27 +01:00
Petr Mrázek
405833bbbe NOISSUE eliminate qt5_use_modules from build 2015-03-16 08:43:00 +01:00
Petr Mrázek
8be865fb2a SCRATCH more experiments 2015-03-16 02:11:02 +01:00
Petr Mrázek
568a79e7b1 SCRATCH more quazip experiments 2015-03-16 01:53:19 +01:00
Petr Mrázek
06c9a64a87 NOISSUE fix bad export in iconfix 2015-03-16 00:27:06 +01:00
Petr Mrázek
ceec70e014 GH-796 Icon theme loading workaround
Replacing the Qt machinery with other Qt machinery under our control
2015-03-01 22:20:57 +01:00
Jan Dalheimer
ef34cafe17 Fix QuaZIP target dependency 2015-02-21 21:23:23 +01:00
Petr Mrázek
93b247592d NOISSUE actually make INI file saving work again... oops :P 2015-02-21 08:59:38 +01:00
Petr Mrázek
b8a8b09796 NOISSUE make sure saving config files is atomic 2015-02-21 00:21:19 +01:00
Jan Dalheimer
a53f8d506e GH-366: Plain and simple modpack export/import/download
Also removed the in-source QuaZIP in favour of upstream version
2015-02-19 21:04:27 +01:00
Jan Dalheimer
f9a17eb9de Fix the updater on OS X 2015-02-16 22:05:39 +01:00
Greenphlem
c6c5134398 Change copyright dates to 2015 2015-02-06 01:18:02 +01:00
Petr Mrázek
49ff31f131 NOISSUE do not use proxy by default 2015-02-06 00:41:36 +01:00
Petr Mrázek
e25e076d2e NOISSUE ignore PermGen warnings in log 2015-02-02 21:42:01 +01:00
Greenphlem
125ddc5f93 Change default PermGen from 64 to 128 mb
This will help users who run mods. Too many issues have been made over the lack of more permgen being assigned.
2015-02-02 00:42:36 -08:00
Petr Mrázek
d03dbea1b7 NOISSUE change icon themes without restart 2015-01-28 20:56:23 +01:00
Petr Mrázek
c6427caa9e GH-734: block more java env variable holes. 2015-01-17 23:05:34 +01:00
Petr Mrázek
0be0e822e4 GH-720 Add timestamps (since mmc start) to log 2015-01-12 22:18:26 +01:00
Petr Mrázek
55e5322fbe GH-721 Log errors in asset and MMC update downloads. 2015-01-11 22:30:54 +01:00
Petr Mrázek
0886786bb5 GH-721 Redo internal NetJob implementation.
NetJob is now using its own task queue and does not start more than 6 actions at the same time
2015-01-11 22:04:31 +01:00
Petr Mrázek
1151037f96 GH-719 Fix paste upload encoding and do not try to upload over limit 2015-01-11 03:08:41 +01:00
robotbrain
acb3346409 NOISSUE Update and sort modlist after adding mods 2015-01-06 21:23:02 +01:00
Alex Sieberer
85756d0e78 Remove IRC in favor of ElrosGem 2014-12-29 12:38:02 -05:00
Alex Sieberer
5c599d8658 Add translation badge to README 2014-12-29 10:44:23 -05:00
Petr Mrázek
4db31aacd6 NOISSUE Treat any forge downloads <= 4KB as stale. 2014-12-27 22:45:49 +01:00
Petr Mrázek
a30a9559c7 NOISSUE Fix jar mods for OnesSix 2014-12-27 20:50:33 +01:00
Petr Mrázek
01f44e0f39 NOISSUE Set minimum Minecraft window size to 1x1 2014-12-12 21:52:24 +01:00
Petr Mrázek
bbcd44a657 NOISSUE Always follow redirects for NetAction based downloads 2014-12-12 00:44:55 +01:00
Petr Mrázek
a060d79c12 GH-631, GH-658 Implement yellow status icon, refresh status icons in themes. 2014-12-07 22:55:29 +01:00
Petr Mrázek
28140b1db6 NOISSUE Change default font on Windows to Courier size 10 2014-12-03 21:48:27 +01:00
Petr Mrázek
5af1f0cf50 GH-640 Make legacy forge downloads follow redirects 2014-11-29 13:03:20 +01:00
Petr Mrázek
7778c84121 NOISSUE No more symlinks for LWJGL. 2014-11-29 13:03:08 +01:00
Petr Mrázek
4ae0d8e0af NOISSUE Update changelog for version 0.4.5 2014-11-21 11:20:18 +01:00
Petr Mrázek
9f14b319df NOISSUE Remove obsolete ReleaseCandidate logic from the build system 2014-11-21 10:53:59 +01:00
Petr Mrázek
a9af17eebb NOISSUE Remove remnant of crash handler 2014-11-21 10:34:38 +01:00
Petr Mrázek
d3c2230a24 NOISSUE fix OSX version number and (C) year 2014-11-20 19:07:10 +01:00
Petr Mrázek
f8bd687994 NOISSUE Fix console window hiding 2014-11-17 22:43:10 +01:00
Petr Mrázek
fa8d3c564d GH-618 Add updated light and dark simple icons 2014-11-17 22:10:58 +01:00
Petr Mrázek
80d3f734c6 GH-619 Add libraries missing in copies of 1.7.10 FTB packs 2014-11-17 22:01:32 +01:00
Petr Mrázek
9ad9826d08 GH-608 Re-detect java when the binary goes missing 2014-11-16 12:56:33 +01:00
Petr Mrázek
6a09fd2898 Set versions back to develop 2014-11-16 04:04:24 +01:00
Petr Mrázek
03a25c9a5d Do not do symlink hackery for java < 8 2014-11-16 04:04:24 +01:00
Petr Mrázek
83b90d8bfb Fix typo in changelog 2014-11-16 02:12:52 +01:00
Petr Mrázek
1d5c09051c VersionSelectDialog is for SELECTING versions.
Not blinking in and out of existence. That is not the job of a version SELECTION dialog.
2014-11-16 01:56:07 +01:00
Petr Mrázek
940f160091 Set version to 0.4.4 for release 2014-11-15 23:37:12 +01:00
Petr Mrázek
3d2de6add8 Add Batch icon set to the about dialog. 2014-11-15 23:37:12 +01:00
Petr Mrázek
b7c4284019 Remove crash handler 2014-11-15 23:35:24 +01:00
Jan Dalheimer
2315bf7bc5 Fix a OS X build error (missing a defined()) 2014-11-15 19:45:49 +01:00
Petr Mrázek
41bd2a6634 Add console font size setting and a preview\
Also moves the console settings from the minecraft page.
2014-11-15 19:45:49 +01:00
Petr Mrázek
5711b1be95 'Fix' instance group sorting 2014-11-10 08:51:24 +01:00
Petr Mrázek
90eea4f05c More pixels. 2014-11-10 07:37:30 +01:00
Petr Mrázek
cbb1e0ea45 HACK for scalable icons in QListView on Windows 2014-11-10 07:33:49 +01:00
Petr Mrázek
ec4805cce8 Default console font tweaks
* Lucida Console on Windows
* Menlo on OSX
* Monospace (resolved to whatever Monospace means) on linux
* Added ability to select proportional fonts in settings
2014-11-10 06:26:17 +01:00
Petr Mrázek
a2ac9c5a3a Fix coloring and processing of console output
* Removing \r
* Adding missing break statements for coloring
2014-11-10 05:10:58 +01:00
Petr Mrázek
551e101146 Follow redirects for skins 2014-11-10 05:10:23 +01:00
Petr Mrázek
1dd8978f8c Do not use QFont without Xorg 2014-11-09 20:49:23 +01:00
Petr Mrázek
24a0635b62 Allow changing the console font family 2014-11-09 19:48:35 +01:00
Petr Mrázek
2e9284951c Improve log formatting
* Updated log level detection for current Minecraft versions
* Better formatting: using a monospaced font and raw text instead of HTML (fixes #111)
2014-11-09 14:53:08 +01:00
Jan Dalheimer
f9a7c1cf21 Fix #208: Allow double clicking an account in the account selection dialog 2014-11-09 02:50:30 +01:00
Petr Mrázek
28eebc09fc Give paste upload a nice status message
Fixes #364
2014-11-09 02:09:01 +01:00
Petr Mrázek
cc19159f4d Document pre/post commands better
Fixes #398
2014-11-09 01:20:09 +01:00
Petr Mrázek
587fedce84 Implement search and logging pause in minecraft log
Fixes #47
2014-11-09 00:59:22 +01:00
Petr Mrázek
fa42a27525 Workaround for QTBUG-42500
Process has to have LD_LIBRARY_PATH set to empty string to not inherit it by default
2014-11-09 00:19:54 +01:00
Petr Mrázek
84723add8f Fix #537
Core Mods help now goes to Loader Mods
Fixed Minecraft Log -> Minecraft Logs problem
2014-11-08 21:47:51 +01:00
Petr Mrázek
992ba0c3f8 Implement #545
* Instance group can be selected when creating and copying instances
* Original group is pre-selected when copying
* Last used group is pre-selected when creating new instances
2014-11-08 21:17:28 +01:00
Jan Dalheimer
7d1dd2a32f Fix #474: Bad jvisualvm check 2014-11-02 20:29:09 +01:00
Jan Dalheimer
add23a9a0b Fix #220: Use .exe suffix on windows for jprofiler 2014-11-02 20:16:29 +01:00
Jan Dalheimer
d9b2f0ed42 Fix another bunch of copyright years, including fixing #397 2014-11-02 20:08:26 +01:00
Jan Dalheimer
9217d9263e Update copyright year (finally...) 2014-11-02 19:49:58 +01:00
Jan Dalheimer
a3a5afe119 Fix #231: Enable translation for more strings 2014-11-02 19:25:11 +01:00
Petr Mrázek
7fb6cafe9e Add LWJGL 2.9.2-nightly-20140822 2014-11-02 12:47:39 +01:00
Petr Mrázek
c1b6f42551 Also block other java-related env vars, for good measure
"JAVA_ARGS"
"CLASSPATH"
"CONFIGPATH"
"JAVA_HOME"
"JRE_HOME"
2014-11-02 11:13:18 +01:00
Petr Mrázek
3d1426b559 Filter env variables passed to Minecraft
QT_* and LD_* are not passed through
env variables are logged on launch
2014-11-01 14:11:20 +01:00
Petr Mrázek
095640ed01 Fix Java 8 issues with LWJGL native libs on OSX? 2014-11-01 10:39:32 +01:00
Petr Mrázek
9e8a74cc89 Fix some issues in the newly added themes 2014-10-27 00:41:40 +01:00
Petr Mrázek
547f6f77d0 Add iOS and OSX icon themes by pe 2014-10-27 00:15:52 +01:00
Petr Mrázek
8f7aec032b Add dark, light, blue and colored theme from pe.
Replaces the old dark and light themes
2014-10-26 23:44:20 +01:00
TakSuyu
92560bf0cd Merge pull request #561 from max96at/patch-1
One line change that was already attempted. Easy enough to revert if in the off chance it regresses.
2014-10-19 10:28:05 -07:00
max96at
2dd2b7a291 Finally with Qt5.3 this should work. :D 2014-10-19 18:19:37 +02:00
robotbrain
b4122cff89 Fix translation downloading 2014-10-05 11:37:49 -04:00
Petr Mrázek
43b9706b5c Fix translations path for the meta cache 2014-10-01 00:24:56 +02:00
robotbrain
bbdf5c1395 Translation downloading! 2014-09-30 16:22:39 -04:00
Petr Mrázek
382e167d64 Do not choke on large files when showing them in the 'other logs' page. 2014-09-21 00:05:48 +02:00
Sky
de2bb0c6f3 README tweaks 2014-09-15 15:53:20 +01:00
Petr Mrázek
2083ac8cc1 Add a dot. 2014-09-14 11:31:43 +02:00
Petr Mrázek
16955c6188 Delete old translations 2014-09-09 07:57:27 +02:00
Petr Mrázek
b00e63dbe8 More sync from quickmods
Also a small VersionSelectDialog refactor
2014-09-06 21:01:23 +02:00
Petr Mrázek
20cb97a35a Sync from quickmods 2014-09-06 19:03:05 +02:00
Petr Mrázek
36efcf8d3c Back to develop 2014-09-06 13:05:17 +02:00
Petr Mrázek
a59e83cd12 Update changelog
Conflicts:
	changelog.md
2014-08-21 00:48:14 +02:00
Petr Mrázek
febf3645d0 Fix version file problems, fix console window not being destroyed 2014-08-21 00:32:32 +02:00
Petr Mrázek
01ca3d6aad Add some logging to places where version updates might not trigger. 2014-08-21 00:32:32 +02:00
Mrazek, Petr
6deb41e32d Travis build: take negative test results as build errors 2014-08-21 00:32:32 +02:00
Petr Mrázek
74f7bd9a1c Add changelog pieces, version 2014-08-16 11:33:01 +02:00
Petr Mrázek
7d8c5ac9b5 Revert "Do not include jutils in LWJGL versions."
This reverts commit 376467740b.
2014-08-15 00:02:44 +02:00
Petr Mrázek
d172daf3a9 Include QtXml, we use it.
Fixes KDevelop semantic analysis magic.
2014-08-15 00:01:55 +02:00
Petr Mrázek
56b16320dd Print the Minecraft window size on launch. 2014-08-13 04:44:19 +02:00
Petr Mrázek
376467740b Do not include jutils in LWJGL versions. 2014-08-13 04:44:01 +02:00
Petr Mrázek
814d5d3315 Properly detect if the instance is vanilla and don't treat it as custom. 2014-08-11 02:17:48 +02:00
Petr Mrázek
fd6706391b Increase the java checker timeout from 5s to 15s 2014-08-10 15:13:35 +02:00
Jan Dalheimer
21597da33d Merge branch 'Loetkolben-pr_feature_warnProblematicInstPath' into develop
Closes #400
2014-07-30 21:45:12 +02:00
Loetkolben
c0254d9a75 Show a warning if the instance path contains a '!'
The checks and warnings happen the time MMC loads (via QLOG_INFO), the time the GUI starts (via a dialog) and when the user changes the instance path via the settings window.
2014-07-30 21:40:18 +02:00
Mrazek, Petr
151fbde8d0 Fix loading of Minecraft versions from FTB packs 2014-07-30 06:25:17 -04:00
Petr Mrázek
03b13b0b3f Rearrange RawLibrary and OneSixLibrary heavily.
Fix #396
2014-07-26 23:00:35 +02:00
Petr Mrázek
9b82c87c92 Tweak windows rc file description (Minecraft Launcher -> MultiMC Launcher) 2014-07-23 22:54:03 +02:00
Petr Mrázek
75cb329f17 Check if the java binary can be found before launch.
Fix #386
2014-07-23 00:16:31 +02:00
Petr Mrázek
bef869ff76 Add a note about MultiMC not working in system folders to build instructions.
Fix #185
2014-07-22 23:29:10 +02:00
Petr Mrázek
0a64579401 Merge branch 'pullrequest2' of https://github.com/p-schneider/MultiMC5 into develop 2014-07-22 23:21:17 +02:00
Petr Mrázek
d71697efb3 Do not deploy debug version of the svg icon lib in release builds. 2014-07-22 23:19:53 +02:00
Mrazek, Petr
e5b393318f Include the SVG icon engine. 2014-07-22 12:27:00 +02:00
Petr Mrázek
1ed90293ac Unify look of all pages.
Now they have a QTabWidget with no tabs as a background.
2014-07-21 00:10:13 +02:00
Petr Mrázek
bc05ad30aa Rework the settings dialog. Rework all of it. Thoroughly.
Also introduces the ColumnResizer from:
https://github.com/agateau/columnresizer/
2014-07-20 23:47:46 +02:00
Jan Dalheimer
e178284172 Merge global settings and accounts into a pagedialog
Also split external tools into it's own page
2014-07-20 15:01:02 +02:00
Petr Mrázek
c91adfb3d1 Merge branch 'master' into develop
Conflicts:
	CMakeLists.txt
	changelog.md
2014-07-20 14:23:14 +02:00
Petr Mrázek
77d9360d25 Bump version and add to changelog. 2014-07-20 13:01:21 +02:00
Petr Mrázek
3403553d44 Fix LWJGL version list loading.
SourceForge has changed its API again.
2014-07-20 12:59:44 +02:00
Petr Mrázek
c767707c95 Make forge work.
Using classifiers FTW.
2014-07-19 23:16:02 +02:00
p-schneider
66fa241257 Show a warning in the log if a library is missing 2014-07-19 15:46:55 +02:00
Petr Mrázek
8a56ab6780 Implement gradle spec reader/writer 2014-07-16 02:03:52 +02:00
Petr Mrázek
71575a5022 Fix #367 2014-07-14 20:48:51 +02:00
Petr Mrázek
222c26f9cf Bump dev version 2014-07-14 02:43:10 +02:00
Petr Mrázek
ce68efa174 Change one small thing
Bunnies!
2014-07-14 01:41:18 +02:00
Petr Mrázek
8bb906bbd7 Fix last minute derps
* Changelog formatting
* Update dialog popping up on start even when it shouldn't
2014-07-14 01:11:52 +02:00
Petr Mrázek
e7f67a73b3 Update changelog 2014-07-14 00:58:19 +02:00
Petr Mrázek
3821569363 Show changelog even when there are no new updates available. 2014-07-14 00:57:54 +02:00
Petr Mrázek
d8d6f5929b Fix #361 2014-07-13 15:26:26 +02:00
Petr Mrázek
977cc1cfbb Mess around with log UI. Herp Derp. 2014-07-13 01:49:46 +02:00
Jan Dalheimer
4c0dc51110 Finish of the OtherLogs page, and (re)format page related files 2014-07-12 23:31:06 +02:00
Jan Dalheimer
5c43842359 Add a new page that can show all sorts of logs 2014-07-12 23:31:05 +02:00
Petr Mrázek
aba1f89e2a Add home/end and fix navigation with collapsed groups (they are skipped) 2014-07-12 23:27:32 +02:00
Petr Mrázek
cc6968e9a3 Group view gets keyboard navigation back.
And a bunch of fixes.
2014-07-12 21:13:23 +02:00
Petr Mrázek
d570037331 Cache group view geometry -- speed up instance view by caching sizes of stuff 2014-07-12 12:49:07 +02:00
Petr Mrázek
6a8984a21d Fix #356 2014-07-11 01:51:07 +02:00
Petr Mrázek
0d4046de39 Add clear and copy buttons to the log page. 2014-07-11 01:50:36 +02:00
Petr Mrázek
24698fe85f Fix #355 2014-07-10 21:13:17 +02:00
Petr Mrázek
40c238442f Fix #354, make jar mods and patch files in general more resilient. 2014-07-10 01:26:45 +02:00
Petr Mrázek
ff06489fed Do not show core mods page for minecraft newer than 1.5.2. 2014-07-10 00:47:08 +02:00
Petr Mrázek
08fbfa7434 Make the auth timeout longer (30s) 2014-07-09 19:53:35 +02:00
Petr Mrázek
6f75009a80 Show update channel in the update dialog, actually show changelog for the selected update channel. 2014-07-09 19:48:46 +02:00
Petr Mrázek
7c51cc475b Better regexp for links 2014-07-09 01:17:59 +02:00
Petr Mrázek
fc911add58 Show changelog in the update dialog. 2014-07-09 00:49:37 +02:00
Petr Mrázek
6349800f07 Fix mod list sorting predicate, convert the changelog to markdown and reverse it. 2014-07-08 23:05:33 +02:00
Petr Mrázek
9b3ae29a36 Make the FTB packs a set instead of a list. 2014-07-08 08:42:48 +02:00
Petr Mrázek
19278c853b Merge branch 'master' into develop
Conflicts:
	CMakeLists.txt
	changelog.yaml
	logic/forge/ForgeInstaller.cpp
2014-07-08 01:10:45 +02:00
Petr Mrázek
b3cf19190f Bump version, update the changelog 2014-07-08 01:03:18 +02:00
Petr Mrázek
842328df8e Update the forge hacks.
Conflicts:
	logic/ForgeInstaller.cpp
2014-07-08 01:00:52 +02:00
Petr Mrázek
f72a38b06c Update the forge hacks. 2014-07-07 08:40:03 +02:00
Petr Mrázek
d934e64831 Tweak the response to successful uploads (screenshots, log pastes)
The url will now be shown as link, put into the clipboard AND opened in a browser.
At the same time. To avoid losing the URL.
2014-07-07 00:02:04 +02:00
Petr Mrázek
15775bd30a One more liteloader fix 2014-07-06 23:23:48 +02:00
Petr Mrázek
cc499488db Fix liteloader, some cleanups. 2014-07-06 11:15:15 +02:00
Petr Mrázek
a218d7b7f6 Improve screenshot view/model.
Changes to screenshots are tracked.
Thumbnails are generated in a thread pool.
2014-07-05 13:27:32 +02:00
Petr Mrázek
b5d6f50fb1 Make paste.ee logs expire after a month 2014-07-03 20:29:44 +02:00
Petr Mrázek
252f375454 fix the instance toolbar 2014-07-03 08:16:02 +02:00
Petr Mrázek
a75e64dd18 disable that upgrade page 2014-07-03 02:26:00 +02:00
Petr Mrázek
18a342ef14 Move settings lib into the main code, fixing error logging in it. 2014-07-01 01:48:09 +02:00
Petr Mrázek
8b86306d48 Handle a bunch more clang warnings 2014-07-01 01:23:49 +02:00
Taeyeon Mori
dd0752e69f [Clang Warnings] Silence Pack200 unused variable variable warnings
They're caused by a big define-everything-you-ever-need macro
2014-06-30 23:53:42 +02:00
Taeyeon Mori
d166340097 [Clang Warnings] Add empty default clause 2014-06-30 23:53:42 +02:00
Taeyeon Mori
eb5699c835 [Clang Warnings] Fix char* cast from string literal in Tests 2014-06-30 23:53:41 +02:00
Taeyeon Mori
b9fb718822 [Clang Warnings] Remove unused variables 2014-06-30 23:53:41 +02:00
Taeyeon Mori
1f498266d8 Fix bug in OneSixInstance's modification detection.
OneSixInstance would report all instances as custom because of a typo.
Thanks Clang :)
2014-06-30 23:51:40 +02:00
Petr Mrázek
e241c3625c Merge branch 'feature_theme_support' into develop
Conflicts:
	main.cpp
2014-06-30 22:22:09 +02:00
Petr Mrázek
421a46e3d3 Redo the console window. Log is now a page. Console window has relevant pages.
Dirty fix for screenshot thumbnail generation. Needs more QTimer.
2014-06-30 02:02:57 +02:00
Petr Mrázek
5179aed3a0 Separate page dialog into a page container and a dialog. 2014-06-29 19:59:08 +02:00
Petr Mrázek
77de2d1e54 Hack. 2014-06-29 13:11:13 +02:00
Petr Mrázek
e422eff959 Add screenshots icon 2014-06-29 11:27:24 +02:00
Petr Mrázek
828254dd11 ~_~_~_~ 2014-06-28 17:15:53 +02:00
Petr Mrázek
1f3a840f3c Derp^2!! 2014-06-28 17:11:50 +02:00
Petr Mrázek
56d91fda3a Derp! 2014-06-28 17:09:23 +02:00
Petr Mrázek
e8731c5d01 Turn screenshot management into a page. 2014-06-28 17:07:08 +02:00
Petr Mrázek
30b1f5e5cf Merge branch 'feature/fix_intel' into develop
Conflicts:
	CMakeLists.txt
	changelog.yaml
	gui/MainWindow.cpp
2014-06-28 08:49:18 +02:00
Petr Mrázek
7f4073840a Bump version, fix typo, update the changelog. 2014-06-28 00:21:36 +02:00
Petr Mrázek
f0d850e1ee Fix issues with intel drivers. Forced java re-detection on Windows. 2014-06-28 00:05:00 +02:00
Petr Mrázek
d6e5c472b5 Update changelog and bump version to 0.4.0 (no tag yet, not final) 2014-06-26 21:49:05 +02:00
Petr Mrázek
c31dbf13cb Bump version to 0.3.7, update changelog 2014-06-26 08:41:50 +02:00
Petr Mrázek
affb2fdd6c Merge branch 'feature/forge_pre4_fix' into develop
Conflicts:
	logic/forge/ForgeVersionList.cpp
	logic/forge/ForgeVersionList.h
2014-06-25 00:54:00 +02:00
Petr Mrázek
c081cd8021 Fix forge prerelease mess.
This adds a HACK that assumes Mojang will be consistent with their versioning. What could possibly go wrong?
2014-06-25 00:36:42 +02:00
Petr Mrázek
1194ec9a8e No more disabling of actions in the instance toolbar. It makes no sense. 2014-06-20 01:24:32 +02:00
Petr Mrázek
d911c9908c Replace notes dialog with a page. 2014-06-18 01:15:01 +02:00
Petr Mrázek
702e00e059 Fix #313 - used texture pack list instead of resource pack list 2014-06-11 19:35:21 +02:00
Petr Mrázek
478815dae6 Tweak version page: select first item by default, allow changing version of 'version.json'. 2014-06-10 08:39:11 +02:00
Petr Mrázek
c08bfce5f2 More github wiki friendly help page names 2014-06-10 02:05:31 +02:00
Petr Mrázek
9ec6deea84 Add close button to page dialog. Add help button to page dialog.
Smile.
2014-06-10 00:46:05 +02:00
Petr Mrázek
0bccc94471 Cleanup - QFileInfo derp and unused variables 2014-06-09 01:57:10 +02:00
Petr Mrázek
a0a805735b Remove margins from settings page. 2014-06-09 01:38:32 +02:00
Petr Mrázek
171325d427 Instance settings moved to a page. 2014-06-09 01:38:31 +02:00
Petr Mrázek
be73eb3322 Version revert logic improvements, colorful icons for mod lists and resource pack list.
Icons are from Oxygen.
2014-06-09 01:38:31 +02:00
Petr Mrázek
bf7b070508 Show texture/resource packs when appropriate. 2014-06-09 01:38:31 +02:00
Petr Mrázek
223a7aba7b Hardcode LWJGL 2.9.1 for OneSix, only allow chancging Minecraft versions for now. 2014-06-09 01:38:31 +02:00
Petr Mrázek
84ae67fff5 Page dialog for legacy instances. 2014-06-09 01:38:31 +02:00
Petr Mrázek
694067c603 Only enable move buttons for version patches that can move. HACK HACK 2014-06-09 01:38:31 +02:00
Petr Mrázek
6b3d1101cb Tweaks to page dialog and version page. 2014-06-09 01:38:31 +02:00
Petr Mrázek
f485885757 Add and implement pages and page dialog. 2014-06-09 01:38:31 +02:00
Petr Mrázek
48d3052ac1 New, simpler and versioned format for the patch load order. 2014-06-09 01:38:31 +02:00
Petr Mrázek
e118b1f990 Implement adding jar mods, break saving library order. 2014-06-09 01:38:31 +02:00
Petr Mrázek
55a0d110b6 Lock down the version cache. Just enough to make it annoying to corrupt the files. 2014-06-09 01:38:31 +02:00
Petr Mrázek
f3900f2966 Reduce startup logging verbosity 2014-06-09 01:38:31 +02:00
Petr Mrázek
db8b47e7f6 Break FTB. Yep. It has to be done better. 2014-06-09 01:38:30 +02:00
Petr Mrázek
439e17b149 Add back legacy mod edit, add checksums for all legacy jars 2014-06-09 01:38:30 +02:00
Petr Mrázek
8c71a5d61f Move instance settings back to the main window. 2014-06-09 01:38:30 +02:00
Petr Mrázek
6d34411f54 Fix last instance remaining selected when deleted 2014-06-09 01:38:30 +02:00
Petr Mrázek
68ef451be5 Small fixes, including release dates of some legacy versions 2014-06-09 01:38:30 +02:00
Petr Mrázek
e993adaf44 Disable window titles and isons again, windows build fixes 2014-06-09 01:38:30 +02:00
Petr Mrázek
ad1f2c530c Use window icons and titles in 1.6+ 2014-06-09 01:38:30 +02:00
Petr Mrázek
69c3e7111f Make 1.6+ work with new instance format. 2014-06-09 01:38:30 +02:00
Petr Mrázek
92abe4c603 All of the broken legacy things work. 2014-06-09 01:38:30 +02:00
Petr Mrázek
9860d5ee12 Introducing VersionPatch base class for version files and minecraft versions 2014-06-09 01:38:30 +02:00
Petr Mrázek
8a3a0f5a52 Reorganize logic code. 2014-06-09 01:38:30 +02:00
Petr Mrázek
69a9ca39ad Add builtin Minecraft versions for legacy 2014-06-09 01:38:29 +02:00
Petr Mrázek
825d31bf1a Set the window params inside the launcher part, depending on launcher type.
Also create/change the new internal version files.
2014-06-09 01:38:29 +02:00
Petr Mrázek
2590c6be15 Fix launcher part for legacy in onesix. 2014-06-09 01:38:29 +02:00
Petr Mrázek
4c3bd416c6 Much change, very jarmod. 2014-06-09 01:38:29 +02:00
Petr Mrázek
aade36860c Begin the transformation!
Nuke all the things.
2014-06-09 01:38:29 +02:00
Petr Mrázek
3a0cdf2d3d Tagging 0.3.6 2014-06-03 01:44:19 +02:00
Petr Mrázek
d2b2d55aa9 New flat icon themes from pexner
Squash and rework of commits from robotbrain
2014-05-25 04:01:38 +02:00
Petr Mrázek
eb9661370b Enable SVG icons 2014-05-25 03:38:45 +02:00
Jan Dalheimer
e1f542b5b0 Still trying to fix FTB 2014-05-23 18:41:22 +02:00
Jan Dalheimer
15920aa9d0 Attempt at fixing FTB 2014-05-23 18:19:20 +02:00
Jan Dalheimer
e17364de6b Fix FTB? 2014-05-23 16:39:14 +02:00
Jan Dalheimer
b911a3834c More FTB debug stuff 2014-05-23 16:16:44 +02:00
Jan Dalheimer
94c2c363b2 Some more FTB related debug info 2014-05-23 15:46:12 +02:00
Petr Mrázek
df82d8fadb QDir::exists is not static in Qt 5.1.1 2014-05-22 09:12:04 +02:00
Petr Mrázek
851a77d5bb Merge pull request #275 from MultiMC/feature_ftb_new_paths
Fix FTB paths on windows
2014-05-22 09:04:03 +02:00
Jan Dalheimer
41caf7976d FTB paths changed on windows. Fixes #255 2014-05-22 07:49:45 +02:00
Jan Dalheimer
fc3c0b0971 Merge branch 'feature_crashreport' into develop 2014-05-21 15:57:34 +02:00
Petr Mrázek
94cb5c7d77 Restore manage screenshots in main window. 2014-05-18 23:12:35 +02:00
Petr Mrázek
911ac19a56 Screenshot upload dialog(s) now have the console window as parent. 2014-05-18 19:07:01 +02:00
Petr Mrázek
7f2a16917e Add static data path for ... static data. Like translations. Move translations there. 2014-05-17 18:21:32 +02:00
Petr Mrázek
8a8c4193e6 Finish status pills. 2014-05-17 16:23:48 +02:00
Petr Mrázek
927217c7f0 Status pills. This doesn't build yet. 2014-05-15 23:20:38 +02:00
Forkk
e6ca58a89e Add a missing letter 2014-05-10 15:10:24 -05:00
Forkk
aefa73ad11 Fix stupid tabs.
Thanks, QtCreator... ._.
2014-05-10 14:56:44 -05:00
Andrew
4f6cd65c13 Remove unused function 2014-05-10 14:19:13 -05:00
Andrew
5099964c67 Implement backtraces on Windows.
Much !!FUN!! was had
2014-05-10 14:16:27 -05:00
Andrew
9e80ddb040 Implement crash report system on Windows. 2014-05-09 20:08:07 -05:00
Forkk
489cb4dbf5 Change dump file extension 2014-05-09 18:21:15 -05:00
Forkk
e3b9b30302 Remove some unnecessary dummy functions. 2014-05-09 17:36:53 -05:00
Forkk
93ae21abfc Implement crash handling on Linux
This will allow us to generate crash dumps and have users report
crashes.
2014-05-09 17:33:32 -05:00
920 changed files with 43061 additions and 34591 deletions

View File

@@ -9,6 +9,7 @@ NamespaceIndentation: None
BreakBeforeBraces: Allman
AllowShortIfStatementsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 96
MaxEmptyLinesToKeep: 1

1
.gitattributes vendored Normal file
View File

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

View File

@@ -4,26 +4,20 @@ compiler:
- clang
cache: apt
before_install:
- sudo apt-add-repository -y ppa:beineri/opt-qt521
- 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
install:
- sudo apt-get install -y -qq cmake qt52base qt52svg qt52tools qt52x11extras
- 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
before_script:
- mkdir build
- cd build
- cmake -DCMAKE_PREFIX_PATH=/opt/qt52/lib/cmake ..
- cmake -DCMAKE_PREFIX_PATH=/opt/qt53/lib/cmake ..
script:
- make -j4
after_script:
- make test
- make test ARGS="-V"
notifications:
irc:
channels:
- "irc.esper.net#MultiMC"
template:
- "%{build_number} (%{branch} - %{commit} : %{author}): %{message} (%{build_url})"
email: false

View File

@@ -2,10 +2,18 @@ Build Instructions
==================
# Contents
* [Note](#note)
* [Linux](#linux)
* [Windows](#windows)
* [OS X](#os-x)
# Note
MultiMC is a portable application and is not supposed to be installed into any system folders.
That would be anything outside your home folder. Before runing `make install`, make sure
you set the install path to something you have write access to. Never build this under
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
# 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.

View File

@@ -10,13 +10,12 @@ if(WIN32)
cmake_policy(SET CMP0020 OLD)
endif()
project(MultiMC)
project(Megatron)
enable_testing()
######## Set CMake options ########
set(CMAKE_AUTOMOC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(FILES_TO_TRANSLATE )
######## Set module path ########
set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
@@ -35,25 +34,29 @@ set(CMAKE_JAVA_TARGET_OUTPUT_DIR ${PROJECT_BINARY_DIR}/jars)
######## Set compiler flags ########
include(UseCXX11)
include(Coverage)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS " -Wall ${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror=return-type")
# cmake code needed for the coverity scan upload
include(Coverity)
################################ 3rd Party Libs ################################
# Find the required Qt parts
find_package(Qt5Core REQUIRED)
find_package(Qt5Widgets REQUIRED)
find_package(Qt5Concurrent REQUIRED)
find_package(Qt5Network REQUIRED)
find_package(Qt5Test REQUIRED)
find_package(Qt5Concurrent REQUIRED)
find_package(Qt5LinguistTools REQUIRED)
find_package(Qt5Xml REQUIRED)
include_directories(
${Qt5Core_INCLUDE_DIRS}
${Qt5Widgets_INCLUDE_DIRS}
${Qt5Concurrent_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${Qt5Test_INCLUDE_DIRS}
${Qt5Network_INCLUDE_DIRS}
${Qt5Xml_INCLUDE_DIRS}
)
# The Qt5 cmake files don't provide its install paths, so ask qmake.
@@ -61,156 +64,37 @@ include(QMakeQuery)
query_qmake(QT_INSTALL_PLUGINS QT_PLUGINS_DIR)
query_qmake(QT_INSTALL_IMPORTS QT_IMPORTS_DIR)
query_qmake(QT_INSTALL_LIBS QT_LIBS_DIR)
query_qmake(QT_INSTALL_LIBEXECS QT_LIBEXECS_DIR)
query_qmake(QT_HOST_DATA QT_DATA_DIR)
set(QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
################################ SET UP BUILD OPTIONS ################################
######## Check endianness ########
include(TestBigEndian)
test_big_endian(BIGENDIAN)
if(${BIGENDIAN})
add_definitions(-DMULTIMC_BIG_ENDIAN)
endif(${BIGENDIAN})
######## Set URLs ########
set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
######## Set version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 3)
set(MultiMC_VERSION_HOTFIX 5)
# Build number
set(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.")
# Version type
set(MultiMC_VERSION_TYPE "Custom" CACHE STRING "MultiMC's version type. This should be one of 'Custom', 'Release', 'ReleaseCandidate', or 'Development', depending on what type of version this is.")
# Build platform.
set(MultiMC_BUILD_PLATFORM "" CACHE STRING "A short string identifying the platform that this build was built for. Only used by the notification system and to display in the about dialog.")
# Version channel
set(MultiMC_VERSION_CHANNEL "" CACHE STRING "The current build's channel. Included in the version string.")
# Channel list URL
set(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.")
# Updater enabled?
set(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.")
# Notification URL
set(MultiMC_NOTIFICATION_URL "" CACHE STRING "URL for checking for notifications.")
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}")
if(MultiMC_VERSION_HOTFIX GREATER 0)
set(MultiMC_RELEASE_VERSION_NAME "${MultiMC_RELEASE_VERSION_NAME}.${MultiMC_VERSION_HOTFIX}")
endif()
# Build a version string to display in the configure logs.
if(MultiMC_VERSION_TYPE STREQUAL "Custom")
message(STATUS "Version Type: Custom")
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
elseif(MultiMC_VERSION_TYPE STREQUAL "Release")
message(STATUS "Version Type: Stable Release")
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}")
elseif(MultiMC_VERSION_TYPE STREQUAL "ReleaseCandidate")
message(STATUS "Version Type: Release Candidate")
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-rc${MultiMC_VERSION_BUILD}")
elseif(MultiMC_VERSION_TYPE STREQUAL "Development")
message(STATUS "Version Type: Development")
set(MultiMC_VERSION_STRING "${MultiMC_RELEASE_VERSION_NAME}-dev${MultiMC_VERSION_BUILD}")
else()
message(ERROR "Invalid build type.")
endif()
message(STATUS "MultiMC 5 Version: ${MultiMC_VERSION_STRING}")
# If the update system is enabled, make sure MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL are set.
if(MultiMC_UPDATER)
if(MultiMC_VERSION_CHANNEL STREQUAL "")
message(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_CHANNEL is not set.\n"
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
endif()
if(MultiMC_CHANLIST_URL STREQUAL "")
message(FATAL_ERROR "Update system is enabled, but MultiMC_CHANLIST_URL is not set.\n"
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
endif()
if(MultiMC_VERSION_BUILD LESS 0)
message(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_BUILD is not set.\n"
"Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.")
endif()
message(STATUS "Updater is enabled. Channel list URL: ${MultiMC_CHANLIST_URL}")
endif()
#### Updater-related build config options ####
option(MultiMC_UPDATER_DRY_RUN "Enable updater dry-run mode -- for updater development." OFF)
option(MultiMC_UPDATER_FORCE_LOCAL "Do not download updated updater -- for updater development." OFF)
if(MultiMC_UPDATER_DRY_RUN)
set(MultiMC_UPDATER_DRY_RUN_value "true")
else()
set(MultiMC_UPDATER_DRY_RUN_value "false")
endif()
if(MultiMC_UPDATER_FORCE_LOCAL)
set(MultiMC_UPDATER_FORCE_LOCAL_value "true")
else()
set(MultiMC_UPDATER_FORCE_LOCAL_value "false")
endif()
#### Custom target to just print the version.
add_custom_target(version echo "Version: ${MultiMC_VERSION_STRING}")
#### Check the current Git commit
include(GitFunctions)
git_run(COMMAND rev-parse HEAD DEFAULT "Unknown" OUTPUT_VAR MultiMC_GIT_COMMIT)
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
######## Configure header ########
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
######## Packaging/install paths setup ########
if(UNIX AND APPLE)
set(BINARY_DEST_DIR MultiMC.app/Contents/MacOS)
set(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
set(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2013 MultiMC Contributors")
elseif(UNIX)
set(BINARY_DEST_DIR bin)
set(PLUGIN_DEST_DIR plugins)
set(QTCONF_DEST_DIR .)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
elseif(WIN32)
set(BINARY_DEST_DIR .)
set(PLUGIN_DEST_DIR .)
set(QTCONF_DEST_DIR .)
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
endif()
# directories to look for dependencies
set(DIRS "${QT_LIBS_DIR}")
################################ Included Libs ################################
include(ExternalProject)
set_directory_properties(PROPERTIES EP_BASE External)
# Add quazip
add_definitions(-DQUAZIP_STATIC)
add_subdirectory(depends/quazip)
include_directories(depends/quazip)
set(QUAZIP_VERSION "0.7.1")
if(NOT EXISTS ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz)
file(DOWNLOAD http://downloads.sourceforge.net/project/quazip/quazip/${QUAZIP_VERSION}/quazip-${QUAZIP_VERSION}.tar.gz ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz)
endif()
ExternalProject_Add(QuaZIP
SOURCE_DIR <BINARY_DIR>/../Source/quazip-${QUAZIP_VERSION}
DOWNLOAD_COMMAND ${CMAKE_COMMAND} -E chdir <SOURCE_DIR>/.. ${CMAKE_COMMAND} -E tar xzf ${CMAKE_BINARY_DIR}/quazip-${QUAZIP_VERSION}.tar.gz
PATCH_COMMAND patch -p0 -i ${CMAKE_SOURCE_DIR}/quazip.patch
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG} -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
)
include_directories("${CMAKE_BINARY_DIR}/External/Install/QuaZIP/include/quazip")
if(UNIX)
set(QUAZIP_LIBRARIES -L"${CMAKE_BINARY_DIR}/External/Install/QuaZIP/lib" quazip z)
else()
set(QUAZIP_LIBRARIES -L"${CMAKE_BINARY_DIR}/External/Install/QuaZIP/lib" quazip)
endif()
# Add the markdown parser thing
add_subdirectory(depends/hoedown)
include_directories(${HOEDOWN_INCLUDE_DIR})
# Add the java launcher and checker
add_subdirectory(depends/launcher)
@@ -231,540 +115,22 @@ add_definitions(-DLIBUTIL_STATIC)
add_subdirectory(depends/util)
include_directories(${LIBUTIL_INCLUDE_DIR})
# Add the settings library.
add_definitions(-DLIBSETTINGS_STATIC)
add_subdirectory(depends/settings)
include_directories(${LIBSETTINGS_INCLUDE_DIR})
# Add the GUI -> Logic connection header
add_subdirectory(depends/LogicalGui)
include_directories(${LOGICALGUI_INCLUDE_DIR})
# Add the updater
add_subdirectory(mmc_updater)
################################ FILES ################################
######## Sources and headers ########
SET(MULTIMC_SOURCES
# Application base
MultiMC.h
MultiMC.cpp
MMCError.h
BuildConfig.h
${PROJECT_BINARY_DIR}/BuildConfig.cpp
# Logging
logger/QsDebugOutput.cpp
logger/QsDebugOutput.h
logger/QsLog.cpp
logger/QsLog.h
logger/QsLogDest.cpp
logger/QsLogDest.h
# GUI - windows
gui/MainWindow.h
gui/MainWindow.cpp
gui/ConsoleWindow.h
gui/ConsoleWindow.cpp
# GUI - dialogs
gui/dialogs/SettingsDialog.h
gui/dialogs/SettingsDialog.cpp
gui/dialogs/CopyInstanceDialog.h
gui/dialogs/CopyInstanceDialog.cpp
gui/dialogs/NewInstanceDialog.cpp
gui/dialogs/ProgressDialog.h
gui/dialogs/ProgressDialog.cpp
gui/dialogs/AboutDialog.h
gui/dialogs/AboutDialog.cpp
gui/dialogs/VersionSelectDialog.h
gui/dialogs/VersionSelectDialog.cpp
gui/dialogs/LwjglSelectDialog.h
gui/dialogs/LwjglSelectDialog.cpp
gui/dialogs/InstanceSettings.h
gui/dialogs/InstanceSettings.cpp
gui/dialogs/IconPickerDialog.h
gui/dialogs/IconPickerDialog.cpp
gui/dialogs/LegacyModEditDialog.h
gui/dialogs/LegacyModEditDialog.cpp
gui/dialogs/OneSixModEditDialog.h
gui/dialogs/OneSixModEditDialog.cpp
gui/dialogs/ModEditDialogCommon.h
gui/dialogs/ModEditDialogCommon.cpp
gui/dialogs/EditNotesDialog.h
gui/dialogs/EditNotesDialog.cpp
gui/dialogs/CustomMessageBox.h
gui/dialogs/CustomMessageBox.cpp
gui/dialogs/EditAccountDialog.h
gui/dialogs/EditAccountDialog.cpp
gui/dialogs/LoginDialog.h
gui/dialogs/LoginDialog.cpp
gui/dialogs/AccountListDialog.h
gui/dialogs/AccountListDialog.cpp
gui/dialogs/AccountSelectDialog.h
gui/dialogs/AccountSelectDialog.cpp
gui/dialogs/UpdateDialog.h
gui/dialogs/UpdateDialog.cpp
gui/dialogs/ScreenshotDialog.h
gui/dialogs/ScreenshotDialog.cpp
gui/dialogs/NotificationDialog.h
gui/dialogs/NotificationDialog.cpp
# GUI - widgets
gui/widgets/Common.h
gui/widgets/Common.cpp
gui/widgets/ModListView.h
gui/widgets/ModListView.cpp
gui/widgets/VersionListView.h
gui/widgets/VersionListView.cpp
gui/widgets/LabeledToolButton.h
gui/widgets/LabeledToolButton.cpp
gui/widgets/MCModInfoFrame.h
gui/widgets/MCModInfoFrame.cpp
# GUI - instance group view
gui/groupview/Group.cpp
gui/groupview/Group.h
gui/groupview/GroupedProxyModel.cpp
gui/groupview/GroupedProxyModel.h
gui/groupview/GroupView.cpp
gui/groupview/GroupView.h
gui/groupview/InstanceDelegate.cpp
gui/groupview/InstanceDelegate.h
# Base classes and infrastructure
logic/BaseVersion.h
logic/MinecraftVersion.h
logic/InstanceFactory.h
logic/InstanceFactory.cpp
logic/BaseInstance.h
logic/BaseInstance.cpp
logic/BaseInstance_p.h
logic/MinecraftProcess.h
logic/MinecraftProcess.cpp
logic/Mod.h
logic/Mod.cpp
logic/ModList.h
logic/ModList.cpp
# Basic instance launcher for starting from terminal
logic/InstanceLauncher.h
logic/InstanceLauncher.cpp
# JSON parsing helpers
logic/MMCJson.h
logic/MMCJson.cpp
# network stuffs
logic/net/NetAction.h
logic/net/MD5EtagDownload.h
logic/net/MD5EtagDownload.cpp
logic/net/ByteArrayDownload.h
logic/net/ByteArrayDownload.cpp
logic/net/CacheDownload.h
logic/net/CacheDownload.cpp
logic/net/ForgeMirrors.h
logic/net/ForgeMirrors.cpp
logic/net/ForgeXzDownload.h
logic/net/ForgeXzDownload.cpp
logic/net/NetJob.h
logic/net/NetJob.cpp
logic/net/HttpMetaCache.h
logic/net/HttpMetaCache.cpp
logic/net/PasteUpload.h
logic/net/PasteUpload.cpp
logic/net/URLConstants.h
logic/net/URLConstants.cpp
# Yggdrasil login stuff
logic/auth/AuthSession.h
logic/auth/AuthSession.cpp
logic/auth/MojangAccountList.h
logic/auth/MojangAccountList.cpp
logic/auth/MojangAccount.h
logic/auth/MojangAccount.cpp
logic/auth/YggdrasilTask.h
logic/auth/YggdrasilTask.cpp
logic/auth/flows/AuthenticateTask.h
logic/auth/flows/AuthenticateTask.cpp
logic/auth/flows/RefreshTask.cpp
logic/auth/flows/RefreshTask.cpp
logic/auth/flows/ValidateTask.h
logic/auth/flows/ValidateTask.cpp
# Update system
logic/updater/UpdateChecker.h
logic/updater/UpdateChecker.cpp
logic/updater/DownloadUpdateTask.h
logic/updater/DownloadUpdateTask.cpp
logic/updater/NotificationChecker.h
logic/updater/NotificationChecker.cpp
# News System
logic/news/NewsChecker.h
logic/news/NewsChecker.cpp
logic/news/NewsEntry.h
logic/news/NewsEntry.cpp
# Status system
logic/status/StatusChecker.h
logic/status/StatusChecker.cpp
# legacy instances
logic/LegacyInstance.h
logic/LegacyInstance.cpp
logic/LegacyInstance_p.h
logic/LegacyUpdate.h
logic/LegacyUpdate.cpp
logic/LegacyForge.h
logic/LegacyForge.cpp
# OneSix instances
logic/OneSixUpdate.h
logic/OneSixUpdate.cpp
logic/OneSixInstance.h
logic/OneSixInstance.cpp
logic/OneSixInstance_p.h
# OneSix version json infrastructure
logic/OneSixVersionBuilder.h
logic/OneSixVersionBuilder.cpp
logic/VersionFile.h
logic/VersionFile.cpp
logic/VersionFinal.h
logic/VersionFinal.cpp
logic/OneSixLibrary.h
logic/OneSixLibrary.cpp
logic/OneSixRule.h
logic/OneSixRule.cpp
logic/OpSys.h
logic/OpSys.cpp
# Mod installers
logic/BaseInstaller.h
logic/BaseInstaller.cpp
logic/ForgeInstaller.h
logic/ForgeInstaller.cpp
logic/LiteLoaderInstaller.h
logic/LiteLoaderInstaller.cpp
# Nostalgia
logic/NostalgiaInstance.h
logic/NostalgiaInstance.cpp
# FTB
logic/OneSixFTBInstance.h
logic/OneSixFTBInstance.cpp
logic/LegacyFTBInstance.h
logic/LegacyFTBInstance.cpp
# Lists
logic/lists/InstanceList.h
logic/lists/InstanceList.cpp
logic/lists/BaseVersionList.h
logic/lists/BaseVersionList.cpp
logic/lists/MinecraftVersionList.h
logic/lists/MinecraftVersionList.cpp
logic/lists/LwjglVersionList.h
logic/lists/LwjglVersionList.cpp
logic/lists/ForgeVersionList.h
logic/lists/ForgeVersionList.cpp
logic/lists/JavaVersionList.h
logic/lists/JavaVersionList.cpp
logic/lists/LiteLoaderVersionList.h
logic/lists/LiteLoaderVersionList.cpp
# the screenshots feature
logic/screenshots/Screenshot.h
logic/screenshots/Screenshot.cpp
logic/screenshots/ScreenshotList.h
logic/screenshots/ScreenshotList.cpp
logic/screenshots/ImgurUpload.h
logic/screenshots/ImgurUpload.cpp
logic/screenshots/ImgurAlbumCreation.h
logic/screenshots/ImgurAlbumCreation.cpp
# Icons
logic/icons/MMCIcon.h
logic/icons/MMCIcon.cpp
logic/icons/IconList.h
logic/icons/IconList.cpp
# misc model/view
logic/EnabledItemFilter.h
logic/EnabledItemFilter.cpp
# Tasks
logic/tasks/ProgressProvider.h
logic/tasks/Task.h
logic/tasks/Task.cpp
logic/tasks/ThreadTask.h
logic/tasks/ThreadTask.cpp
logic/tasks/SequentialTask.h
logic/tasks/SequentialTask.cpp
# Utilities
logic/JavaChecker.h
logic/JavaChecker.cpp
logic/JavaUtils.h
logic/JavaUtils.cpp
logic/NagUtils.h
logic/NagUtils.cpp
logic/SkinUtils.h
logic/SkinUtils.cpp
logic/JavaCheckerJob.h
logic/JavaCheckerJob.cpp
# Assets
logic/assets/AssetsMigrateTask.h
logic/assets/AssetsMigrateTask.cpp
logic/assets/AssetsUtils.h
logic/assets/AssetsUtils.cpp
# Tools
logic/tools/BaseExternalTool.h
logic/tools/BaseExternalTool.cpp
logic/tools/MCEditTool.h
logic/tools/MCEditTool.cpp
logic/tools/BaseProfiler.h
logic/tools/BaseProfiler.cpp
logic/tools/JProfiler.h
logic/tools/JProfiler.cpp
logic/tools/JVisualVM.h
logic/tools/JVisualVM.cpp
)
######## UIs ########
SET(MULTIMC_UIS
# Windows
gui/MainWindow.ui
gui/ConsoleWindow.ui
# Dialogs
gui/dialogs/SettingsDialog.ui
gui/dialogs/CopyInstanceDialog.ui
gui/dialogs/NewInstanceDialog.ui
gui/dialogs/AboutDialog.ui
gui/dialogs/VersionSelectDialog.ui
gui/dialogs/LwjglSelectDialog.ui
gui/dialogs/InstanceSettings.ui
gui/dialogs/ProgressDialog.ui
gui/dialogs/IconPickerDialog.ui
gui/dialogs/LegacyModEditDialog.ui
gui/dialogs/OneSixModEditDialog.ui
gui/dialogs/EditNotesDialog.ui
gui/dialogs/AccountListDialog.ui
gui/dialogs/AccountSelectDialog.ui
gui/dialogs/EditAccountDialog.ui
gui/dialogs/LoginDialog.ui
gui/dialogs/UpdateDialog.ui
gui/dialogs/ScreenshotDialog.ui
gui/dialogs/NotificationDialog.ui
# Widgets/other
gui/widgets/MCModInfoFrame.ui
)
set(FILES_TO_TRANSLATE)
foreach(file ${MULTIMC_SOURCES})
get_filename_component(absfile "${file}" ABSOLUTE)
list(APPEND FILES_TO_TRANSLATE "${absfile}")
endforeach()
foreach(file ${MULTIMC_UIS})
get_filename_component(absfile "${file}" ABSOLUTE)
list(APPEND FILES_TO_TRANSLATE "${absfile}")
endforeach()
set(MULTIMC_QRCS
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/instances/instances.qrc
)
######## Windows resource files ########
if(WIN32)
set(MULTIMC_RCS resources/multimc.rc)
endif()
####### X11 Stuff #######
if(UNIX AND NOT APPLE)
set(MultiMC_QT_ADDITIONAL_MODULES ${MultiMC_QT_ADDITIONAL_MODULES} X11Extras)
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} xcb)
list(APPEND MULTIMC_SOURCES gui/Platform_X11.cpp)
else()
list(APPEND MULTIMC_SOURCES gui/Platform_Other.cpp)
endif()
################################ COMPILE ################################
# Link additional libraries
if(WIN32)
set(MultiMC_LINK_ADDITIONAL_LIBS ${MultiMC_LINK_ADDITIONAL_LIBS} Qt5::WinMain)
endif(WIN32)
# Tell CMake that MultiMCLauncher.jar is generated.
#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/launcher/MultiMCLauncher.jar GENERATED)
#SET_SOURCE_FILES_PROPERTIES(${PROJECT_BINARY_DIR}/depends/javacheck/JavaCheck.jar GENERATED)
# Qt 5 stuff
qt5_wrap_ui(MULTIMC_UI ${MULTIMC_UIS})
qt5_add_resources(MULTIMC_RESOURCES ${MULTIMC_QRCS})
# Add common library
add_library(MultiMC_common STATIC ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_RESOURCES})
# Add executable
add_executable(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS})
# Link
target_link_libraries(MultiMC MultiMC_common)
target_link_libraries(MultiMC_common xz-embedded unpack200 quazip libUtil libSettings ${MultiMC_LINK_ADDITIONAL_LIBS})
qt5_use_modules(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
qt5_use_modules(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES})
################################ INSTALLATION AND PACKAGING ################################
######## Install ########
#### Executable ####
if(APPLE AND UNIX) ## OSX
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
)
elseif(UNIX) ## LINUX and similar
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
)
install(PROGRAMS package/linux/MultiMC DESTINATION .)
elseif(WIN32) ## WINDOWS
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
LIBRARY DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION . COMPONENT Runtime
)
endif()
#### Dist package logic ####
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|svg|tiff|mng" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
)
else()
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|svg|tiff|mng" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
if(APPLE)
# Accessible plugin to make buttons look decent on osx
install(
DIRECTORY "${QT_PLUGINS_DIR}/accessible"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "quick" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
endif()
endif()
# qtconf
install(
CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
"
COMPONENT Runtime
)
# ICNS file for OS X
if(APPLE)
install(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
@ONLY
)
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
######## Package ########
# Package with CPack
if(UNIX)
if(APPLE)
set(CPACK_GENERATOR "ZIP")
else()
set(CPACK_GENERATOR "TGZ")
endif()
elseif(WIN32)
set(CPACK_GENERATOR "ZIP")
endif()
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
set(CPACK_PACKAGE_NAME "MultiMC 5")
set(CPACK_PACKAGE_VENDOR "")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MultiMC - Minecraft launcher and management tool.")
set(CPACK_PACKAGE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
set(CPACK_PACKAGE_VERSION_MAJOR ${MultiMC_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${MultiMC_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${MultiMC_VERSION_REV})
if(CPACK_GENERATOR STREQUAL "NSIS")
set(CPACK_PACKAGE_FILE_NAME "Setup-MultiMC")
else()
set(CPACK_PACKAGE_FILE_NAME "MultiMC")
endif()
if(WIN32)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "MultiMC 5")
endif()
include(CPack)
# Add the GUI -> Logic connection header
add_subdirectory(depends/iconfix)
include_directories(${ICONFIX_INCLUDE_DIR})
include(Coverity)
include_directories(${PROJECT_BINARY_DIR}/include)
############################### Built Artifacts ###############################
# Translations
add_subdirectory(translations)
# Tests
add_subdirectory(tests)
add_subdirectory(logic)
add_subdirectory(application)
add_subdirectory(mmc_updater)

View File

@@ -1,6 +1,6 @@
![MultiMC](http://i.imgur.com/QJXbz.png)
MultiMC 5 [![Build Status](https://travis-ci.org/MultiMC/MultiMC5.svg?branch=develop)](https://travis-ci.org/MultiMC/MultiMC5)
MultiMC 5 [![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 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.
@@ -9,7 +9,7 @@ MultiMC is a custom launcher for Minecraft that allows you to easily manage mult
Check [BUILD.md](BUILD.md) for build instructions.
## Contributing
The repository is currently managed by @peterix and @drayshak - we're the ones likely to review pull requests. If you'd like to contribute to the project please talk to us on IRC (Esper/#MultiMC) first! This helps us organise ideas and keep in contact with you, and we're unlikely to accept anything blindly.
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.
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.
@@ -22,7 +22,7 @@ Apache covers reasonable use for the name - a mention of the project's origins i
## License
Copyright &copy; 2013 MultiMC Contributors
Copyright &copy; 2013-2015 MultiMC Contributors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this program except in compliance with the License. You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0).

View File

@@ -1,4 +1,5 @@
#include "BuildConfig.h"
#include <QObject>
Config BuildConfig;
@@ -9,16 +10,7 @@ Config::Config()
VERSION_MINOR = @MultiMC_VERSION_MINOR@;
VERSION_HOTFIX = @MultiMC_VERSION_HOTFIX@;
VERSION_BUILD = @MultiMC_VERSION_BUILD@;
VERSION_TYPE = "@MultiMC_VERSION_TYPE@";
if(VERSION_TYPE == "Release")
versionTypeEnum = Release;
else if(VERSION_TYPE == "ReleaseCandidate")
versionTypeEnum = ReleaseCandidate;
else if(VERSION_TYPE == "Development")
versionTypeEnum = Development;
else
versionTypeEnum = Custom;
VERSION_CHANNEL = "@MultiMC_VERSION_CHANNEL@";
BUILD_PLATFORM = "@MultiMC_BUILD_PLATFORM@";
CHANLIST_URL = "@MultiMC_CHANLIST_URL@";
@@ -33,22 +25,6 @@ Config::Config()
NEWS_RSS_URL = "@MultiMC_NEWS_RSS_URL@";
}
QString Config::versionTypeName() const
{
switch (versionTypeEnum)
{
case Release:
return "Stable Release";
case ReleaseCandidate:
return "Release Candidate";
case Development:
return "Development";
case Custom:
default:
return "Custom";
}
}
QString Config::printableVersionString() const
{
QString vstr = QString("%1.%2").arg(QString::number(VERSION_MAJOR), QString::number(VERSION_MINOR));
@@ -56,8 +32,25 @@ QString Config::printableVersionString() const
if (VERSION_HOTFIX > 0) vstr += "." + QString::number(VERSION_HOTFIX);
// If the build is a development build or release candidate, add that info to the end.
if (versionTypeEnum == Development) vstr += "-dev" + QString::number(VERSION_BUILD);
else if (versionTypeEnum == ReleaseCandidate) vstr += "-rc" + QString::number(VERSION_BUILD);
if(VERSION_CHANNEL == "stable")
{
return vstr;
}
else if(VERSION_CHANNEL == "develop")
{
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);
}
return vstr;
}

View File

@@ -16,24 +16,6 @@ public:
int VERSION_HOTFIX;
/// The build number.
int VERSION_BUILD;
/// The build type, as specified at build time.
QString VERSION_TYPE;
/// The build type, transformed.
enum Type
{
/// Version type for stable release builds.
Release,
/// Version type for release candidates.
ReleaseCandidate,
/// Version type for development builds.
Development,
/// Version type for custom builds. This is the default when no version type is specified.
Custom
} versionTypeEnum;
/**
* The version channel
@@ -76,11 +58,6 @@ public:
* \return The version number in string format (major.minor.revision.build).
*/
QString printableVersionString() const;
/**
* returns a string representation of the version channel type, suitable for printing.
*/
QString versionTypeName() const;
};
extern Config BuildConfig;

493
application/CMakeLists.txt Normal file
View File

@@ -0,0 +1,493 @@
project(MultiMC-Application)
######## Set URLs ########
set(MultiMC_NEWS_RSS_URL "http://multimc.org/rss.xml" CACHE STRING "URL to fetch MultiMC's news RSS feed from.")
######## Set version numbers ########
set(MultiMC_VERSION_MAJOR 0)
set(MultiMC_VERSION_MINOR 4)
set(MultiMC_VERSION_HOTFIX 6)
# 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)
message(STATUS "Git commit: ${MultiMC_GIT_COMMIT}")
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()
######## Configure header ########
configure_file("${PROJECT_SOURCE_DIR}/BuildConfig.cpp.in" "${PROJECT_BINARY_DIR}/BuildConfig.cpp")
######## Packaging/install paths setup ########
if(UNIX AND APPLE)
set(BINARY_DEST_DIR MultiMC.app/Contents/MacOS)
set(PLUGIN_DEST_DIR MultiMC.app/Contents/MacOS)
set(QTCONF_DEST_DIR MultiMC.app/Contents/Resources)
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.app")
set(MACOSX_BUNDLE_BUNDLE_NAME "MultiMC")
set(MACOSX_BUNDLE_INFO_STRING "MultiMC Minecraft launcher and management utility.")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.multimc.MultiMC5")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_HOTFIX}.${MultiMC_VERSION_BUILD}")
set(MACOSX_BUNDLE_ICON_FILE MultiMC.icns)
set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2015 MultiMC Contributors")
elseif(UNIX)
set(BINARY_DEST_DIR bin)
set(PLUGIN_DEST_DIR plugins)
set(QTCONF_DEST_DIR .)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/MultiMC")
elseif(WIN32)
set(BINARY_DEST_DIR .)
set(PLUGIN_DEST_DIR .)
set(QTCONF_DEST_DIR .)
set(APPS "\${CMAKE_INSTALL_PREFIX}/MultiMC.exe")
endif()
# directories to look for dependencies
set(DIRS ${QT_LIBS_DIR} ${QT_LIBEXECS_DIR})
################################ FILES ################################
######## Sources and headers ########
SET(MULTIMC_SOURCES
# Application base
main.cpp
MultiMC.h
MultiMC.cpp
BuildConfig.h
${PROJECT_BINARY_DIR}/BuildConfig.cpp
# GUI - general utilities
GuiUtil.h
GuiUtil.cpp
ColumnResizer.h
ColumnResizer.cpp
InstanceProxyModel.h
InstanceProxyModel.cpp
VersionProxyModel.h
VersionProxyModel.cpp
# GUI - windows
MainWindow.h
MainWindow.cpp
ConsoleWindow.h
ConsoleWindow.cpp
# page provider for instances
InstancePageProvider.h
InstancePageProvider.cpp
# Common java checking UI
JavaCommon.h
JavaCommon.cpp
# GUI - page dialog pages
pages/BasePage.h
pages/VersionPage.cpp
pages/VersionPage.h
pages/TexturePackPage.h
pages/ResourcePackPage.h
pages/ModFolderPage.cpp
pages/ModFolderPage.h
pages/NotesPage.cpp
pages/NotesPage.h
pages/LogPage.cpp
pages/LogPage.h
pages/InstanceSettingsPage.cpp
pages/InstanceSettingsPage.h
pages/ScreenshotsPage.cpp
pages/ScreenshotsPage.h
pages/OtherLogsPage.cpp
pages/OtherLogsPage.h
pages/LegacyJarModPage.cpp
pages/LegacyJarModPage.h
pages/LegacyUpgradePage.cpp
pages/LegacyUpgradePage.h
# GUI - global settings pages
pages/global/AccountListPage.cpp
pages/global/AccountListPage.h
pages/global/ExternalToolsPage.cpp
pages/global/ExternalToolsPage.h
pages/global/JavaPage.cpp
pages/global/JavaPage.h
pages/global/MinecraftPage.cpp
pages/global/MinecraftPage.h
pages/global/MultiMCPage.cpp
pages/global/MultiMCPage.h
pages/global/ProxyPage.cpp
pages/global/ProxyPage.h
# GUI - dialogs
dialogs/AboutDialog.cpp
dialogs/AboutDialog.h
dialogs/AccountSelectDialog.cpp
dialogs/AccountSelectDialog.h
dialogs/CopyInstanceDialog.cpp
dialogs/CopyInstanceDialog.h
dialogs/CustomMessageBox.cpp
dialogs/CustomMessageBox.h
dialogs/EditAccountDialog.cpp
dialogs/EditAccountDialog.h
dialogs/ExportInstanceDialog.cpp
dialogs/ExportInstanceDialog.h
dialogs/IconPickerDialog.cpp
dialogs/IconPickerDialog.h
dialogs/LoginDialog.cpp
dialogs/LoginDialog.h
dialogs/ModEditDialogCommon.cpp
dialogs/ModEditDialogCommon.h
dialogs/NewInstanceDialog.cpp
dialogs/NewInstanceDialog.h
dialogs/NotificationDialog.cpp
dialogs/NotificationDialog.h
pagedialog/PageDialog.cpp
pagedialog/PageDialog.h
dialogs/ProgressDialog.cpp
dialogs/ProgressDialog.h
dialogs/UpdateDialog.cpp
dialogs/UpdateDialog.h
dialogs/VersionSelectDialog.cpp
dialogs/VersionSelectDialog.h
# GUI - widgets
widgets/Common.cpp
widgets/Common.h
widgets/IconLabel.cpp
widgets/IconLabel.h
widgets/LabeledToolButton.cpp
widgets/LabeledToolButton.h
widgets/LineSeparator.cpp
widgets/LineSeparator.h
widgets/MCModInfoFrame.cpp
widgets/MCModInfoFrame.h
widgets/ModListView.cpp
widgets/ModListView.h
widgets/PageContainer.cpp
widgets/PageContainer.h
widgets/PageContainer_p.h
widgets/ServerStatus.cpp
widgets/ServerStatus.h
widgets/VersionListView.cpp
widgets/VersionListView.h
# GUI - instance group view
groupview/GroupedProxyModel.cpp
groupview/GroupedProxyModel.h
groupview/GroupView.cpp
groupview/GroupView.h
groupview/InstanceDelegate.cpp
groupview/InstanceDelegate.h
groupview/VisualGroup.cpp
groupview/VisualGroup.h
)
######## UIs ########
SET(MULTIMC_UIS
# Option pages
pages/VersionPage.ui
pages/ModFolderPage.ui
pages/LogPage.ui
pages/InstanceSettingsPage.ui
pages/NotesPage.ui
pages/ScreenshotsPage.ui
pages/OtherLogsPage.ui
pages/LegacyJarModPage.ui
pages/LegacyUpgradePage.ui
# Global settings pages
pages/global/AccountListPage.ui
pages/global/ExternalToolsPage.ui
pages/global/JavaPage.ui
pages/global/MinecraftPage.ui
pages/global/MultiMCPage.ui
pages/global/ProxyPage.ui
# Dialogs
dialogs/CopyInstanceDialog.ui
dialogs/NewInstanceDialog.ui
dialogs/AboutDialog.ui
dialogs/VersionSelectDialog.ui
dialogs/ProgressDialog.ui
dialogs/IconPickerDialog.ui
dialogs/AccountSelectDialog.ui
dialogs/EditAccountDialog.ui
dialogs/ExportInstanceDialog.ui
dialogs/LoginDialog.ui
dialogs/UpdateDialog.ui
dialogs/NotificationDialog.ui
# Widgets/other
widgets/MCModInfoFrame.ui
)
set(MULTIMC_QRCS
resources/backgrounds/backgrounds.qrc
resources/multimc/multimc.qrc
resources/pe_dark/pe_dark.qrc
resources/pe_light/pe_light.qrc
resources/pe_colored/pe_colored.qrc
resources/pe_blue/pe_blue.qrc
resources/OSX/OSX.qrc
resources/iOS/iOS.qrc
resources/instances/instances.qrc
resources/versions/versions.qrc
resources/certs/certs.qrc
)
######## 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})
################################ INSTALLATION AND PACKAGING ################################
######## Install ########
#### Executable ####
if(APPLE AND UNIX) ## OSX
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime
)
elseif(UNIX) ## LINUX and similar
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION bin COMPONENT Runtime
)
install(PROGRAMS package/linux/MultiMC DESTINATION .)
elseif(WIN32) ## WINDOWS
install(TARGETS MultiMC
BUNDLE DESTINATION . COMPONENT Runtime
LIBRARY DESTINATION . COMPONENT Runtime
RUNTIME DESTINATION . COMPONENT Runtime
)
endif()
#### Java bits ####
install_jar(JavaCheck "${BINARY_DEST_DIR}/jars")
install_jar(NewLaunch "${BINARY_DEST_DIR}/jars")
#### Dist package logic ####
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
)
else()
# Image formats
install(
DIRECTORY "${QT_PLUGINS_DIR}/imageformats"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "tga|tiff|mng" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
# Icon engines
install(
DIRECTORY "${QT_PLUGINS_DIR}/iconengines"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "fontawesome" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
# Platform plugins
install(
DIRECTORY "${QT_PLUGINS_DIR}/platforms"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "minimal|linuxfb|offscreen" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
if(APPLE)
# Accessible plugin to make buttons look decent on osx
install(
DIRECTORY "${QT_PLUGINS_DIR}/accessible"
DESTINATION ${PLUGIN_DEST_DIR}
COMPONENT Runtime
REGEX "quick" EXCLUDE
REGEX "d\\." EXCLUDE
REGEX "_debug\\." EXCLUDE
)
endif()
endif()
# qtconf
install(
CODE "
file(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\")
"
COMPONENT Runtime
)
# ICNS file for OS X
if(APPLE)
install(FILES resources/MultiMC.icns DESTINATION MultiMC.app/Contents/Resources)
endif()
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake"
@ONLY
)
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime)
######## Package ########
# Package with CPack
if(UNIX)
if(APPLE)
set(CPACK_GENERATOR "ZIP")
else()
set(CPACK_GENERATOR "TGZ")
endif()
elseif(WIN32)
set(CPACK_GENERATOR "ZIP")
endif()
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
set(CPACK_PACKAGE_NAME "MultiMC 5")
set(CPACK_PACKAGE_VENDOR "")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MultiMC - Minecraft launcher and management tool.")
set(CPACK_PACKAGE_VERSION "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}.${MultiMC_VERSION_REV}.${MultiMC_VERSION_BUILD}")
set(CPACK_PACKAGE_VERSION_MAJOR ${MultiMC_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${MultiMC_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${MultiMC_VERSION_REV})
set(CPACK_PACKAGE_FILE_NAME "MultiMC")
include(CPack)

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
* License: LGPL v2.1 or later (see COPYING)
*/
#include "ColumnResizer.h"
#include <QDebug>
#include <QEvent>
#include <QFormLayout>
#include <QGridLayout>
#include <QTimer>
#include <QWidget>
class FormLayoutWidgetItem : public QWidgetItem
{
public:
FormLayoutWidgetItem(QWidget* widget, QFormLayout* formLayout, QFormLayout::ItemRole itemRole)
: QWidgetItem(widget)
, m_width(-1)
, m_formLayout(formLayout)
, m_itemRole(itemRole)
{}
QSize sizeHint() const
{
QSize size = QWidgetItem::sizeHint();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize minimumSize() const
{
QSize size = QWidgetItem::minimumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
QSize maximumSize() const
{
QSize size = QWidgetItem::maximumSize();
if (m_width != -1) {
size.setWidth(m_width);
}
return size;
}
void setWidth(int width)
{
if (width != m_width) {
m_width = width;
invalidate();
}
}
void setGeometry(const QRect& _rect)
{
QRect rect = _rect;
int width = widget()->sizeHint().width();
if (m_itemRole == QFormLayout::LabelRole && m_formLayout->labelAlignment() & Qt::AlignRight) {
rect.setLeft(rect.right() - width);
}
QWidgetItem::setGeometry(rect);
}
QFormLayout* formLayout() const
{
return m_formLayout;
}
private:
int m_width;
QFormLayout* m_formLayout;
QFormLayout::ItemRole m_itemRole;
};
typedef QPair<QGridLayout*, int> GridColumnInfo;
class ColumnResizerPrivate
{
public:
ColumnResizerPrivate(ColumnResizer* q_ptr)
: q(q_ptr)
, m_updateTimer(new QTimer(q))
{
m_updateTimer->setSingleShot(true);
m_updateTimer->setInterval(0);
QObject::connect(m_updateTimer, SIGNAL(timeout()), q, SLOT(updateWidth()));
}
void scheduleWidthUpdate()
{
m_updateTimer->start();
}
ColumnResizer* q;
QTimer* m_updateTimer;
QList<QWidget*> m_widgets;
QList<FormLayoutWidgetItem*> m_wrWidgetItemList;
QList<GridColumnInfo> m_gridColumnInfoList;
};
ColumnResizer::ColumnResizer(QObject* parent)
: QObject(parent)
, d(new ColumnResizerPrivate(this))
{}
ColumnResizer::~ColumnResizer()
{
delete d;
}
void ColumnResizer::addWidget(QWidget* widget)
{
d->m_widgets.append(widget);
widget->installEventFilter(this);
d->scheduleWidthUpdate();
}
void ColumnResizer::updateWidth()
{
int width = 0;
Q_FOREACH(QWidget* widget, d->m_widgets) {
width = qMax(widget->sizeHint().width(), width);
}
Q_FOREACH(FormLayoutWidgetItem* item, d->m_wrWidgetItemList) {
item->setWidth(width);
item->formLayout()->update();
}
Q_FOREACH(GridColumnInfo info, d->m_gridColumnInfoList) {
info.first->setColumnMinimumWidth(info.second, width);
}
}
bool ColumnResizer::eventFilter(QObject*, QEvent* event)
{
if (event->type() == QEvent::Resize) {
d->scheduleWidthUpdate();
}
return false;
}
void ColumnResizer::addWidgetsFromLayout(QLayout* layout, int column)
{
Q_ASSERT(column >= 0);
QGridLayout* gridLayout = qobject_cast<QGridLayout*>(layout);
QFormLayout* formLayout = qobject_cast<QFormLayout*>(layout);
if (gridLayout) {
addWidgetsFromGridLayout(gridLayout, column);
} else if (formLayout) {
if (column > QFormLayout::SpanningRole) {
qCritical() << "column should not be more than" << QFormLayout::SpanningRole << "for QFormLayout";
return;
}
QFormLayout::ItemRole role = static_cast<QFormLayout::ItemRole>(column);
addWidgetsFromFormLayout(formLayout, role);
} else {
qCritical() << "Don't know how to handle layout" << layout;
}
}
void ColumnResizer::addWidgetsFromGridLayout(QGridLayout* layout, int column)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem* item = layout->itemAtPosition(row, column);
if (!item) {
continue;
}
QWidget* widget = item->widget();
if (!widget) {
continue;
}
addWidget(widget);
}
d->m_gridColumnInfoList << GridColumnInfo(layout, column);
}
void ColumnResizer::addWidgetsFromFormLayout(QFormLayout* layout, QFormLayout::ItemRole role)
{
for (int row = 0; row < layout->rowCount(); ++row) {
QLayoutItem* item = layout->itemAt(row, role);
if (!item) {
continue;
}
QWidget* widget = item->widget();
if (!widget) {
continue;
}
layout->removeItem(item);
delete item;
FormLayoutWidgetItem* newItem = new FormLayoutWidgetItem(widget, layout, role);
layout->setItem(row, role, newItem);
addWidget(widget);
d->m_wrWidgetItemList << newItem;
}
}
#include <ColumnResizer.moc>
// vi: ts=4 sw=4 et

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2011 Aurélien Gâteau <agateau@kde.org>
* License: LGPL v2.1 or later (see COPYING)
*/
#pragma once
#include <QFormLayout>
#include <QtCore/QObject>
#include <QtCore/QList>
class QEvent;
class QGridLayout;
class QLayout;
class QWidget;
class ColumnResizerPrivate;
class ColumnResizer : public QObject
{
Q_OBJECT
public:
ColumnResizer(QObject* parent = 0);
~ColumnResizer();
void addWidget(QWidget* widget);
void addWidgetsFromLayout(QLayout*, int column);
void addWidgetsFromGridLayout(QGridLayout*, int column);
void addWidgetsFromFormLayout(QFormLayout*, QFormLayout::ItemRole role);
private Q_SLOTS:
void updateWidth();
protected:
bool eventFilter(QObject*, QEvent* event);
private:
ColumnResizerPrivate* const d;
};

View File

@@ -0,0 +1,260 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ConsoleWindow.h"
#include "MultiMC.h"
#include <QScrollBar>
#include <QMessageBox>
#include <QSystemTrayIcon>
#include <QHBoxLayout>
#include <QPushButton>
#include <qlayoutitem.h>
#include <QCloseEvent>
#include <Platform.h>
#include <dialogs/CustomMessageBox.h>
#include <dialogs/ProgressDialog.h>
#include "widgets/PageContainer.h"
#include "pages/LogPage.h"
#include "InstancePageProvider.h"
#include "icons/IconList.h"
class LogPageProvider : public BasePageProvider
{
public:
LogPageProvider(BasePageProviderPtr parent, BasePage * log_page)
{
m_parent = parent;
m_log_page = log_page;
}
virtual QString dialogTitle() {return "Fake";};
virtual QList<BasePage *> getPages()
{
auto pages = m_parent->getPages();
pages.prepend(m_log_page);
return pages;
}
private:
BasePageProviderPtr m_parent;
BasePage * m_log_page;
};
ConsoleWindow::ConsoleWindow(BaseProcess *process, QWidget *parent)
: QMainWindow(parent), m_proc(process)
{
MultiMCPlatform::fixWM_CLASS(this);
setAttribute(Qt::WA_DeleteOnClose);
auto instance = m_proc->instance();
auto icon = ENV.icons()->getIcon(instance->iconKey());
QString windowTitle = tr("Console window for ") + instance->name();
// Set window properties
{
setWindowIcon(icon);
setWindowTitle(windowTitle);
}
// Add page container
{
auto mainLayout = new QVBoxLayout;
auto provider = std::make_shared<InstancePageProvider>(m_proc->instance());
auto baseprovider = std::dynamic_pointer_cast<BasePageProvider>(provider);
auto proxy_provider = std::make_shared<LogPageProvider>(baseprovider, new LogPage(m_proc));
m_container = new PageContainer(proxy_provider, "console", this);
mainLayout->addWidget(m_container);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0,0,0,0);
setLayout(mainLayout);
setCentralWidget(m_container);
}
// Add custom buttons to the page container layout.
{
auto horizontalLayout = new QHBoxLayout();
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
horizontalLayout->setContentsMargins(6, -1, 6, -1);
auto btnHelp = new QPushButton();
btnHelp->setText(tr("Help"));
horizontalLayout->addWidget(btnHelp);
connect(btnHelp, SIGNAL(clicked(bool)), m_container, SLOT(help()));
auto spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
horizontalLayout->addSpacerItem(spacer);
m_killButton = new QPushButton();
m_killButton->setText(tr("Kill Minecraft"));
horizontalLayout->addWidget(m_killButton);
connect(m_killButton, SIGNAL(clicked(bool)), SLOT(on_btnKillMinecraft_clicked()));
m_closeButton = new QPushButton();
m_closeButton->setText(tr("Close"));
horizontalLayout->addWidget(m_closeButton);
connect(m_closeButton, SIGNAL(clicked(bool)), SLOT(on_closeButton_clicked()));
m_container->addButtons(horizontalLayout);
}
// restore window state
{
auto base64State = MMC->settings()->get("ConsoleWindowState").toByteArray();
restoreState(QByteArray::fromBase64(base64State));
auto base64Geometry = MMC->settings()->get("ConsoleWindowGeometry").toByteArray();
restoreGeometry(QByteArray::fromBase64(base64Geometry));
}
// Set up tray icon
{
m_trayIcon = new QSystemTrayIcon(icon, this);
m_trayIcon->setToolTip(windowTitle);
connect(m_trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
m_trayIcon->show();
}
// Set up signal connections
connect(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)));
setMayClose(false);
if (m_proc->instance()->settings().get("ShowConsole").toBool())
{
show();
}
}
void ConsoleWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
{
switch (reason)
{
case QSystemTrayIcon::Trigger:
{
toggleConsole();
}
default:
return;
}
}
void ConsoleWindow::on_closeButton_clicked()
{
close();
}
void ConsoleWindow::setMayClose(bool mayclose)
{
if(mayclose)
m_closeButton->setText(tr("Close"));
else
m_closeButton->setText(tr("Hide"));
m_mayclose = mayclose;
}
void ConsoleWindow::toggleConsole()
{
if (isVisible())
{
if(!isActiveWindow())
{
activateWindow();
return;
}
hide();
}
else
{
show();
}
}
void ConsoleWindow::closeEvent(QCloseEvent *event)
{
if (!m_mayclose)
{
toggleConsole();
event->ignore();
}
else if(m_container->requestClose(event))
{
MMC->settings()->set("ConsoleWindowState", saveState().toBase64());
MMC->settings()->set("ConsoleWindowGeometry", saveGeometry().toBase64());
emit isClosing();
m_trayIcon->hide();
event->accept();
}
}
void ConsoleWindow::on_btnKillMinecraft_clicked()
{
m_killButton->setEnabled(false);
auto response = CustomMessageBox::selectable(
this, tr("Kill Minecraft?"),
tr("This can cause the instance to get corrupted and should only be used if Minecraft "
"is frozen for some reason"),
QMessageBox::Question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes)->exec();
if (response == QMessageBox::Yes)
m_proc->killProcess();
else
m_killButton->setEnabled(true);
}
void ConsoleWindow::onEnded(InstancePtr instance, int code, QProcess::ExitStatus status)
{
bool peacefulExit = code == 0 && status != QProcess::CrashExit;
m_killButton->setEnabled(false);
setMayClose(true);
if (instance->settings().get("AutoCloseConsole").toBool())
{
if (peacefulExit)
{
this->close();
return;
}
}
if (!isVisible())
{
show();
}
// Raise Window
if (MMC->settings()->get("RaiseConsole").toBool())
{
raise();
activateWindow();
}
}
void ConsoleWindow::onLaunchFailed(InstancePtr instance)
{
m_killButton->setEnabled(false);
setMayClose(true);
if (!isVisible())
show();
}
ConsoleWindow::~ConsoleWindow()
{
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,20 +17,17 @@
#include <QMainWindow>
#include <QSystemTrayIcon>
#include "logic/MinecraftProcess.h"
namespace Ui
{
class ConsoleWindow;
}
#include "BaseProcess.h"
class QPushButton;
class PageContainer;
class ConsoleWindow : public QMainWindow
{
Q_OBJECT
public:
explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0);
~ConsoleWindow();
explicit ConsoleWindow(BaseProcess *proc, QWidget *parent = 0);
virtual ~ConsoleWindow();
/**
* @brief specify if the window is allowed to close
@@ -39,57 +36,30 @@ public:
*/
void setMayClose(bool mayclose);
private:
/**
* @brief write a colored paragraph
* @param data the string
* @param color the css color name
* this will only insert a single paragraph.
* \n are ignored. a real \n is always appended.
*/
void writeColor(QString text, const char *color, const char *background);
signals:
void isClosing();
void uploadScreenshots();
public
slots:
/**
* @brief write a string
* @param data the string
* @param mode the WriteMode
* lines have to be put through this as a whole!
*/
void write(QString data, MessageLevel::Enum level = MessageLevel::MultiMC);
/**
* @brief clear the text widget
*/
void clear();
private
slots:
void on_closeButton_clicked();
void on_btnKillMinecraft_clicked();
void onEnded(BaseInstance *instance, int code, QProcess::ExitStatus status);
void onLaunchFailed(BaseInstance *instance);
void onEnded(InstancePtr instance, int code, QProcess::ExitStatus status);
void onLaunchFailed(InstancePtr instance);
// FIXME: add handlers for the other MinecraftProcess signals (pre/post launch command
// failures)
void on_btnPaste_clicked();
void iconActivated(QSystemTrayIcon::ActivationReason);
void toggleConsole();
protected:
void closeEvent(QCloseEvent *);
private:
Ui::ConsoleWindow *ui = nullptr;
MinecraftProcess *proc = nullptr;
BaseProcess *m_proc = nullptr;
bool m_mayclose = true;
int m_last_scroll_value = 0;
bool m_scroll_active = true;
QSystemTrayIcon *m_trayIcon = nullptr;
int m_saved_offset = 0;
PageContainer *m_container = nullptr;
QPushButton *m_closeButton = nullptr;
QPushButton *m_killButton = nullptr;
};

101
application/GuiUtil.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "GuiUtil.h"
#include <QClipboard>
#include <QDesktopServices>
#include <QApplication>
#include <QFileDialog>
#include "dialogs/ProgressDialog.h"
#include "net/PasteUpload.h"
#include "dialogs/CustomMessageBox.h"
#include "MultiMC.h"
#include <settings/SettingsObject.h>
void GuiUtil::uploadPaste(const QString &text, QWidget *parentWidget)
{
ProgressDialog dialog(parentWidget);
std::unique_ptr<PasteUpload> paste(new PasteUpload(parentWidget, text));
if (!paste->validateText())
{
CustomMessageBox::selectable(
parentWidget, QObject::tr("Upload failed"),
QObject::tr("The log file is too big. You'll have to upload it manually."),
QMessageBox::Warning)->exec();
return;
}
dialog.exec(paste.get());
if (!paste->successful())
{
CustomMessageBox::selectable(parentWidget, QObject::tr("Upload failed"),
paste->failReason(), QMessageBox::Critical)->exec();
}
else
{
const QString link = paste->pasteLink();
setClipboardText(link);
QDesktopServices::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();
}
}
void GuiUtil::setClipboardText(const QString &text)
{
QApplication::clipboard()->setText(text);
}
QStringList GuiUtil::BrowseForMods(QString context, QString caption, QString filter,
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);
QFileInfo finfo(location);
if (!finfo.exists())
return;
locations.insert(location);
};
f(QStandardPaths::DesktopLocation);
f(QStandardPaths::DocumentsLocation);
f(QStandardPaths::DownloadLocation);
f(QStandardPaths::HomeLocation);
QList<QUrl> urls;
for (auto location : locations)
{
urls.append(QUrl::fromLocalFile(location));
}
urls.append(QUrl::fromLocalFile(modsFolder));
w.setFileMode(QFileDialog::ExistingFiles);
w.setAcceptMode(QFileDialog::AcceptOpen);
w.setNameFilter(filter);
if(savedPaths.contains(context))
{
w.setDirectory(savedPaths[context]);
}
else
{
w.setDirectory(modsFolder);
}
w.setSidebarUrls(urls);
if (w.exec())
{
savedPaths[context] = w.directory().absolutePath();
return w.selectedFiles();
}
savedPaths[context] = w.directory().absolutePath();
return {};
}

10
application/GuiUtil.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include <QWidget>
namespace GuiUtil
{
void uploadPaste(const QString &text, QWidget *parentWidget);
void setClipboardText(const QString &text);
QStringList BrowseForMods(QString context, QString caption, QString filter, QWidget *parentWidget);
}

View File

View File

@@ -0,0 +1,73 @@
#pragma once
#include "minecraft/OneSixInstance.h"
#include "minecraft/LegacyInstance.h"
#include "pages/BasePage.h"
#include "pages/VersionPage.h"
#include "pages/ModFolderPage.h"
#include "pages/ResourcePackPage.h"
#include "pages/TexturePackPage.h"
#include "pages/NotesPage.h"
#include "pages/ScreenshotsPage.h"
#include "pages/InstanceSettingsPage.h"
#include "pages/OtherLogsPage.h"
#include "pages/BasePageProvider.h"
#include "pages/LegacyJarModPage.h"
#include <pathutils.h>
class InstancePageProvider : public QObject, public BasePageProvider
{
Q_OBJECT
public:
explicit InstancePageProvider(InstancePtr parent)
{
inst = parent;
}
virtual ~InstancePageProvider() {};
virtual QList<BasePage *> getPages() override
{
QList<BasePage *> values;
std::shared_ptr<OneSixInstance> onesix = std::dynamic_pointer_cast<OneSixInstance>(inst);
if(onesix)
{
values.append(new VersionPage(onesix.get()));
auto modsPage = new ModFolderPage(onesix.get(), onesix->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
modsPage->setFilter(tr("%1 (*.zip *.jar *.litemod)"));
values.append(modsPage);
values.append(new CoreModFolderPage(onesix.get(), onesix->coreModList(), "coremods", "coremods", tr("Core mods"), "Core-mods"));
values.append(new ResourcePackPage(onesix.get()));
values.append(new TexturePackPage(onesix.get()));
values.append(new NotesPage(onesix.get()));
values.append(new ScreenshotsPage(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()));
auto modsPage = new ModFolderPage(legacy.get(), legacy->loaderModList(), "mods", "loadermods", tr("Loader mods"), "Loader-mods");
modsPage->setFilter(tr("%1 (*.zip *.jar *.litemod)"));
values.append(modsPage);
values.append(new ModFolderPage(legacy.get(), legacy->coreModList(), "coremods", "coremods", tr("Core mods"), "Loader-mods"));
values.append(new TexturePackPage(legacy.get()));
values.append(new NotesPage(legacy.get()));
values.append(new ScreenshotsPage(PathCombine(legacy->minecraftRoot(), "screenshots")));
values.append(new InstanceSettingsPage(legacy.get()));
values.append(new OtherLogsPage(legacy->minecraftRoot()));
return values;
}
return values;
}
virtual QString dialogTitle() override
{
return tr("Edit Instance (%1)").arg(inst->name());
}
protected:
InstancePtr inst;
};

View File

@@ -0,0 +1,23 @@
#include "InstanceProxyModel.h"
#include "MultiMC.h"
#include <BaseInstance.h>
InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(parent)
{
}
bool InstanceProxyModel::subSortLessThan(const QModelIndex &left,
const QModelIndex &right) const
{
BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer());
BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer());
QString sortMode = MMC->settings()->get("InstSortMode").toString();
if (sortMode == "LastLaunch")
{
return pdataLeft->lastLaunch() > pdataRight->lastLaunch();
}
else
{
return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0;
}
}

View File

@@ -0,0 +1,13 @@
#include "groupview/GroupedProxyModel.h"
/**
* A proxy model that is responsible for sorting instances into groups
*/
class InstanceProxyModel : public GroupedProxyModel
{
public:
explicit InstanceProxyModel(QObject *parent = 0);
protected:
virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const;
};

107
application/JavaCommon.cpp Normal file
View File

@@ -0,0 +1,107 @@
#include "JavaCommon.h"
#include "dialogs/CustomMessageBox.h"
#include <MMCStrings.h>
bool JavaCommon::checkJVMArgs(QString jvmargs, QWidget *parent)
{
if (jvmargs.contains("-XX:PermSize=") || jvmargs.contains(QRegExp("-Xm[sx]")))
{
CustomMessageBox::selectable(
parent, QObject::tr("JVM arguments warning"),
QObject::tr("You tried to manually set a JVM memory option (using "
" \"-XX:PermSize\", \"-Xmx\" or \"-Xms\") - there"
" are dedicated boxes for these in the settings (Java"
" tab, in the Memory group at the top).\n"
"Your manual settings will be overridden by the"
" dedicated options.\n"
"This message will be displayed until you remove them"
" from the JVM arguments."),
QMessageBox::Warning)->exec();
return false;
}
return true;
}
void JavaCommon::TestCheck::javaWasOk(JavaCheckResult result)
{
QString text;
text += tr("Java test succeeded!<br />Platform reported: %1<br />Java version "
"reported: %2<br />").arg(result.realPlatform, result.javaVersion);
if (result.errorLog.size())
{
auto htmlError = result.errorLog;
htmlError.replace('\n', "<br />");
text += tr("<br />Warnings:<br /><font color=\"orange\">%1</font>").arg(htmlError);
}
CustomMessageBox::selectable(m_parent, tr("Java test success"), text,
QMessageBox::Information)->show();
}
void JavaCommon::TestCheck::javaArgsWereBad(JavaCheckResult result)
{
auto htmlError = result.errorLog;
QString text;
htmlError.replace('\n', "<br />");
text += tr("The specified java binary didn't work with the arguments you provided:<br />");
text += tr("<font color=\"red\">%1</font>").arg(htmlError);
CustomMessageBox::selectable(m_parent, tr("Java test failure"), text, QMessageBox::Warning)
->show();
}
void JavaCommon::TestCheck::javaBinaryWasBad(JavaCheckResult result)
{
QString text;
text += tr(
"The specified java binary didn't work.<br />You should use the auto-detect feature, "
"or set the path to the java executable.<br />");
CustomMessageBox::selectable(m_parent, tr("Java test failure"), text, QMessageBox::Warning)
->show();
}
void JavaCommon::TestCheck::run()
{
if (!JavaCommon::checkJVMArgs(m_args, m_parent))
{
emit finished();
return;
}
checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinished(JavaCheckResult)));
checker->m_path = m_path;
checker->performCheck();
}
void JavaCommon::TestCheck::checkFinished(JavaCheckResult result)
{
if (!result.valid)
{
javaBinaryWasBad(result);
emit finished();
return;
}
checker.reset(new JavaChecker());
connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this,
SLOT(checkFinishedWithArgs(JavaCheckResult)));
checker->m_path = m_path;
checker->m_args = m_args;
checker->m_minMem = m_minMem;
checker->m_maxMem = m_maxMem;
if (Strings::naturalCompare(result.javaVersion, "1.8", Qt::CaseInsensitive) < 0)
{
checker->m_permGen = m_permGen;
}
checker->performCheck();
}
void JavaCommon::TestCheck::checkFinishedWithArgs(JavaCheckResult result)
{
if (result.valid)
{
javaWasOk(result);
emit finished();
return;
}
javaArgsWereBad(result);
emit finished();
}

46
application/JavaCommon.h Normal file
View File

@@ -0,0 +1,46 @@
#pragma once
#include <java/JavaChecker.h>
class QWidget;
/**
* Common UI bits for the java pages to use.
*/
namespace JavaCommon
{
bool checkJVMArgs(QString args, QWidget *parent);
class TestCheck : public QObject
{
Q_OBJECT
public:
TestCheck(QWidget *parent, QString path, QString args, int minMem, int maxMem, int permGen)
:m_parent(parent), m_path(path), m_args(args), m_minMem(minMem), m_maxMem(maxMem), m_permGen(permGen)
{
}
virtual ~TestCheck() {};
void run();
signals:
void finished();
private:
void javaBinaryWasBad(JavaCheckResult result);
void javaArgsWereBad(JavaCheckResult result);
void javaWasOk(JavaCheckResult result);
private slots:
void checkFinished(JavaCheckResult result);
void checkFinishedWithArgs(JavaCheckResult result);
private:
std::shared_ptr<JavaChecker> checker;
QWidget *m_parent = nullptr;
QString m_path;
QString m_args;
int m_minMem = 0;
int m_maxMem = 0;
int m_permGen = 64;
};
}

2000
application/MainWindow.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,18 +19,21 @@
#include <QProcess>
#include <QTimer>
#include "logic/lists/InstanceList.h"
#include "logic/BaseInstance.h"
#include "logic/auth/MojangAccount.h"
#include <logic/net/NetJob.h>
#include "BaseInstance.h"
#include "auth/MojangAccount.h"
#include "net/NetJob.h"
#include "updater/GoUpdate.h"
class NewsChecker;
class NotificationChecker;
class QToolButton;
class InstanceProxyModel;
class LabeledToolButton;
class QLabel;
class MinecraftProcess;
class ConsoleWindow;
class BaseProfilerFactory;
class GenericPageProvider;
namespace Ui
{
@@ -51,7 +54,7 @@ public:
void openWebPage(QUrl url);
void checkSetDefaultJava();
void checkMigrateLegacyAssets();
void checkInstancePathForProblems();
private
slots:
@@ -81,6 +84,8 @@ slots:
void on_actionSettings_triggered();
void on_actionInstanceSettings_triggered();
void on_actionManageAccounts_triggered();
void on_actionReportBug_triggered();
@@ -101,16 +106,16 @@ slots:
void on_actionDeleteInstance_triggered();
void on_actionExportInstance_triggered();
void on_actionRenameInstance_triggered();
void on_actionMakeDesktopShortcut_triggered();
void on_actionChangeInstMCVersion_triggered();
void on_actionEditInstMods_triggered();
void on_actionEditInstance_triggered();
void on_actionEditInstNotes_triggered();
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.
@@ -133,19 +138,13 @@ slots:
void taskStart();
void taskEnd();
void on_actionChangeInstLWJGLVersion_triggered();
void instanceEnded();
void on_actionInstanceSettings_triggered();
// called when an icon is changed in the icon model.
void iconUpdated(QString);
void showInstanceContextMenu(const QPoint &);
void on_actionScreenshots_triggered();
void updateToolsMenu();
void skinJobFinished();
@@ -155,11 +154,15 @@ slots:
void instanceChanged(const QModelIndex &current, const QModelIndex &previous);
void instanceDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void selectionBad();
void startTask(Task *task);
void updateAvailable(QString repo, QString versionName, int versionId);
void updateAvailable(GoUpdate::Status status);
void updateNotAvailable();
void notificationsChanged();
@@ -171,16 +174,10 @@ slots:
void updateNewsLabel();
void updateStatusUI();
void updateStatusFailedUI();
void reloadStatus();
/*!
* Runs the DownloadUpdateTask and installs updates.
* Runs the DownloadTask and installs updates.
*/
void downloadUpdates(QString repo, int versionId, bool installOnExit = false);
void downloadUpdates(GoUpdate::Status status, bool installOnExit = false);
protected:
bool eventFilter(QObject *obj, QEvent *ev);
@@ -189,6 +186,11 @@ protected:
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);
void finalizeInstance(InstancePtr inst);
private:
Ui::MainWindow *ui;
class GroupView *view;
@@ -200,18 +202,19 @@ private:
QToolButton *changeIconButton;
QToolButton *newsLabel;
std::shared_ptr<GenericPageProvider> m_globalSettingsProvider;
std::shared_ptr<NewsChecker> m_newsChecker;
std::shared_ptr<NotificationChecker> m_notificationChecker;
InstancePtr m_selectedInstance;
QString m_currentInstIcon;
Task *m_versionLoadTask;
QLabel *m_statusLeft;
QLabel *m_statusRight;
QToolButton *m_statusRefresh;
class ServerStatus *m_statusRight;
QMenu *accountMenu;
QToolButton *accountMenuButton;
QAction *manageAccountsAction;
QTimer statusTimer;
};

View File

@@ -10,53 +10,48 @@
#include <QMessageBox>
#include <QStringList>
#include <QDesktopServices>
#include <QDebug>
#include "gui/dialogs/VersionSelectDialog.h"
#include "logic/lists/InstanceList.h"
#include "logic/auth/MojangAccountList.h"
#include "logic/icons/IconList.h"
#include "logic/lists/LwjglVersionList.h"
#include "logic/lists/MinecraftVersionList.h"
#include "logic/lists/ForgeVersionList.h"
#include "logic/lists/LiteLoaderVersionList.h"
#include "InstanceList.h"
#include "auth/MojangAccountList.h"
#include "icons/IconList.h"
#include "minecraft/LwjglVersionList.h"
#include "minecraft/MinecraftVersionList.h"
#include "liteloader/LiteLoaderVersionList.h"
#include "logic/news/NewsChecker.h"
#include "forge/ForgeVersionList.h"
#include "logic/status/StatusChecker.h"
#include "net/HttpMetaCache.h"
#include "net/URLConstants.h"
#include "Env.h"
#include "logic/InstanceLauncher.h"
#include "logic/net/HttpMetaCache.h"
#include "logic/net/URLConstants.h"
#include "java/JavaUtils.h"
#include "logic/JavaUtils.h"
#include "updater/UpdateChecker.h"
#include "logic/updater/UpdateChecker.h"
#include "logic/updater/NotificationChecker.h"
#include "logic/tools/JProfiler.h"
#include "logic/tools/JVisualVM.h"
#include "logic/tools/MCEditTool.h"
#include "tools/JProfiler.h"
#include "tools/JVisualVM.h"
#include "tools/MCEditTool.h"
#include "pathutils.h"
#include "cmdutils.h"
#include <inisettingsobject.h>
#include <setting.h>
#include "logger/QsLog.h"
#include <logger/QsLogDest.h>
#include <xdgicon.h>
#include "settings/INISettingsObject.h"
#include "settings/Setting.h"
#ifdef Q_OS_WIN32
#include <windows.h>
static const int APPDATA_BUFFER_SIZE = 1024;
#endif
#include "trans/TranslationDownloader.h"
#include "ftb/FTBPlugin.h"
using namespace Util::Commandline;
MultiMC::MultiMC(int &argc, char **argv, bool root_override)
: QApplication(argc, argv)
MultiMC::MultiMC(int &argc, char **argv, bool test_mode) : QApplication(argc, argv)
{
setOrganizationName("MultiMC");
setApplicationName("MultiMC5");
startTime = QDateTime::currentDateTime();
setAttribute(Qt::AA_UseHighDpiPixmaps);
// Don't quit on hiding the last window
this->setQuitOnLastWindowClosed(false);
@@ -79,13 +74,7 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
parser.addShortOpt("dir", 'd');
parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of "
"the binary location (use '.' for current)");
// WARNING: disabled until further notice
/*
// --launch
parser.addOption("launch");
parser.addShortOpt("launch", 'l');
parser.addDocumentation("launch", "tries to launch the given instance", "<inst>");
*/
// parse the arguments
try
{
@@ -117,8 +106,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
return;
}
}
origcwdPath = QDir::currentPath();
binPath = applicationDirPath();
QString origcwdPath = QDir::currentPath();
QString binPath = applicationDirPath();
QString adjustedBy;
// change directory
QString dirParam = args["dir"].toString();
@@ -135,119 +125,138 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
adjustedBy += "Fallback to binary path " + dataPath;
}
if(!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
if (!ensureFolderPathExists(dataPath) || !QDir::setCurrent(dataPath))
{
// BAD STUFF. WHAT DO?
initLogger();
QLOG_ERROR() << "Failed to set work path. Will exit. NOW.";
qCritical() << "Failed to set work path. Will exit. NOW.";
m_status = MultiMC::Failed;
return;
}
if (root_override)
// in test mode, root path is the same as the binary path.
if (test_mode)
{
rootPath = binPath;
}
else
{
#ifdef Q_OS_LINUX
#ifdef Q_OS_LINUX
QDir foo(PathCombine(binPath, ".."));
rootPath = foo.absolutePath();
#elif defined(Q_OS_WIN32)
#elif defined(Q_OS_WIN32)
rootPath = binPath;
#elif defined(Q_OS_MAC)
#elif defined(Q_OS_MAC)
QDir foo(PathCombine(binPath, "../.."));
rootPath = foo.absolutePath();
#endif
#endif
}
// static data paths... mostly just for translations
#ifdef Q_OS_LINUX
QDir foo(PathCombine(binPath, ".."));
staticDataPath = foo.absolutePath();
#elif defined(Q_OS_WIN32)
staticDataPath = binPath;
#elif defined(Q_OS_MAC)
QDir foo(PathCombine(rootPath, "Contents/Resources"));
staticDataPath = foo.absolutePath();
#endif
// init the logger
initLogger();
QLOG_INFO() << "MultiMC 5, (c) 2013 MultiMC Contributors";
QLOG_INFO() << "Version : " << BuildConfig.VERSION_STR;
QLOG_INFO() << "Git commit : " << BuildConfig.GIT_COMMIT;
qDebug() << "MultiMC 5, (c) 2013-2015 MultiMC Contributors";
qDebug() << "Version : " << BuildConfig.VERSION_STR;
qDebug() << "Git commit : " << BuildConfig.GIT_COMMIT;
if (adjustedBy.size())
{
QLOG_INFO() << "Work dir before adjustment : " << origcwdPath;
QLOG_INFO() << "Work dir after adjustment : " << QDir::currentPath();
QLOG_INFO() << "Adjusted by : " << adjustedBy;
qDebug() << "Work dir before adjustment : " << origcwdPath;
qDebug() << "Work dir after adjustment : " << QDir::currentPath();
qDebug() << "Adjusted by : " << adjustedBy;
}
else
{
QLOG_INFO() << "Work dir : " << QDir::currentPath();
qDebug() << "Work dir : " << QDir::currentPath();
}
QLOG_INFO() << "Binary path : " << binPath;
QLOG_INFO() << "Application root path : " << rootPath;
qDebug() << "Binary path : " << binPath;
qDebug() << "Application root path : " << rootPath;
qDebug() << "Static data path : " << staticDataPath;
// load settings
initGlobalSettings();
initGlobalSettings(test_mode);
// load translations
initTranslations();
// initialize the updater
m_updateChecker.reset(new UpdateChecker());
m_updateChecker.reset(new UpdateChecker(BuildConfig.CHANLIST_URL, BuildConfig.VERSION_CHANNEL, BuildConfig.VERSION_BUILD));
// initialize the notification checker
m_notificationChecker.reset(new NotificationChecker());
m_translationChecker.reset(new TranslationDownloader());
// initialize the news checker
m_newsChecker.reset(new NewsChecker(BuildConfig.NEWS_RSS_URL));
// initialize the status checker
m_statusChecker.reset(new StatusChecker());
// load icons
initIcons();
// and instances
auto InstDirSetting = m_settings->getSetting("InstanceDir");
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
QLOG_INFO() << "Loading Instances...";
// instance path: check for problems with '!' in instance path and warn the user in the log
// and rememer that we have to show him a dialog when the gui starts (if it does so)
QString instDir = m_settings->get("InstanceDir").toString();
qDebug() << "Instance path : " << instDir;
if (checkProblemticPathJava(QDir(instDir)))
{
qWarning()
<< "Your instance path contains \'!\' and this is known to cause java problems";
}
m_instances.reset(new InstanceList(m_settings, InstDirSetting->get().toString(), this));
qDebug() << "Loading Instances...";
m_instances->loadList();
connect(InstDirSetting.get(), SIGNAL(settingChanged(const Setting &, QVariant)),
connect(InstDirSetting.get(), SIGNAL(SettingChanged(const Setting &, QVariant)),
m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant)));
// and accounts
m_accounts.reset(new MojangAccountList(this));
QLOG_INFO() << "Loading accounts...";
qDebug() << "Loading accounts...";
m_accounts->setListFilePath("accounts.json", true);
m_accounts->loadList();
// init the http meta cache
initHttpMetaCache();
ENV.initHttpMetaCache(rootPath, staticDataPath);
// create the global network manager
m_qnam.reset(new QNetworkAccessManager(this));
ENV.m_qnam.reset(new QNetworkAccessManager(this));
// init proxy settings
updateProxySettings();
{
QString proxyTypeStr = settings()->get("ProxyType").toString();
QString addr = settings()->get("ProxyAddr").toString();
int port = settings()->get("ProxyPort").value<qint16>();
QString user = settings()->get("ProxyUser").toString();
QString pass = settings()->get("ProxyPass").toString();
ENV.updateProxySettings(proxyTypeStr, addr, port, user, pass);
}
initSSL();
m_translationChecker->downloadTranslations();
//FIXME: what to do with these?
m_profilers.insert("jprofiler",
std::shared_ptr<BaseProfilerFactory>(new JProfilerFactory()));
m_profilers.insert("jvisualvm",
std::shared_ptr<BaseProfilerFactory>(new JVisualVMFactory()));
for (auto profiler : m_profilers.values())
{
profiler->registerSettings(m_settings.get());
}
m_tools.insert("mcedit",
std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
for (auto tool : m_tools.values())
{
tool->registerSettings(m_settings.get());
profiler->registerSettings(m_settings);
}
// launch instance, if that's what should be done
// WARNING: disabled until further notice
/*
if (!args["launch"].isNull())
//FIXME: what to do with these?
m_tools.insert("mcedit", std::shared_ptr<BaseDetachedToolFactory>(new MCEditFactory()));
for (auto tool : m_tools.values())
{
if (InstanceLauncher(args["launch"].toString()).launch())
m_status = MultiMC::Succeeded;
else
m_status = MultiMC::Failed;
return;
tool->registerSettings(m_settings);
}
*/
connect(this, SIGNAL(aboutToQuit()), SLOT(onExit()));
m_status = MultiMC::Initialized;
}
@@ -264,20 +273,31 @@ MultiMC::~MultiMC()
}
}
void MultiMC::initSSL()
{
#ifdef Q_OS_MAC
Q_INIT_RESOURCE(certs);
QFile equifaxFile(":/certs/Equifax_Secure_Certificate_Authority.pem");
equifaxFile.open(QIODevice::ReadOnly);
QSslCertificate equifaxCert(equifaxFile.readAll(), QSsl::Pem);
QSslSocket::addDefaultCaCertificate(equifaxCert);
#endif
}
void MultiMC::initTranslations()
{
QLocale locale(m_settings->get("Language").toString());
QLocale::setDefault(locale);
QLOG_INFO() << "Your language is" << locale.bcp47Name();
qDebug() << "Your language is" << locale.bcp47Name();
m_qt_translator.reset(new QTranslator());
if (m_qt_translator->load("qt_" + locale.bcp47Name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
{
QLOG_DEBUG() << "Loading Qt Language File for"
qDebug() << "Loading Qt Language File for"
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
if (!installTranslator(m_qt_translator.get()))
{
QLOG_ERROR() << "Loading Qt Language File failed.";
qCritical() << "Loading Qt Language File failed.";
m_qt_translator.reset();
}
}
@@ -287,13 +307,13 @@ void MultiMC::initTranslations()
}
m_mmc_translator.reset(new QTranslator());
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), MMC->root() + "/translations"))
if (m_mmc_translator->load("mmc_" + locale.bcp47Name(), staticDataPath + "/translations"))
{
QLOG_DEBUG() << "Loading MMC Language File for"
qDebug() << "Loading MMC Language File for"
<< locale.bcp47Name().toLocal8Bit().constData() << "...";
if (!installTranslator(m_mmc_translator.get()))
{
QLOG_ERROR() << "Loading MMC Language File failed.";
qCritical() << "Loading MMC Language File failed.";
m_mmc_translator.reset();
}
}
@@ -303,12 +323,45 @@ void MultiMC::initTranslations()
}
}
void MultiMC::initIcons()
{
auto setting = MMC->settings()->getSetting("IconsDir");
ENV.m_icons.reset(new IconList(QString(":/icons/instances/"), setting->get().toString()));
connect(setting.get(), &Setting::SettingChanged,[&](const Setting &, QVariant value)
{
ENV.m_icons->directoryChanged(value.toString());
});
}
void moveFile(const QString &oldName, const QString &newName)
{
QFile::remove(newName);
QFile::copy(oldName, newName);
QFile::remove(oldName);
}
void appDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
const char *levels = "DWCF";
const QString format("%1 %2 %3\n");
qint64 msecstotal = MMC->timeSinceStart();
qint64 seconds = msecstotal / 1000;
qint64 msecs = msecstotal % 1000;
QString foo;
char buf[1025] = {0};
::snprintf(buf, 1024, "%5lld.%03lld", seconds, msecs);
QString out = format.arg(buf).arg(levels[type]).arg(msg);
MMC->logFile->write(out.toUtf8());
MMC->logFile->flush();
QTextStream(stderr) << out.toLocal8Bit();
fflush(stderr);
}
void MultiMC::initLogger()
{
static const QString logBase = "MultiMC-%0.log";
@@ -318,85 +371,58 @@ void MultiMC::initLogger()
moveFile(logBase.arg(1), logBase.arg(2));
moveFile(logBase.arg(0), logBase.arg(1));
// init the logging mechanism
QsLogging::Logger &logger = QsLogging::Logger::instance();
logger.setLoggingLevel(QsLogging::TraceLevel);
m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination(logBase.arg(0));
m_debugDestination = QsLogging::DestinationFactory::MakeQDebugDestination();
logger.addDestination(m_fileDestination.get());
logger.addDestination(m_debugDestination.get());
// log all the things
logger.setLoggingLevel(QsLogging::TraceLevel);
qInstallMessageHandler(appDebugOutput);
logFile = std::make_shared<QFile>(logBase.arg(0));
logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
}
void MultiMC::initGlobalSettings()
void MultiMC::initGlobalSettings(bool test_mode)
{
m_settings.reset(new INISettingsObject("multimc.cfg", this));
// Updates
m_settings->registerSetting("UpdateChannel", BuildConfig.VERSION_CHANNEL);
m_settings->registerSetting("AutoUpdate", true);
m_settings->registerSetting("IconTheme", QString("multimc"));
// Notifications
m_settings->registerSetting("ShownNotifications", QString());
// FTB
m_settings->registerSetting("TrackFTBInstances", false);
#ifdef Q_OS_LINUX
QString ftbDefault = QDir::home().absoluteFilePath(".ftblauncher");
#elif defined(Q_OS_WIN32)
wchar_t buf[APPDATA_BUFFER_SIZE];
QString ftbDefault;
if(!GetEnvironmentVariableW(L"APPDATA", buf, APPDATA_BUFFER_SIZE))
// Remembered state
m_settings->registerSetting("LastUsedGroupForNewInstance", QString());
QString defaultMonospace;
int defaultSize = 11;
#ifdef Q_OS_WIN32
defaultMonospace = "Courier";
defaultSize = 10;
#elif defined(Q_OS_MAC)
defaultMonospace = "Menlo";
#else
defaultMonospace = "Monospace";
#endif
if(!test_mode)
{
QLOG_FATAL() << "Your APPDATA folder is missing! If you are on windows, this means your system is broken. If you aren't on windows, how the **** are you running the windows build????";
// resolve the font so the default actually matches
QFont consoleFont;
consoleFont.setFamily(defaultMonospace);
consoleFont.setStyleHint(QFont::Monospace);
consoleFont.setFixedPitch(true);
QFontInfo consoleFontInfo(consoleFont);
QString resolvedDefaultMonospace = consoleFontInfo.family();
QFont resolvedFont(resolvedDefaultMonospace);
qDebug() << "Detected default console font:" << resolvedDefaultMonospace
<< ", substitutions:" << resolvedFont.substitutions().join(',');
m_settings->registerSetting("ConsoleFont", resolvedDefaultMonospace);
}
else
{
ftbDefault = PathCombine(QString::fromWCharArray(buf), "ftblauncher");
// in test mode, we don't have UI, so we don't do any font resolving
m_settings->registerSetting("ConsoleFont", defaultMonospace);
}
#elif defined(Q_OS_MAC)
QString ftbDefault =
PathCombine(QDir::homePath(), "Library/Application Support/ftblauncher");
#endif
m_settings->registerSetting("FTBLauncherRoot", ftbDefault);
m_settings->registerSetting("ConsoleFontSize", defaultSize);
m_settings->registerSetting("FTBRoot");
if (m_settings->get("FTBRoot").isNull())
{
QString ftbRoot;
QFile f(QDir(m_settings->get("FTBLauncherRoot").toString())
.absoluteFilePath("ftblaunch.cfg"));
QLOG_INFO() << "Attempting to read" << f.fileName();
if (f.open(QFile::ReadOnly))
{
const QString data = QString::fromLatin1(f.readAll());
QRegularExpression exp("installPath=(.*)");
ftbRoot = QDir::cleanPath(exp.match(data).captured(1));
#ifdef Q_OS_WIN32
if (!ftbRoot.isEmpty())
{
if (ftbRoot.at(0).isLetter() && ftbRoot.size() > 1 && ftbRoot.at(1) == '/')
{
ftbRoot.remove(1, 1);
}
}
#endif
if (ftbRoot.isEmpty())
{
QLOG_INFO() << "Failed to get FTB root path";
}
else
{
QLOG_INFO() << "FTB is installed at" << ftbRoot;
m_settings->set("FTBRoot", ftbRoot);
}
}
else
{
QLOG_WARN() << "Couldn't open" << f.fileName() << ":" << f.errorString();
QLOG_WARN() << "This is perfectly normal if you don't have FTB installed";
}
}
FTBPlugin::initialize(m_settings);
// Folders
m_settings->registerSetting("InstanceDir", "instances");
@@ -427,7 +453,7 @@ void MultiMC::initGlobalSettings()
m_settings->registerSetting({"MinecraftWinHeight", "MCWindowHeight"}, 480);
// Proxy Settings
m_settings->registerSetting("ProxyType", "Default");
m_settings->registerSetting("ProxyType", "None");
m_settings->registerSetting({"ProxyAddr", "ProxyHostName"}, "127.0.0.1");
m_settings->registerSetting("ProxyPort", 8080);
m_settings->registerSetting({"ProxyUser", "ProxyUsername"}, "");
@@ -436,11 +462,14 @@ void MultiMC::initGlobalSettings()
// Memory
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
m_settings->registerSetting("PermGen", 64);
m_settings->registerSetting("PermGen", 128);
// Java Settings
m_settings->registerSetting("JavaPath", "");
m_settings->registerSetting("JavaTimestamp", 0);
m_settings->registerSetting("JavaVersion", "");
m_settings->registerSetting("LastHostname", "");
m_settings->registerSetting("JavaDetectionHack", "");
m_settings->registerSetting("JvmArgs", "");
// Custom Commands
@@ -461,98 +490,8 @@ void MultiMC::initGlobalSettings()
m_settings->registerSetting("ConsoleWindowGeometry", "");
m_settings->registerSetting("SettingsGeometry", "");
}
void MultiMC::initHttpMetaCache()
{
m_metacache.reset(new HttpMetaCache("metacache"));
m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath());
m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath());
m_metacache->addBase("versions", QDir("versions").absolutePath());
m_metacache->addBase("libraries", QDir("libraries").absolutePath());
m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath());
m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath());
m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath());
m_metacache->addBase("skins", QDir("accounts/skins").absolutePath());
m_metacache->addBase("root", QDir(root()).absolutePath());
m_metacache->Load();
}
void MultiMC::updateProxySettings()
{
QString proxyTypeStr = settings()->get("ProxyType").toString();
// Get the proxy settings from the settings object.
QString addr = settings()->get("ProxyAddr").toString();
int port = settings()->get("ProxyPort").value<qint16>();
QString user = settings()->get("ProxyUser").toString();
QString pass = settings()->get("ProxyPass").toString();
// Set the application proxy settings.
if (proxyTypeStr == "SOCKS5")
{
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, addr, port, user, pass));
}
else if (proxyTypeStr == "HTTP")
{
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, addr, port, user, pass));
}
else if (proxyTypeStr == "None")
{
// If we have no proxy set, set no proxy and return.
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::NoProxy));
}
else
{
// If we have "Default" selected, set Qt to use the system proxy settings.
QNetworkProxyFactory::setUseSystemConfiguration(true);
}
QLOG_INFO() << "Detecting proxy settings...";
QNetworkProxy proxy = QNetworkProxy::applicationProxy();
if (m_qnam.get()) m_qnam->setProxy(proxy);
QString proxyDesc;
if (proxy.type() == QNetworkProxy::NoProxy)
{
QLOG_INFO() << "Using no proxy is an option!";
return;
}
switch (proxy.type())
{
case QNetworkProxy::DefaultProxy:
proxyDesc = "Default proxy: ";
break;
case QNetworkProxy::Socks5Proxy:
proxyDesc = "Socks5 proxy: ";
break;
case QNetworkProxy::HttpProxy:
proxyDesc = "HTTP proxy: ";
break;
case QNetworkProxy::HttpCachingProxy:
proxyDesc = "HTTP caching: ";
break;
case QNetworkProxy::FtpCachingProxy:
proxyDesc = "FTP caching: ";
break;
default:
proxyDesc = "DERP proxy: ";
break;
}
proxyDesc += QString("%3@%1:%2 pass %4")
.arg(proxy.hostName())
.arg(proxy.port())
.arg(proxy.user())
.arg(proxy.password());
QLOG_INFO() << proxyDesc;
}
std::shared_ptr<IconList> MultiMC::icons()
{
if (!m_icons)
{
m_icons.reset(new IconList);
}
return m_icons;
m_settings->registerSetting("PagedGeometry", "");
}
std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
@@ -560,6 +499,7 @@ std::shared_ptr<LWJGLVersionList> MultiMC::lwjgllist()
if (!m_lwjgllist)
{
m_lwjgllist.reset(new LWJGLVersionList());
ENV.registerVersionList("org.lwjgl.legacy", m_lwjgllist);
}
return m_lwjgllist;
}
@@ -569,6 +509,7 @@ std::shared_ptr<ForgeVersionList> MultiMC::forgelist()
if (!m_forgelist)
{
m_forgelist.reset(new ForgeVersionList());
ENV.registerVersionList("net.minecraftforge", m_forgelist);
}
return m_forgelist;
}
@@ -578,6 +519,7 @@ std::shared_ptr<LiteLoaderVersionList> MultiMC::liteloaderlist()
if (!m_liteloaderlist)
{
m_liteloaderlist.reset(new LiteLoaderVersionList());
ENV.registerVersionList("com.mumfrey.liteloader", m_liteloaderlist);
}
return m_liteloaderlist;
}
@@ -587,6 +529,7 @@ std::shared_ptr<MinecraftVersionList> MultiMC::minecraftlist()
if (!m_minecraftlist)
{
m_minecraftlist.reset(new MinecraftVersionList());
ENV.registerVersionList("net.minecraft", m_minecraftlist);
}
return m_minecraftlist;
}
@@ -596,6 +539,7 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist()
if (!m_javalist)
{
m_javalist.reset(new JavaVersionList());
ENV.registerVersionList("com.java", m_javalist);
}
return m_javalist;
}
@@ -603,31 +547,31 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist()
void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
{
// if we are going to update on exit, save the params now
if(flags & OnExit)
if (flags & OnExit)
{
m_updateOnExitPath = updateFilesDir;
m_updateOnExitFlags = flags & ~OnExit;
return;
}
// otherwise if there already were some params for on exit update, clear them and continue
else if(m_updateOnExitPath.size())
else if (m_updateOnExitPath.size())
{
m_updateOnExitFlags = None;
m_updateOnExitPath.clear();
}
QLOG_INFO() << "Installing updates.";
#ifdef WINDOWS
QString finishCmd = MMC->applicationFilePath();
QString updaterBinary = PathCombine(bin(), "updater.exe");
#elif LINUX
QString finishCmd = PathCombine(root(), "MultiMC");
QString updaterBinary = PathCombine(bin(), "updater");
#elif OSX
QString finishCmd = MMC->applicationFilePath();
QString updaterBinary = PathCombine(bin(), "updater");
#else
#error Unsupported operating system.
#endif
qDebug() << "Installing updates.";
#ifdef WINDOWS
QString finishCmd = applicationFilePath();
QString updaterBinary = PathCombine(applicationDirPath(), "updater.exe");
#elif LINUX
QString finishCmd = PathCombine(root(), "MultiMC");
QString updaterBinary = PathCombine(applicationDirPath(), "updater");
#elif OSX
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
@@ -635,33 +579,54 @@ void MultiMC::installUpdates(const QString updateFilesDir, UpdateFlags flags)
args << "--install-dir" << root();
args << "--package-dir" << updateFilesDir;
args << "--script" << PathCombine(updateFilesDir, "file_list.xml");
args << "--wait" << QString::number(MMC->applicationPid());
if(flags & DryRun)
args << "--wait" << QString::number(applicationPid());
if (flags & DryRun)
args << "--dry-run";
if (flags & RestartOnFinish)
{
args << "--finish-cmd" << finishCmd;
args << "--finish-dir" << data();
args << "--finish-dir" << dataPath;
}
QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" ");
qDebug() << "Running updater with command" << updaterBinary << args.join(" ");
QFile::setPermissions(updaterBinary, (QFileDevice::Permission)0x7755);
if (!QProcess::startDetached(updaterBinary, args/*, root()*/))
if (!QProcess::startDetached(updaterBinary, args /*, root()*/))
{
QLOG_ERROR() << "Failed to start the updater process!";
qCritical() << "Failed to start the updater process!";
return;
}
ENV.destroy();
// Now that we've started the updater, quit MultiMC.
MMC->quit();
quit();
}
void MultiMC::setIconTheme(const QString& name)
{
XdgIcon::setThemeName(name);
}
QIcon MultiMC::getThemedIcon(const QString& name)
{
return XdgIcon::fromTheme(name);
}
void MultiMC::onExit()
{
if(m_updateOnExitPath.size())
if(m_instances)
{
m_instances->saveGroupList();
}
if (m_updateOnExitPath.size())
{
installUpdates(m_updateOnExitPath, m_updateOnExitFlags);
}
ENV.destroy();
if(logFile)
{
logFile->flush();
logFile->close();
}
}
bool MultiMC::openJsonEditor(const QString &filename)

View File

@@ -2,10 +2,12 @@
#include <QApplication>
#include <memory>
#include "logger/QsLog.h"
#include "logger/QsLogDest.h"
#include <QDebug>
#include <QFlag>
#include <QIcon>
#include <QDateTime>
class QFile;
class MinecraftVersionList;
class LWJGLVersionList;
class HttpMetaCache;
@@ -18,26 +20,15 @@ class ForgeVersionList;
class LiteLoaderVersionList;
class JavaVersionList;
class UpdateChecker;
class NotificationChecker;
class NewsChecker;
class StatusChecker;
class BaseProfilerFactory;
class BaseDetachedToolFactory;
class TranslationDownloader;
#if defined(MMC)
#undef MMC
#endif
#define MMC (static_cast<MultiMC *>(QCoreApplication::instance()))
// FIXME: possibly move elsewhere
enum InstSortMode
{
// Sort alphabetically by name.
Sort_Name,
// Sort by which instance was launched most recently.
Sort_LastLaunch
};
enum UpdateFlag
{
None = 0x0,
@@ -50,6 +41,9 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(UpdateFlags);
class MultiMC : public QApplication
{
// friends for the purpose of limiting access to deprecated stuff
friend class MultiMCPage;
friend class MainWindow;
Q_OBJECT
public:
enum Status
@@ -60,115 +54,89 @@ public:
};
public:
MultiMC(int &argc, char **argv, bool root_override = false);
MultiMC(int &argc, char **argv, bool test_mode = false);
virtual ~MultiMC();
// InstanceList, IconList, OneSixFTBInstance, LegacyUpdate, LegacyInstance, MCEditTool, JVisualVM, MinecraftInstance, JProfiler, BaseInstance
std::shared_ptr<SettingsObject> settings()
{
return m_settings;
}
std::shared_ptr<InstanceList> instances()
qint64 timeSinceStart() const
{
return m_instances;
return startTime.msecsTo(QDateTime::currentDateTime());
}
std::shared_ptr<MojangAccountList> accounts()
{
return m_accounts;
}
QIcon getThemedIcon(const QString& name);
std::shared_ptr<IconList> icons();
Status status()
{
return m_status;
}
std::shared_ptr<QNetworkAccessManager> qnam()
{
return m_qnam;
}
std::shared_ptr<HttpMetaCache> metacache()
{
return m_metacache;
}
void setIconTheme(const QString& name);
// DownloadUpdateTask
std::shared_ptr<UpdateChecker> updateChecker()
{
return m_updateChecker;
}
std::shared_ptr<NotificationChecker> notificationChecker()
{
return m_notificationChecker;
}
std::shared_ptr<NewsChecker> newsChecker()
{
return m_newsChecker;
}
std::shared_ptr<StatusChecker> statusChecker()
{
return m_statusChecker;
}
std::shared_ptr<LWJGLVersionList> lwjgllist();
std::shared_ptr<ForgeVersionList> forgelist();
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
std::shared_ptr<MinecraftVersionList> minecraftlist();
std::shared_ptr<LWJGLVersionList> lwjgllist();
std::shared_ptr<ForgeVersionList> forgelist();
std::shared_ptr<LiteLoaderVersionList> liteloaderlist();
std::shared_ptr<JavaVersionList> javalist();
// APPLICATION ONLY
std::shared_ptr<InstanceList> instances()
{
return m_instances;
}
// APPLICATION ONLY
std::shared_ptr<MojangAccountList> accounts()
{
return m_accounts;
}
// APPLICATION ONLY
Status status()
{
return m_status;
}
// APPLICATION ONLY
QMap<QString, std::shared_ptr<BaseProfilerFactory>> profilers()
{
return m_profilers;
}
// APPLICATION ONLY
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> tools()
{
return m_tools;
}
// APPLICATION ONLY
void installUpdates(const QString updateFilesDir, UpdateFlags flags = None);
/*!
* Updates the application proxy settings from the settings object.
*/
void updateProxySettings();
/*!
* Opens a json file using either a system default editor, or, if note empty, the editor
* specified in the settings
*/
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()
{
return rootPath;
}
/// this is the where the binary files reside
const QString &bin()
{
return binPath;
}
/// this is the work/data path. All user data is here.
const QString &data()
{
return dataPath;
}
/**
* this is the original work path before it was changed by the adjustment mechanism
*/
const QString &origcwd()
{
return origcwdPath;
}
private slots:
/**
@@ -179,45 +147,43 @@ private slots:
private:
void initLogger();
void initGlobalSettings();
void initIcons();
void initHttpMetaCache();
void initGlobalSettings(bool test_mode);
void initTranslations();
void initSSL();
private:
friend class UpdateCheckerTest;
friend class DownloadUpdateTaskTest;
friend class DownloadTaskTest;
QDateTime startTime;
std::shared_ptr<QTranslator> m_qt_translator;
std::shared_ptr<QTranslator> m_mmc_translator;
std::shared_ptr<SettingsObject> m_settings;
std::shared_ptr<InstanceList> m_instances;
std::shared_ptr<UpdateChecker> m_updateChecker;
std::shared_ptr<NotificationChecker> m_notificationChecker;
std::shared_ptr<NewsChecker> m_newsChecker;
std::shared_ptr<StatusChecker> m_statusChecker;
std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<IconList> m_icons;
std::shared_ptr<QNetworkAccessManager> m_qnam;
std::shared_ptr<HttpMetaCache> m_metacache;
std::shared_ptr<LWJGLVersionList> m_lwjgllist;
std::shared_ptr<ForgeVersionList> m_forgelist;
std::shared_ptr<LiteLoaderVersionList> m_liteloaderlist;
std::shared_ptr<MinecraftVersionList> m_minecraftlist;
std::shared_ptr<JavaVersionList> m_javalist;
std::shared_ptr<TranslationDownloader> m_translationChecker;
QMap<QString, std::shared_ptr<BaseProfilerFactory>> m_profilers;
QMap<QString, std::shared_ptr<BaseDetachedToolFactory>> m_tools;
QsLogging::DestinationPtr m_fileDestination;
QsLogging::DestinationPtr m_debugDestination;
QString m_updateOnExitPath;
UpdateFlags m_updateOnExitFlags = None;
QString rootPath;
QString binPath;
QString staticDataPath;
QString dataPath;
QString origcwdPath;
Status m_status = MultiMC::Failed;
public:
std::shared_ptr<QFile> logFile;
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -15,7 +15,7 @@
* limitations under the License.
*/
#include <gui/Platform.h>
#include <Platform.h>
/**
* Stub for non-X11 platforms
* @brief MultiMCPlatform::fixWM_CLASS

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Authors: Orochimarufan <orochimarufan.x3@gmail.com>
*
@@ -15,7 +15,7 @@
* limitations under the License.
*/
#include <gui/Platform.h>
#include <Platform.h>
#include <QtX11Extras/QX11Info>
#include <xcb/xcb.h>

View File

@@ -0,0 +1,412 @@
#include "VersionProxyModel.h"
#include "MultiMC.h"
#include <QSortFilterProxyModel>
#include <QPixmapCache>
#include <modutils.h>
class VersionFilterModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
VersionFilterModel(VersionProxyModel *parent) : QSortFilterProxyModel(parent)
{
m_parent = parent;
}
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
const auto &filters = m_parent->filters();
for (auto it = filters.begin(); it != filters.end(); ++it)
{
auto role = it.key();
auto idx = sourceModel()->index(source_row, 0, source_parent);
auto data = sourceModel()->data(idx, role);
switch(role)
{
case BaseVersionList::ParentGameVersionRole:
case BaseVersionList::VersionIdRole:
{
auto versionString = data.toString();
if(it.value().exact)
{
if (versionString != it.value().string)
{
return false;
}
}
else if (!Util::versionIsInInterval(versionString, it.value().string))
{
return false;
}
}
default:
{
auto match = data.toString();
if(it.value().exact)
{
if (match != it.value().string)
{
return false;
}
}
else if (match.contains(it.value().string))
{
return false;
}
}
}
}
return true;
}
private:
VersionProxyModel *m_parent;
};
VersionProxyModel::VersionProxyModel(QObject *parent) : QAbstractProxyModel(parent)
{
filterModel = new VersionFilterModel(this);
connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged);
// FIXME: implement when needed
/*
connect(replacing, &QAbstractItemModel::rowsAboutToBeInserted, this, &VersionProxyModel::sourceRowsAboutToBeInserted);
connect(replacing, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted);
connect(replacing, &QAbstractItemModel::rowsAboutToBeRemoved, this, &VersionProxyModel::sourceRowsAboutToBeRemoved);
connect(replacing, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved);
connect(replacing, &QAbstractItemModel::rowsAboutToBeMoved, this, &VersionProxyModel::sourceRowsAboutToBeMoved);
connect(replacing, &QAbstractItemModel::rowsMoved, this, &VersionProxyModel::sourceRowsMoved);
connect(replacing, &QAbstractItemModel::layoutAboutToBeChanged, this, &VersionProxyModel::sourceLayoutAboutToBeChanged);
connect(replacing, &QAbstractItemModel::layoutChanged, this, &VersionProxyModel::sourceLayoutChanged);
*/
connect(filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset);
connect(filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset);
QAbstractProxyModel::setSourceModel(filterModel);
}
QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if(section < 0 || section >= m_columns.size())
return QVariant();
if(orientation != Qt::Horizontal)
return QVariant();
auto column = m_columns[section];
if(role == Qt::DisplayRole)
{
switch(column)
{
case Name:
return tr("Version");
case ParentVersion:
return tr("Minecraft"); //FIXME: this should come from metadata
case Branch:
return tr("Branch");
case Type:
return tr("Type");
case Architecture:
return tr("Architecture");
case Path:
return tr("Path");
}
}
else if(role == Qt::ToolTipRole)
{
switch(column)
{
case Name:
return tr("The name of the version.");
case ParentVersion:
return tr("Minecraft version"); //FIXME: this should come from metadata
case Branch:
return tr("The version's branch");
case Type:
return tr("The version's type");
case Architecture:
return tr("CPU Architecture");
case Path:
return tr("Filesystem path to this version");
}
}
return QVariant();
}
QVariant VersionProxyModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
{
return QVariant();
}
auto column = m_columns[index.column()];
auto parentIndex = mapToSource(index);
switch(role)
{
case Qt::DisplayRole:
{
switch(column)
{
case Name:
return sourceModel()->data(parentIndex, BaseVersionList::VersionRole);
case ParentVersion:
return sourceModel()->data(parentIndex, BaseVersionList::ParentGameVersionRole);
case Branch:
return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
case Type:
return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
case Architecture:
return sourceModel()->data(parentIndex, BaseVersionList::ArchitectureRole);
case Path:
return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
default:
return QVariant();
}
}
case Qt::ToolTipRole:
{
switch(column)
{
case Name:
{
if(hasRecommended)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
{
return tr("Recommended");
}
else if(hasLatest)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
{
return tr("Latest");
}
}
else if(index.row() == 0)
{
return tr("Latest");
}
}
}
default:
{
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
}
}
}
case Qt::DecorationRole:
{
switch(column)
{
case Name:
{
if(hasRecommended)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
if(value.toBool())
{
return MMC->getThemedIcon("star");
}
else if(hasLatest)
{
auto value = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
if(value.toBool())
{
return MMC->getThemedIcon("bug");
}
}
else if(index.row() == 0)
{
return MMC->getThemedIcon("bug");
}
auto pixmap = QPixmapCache::find("placeholder");
if(!pixmap)
{
QPixmap px(16,16);
px.fill(Qt::transparent);
QPixmapCache::insert("placeholder", px);
return px;
}
return *pixmap;
}
}
default:
{
return QVariant();
}
}
}
default:
{
if(roles.contains((BaseVersionList::ModelRoles)role))
{
return sourceModel()->data(parentIndex, role);
}
return QVariant();
}
}
};
QModelIndex VersionProxyModel::parent(const QModelIndex &child) const
{
return QModelIndex();
}
QModelIndex VersionProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if(sourceIndex.isValid())
{
return index(sourceIndex.row(), 0);
}
return QModelIndex();
}
QModelIndex VersionProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if(proxyIndex.isValid())
{
return sourceModel()->index(proxyIndex.row(), 0);
}
return QModelIndex();
}
QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex &parent) const
{
// no trees here... shoo
if(parent.isValid())
{
return QModelIndex();
}
if(row < 0 || row >= sourceModel()->rowCount())
return QModelIndex();
if(column < 0 || column >= columnCount())
return QModelIndex();
return QAbstractItemModel::createIndex(row, column);
}
int VersionProxyModel::columnCount(const QModelIndex &parent) const
{
return m_columns.size();
}
int VersionProxyModel::rowCount(const QModelIndex &parent) const
{
if(sourceModel())
{
return sourceModel()->rowCount();
}
return 0;
}
void VersionProxyModel::sourceDataChanged(const QModelIndex &source_top_left,
const QModelIndex &source_bottom_right)
{
if(source_top_left.parent() != source_bottom_right.parent())
return;
// whole row is getting changed
auto topLeft = createIndex(source_top_left.row(), 0);
auto bottomRight = createIndex(source_bottom_right.row(), columnCount() - 1);
emit dataChanged(topLeft, bottomRight);
}
void VersionProxyModel::setSourceModel(BaseVersionList *replacing)
{
beginResetModel();
if(!replacing)
{
m_columns.clear();
roles.clear();
filterModel->setSourceModel(replacing);
return;
}
roles = replacing->providesRoles();
if(roles.contains(BaseVersionList::VersionRole))
{
m_columns.push_back(Name);
}
/*
if(roles.contains(BaseVersionList::ParentGameVersionRole))
{
m_columns.push_back(ParentVersion);
}
*/
if(roles.contains(BaseVersionList::ArchitectureRole))
{
m_columns.push_back(Architecture);
}
if(roles.contains(BaseVersionList::PathRole))
{
m_columns.push_back(Path);
}
if(roles.contains(BaseVersionList::BranchRole))
{
m_columns.push_back(Branch);
}
if(roles.contains(BaseVersionList::TypeRole))
{
m_columns.push_back(Type);
}
if(roles.contains(BaseVersionList::RecommendedRole))
{
hasRecommended = true;
}
if(roles.contains(BaseVersionList::LatestRole))
{
hasLatest = true;
}
filterModel->setSourceModel(replacing);
endResetModel();
}
QModelIndex VersionProxyModel::getRecommended() const
{
if(!roles.contains(BaseVersionList::RecommendedRole))
{
return index(0, 0);
}
int recommended = 0;
for (int i = 0; i < rowCount(); i++)
{
auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole);
if (value.toBool())
{
recommended = i;
}
}
return index(recommended, 0);
}
void VersionProxyModel::clearFilters()
{
m_filters.clear();
filterModel->invalidate();
}
void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, const QString &filter, const bool exact)
{
Filter f;
f.string = filter;
f.exact = exact;
m_filters[column] = f;
filterModel->invalidate();
}
const VersionProxyModel::FilterMap &VersionProxyModel::filters() const
{
return m_filters;
}
void VersionProxyModel::sourceAboutToBeReset()
{
beginResetModel();
}
void VersionProxyModel::sourceReset()
{
endResetModel();
}
#include "VersionProxyModel.moc"

View File

@@ -0,0 +1,58 @@
#pragma once
#include <QAbstractProxyModel>
#include "BaseVersionList.h"
class VersionFilterModel;
class VersionProxyModel: public QAbstractProxyModel
{
Q_OBJECT
public:
struct Filter
{
QString string;
bool exact = false;
};
enum Column
{
Name,
ParentVersion,
Branch,
Type,
Architecture,
Path
};
typedef QHash<BaseVersionList::ModelRoles, Filter> FilterMap;
public:
VersionProxyModel ( QObject* parent = 0 );
virtual ~VersionProxyModel() {};
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const override;
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override;
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
virtual QModelIndex parent(const QModelIndex &child) const override;
void setSourceModel(BaseVersionList *sourceModel);
const FilterMap &filters() const;
void setFilter(const BaseVersionList::ModelRoles column, const QString &filter, const bool exact);
void clearFilters();
QModelIndex getRecommended() const;
private slots:
void sourceDataChanged(const QModelIndex &source_top_left,const QModelIndex &source_bottom_right);
void sourceAboutToBeReset();
void sourceReset();
private:
QList<Column> m_columns;
FilterMap m_filters;
BaseVersionList::RoleList roles;
VersionFilterModel * filterModel;
bool hasRecommended = false;
bool hasLatest = false;
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,16 +17,16 @@
#include "ui_AboutDialog.h"
#include <QIcon>
#include "MultiMC.h"
#include "gui/Platform.h"
#include "Platform.h"
#include "BuildConfig.h"
#include <logic/net/NetJob.h>
#include <net/NetJob.h>
// Credits
// This is a hack, but I can't think of a better way to do this easily without screwing with QTextDocument...
QString getCreditsHtml(QStringList patrons)
{
QString creditsHtml =
QString creditsHtml = QObject::tr(
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0//EN' 'http://www.w3.org/TR/REC-html40/strict.dtd'>"
"<html>"
""
@@ -42,7 +42,7 @@ QString getCreditsHtml(QStringList patrons)
"<h3>MultiMC Developers</h3>"
"<p>Andrew Okin &lt;<a href='mailto:forkk@forkk.net'>forkk@forkk.net</a>&gt;</p>"
"<p>Petr Mrázek &lt;<a href='mailto:peterix@gmail.com'>peterix@gmail.com</a>&gt;</p>"
"<p>Sky &lt;<a href='https://www.twitter.com/drayshak'>@drayshak</a>&gt;</p>"
"<p>Sky Welch &lt;<a href='mailto:multimc@bunnies.io'>multimc@bunnies.io</a>&gt;</p>"
"<p>Jan (02JanDal) &lt;<a href='mailto:02jandal@gmail.com'>02jandal@gmail.com</a>&gt;</p>"
""
"<h3>With thanks to</h3>"
@@ -56,9 +56,9 @@ QString getCreditsHtml(QStringList patrons)
"%1"
""
"</body>"
"</html>";
"</html>");
if (patrons.isEmpty())
return creditsHtml.arg("<p>Loading...</p>");
return creditsHtml.arg(QObject::tr("<p>Loading...</p>"));
else
{
QString patronsStr;
@@ -81,11 +81,10 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
ui->urlLabel->setOpenExternalLinks(true);
ui->icon->setPixmap(QIcon(":/icons/multimc/scalable/apps/multimc.svg").pixmap(64));
ui->title->setText("MultiMC 5 " + BuildConfig.printableVersionString());
ui->icon->setPixmap(MMC->getThemedIcon("multimc").pixmap(64));
ui->title->setText("MultiMC 5");
ui->versionLabel->setText(tr("Version") +": " + BuildConfig.printableVersionString());
ui->vtypeLabel->setText(tr("Version Type") +": " + BuildConfig.versionTypeName());
ui->platformLabel->setText(tr("Platform") +": " + BuildConfig.BUILD_PLATFORM);
if (BuildConfig.VERSION_BUILD >= 0)
@@ -100,7 +99,7 @@ AboutDialog::AboutDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AboutDia
connect(ui->closeButton, SIGNAL(clicked()), SLOT(close()));
MMC->connect(ui->aboutQt, SIGNAL(clicked()), SLOT(aboutQt()));
connect(ui->aboutQt, &QPushButton::clicked, &QApplication::aboutQt);
loadPatronList();
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
#include <QDialog>
#include <logic/net/ByteArrayDownload.h>
#include <net/ByteArrayDownload.h>
namespace Ui
{

View File

@@ -58,9 +58,6 @@
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="../../resources/multimc/multimc.qrc">:/icons/multimc/scalable/apps/multimc.svg</pixmap>
</property>
</widget>
</item>
<item>
@@ -96,15 +93,15 @@
<item>
<widget class="QToolBox" name="toolBox">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="aboutPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>331</height>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
@@ -121,16 +118,6 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="vtypeLabel">
<property name="text">
<string>Version Type:</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="platformLabel">
<property name="text">
@@ -186,7 +173,7 @@
</font>
</property>
<property name="text">
<string>© 2013 MultiMC Contributors</string>
<string>© 2013-2015 MultiMC Contributors</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -228,8 +215,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>331</height>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
@@ -245,11 +232,11 @@
<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:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Oxygen-Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
@@ -270,8 +257,8 @@ p, li { white-space: pre-wrap; }
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>331</height>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
@@ -298,7 +285,7 @@ p, li { white-space: pre-wrap; }
<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:'DejaVu Sans Mono'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&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;
@@ -359,7 +346,24 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top: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;
@@ -422,7 +426,7 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top: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; font-size:11pt;&quot;&gt;&lt;br /&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;
@@ -447,8 +451,16 @@ p, li { white-space: pre-wrap; }
&lt;p style=&quot; margin-top: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;-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;/body&gt;&lt;/html&gt;</string>
&lt;p style=&quot; margin-top: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>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
@@ -459,8 +471,8 @@ p, li { white-space: pre-wrap; }
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>331</height>
<width>695</width>
<height>297</height>
</rect>
</property>
<attribute name="label">
@@ -473,12 +485,12 @@ p, li { white-space: pre-wrap; }
<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:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;Part of the reason for using the Apache license is we don't want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt;&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Oxygen-Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;We keep MultiMC open source because we think it's important to be able to see the source code for a project like this, and we do so using the Apache license.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;Part of the reason for using the Apache license is we don't want people using the &amp;quot;MultiMC&amp;quot; name when redistributing the project. This means people must take the time to go through the source code and remove all references to &amp;quot;MultiMC&amp;quot;, including but not limited to the project icon and the title of windows, (no *MultiMC-fork* in the title).&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Bitstream Vera Sans';&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt;The Apache license covers reasonable use for the name - a mention of the project's origins in the About dialog and the license is acceptable. However, it should be abundantly clear that the project is a fork &lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans'; font-weight:600;&quot;&gt;without&lt;/span&gt;&lt;span style=&quot; font-family:'Bitstream Vera Sans';&quot;&gt; implying that you have our blessing.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@
#include <QItemSelectionModel>
#include <logger/QsLog.h>
#include <QDebug>
#include <gui/dialogs/ProgressDialog.h>
#include <dialogs/ProgressDialog.h>
#include <MultiMC.h>
@@ -40,10 +40,12 @@ AccountSelectDialog::AccountSelectDialog(const QString &message, int flags, QWid
// Flags...
ui->globalDefaultCheck->setVisible(flags & GlobalDefaultCheckbox);
ui->instDefaultCheck->setVisible(flags & InstanceDefaultCheckbox);
QLOG_DEBUG() << flags;
qDebug() << flags;
// Select the first entry in the list.
ui->listView->setCurrentIndex(ui->listView->model()->index(0, 0));
connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(on_buttonBox_accepted()));
}
AccountSelectDialog::~AccountSelectDialog()
@@ -72,8 +74,7 @@ void AccountSelectDialog::on_buttonBox_accepted()
if (selection.size() > 0)
{
QModelIndex selected = selection.first();
MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
m_selected = account;
m_selected = selected.data(MojangAccountList::PointerRole).value<MojangAccountPtr>();
}
close();
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +19,7 @@
#include <memory>
#include "logic/auth/MojangAccountList.h"
#include "auth/MojangAccountList.h"
namespace Ui
{

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,17 +20,14 @@
#include "CopyInstanceDialog.h"
#include "ui_CopyInstanceDialog.h"
#include "gui/Platform.h"
#include "gui/dialogs/VersionSelectDialog.h"
#include "gui/dialogs/ProgressDialog.h"
#include "gui/dialogs/IconPickerDialog.h"
#include "Platform.h"
#include "dialogs/IconPickerDialog.h"
#include "logic/InstanceFactory.h"
#include "logic/BaseVersion.h"
#include "logic/icons/IconList.h"
#include "logic/lists/MinecraftVersionList.h"
#include "logic/tasks/Task.h"
#include "logic/BaseInstance.h"
#include "BaseVersion.h"
#include "icons/IconList.h"
#include "tasks/Task.h"
#include "BaseInstance.h"
#include "InstanceList.h"
CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
:QDialog(parent), ui(new Ui::CopyInstanceDialog), m_original(original)
@@ -41,9 +38,22 @@ CopyInstanceDialog::CopyInstanceDialog(InstancePtr original, QWidget *parent)
layout()->setSizeConstraint(QLayout::SetFixedSize);
InstIconKey = original->iconKey();
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
ui->instNameTextBox->setText(original->name());
ui->instNameTextBox->setFocus();
auto groups = MMC->instances()->getGroups().toSet();
auto groupList = QStringList(groups.toList());
groupList.sort(Qt::CaseInsensitive);
groupList.removeOne("");
groupList.push_front("");
ui->groupBox->addItems(groupList);
int index = groupList.indexOf(m_original->group());
if(index == -1)
{
index = 0;
}
ui->groupBox->setCurrentIndex(index);
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
}
CopyInstanceDialog::~CopyInstanceDialog()
@@ -66,6 +76,11 @@ QString CopyInstanceDialog::iconKey() const
return InstIconKey;
}
QString CopyInstanceDialog::instGroup() const
{
return ui->groupBox->currentText();
}
void CopyInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
@@ -74,7 +89,7 @@ void CopyInstanceDialog::on_iconButton_clicked()
if (dlg.result() == QDialog::Accepted)
{
InstIconKey = dlg.selectedIconKey;
ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey));
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,8 +16,8 @@
#pragma once
#include <QDialog>
#include "logic/BaseVersion.h"
#include <logic/BaseInstance.h>
#include "BaseVersion.h"
#include <BaseInstance.h>
class BaseInstance;
@@ -37,6 +37,7 @@ public:
void updateDialogState();
QString instName() const;
QString instGroup() const;
QString iconKey() const;
private

View File

@@ -17,7 +17,7 @@
<string>Copy Instance</string>
</property>
<property name="windowIcon">
<iconset resource="../../graphics.qrc">
<iconset>
<normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset>
</property>
<property name="modal">
@@ -42,7 +42,7 @@
<item>
<widget class="QToolButton" name="iconButton">
<property name="icon">
<iconset resource="../../graphics.qrc">
<iconset>
<normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
</property>
<property name="iconSize">
@@ -82,6 +82,33 @@
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelVersion_3">
<property name="text">
<string>Group</string>
</property>
<property name="buddy">
<cstring>groupBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -0,0 +1,434 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ExportInstanceDialog.h"
#include "ui_ExportInstanceDialog.h"
#include <BaseInstance.h>
#include <MMCZip.h>
#include <pathutils.h>
#include <QFileDialog>
#include <QMessageBox>
#include <qfilesystemmodel.h>
#include <QSortFilterProxyModel>
#include <QDebug>
#include <qstack.h>
#include <QSaveFile>
#include "MMCStrings.h"
#include "SeparatorPrefixTree.h"
class PackIgnoreProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
PackIgnoreProxy(InstancePtr instance, QObject *parent) : QSortFilterProxyModel(parent)
{
m_instance = instance;
}
// NOTE: Sadly, we have to do sorting ourselves.
bool lessThan(const QModelIndex &left, const QModelIndex &right) const
{
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
if (!fsm)
{
return QSortFilterProxyModel::lessThan(left, right);
}
bool asc = sortOrder() == Qt::AscendingOrder ? true : false;
QFileInfo leftFileInfo = fsm->fileInfo(left);
QFileInfo rightFileInfo = fsm->fileInfo(right);
if (!leftFileInfo.isDir() && rightFileInfo.isDir())
{
return !asc;
}
if (leftFileInfo.isDir() && !rightFileInfo.isDir())
{
return asc;
}
// sort and proxy model breaks the original model...
if (sortColumn() == 0)
{
return Strings::naturalCompare(leftFileInfo.fileName(), rightFileInfo.fileName(),
Qt::CaseInsensitive) < 0;
}
if (sortColumn() == 1)
{
auto leftSize = leftFileInfo.size();
auto rightSize = rightFileInfo.size();
if ((leftSize == rightSize) || (leftFileInfo.isDir() && rightFileInfo.isDir()))
{
return Strings::naturalCompare(leftFileInfo.fileName(),
rightFileInfo.fileName(),
Qt::CaseInsensitive) < 0
? asc
: !asc;
}
return leftSize < rightSize;
}
return QSortFilterProxyModel::lessThan(left, right);
}
virtual Qt::ItemFlags flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::NoItemFlags;
auto sourceIndex = mapToSource(index);
Qt::ItemFlags flags = sourceIndex.flags();
if (index.column() == 0)
{
flags |= Qt::ItemIsUserCheckable;
if (sourceIndex.model()->hasChildren(sourceIndex))
{
flags |= Qt::ItemIsTristate;
}
}
return flags;
}
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const
{
QModelIndex sourceIndex = mapToSource(index);
if (index.column() == 0 && role == Qt::CheckStateRole)
{
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto cover = blocked.cover(blockedPath);
if (!cover.isNull())
{
return QVariant(Qt::Unchecked);
}
else if (blocked.exists(blockedPath))
{
return QVariant(Qt::PartiallyChecked);
}
else
{
return QVariant(Qt::Checked);
}
}
return sourceIndex.data(role);
}
virtual bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole)
{
if (index.column() == 0 && role == Qt::CheckStateRole)
{
Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
return setFilterState(index, state);
}
QModelIndex sourceIndex = mapToSource(index);
return QSortFilterProxyModel::sourceModel()->setData(sourceIndex, value, role);
}
QString relPath(const QString &path) const
{
QString prefix = QDir().absoluteFilePath(m_instance->instanceRoot());
prefix += '/';
if (!path.startsWith(prefix))
{
return QString();
}
return path.mid(prefix.size());
}
bool setFilterState(QModelIndex index, Qt::CheckState state)
{
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
if (!fsm)
{
return false;
}
QModelIndex sourceIndex = mapToSource(index);
auto blockedPath = relPath(fsm->filePath(sourceIndex));
bool changed = false;
if (state == Qt::Unchecked)
{
// blocking a path
auto &node = blocked.insert(blockedPath);
// get rid of all blocked nodes below
node.clear();
changed = true;
}
else if (state == Qt::Checked || state == Qt::PartiallyChecked)
{
if (!blocked.remove(blockedPath))
{
auto cover = blocked.cover(blockedPath);
qDebug() << "Blocked by cover" << cover;
// uncover
blocked.remove(cover);
// block all contents, except for any cover
QModelIndex rootIndex =
fsm->index(PathCombine(m_instance->instanceRoot(), cover));
QModelIndex doing = rootIndex;
int row = 0;
QStack<QModelIndex> todo;
while (1)
{
auto node = doing.child(row, 0);
if (!node.isValid())
{
if (!todo.size())
{
break;
}
else
{
doing = todo.pop();
row = 0;
continue;
}
}
auto relpath = relPath(fsm->filePath(node));
if (blockedPath.startsWith(relpath)) // cover found?
{
// continue processing cover later
todo.push(node);
}
else
{
// or just block this one.
blocked.insert(relpath);
}
row++;
}
}
changed = true;
}
if (changed)
{
// update the thing
emit dataChanged(index, index, {Qt::CheckStateRole});
// update everything above index
QModelIndex up = index.parent();
while (1)
{
if (!up.isValid())
break;
emit dataChanged(up, up, {Qt::CheckStateRole});
up = up.parent();
}
// and everything below the index
QModelIndex doing = index;
int row = 0;
QStack<QModelIndex> todo;
while (1)
{
auto node = doing.child(row, 0);
if (!node.isValid())
{
if (!todo.size())
{
break;
}
else
{
doing = todo.pop();
row = 0;
continue;
}
}
emit dataChanged(node, node, {Qt::CheckStateRole});
todo.push(node);
row++;
}
// siblings and unrelated nodes are ignored
}
return true;
}
bool shouldExpand(QModelIndex index)
{
QModelIndex sourceIndex = mapToSource(index);
QFileSystemModel *fsm = qobject_cast<QFileSystemModel *>(sourceModel());
if (!fsm)
{
return false;
}
auto blockedPath = relPath(fsm->filePath(sourceIndex));
auto found = blocked.find(blockedPath);
if(found)
{
return !found->leaf();
}
return false;
}
void setBlockedPaths(QStringList paths)
{
beginResetModel();
blocked.clear();
blocked.insert(paths);
endResetModel();
}
const SeparatorPrefixTree<'/'> & blockedPaths() const
{
return blocked;
}
protected:
bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const
{
Q_UNUSED(source_parent)
// adjust the columns you want to filter out here
// return false for those that will be hidden
if (source_column == 2 || source_column == 3)
return false;
return true;
}
private:
InstancePtr m_instance;
SeparatorPrefixTree<'/'> blocked;
};
ExportInstanceDialog::ExportInstanceDialog(InstancePtr instance, QWidget *parent)
: QDialog(parent), ui(new Ui::ExportInstanceDialog), m_instance(instance)
{
ui->setupUi(this);
auto model = new QFileSystemModel(this);
proxyModel = new PackIgnoreProxy(m_instance, this);
loadPackIgnore();
proxyModel->setSourceModel(model);
auto root = instance->instanceRoot();
ui->treeView->setModel(proxyModel);
ui->treeView->setRootIndex(proxyModel->mapFromSource(model->index(root)));
connect(proxyModel, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int)));
model->setRootPath(root);
auto headerView = ui->treeView->header();
headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
}
ExportInstanceDialog::~ExportInstanceDialog()
{
delete ui;
}
bool ExportInstanceDialog::doExport()
{
auto name = RemoveInvalidFilenameChars(m_instance->name());
const QString output = QFileDialog::getSaveFileName(
this, tr("Export %1").arg(m_instance->name()),
PathCombine(QDir::homePath(), name + ".zip"), "Zip (*.zip)");
if (output.isNull())
{
return false;
}
if (QFile::exists(output))
{
int ret =
QMessageBox::question(this, tr("Overwrite?"),
tr("This file already exists. Do you want to overwrite it?"),
QMessageBox::No, QMessageBox::Yes);
if (ret == QMessageBox::No)
{
return false;
}
}
if (!MMCZip::compressDir(output, m_instance->instanceRoot(), name, &proxyModel->blockedPaths()))
{
QMessageBox::warning(this, tr("Error"), tr("Unable to export instance"));
return false;
}
return true;
}
void ExportInstanceDialog::done(int result)
{
savePackIgnore();
if (result == QDialog::Accepted)
{
if (doExport())
{
QDialog::done(QDialog::Accepted);
return;
}
else
{
return;
}
}
QDialog::done(result);
}
void ExportInstanceDialog::rowsInserted(QModelIndex parent, int top, int bottom)
{
//WARNING: possible off-by-one?
for(int i = top; i < bottom; i++)
{
auto node = parent.child(i, 0);
if(proxyModel->shouldExpand(node))
{
auto expNode = node.parent();
if(!expNode.isValid())
{
continue;
}
ui->treeView->expand(node);
}
}
}
QString ExportInstanceDialog::ignoreFileName()
{
return PathCombine(m_instance->instanceRoot(), ".packignore");
}
void ExportInstanceDialog::loadPackIgnore()
{
auto filename = ignoreFileName();
QFile ignoreFile(filename);
if(!ignoreFile.open(QIODevice::ReadOnly))
{
return;
}
auto data = ignoreFile.readAll();
auto string = QString::fromUtf8(data);
proxyModel->setBlockedPaths(string.split('\n', QString::SkipEmptyParts));
}
void ExportInstanceDialog::savePackIgnore()
{
auto 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();
}
#include "ExportInstanceDialog.moc"

View File

@@ -0,0 +1,54 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QDialog>
#include <QModelIndex>
#include <memory>
class BaseInstance;
class PackIgnoreProxy;
typedef std::shared_ptr<BaseInstance> InstancePtr;
namespace Ui
{
class ExportInstanceDialog;
}
class ExportInstanceDialog : public QDialog
{
Q_OBJECT
public:
explicit ExportInstanceDialog(InstancePtr instance, QWidget *parent = 0);
~ExportInstanceDialog();
virtual void done(int result);
private:
bool doExport();
void loadPackIgnore();
void savePackIgnore();
QString ignoreFileName();
private:
Ui::ExportInstanceDialog *ui;
InstancePtr m_instance;
PackIgnoreProxy * proxyModel;
private slots:
void rowsInserted(QModelIndex parent, int top, int bottom);
};

View File

@@ -1,30 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditNotesDialog</class>
<widget class="QDialog" name="EditNotesDialog">
<class>ExportInstanceDialog</class>
<widget class="QDialog" name="ExportInstanceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>459</width>
<height>399</height>
<width>720</width>
<height>625</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Notes</string>
<string>Export Instance</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextEdit" name="noteEditor">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
<widget class="QTreeView" name="treeView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="acceptRichText">
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="headerStretchLastSection">
<bool>false</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextEditable|Qt::TextEditorInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</attribute>
</widget>
</item>
<item>
@@ -39,12 +42,15 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>treeView</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>EditNotesDialog</receiver>
<receiver>ExportInstanceDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@@ -60,7 +66,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>EditNotesDialog</receiver>
<receiver>ExportInstanceDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,10 +22,10 @@
#include "IconPickerDialog.h"
#include "ui_IconPickerDialog.h"
#include "gui/Platform.h"
#include "gui/groupview/InstanceDelegate.h"
#include "Platform.h"
#include "groupview/InstanceDelegate.h"
#include "logic/icons/IconList.h"
#include "icons/IconList.h"
IconPickerDialog::IconPickerDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::IconPickerDialog)
@@ -59,7 +59,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent)
contentsWidget->installEventFilter(this);
contentsWidget->setModel(MMC->icons().get());
contentsWidget->setModel(ENV.icons().get());
auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"), QDialogButtonBox::ResetRole);
auto buttonRemove =
@@ -104,12 +104,12 @@ void IconPickerDialog::addNewIcon()
//: The type of icon files
QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(),
tr("Icons") + "(*.png *.jpg *.jpeg *.ico)");
MMC->icons()->installIcons(fileNames);
ENV.icons()->installIcons(fileNames);
}
void IconPickerDialog::removeSelectedIcon()
{
MMC->icons()->deleteIcon(selectedIconKey);
ENV.icons()->deleteIcon(selectedIconKey);
}
void IconPickerDialog::activated(QModelIndex index)
@@ -130,7 +130,7 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection
int IconPickerDialog::exec(QString selection)
{
auto list = MMC->icons();
auto list = ENV.icons();
auto contentsWidget = ui->iconView;
selectedIconKey = selection;

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1,4 +1,4 @@
/* Copyright 2014 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
#include "LoginDialog.h"
#include "ui_LoginDialog.h"
#include "logic/auth/YggdrasilTask.h"
#include "auth/YggdrasilTask.h"
#include <QtWidgets/QPushButton>
@@ -44,11 +44,11 @@ void LoginDialog::accept()
// Setup the login task and start it
m_account = MojangAccount::createFromUsername(ui->userTextBox->text());
m_loginTask = m_account->login(nullptr, ui->passTextBox->text());
connect(m_loginTask.get(), &ProgressProvider::failed, this, &LoginDialog::onTaskFailed);
connect(m_loginTask.get(), &ProgressProvider::succeeded, this,
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this,
&LoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &ProgressProvider::status, this, &LoginDialog::onTaskStatus);
connect(m_loginTask.get(), &ProgressProvider::progress, this, &LoginDialog::onTaskProgress);
connect(m_loginTask.get(), &Task::status, this, &LoginDialog::onTaskStatus);
connect(m_loginTask.get(), &Task::progress, this, &LoginDialog::onTaskProgress);
m_loginTask->start();
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2014 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
#include <QtWidgets/QDialog>
#include <QtCore/QEventLoop>
#include "logic/auth/MojangAccount.h"
#include "auth/MojangAccount.h"
namespace Ui
{

View File

@@ -1,27 +1,10 @@
/* Copyright 2013 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ModEditDialogCommon.h"
#include "CustomMessageBox.h"
#include <QDesktopServices>
#include <QMessageBox>
#include <QString>
#include <QUrl>
bool lastfirst(QModelIndexList &list, int &first, int &last)
{
if (!list.size())
if (list.isEmpty())
return false;
first = last = list[0].row();
for (auto item : list)

View File

@@ -0,0 +1,9 @@
#pragma once
#include <QModelIndex>
#include <QDesktopServices>
#include <QWidget>
#include <minecraft/Mod.h>
bool lastfirst(QModelIndexList &list, int &first, int &last);
void showWebsiteForMod(QWidget *parentDlg, Mod &m);

View File

@@ -0,0 +1,207 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MultiMC.h"
#include "NewInstanceDialog.h"
#include "ui_NewInstanceDialog.h"
#include <BaseVersion.h>
#include <icons/IconList.h>
#include <minecraft/MinecraftVersionList.h>
#include <tasks/Task.h>
#include <InstanceList.h>
#include "Platform.h"
#include "VersionSelectDialog.h"
#include "ProgressDialog.h"
#include "IconPickerDialog.h"
#include <QLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QValidator>
class UrlValidator : public QValidator
{
public:
using QValidator::QValidator;
State validate(QString &in, int &pos) const
{
const QUrl url(in);
if (url.isValid() && !url.isRelative() && !url.isEmpty())
{
return Acceptable;
}
else if (QFile::exists(in))
{
return Acceptable;
}
else
{
return Intermediate;
}
}
};
NewInstanceDialog::NewInstanceDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::NewInstanceDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
resize(minimumSizeHint());
layout()->setSizeConstraint(QLayout::SetFixedSize);
setSelectedVersion(MMC->minecraftlist()->getRecommended(), true);
InstIconKey = "infinity";
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
ui->modpackEdit->setValidator(new UrlValidator(ui->modpackEdit));
connect(ui->modpackEdit, &QLineEdit::textChanged, this, &NewInstanceDialog::updateDialogState);
connect(ui->modpackBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState);
connect(ui->versionBox, &QRadioButton::clicked, this, &NewInstanceDialog::updateDialogState);
auto groups = MMC->instances()->getGroups().toSet();
auto groupList = QStringList(groups.toList());
groupList.sort(Qt::CaseInsensitive);
groupList.removeOne("");
QString oldValue = MMC->settings()->get("LastUsedGroupForNewInstance").toString();
groupList.push_front(oldValue);
groupList.push_front("");
ui->groupBox->addItems(groupList);
int index = groupList.indexOf(oldValue);
if(index == -1)
{
index = 0;
}
ui->groupBox->setCurrentIndex(index);
ui->groupBox->lineEdit()->setPlaceholderText(tr("No group"));
}
NewInstanceDialog::~NewInstanceDialog()
{
delete ui;
}
void NewInstanceDialog::updateDialogState()
{
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)
{
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();
}
QString NewInstanceDialog::instGroup() const
{
return ui->groupBox->currentText();
}
QString NewInstanceDialog::iconKey() const
{
return InstIconKey;
}
QUrl NewInstanceDialog::modpackUrl() const
{
if (ui->modpackBox->isChecked())
{
const QUrl url(ui->modpackEdit->text());
if (url.isValid() && !url.isRelative() && !url.host().isEmpty())
{
return url;
}
else
{
return QUrl::fromLocalFile(ui->modpackEdit->text());
}
}
else
{
return QUrl();
}
}
BaseVersionPtr NewInstanceDialog::selectedVersion() const
{
return m_selectedVersion;
}
void NewInstanceDialog::on_btnChangeVersion_clicked()
{
VersionSelectDialog vselect(MMC->minecraftlist().get(), tr("Change Minecraft version"),
this);
vselect.exec();
if (vselect.result() == QDialog::Accepted)
{
BaseVersionPtr version = vselect.selectedVersion();
if (version)
setSelectedVersion(version);
}
}
void NewInstanceDialog::on_iconButton_clicked()
{
IconPickerDialog dlg(this);
dlg.exec(InstIconKey);
if (dlg.result() == QDialog::Accepted)
{
InstIconKey = dlg.selectedIconKey;
ui->iconButton->setIcon(ENV.icons()->getIcon(InstIconKey));
}
}
void NewInstanceDialog::on_instNameTextBox_textChanged(const QString &arg1)
{
updateDialogState();
}
void NewInstanceDialog::on_modpackBtn_clicked()
{
const QUrl url = QFileDialog::getOpenFileUrl(this, tr("Choose modpack"), modpackUrl(), tr("Zip (*.zip)"));
if (url.isValid())
{
if (url.isLocalFile())
{
ui->modpackEdit->setText(url.toLocalFile());
}
else
{
ui->modpackEdit->setText(url.toString());
}
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
#pragma once
#include <QDialog>
#include "logic/BaseVersion.h"
#include "BaseVersion.h"
namespace Ui
{
@@ -33,18 +34,21 @@ public:
void updateDialogState();
void setSelectedVersion(BaseVersionPtr version);
void setSelectedVersion(BaseVersionPtr version, bool initial = false);
void loadVersionList();
QString instName() const;
QString instGroup() const;
QString iconKey() const;
QUrl modpackUrl() const;
BaseVersionPtr selectedVersion() const;
private
slots:
void on_btnChangeVersion_clicked();
void on_iconButton_clicked();
void on_modpackBtn_clicked();
void on_instNameTextBox_textChanged(const QString &arg1);
private:

View File

@@ -9,15 +9,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>220</width>
<height>234</height>
<width>277</width>
<height>404</height>
</rect>
</property>
<property name="windowTitle">
<string>New Instance</string>
</property>
<property name="windowIcon">
<iconset resource="../../graphics.qrc">
<iconset>
<normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset>
</property>
<property name="modal">
@@ -42,7 +42,7 @@
<item>
<widget class="QToolButton" name="iconButton">
<property name="icon">
<iconset resource="../../graphics.qrc">
<iconset>
<normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset>
</property>
<property name="iconSize">
@@ -85,26 +85,83 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="labelVersion">
<widget class="QLabel" name="labelVersion_3">
<property name="text">
<string>Version:</string>
<string>&amp;Group:</string>
</property>
<property name="buddy">
<cstring>groupBox</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="2">
<widget class="QToolButton" name="modpackBtn">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QLineEdit" name="modpackEdit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>http://</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLineEdit" name="versionTextBox">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<item row="2" column="2">
<widget class="QToolButton" name="btnChangeVersion">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QRadioButton" name="modpackBox">
<property name="text">
<string>Impor&amp;t Modpack (local file or link):</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="3">
<widget class="QRadioButton" name="versionBox">
<property name="text">
<string>Vani&amp;lla Minecraft (select version):</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -139,9 +196,18 @@
</item>
</layout>
</widget>
<resources>
<include location="../../graphics.qrc"/>
</resources>
<tabstops>
<tabstop>instNameTextBox</tabstop>
<tabstop>groupBox</tabstop>
<tabstop>versionBox</tabstop>
<tabstop>versionTextBox</tabstop>
<tabstop>btnChangeVersion</tabstop>
<tabstop>modpackBox</tabstop>
<tabstop>modpackEdit</tabstop>
<tabstop>modpackBtn</tabstop>
<tabstop>iconButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
@@ -150,8 +216,8 @@
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
<x>257</x>
<y>333</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
@@ -166,8 +232,8 @@
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
<x>325</x>
<y>333</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
@@ -175,5 +241,69 @@
</hint>
</hints>
</connection>
<connection>
<sender>modpackBox</sender>
<signal>toggled(bool)</signal>
<receiver>modpackEdit</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>81</x>
<y>229</y>
</hint>
<hint type="destinationlabel">
<x>236</x>
<y>221</y>
</hint>
</hints>
</connection>
<connection>
<sender>modpackBox</sender>
<signal>toggled(bool)</signal>
<receiver>modpackBtn</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>129</x>
<y>225</y>
</hint>
<hint type="destinationlabel">
<x>328</x>
<y>229</y>
</hint>
</hints>
</connection>
<connection>
<sender>versionBox</sender>
<signal>toggled(bool)</signal>
<receiver>versionTextBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>93</x>
<y>195</y>
</hint>
<hint type="destinationlabel">
<x>213</x>
<y>191</y>
</hint>
</hints>
</connection>
<connection>
<sender>versionBox</sender>
<signal>toggled(bool)</signal>
<receiver>btnChangeVersion</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>104</x>
<y>198</y>
</hint>
<hint type="destinationlabel">
<x>322</x>
<y>192</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

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

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,8 +18,8 @@
#include <QKeyEvent>
#include "logic/tasks/Task.h"
#include "gui/Platform.h"
#include "tasks/Task.h"
#include "Platform.h"
ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ProgressDialog)
{
@@ -54,7 +54,7 @@ void ProgressDialog::updateSize()
resize(QSize(480, minimumSizeHint().height()));
}
int ProgressDialog::exec(ProgressProvider *task)
int ProgressDialog::exec(Task *task)
{
this->task = task;
@@ -71,10 +71,10 @@ int ProgressDialog::exec(ProgressProvider *task)
if(task->isRunning())
return QDialog::exec();
else
return 0;
return QDialog::Accepted;
}
ProgressProvider *ProgressDialog::getTask()
Task *ProgressDialog::getTask()
{
return task;
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
#include <QDialog>
class ProgressProvider;
class Task;
namespace Ui
{
@@ -34,10 +34,11 @@ public:
void updateSize();
int exec(ProgressProvider *task);
int exec(Task *task);
void setSkipButton(bool present, QString label = QString());
ProgressProvider *getTask();
Task *getTask();
public
slots:
@@ -48,7 +49,7 @@ slots:
void changeStatus(const QString &status);
void changeProgress(qint64 current, qint64 total);
private
slots:
void on_skipButton_clicked(bool checked);
@@ -60,5 +61,5 @@ protected:
private:
Ui::ProgressDialog *ui;
ProgressProvider *task;
Task *task;
};

View File

@@ -0,0 +1,136 @@
#include "UpdateDialog.h"
#include "ui_UpdateDialog.h"
#include "Platform.h"
#include <QDebug>
#include "MultiMC.h"
#include <settings/SettingsObject.h>
#include <hoedown/html.h>
#include <hoedown/document.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)
{
ui->label->setText(tr("A new %1 update is available!").arg(channel));
}
else
{
ui->label->setText(tr("No %1 updates found. You are running the latest version.").arg(channel));
ui->btnUpdateNow->setDisabled(true);
ui->btnUpdateOnExit->setDisabled(true);
}
loadChangelog();
}
UpdateDialog::~UpdateDialog()
{
}
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);
changelogDownload = ByteArrayDownload::make(QUrl(url));
dljob->addNetAction(changelogDownload);
connect(dljob.get(), &NetJob::succeeded, this, &UpdateDialog::changelogLoaded);
connect(dljob.get(), &NetJob::failed, this, &UpdateDialog::changelogFailed);
dljob->start();
}
/**
* hoedown wrapper, because dealing with resource lifetime in C is stupid
*/
class HoeDown
{
public:
class buffer
{
public:
buffer(size_t unit = 4096)
{
buf = hoedown_buffer_new(unit);
}
~buffer()
{
hoedown_buffer_free(buf);
}
const char * cstr()
{
return hoedown_buffer_cstr(buf);
}
void put(QByteArray input)
{
hoedown_buffer_put(buf, (uint8_t *) input.data(), input.size());
}
const uint8_t * data() const
{
return buf->data;
}
size_t size() const
{
return buf->size;
}
hoedown_buffer * buf;
} ib, ob;
HoeDown()
{
renderer = hoedown_html_renderer_new((hoedown_html_flags) 0,0);
document = hoedown_document_new(renderer, (hoedown_extensions) 0, 8);
}
~HoeDown()
{
hoedown_document_free(document);
hoedown_html_renderer_free(renderer);
}
QString process(QByteArray input)
{
ib.put(input);
hoedown_document_render(document, ob.buf, ib.data(), ib.size());
return ob.cstr();
}
private:
hoedown_document * document;
hoedown_renderer * renderer;
};
QString reprocessMarkdown(QByteArray markdown)
{
HoeDown hoedown;
QString output = hoedown.process(markdown);
// HACK: easier than customizing hoedown
output.replace(QRegExp("GH-([0-9]+)"), "<a href=\"https://github.com/MultiMC/MultiMC5/issues/\\1\">GH-\\1</a>");
qDebug() << output;
return output;
}
void UpdateDialog::changelogLoaded()
{
auto html = reprocessMarkdown(changelogDownload->m_data);
ui->changelogBrowser->setHtml(html);
}
void UpdateDialog::changelogFailed(QString reason)
{
ui->changelogBrowser->setHtml(tr("<p align=\"center\" <span style=\"font-size:22pt;\">Failed to fetch changelog... Error: %1</span></p>").arg(reason));
}
void UpdateDialog::on_btnUpdateLater_clicked()
{
reject();
}
void UpdateDialog::on_btnUpdateNow_clicked()
{
done(UPDATE_NOW);
}
void UpdateDialog::on_btnUpdateOnExit_clicked()
{
done(UPDATE_ONEXIT);
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
#pragma once
#include <QDialog>
#include "net/ByteArrayDownload.h"
#include "net/NetJob.h"
namespace Ui
{
@@ -34,7 +36,7 @@ class UpdateDialog : public QDialog
Q_OBJECT
public:
explicit UpdateDialog(QWidget *parent = 0);
explicit UpdateDialog(bool hasUpdate = true, QWidget *parent = 0);
~UpdateDialog();
private:
@@ -43,4 +45,17 @@ public slots:
void on_btnUpdateNow_clicked();
void on_btnUpdateOnExit_clicked();
void on_btnUpdateLater_clicked();
/// Starts loading the changelog
void loadChangelog();
/// Slot for when the chengelog loads successfully.
void changelogLoaded();
/// Slot for when the chengelog fails to load...
void changelogFailed(QString reason);
private:
ByteArrayDownloadPtr changelogDownload;
NetJobPtr dljob;
};

View File

@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UpdateDialog</class>
<widget class="QDialog" name="UpdateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>657</width>
<height>673</height>
</rect>
</property>
<property name="windowTitle">
<string>MultiMC Update</string>
</property>
<property name="windowIcon">
<iconset>
<normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>changelogBrowser</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="changelogBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'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>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="btnUpdateNow">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Update now</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnUpdateOnExit">
<property name="text">
<string>Update after MultiMC closes</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnUpdateLater">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Don't update yet</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>changelogBrowser</tabstop>
<tabstop>btnUpdateNow</tabstop>
<tabstop>btnUpdateOnExit</tabstop>
<tabstop>btnUpdateLater</tabstop>
</tabstops>
<resources>
<include location="../../resources/multimc/multimc.qrc"/>
</resources>
<connections/>
</ui>

View File

@@ -0,0 +1,194 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VersionSelectDialog.h"
#include "ui_VersionSelectDialog.h"
#include <QHeaderView>
#include <dialogs/ProgressDialog.h>
#include "CustomMessageBox.h"
#include "Platform.h"
#include <BaseVersion.h>
#include <BaseVersionList.h>
#include <tasks/Task.h>
#include <modutils.h>
#include <QDebug>
#include "MultiMC.h"
#include <VersionProxyModel.h>
VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent,
bool cancelable)
: QDialog(parent), ui(new Ui::VersionSelectDialog)
{
MultiMCPlatform::fixWM_CLASS(this);
ui->setupUi(this);
setWindowModality(Qt::WindowModal);
setWindowTitle(title);
m_vlist = vlist;
m_proxyModel = new VersionProxyModel(this);
m_proxyModel->setSourceModel(vlist);
ui->listView->setModel(m_proxyModel);
ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
ui->sneakyProgressBar->setHidden(true);
if (!cancelable)
{
ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
}
}
void VersionSelectDialog::setEmptyString(QString emptyString)
{
ui->listView->setEmptyString(emptyString);
}
void VersionSelectDialog::setEmptyErrorString(QString emptyErrorString)
{
ui->listView->setEmptyErrorString(emptyErrorString);
}
VersionSelectDialog::~VersionSelectDialog()
{
delete ui;
}
void VersionSelectDialog::setResizeOn(int column)
{
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents);
resizeOnColumn = column;
ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch);
}
int VersionSelectDialog::exec()
{
QDialog::open();
if (!m_vlist->isLoaded())
{
loadList();
}
else
{
if (m_proxyModel->rowCount() == 0)
{
ui->listView->setEmptyMode(VersionListView::String);
}
preselect();
}
return QDialog::exec();
}
void VersionSelectDialog::closeEvent(QCloseEvent * event)
{
if(loadTask)
{
loadTask->abort();
loadTask->deleteLater();
loadTask = nullptr;
}
QDialog::closeEvent(event);
}
void VersionSelectDialog::loadList()
{
if(loadTask)
{
return;
}
loadTask = m_vlist->getLoadTask();
if (!loadTask)
{
return;
}
connect(loadTask, &Task::finished, this, &VersionSelectDialog::onTaskFinished);
connect(loadTask, &Task::progress, this, &VersionSelectDialog::changeProgress);
loadTask->start();
ui->sneakyProgressBar->setHidden(false);
}
void VersionSelectDialog::onTaskFinished()
{
if (!loadTask->successful())
{
CustomMessageBox::selectable(this, tr("Error"),
tr("List update failed:\n%1").arg(loadTask->failReason()),
QMessageBox::Warning)->show();
if (m_proxyModel->rowCount() == 0)
{
ui->listView->setEmptyMode(VersionListView::ErrorString);
}
}
else if (m_proxyModel->rowCount() == 0)
{
ui->listView->setEmptyMode(VersionListView::String);
}
ui->sneakyProgressBar->setHidden(true);
loadTask->deleteLater();
loadTask = nullptr;
preselect();
}
void VersionSelectDialog::changeProgress(qint64 current, qint64 total)
{
ui->sneakyProgressBar->setMaximum(total);
ui->sneakyProgressBar->setValue(current);
}
void VersionSelectDialog::preselect()
{
if(preselectedAlready)
return;
preselectedAlready = true;
selectRecommended();
}
void VersionSelectDialog::selectRecommended()
{
auto idx = m_proxyModel->getRecommended();
if(idx.isValid())
{
ui->listView->selectionModel()->setCurrentIndex(idx,QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
ui->listView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
}
}
BaseVersionPtr VersionSelectDialog::selectedVersion() const
{
auto currentIndex = ui->listView->selectionModel()->currentIndex();
auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole);
return variant.value<BaseVersionPtr>();
}
void VersionSelectDialog::on_refreshButton_clicked()
{
loadList();
}
void VersionSelectDialog::setExactFilter(BaseVersionList::ModelRoles role, QString filter)
{
m_proxyModel->setFilter(role, filter, true);
}
void VersionSelectDialog::setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter)
{
m_proxyModel->setFilter(role, filter, false);
}
#include "VersionSelectDialog.moc"

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +18,15 @@
#include <QDialog>
#include <QSortFilterProxyModel>
#include "logic/BaseVersion.h"
class BaseVersionList;
#include "BaseVersionList.h"
namespace Ui
{
class VersionSelectDialog;
}
class VersionProxyModel;
class VersionSelectDialog : public QDialog
{
Q_OBJECT
@@ -43,21 +43,37 @@ public:
BaseVersionPtr selectedVersion() const;
void setFuzzyFilter(int column, QString filter);
void setExactFilter(int column, QString filter);
void setFuzzyFilter(BaseVersionList::ModelRoles role, QString filter);
void setExactFilter(BaseVersionList::ModelRoles role, QString filter);
void setEmptyString(QString emptyString);
void setEmptyErrorString(QString emptyErrorString);
void setResizeOn(int column);
void setUseLatest(const bool useLatest);
protected:
virtual void closeEvent ( QCloseEvent* );
private
slots:
void on_refreshButton_clicked();
void onTaskFinished();
void changeProgress(qint64 current, qint64 total);
private:
Ui::VersionSelectDialog *ui;
void preselect();
void selectRecommended();
BaseVersionList *m_vlist;
private:
Ui::VersionSelectDialog *ui = nullptr;
QSortFilterProxyModel *m_proxyModel;
BaseVersionList *m_vlist = nullptr;
VersionProxyModel *m_proxyModel = nullptr;
int resizeOnColumn = 0;
Task * loadTask = nullptr;
bool preselectedAlready = false;
};

View File

@@ -39,6 +39,16 @@
</attribute>
</widget>
</item>
<item>
<widget class="QProgressBar" name="sneakyProgressBar">
<property name="value">
<number>24</number>
</property>
<property name="format">
<string notr="true">%p%</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -69,7 +79,7 @@
<customwidget>
<class>VersionListView</class>
<extends>QTreeView</extends>
<header>gui/widgets/VersionListView.h</header>
<header>widgets/VersionListView.h</header>
</customwidget>
</customwidgets>
<resources/>

View File

@@ -3,15 +3,16 @@
#include <QPainter>
#include <QApplication>
#include <QtMath>
#include <QDebug>
#include <QMouseEvent>
#include <QListView>
#include <QPersistentModelIndex>
#include <QDrag>
#include <QMimeData>
#include <QCache>
#include <QScrollBar>
#include "Group.h"
#include "VisualGroup.h"
#include <QDebug>
template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2)
{
@@ -26,17 +27,12 @@ template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2)
}
GroupView::GroupView(QWidget *parent)
: QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5),
m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0)
: QAbstractItemView(parent)
{
// setViewMode(IconMode);
// setMovement(Snap);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
// setWordWrap(true);
// setDragDropMode(QListView::InternalMove);
setAcceptDrops(true);
m_spacing = 5;
setAutoScroll(true);
}
GroupView::~GroupView()
@@ -66,11 +62,28 @@ void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int e
scheduleDelayedItemsLayout();
}
class LocaleString : public QString
{
public:
LocaleString(const char *s) : QString(s)
{
}
LocaleString(const QString &s) : QString(s)
{
}
};
inline bool operator<(const LocaleString &lhs, const LocaleString &rhs)
{
return (QString::localeAwareCompare(lhs, rhs) < 0);
}
void GroupView::updateGeometries()
{
geometryCache.clear();
int previousScroll = verticalScrollBar()->value();
QMap<QString, Group *> cats;
QMap<LocaleString, VisualGroup *> cats;
for (int i = 0; i < model()->rowCount(); ++i)
{
@@ -78,14 +91,14 @@ void GroupView::updateGeometries()
model()->index(i, 0).data(GroupViewRoles::GroupRole).toString();
if (!cats.contains(groupName))
{
Group *old = this->category(groupName);
VisualGroup *old = this->category(groupName);
if (old)
{
cats.insert(groupName, new Group(old));
cats.insert(groupName, new VisualGroup(old));
}
else
{
cats.insert(groupName, new Group(groupName, this));
cats.insert(groupName, new VisualGroup(groupName, this));
}
}
}
@@ -147,7 +160,7 @@ void GroupView::modelReset()
bool GroupView::isIndexHidden(const QModelIndex &index) const
{
Group *cat = category(index);
VisualGroup *cat = category(index);
if (cat)
{
return cat->collapsed;
@@ -158,12 +171,12 @@ bool GroupView::isIndexHidden(const QModelIndex &index) const
}
}
Group *GroupView::category(const QModelIndex &index) const
VisualGroup *GroupView::category(const QModelIndex &index) const
{
return category(index.data(GroupViewRoles::GroupRole).toString());
}
Group *GroupView::category(const QString &cat) const
VisualGroup *GroupView::category(const QString &cat) const
{
for (auto group : m_groups)
{
@@ -175,11 +188,11 @@ Group *GroupView::category(const QString &cat) const
return nullptr;
}
Group *GroupView::categoryAt(const QPoint &pos) const
VisualGroup *GroupView::categoryAt(const QPoint &pos) const
{
for (auto group : m_groups)
{
if(group->hitScan(pos) & Group::CheckboxHit)
if(group->hitScan(pos) & VisualGroup::CheckboxHit)
{
return group;
}
@@ -187,7 +200,7 @@ Group *GroupView::categoryAt(const QPoint &pos) const
return nullptr;
}
int GroupView::itemsPerRow() const
int GroupView::calculateItemsPerRow() const
{
return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing));
}
@@ -199,77 +212,7 @@ int GroupView::contentWidth() const
int GroupView::itemWidth() const
{
return itemDelegate()
->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0))
.width();
}
int GroupView::categoryRowHeight(const QModelIndex &index) const
{
QModelIndexList indices;
int internalRow = categoryInternalPosition(index).second;
for (auto &i : category(index)->items())
{
if (categoryInternalPosition(i).second == internalRow)
{
indices.append(i);
}
}
int largestHeight = 0;
for (auto &i : indices)
{
largestHeight =
qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height());
}
return largestHeight + m_spacing;
}
QPair<int, int> GroupView::categoryInternalPosition(const QModelIndex &index) const
{
QList<QModelIndex> indices = category(index)->items();
int x = 0;
int y = 0;
const int perRow = itemsPerRow();
for (int i = 0; i < indices.size(); ++i)
{
if (indices.at(i) == index)
{
break;
}
++x;
if (x == perRow)
{
x = 0;
++y;
}
}
return qMakePair(x, y);
}
int GroupView::categoryInternalRowTop(const QModelIndex &index) const
{
Group *cat = category(index);
int categoryInternalRow = categoryInternalPosition(index).second;
int result = 0;
for (int i = 0; i < categoryInternalRow; ++i)
{
result += cat->rowHeights.at(i);
}
return result;
}
int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const
{
for (auto &i : category->items())
{
QPair<int, int> pos = categoryInternalPosition(i);
if (pos.second == internalRow)
{
return categoryRowHeight(i);
}
}
return -1;
return m_itemWidth;
}
void GroupView::mousePressEvent(QMouseEvent *event)
@@ -283,7 +226,6 @@ void GroupView::mousePressEvent(QMouseEvent *event)
m_pressedIndex = index;
m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
QItemSelectionModel::SelectionFlags selection_flags = selectionCommand(index, event);
m_pressedPosition = geometryPos;
m_pressedCategory = categoryAt(geometryPos);
@@ -296,13 +238,19 @@ void GroupView::mousePressEvent(QMouseEvent *event)
if (index.isValid() && (index.flags() & Qt::ItemIsEnabled))
{
if(index != currentIndex())
{
// FIXME: better!
m_currentCursorColumn = -1;
}
// we disable scrollTo for mouse press so the item doesn't change position
// when the user is interacting with it (ie. clicking on it)
bool autoScroll = hasAutoScroll();
setAutoScroll(false);
selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
setAutoScroll(autoScroll);
QRect rect(geometryPos, geometryPos);
QRect rect(visualPos, visualPos);
setSelection(rect, QItemSelectionModel::ClearAndSelect);
// signal handlers may change the model
@@ -359,7 +307,7 @@ void GroupView::mouseMoveEvent(QMouseEvent *event)
{
setState(DragSelectingState);
setSelection(QRect(geometryPos, geometryPos), QItemSelectionModel::ClearAndSelect);
setSelection(QRect(visualPos, visualPos), QItemSelectionModel::ClearAndSelect);
QModelIndex index = indexAt(visualPos);
// set at the end because it might scroll the view
@@ -452,7 +400,7 @@ void GroupView::paintEvent(QPaintEvent *event)
option.rect.setWidth(wpWidth);
for (int i = 0; i < m_groups.size(); ++i)
{
Group *category = m_groups.at(i);
VisualGroup *category = m_groups.at(i);
int y = category->verticalPosition();
y -= verticalOffset();
QRect backup = option.rect;
@@ -528,16 +476,13 @@ void GroupView::paintEvent(QPaintEvent *event)
void GroupView::resizeEvent(QResizeEvent *event)
{
// QListView::resizeEvent(event);
// if (m_categoryEditor)
// {
// m_categoryEditor->resize(qMax(contentWidth() / 2,
// m_editedCategory->textRect.width()),
// m_categoryEditor->height());
// }
updateGeometries();
int newItemsPerRow = calculateItemsPerRow();
if(newItemsPerRow != m_currentItemsPerRow)
{
m_currentCursorColumn = -1;
m_currentItemsPerRow = newItemsPerRow;
updateGeometries();
}
}
void GroupView::dragEnterEvent(QDragEnterEvent *event)
@@ -580,8 +525,8 @@ void GroupView::dropEvent(QDropEvent *event)
return;
}
QPair<Group *, int> dropPos = rowDropPos(event->pos() + offset());
const Group *category = dropPos.first;
QPair<VisualGroup *, int> dropPos = rowDropPos(event->pos() + offset());
const VisualGroup *category = dropPos.first;
const int row = dropPos.second;
if (row == -1)
@@ -605,44 +550,44 @@ void GroupView::dropEvent(QDropEvent *event)
void GroupView::startDrag(Qt::DropActions supportedActions)
{
QModelIndexList indexes = selectionModel()->selectedIndexes();
if (indexes.count() > 0)
{
QMimeData *data = model()->mimeData(indexes);
if (!data)
{
return;
}
QRect rect;
QPixmap pixmap = renderToPixmap(indexes, &rect);
//rect.translate(offset());
// rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
QDrag *drag = new QDrag(this);
drag->setPixmap(pixmap);
drag->setMimeData(data);
Qt::DropAction defaultDropAction = Qt::IgnoreAction;
if (this->defaultDropAction() != Qt::IgnoreAction &&
(supportedActions & this->defaultDropAction()))
{
defaultDropAction = this->defaultDropAction();
}
if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
{
const QItemSelection selection = selectionModel()->selection();
if(indexes.count() == 0)
return;
for (auto it = selection.constBegin(); it != selection.constEnd(); ++it)
QMimeData *data = model()->mimeData(indexes);
if (!data)
{
return;
}
QRect rect;
QPixmap pixmap = renderToPixmap(indexes, &rect);
//rect.translate(offset());
// rect.adjust(horizontalOffset(), verticalOffset(), 0, 0);
QDrag *drag = new QDrag(this);
drag->setPixmap(pixmap);
drag->setMimeData(data);
Qt::DropAction defaultDropAction = Qt::IgnoreAction;
if (this->defaultDropAction() != Qt::IgnoreAction &&
(supportedActions & this->defaultDropAction()))
{
defaultDropAction = this->defaultDropAction();
}
if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction)
{
const QItemSelection selection = selectionModel()->selection();
for (auto it = selection.constBegin(); it != selection.constEnd(); ++it)
{
QModelIndex parent = (*it).parent();
if ((*it).left() != 0)
{
QModelIndex parent = (*it).parent();
if ((*it).left() != 0)
{
continue;
}
if ((*it).right() != (model()->columnCount(parent) - 1))
{
continue;
}
int count = (*it).bottom() - (*it).top() + 1;
model()->removeRows((*it).top(), count, parent);
continue;
}
if ((*it).right() != (model()->columnCount(parent) - 1))
{
continue;
}
int count = (*it).bottom() - (*it).top() + 1;
model()->removeRows((*it).top(), count, parent);
}
}
}
@@ -659,58 +604,25 @@ QRect GroupView::geometryRect(const QModelIndex &index) const
return QRect();
}
const Group *cat = category(index);
QPair<int, int> pos = categoryInternalPosition(index);
int row = index.row();
if(geometryCache.contains(row))
{
return *geometryCache[row];
}
const VisualGroup *cat = category(index);
QPair<int, int> pos = cat->positionOf(index);
int x = pos.first;
// int y = pos.second;
QRect out;
out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index));
out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + cat->rowTopOf(index));
out.setLeft(m_spacing + x * (itemWidth() + m_spacing));
out.setSize(itemDelegate()->sizeHint(viewOptions(), index));
geometryCache.insert(row, new QRect(out));
return out;
}
/*
void CategorizedView::startCategoryEditor(Category *category)
{
if (m_categoryEditor != 0)
{
return;
}
m_editedCategory = category;
m_categoryEditor = new QLineEdit(m_editedCategory->text, this);
QRect rect = m_editedCategory->textRect;
rect.setWidth(qMax(contentWidth() / 2, rect.width()));
m_categoryEditor->setGeometry(rect);
m_categoryEditor->show();
m_categoryEditor->setFocus();
connect(m_categoryEditor, &QLineEdit::returnPressed, this,
&CategorizedView::endCategoryEditor);
}
void CategorizedView::endCategoryEditor()
{
if (m_categoryEditor == 0)
{
return;
}
m_editedCategory->text = m_categoryEditor->text();
m_updatesDisabled = true;
foreach (const QModelIndex &index, itemsForCategory(m_editedCategory))
{
const_cast<QAbstractItemModel *>(index.model())->setData(index,
m_categoryEditor->text(), CategoryRole);
}
m_updatesDisabled = false;
delete m_categoryEditor;
m_categoryEditor = 0;
m_editedCategory = 0;
updateGeometries();
}
*/
QModelIndex GroupView::indexAt(const QPoint &point) const
{
for (int i = 0; i < model()->rowCount(); ++i)
@@ -724,21 +636,19 @@ QModelIndex GroupView::indexAt(const QPoint &point) const
return QModelIndex();
}
// FIXME: is rect supposed to be geometry or visual coords?
void GroupView::setSelection(const QRect &rect,
const QItemSelectionModel::SelectionFlags commands)
{
for (int i = 0; i < model()->rowCount(); ++i)
{
QModelIndex index = model()->index(i, 0);
QRect itemRect = geometryRect(index);
QRect itemRect = visualRect(index);
if (itemRect.intersects(rect))
{
selectionModel()->select(index, commands);
update(itemRect.translated(-offset()));
}
}
}
QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const
@@ -781,33 +691,23 @@ QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelInde
bool GroupView::isDragEventAccepted(QDropEvent *event)
{
if (event->source() != this)
{
return false;
}
if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes()))
{
return false;
}
if (!model()->canDropMimeData(event->mimeData(), event->dropAction(),
rowDropPos(event->pos()).second, 0, QModelIndex()))
{
return false;
}
return true;
return false;
}
QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
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
Group *category = 0;
VisualGroup *category = 0;
{
int y = 0;
for (auto cat : m_groups)
{
if (pos.y() > y && pos.y() < (y + cat->headerHeight()))
{
return qMakePair<Group*, int>(nullptr, -1);
return qMakePair<VisualGroup*, int>(nullptr, -1);
}
y += cat->totalHeight() + m_categoryMargin;
if (pos.y() < y)
@@ -818,7 +718,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
}
if (category == 0)
{
return qMakePair<Group*, int>(nullptr, -1);
return qMakePair<VisualGroup*, int>(nullptr, -1);
}
}
@@ -834,7 +734,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
}
else
{
for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c)
for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 , ++c)
{
if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2))
{
@@ -845,7 +745,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
}
if (internalColumn == -1)
{
return qMakePair<Group*, int>(nullptr, -1);
return qMakePair<VisualGroup*, int>(nullptr, -1);
}
}
@@ -865,13 +765,13 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
}
if (internalRow == -1)
{
return qMakePair<Group*, int>(nullptr, -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<Group*, int>(nullptr, -1);
return qMakePair<VisualGroup*, int>(nullptr, -1);
}
}
@@ -885,6 +785,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos)
}
return qMakePair(category, indices.at(categoryRow).row());
*/
}
QPoint GroupView::offset() const
@@ -912,34 +813,191 @@ QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) con
}
return region;
}
QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction,
Qt::KeyboardModifiers modifiers)
{
auto current = currentIndex();
if(!current.isValid())
{
qDebug() << "model row: invalid";
return current;
}
qDebug() << "model row: " << current.row();
auto cat = category(current);
int i = m_groups.indexOf(cat);
if(i >= 0)
int group_index = m_groups.indexOf(cat);
if(group_index < 0)
return current;
auto real_group = m_groups[group_index];
int beginning_row = 0;
for(auto group: m_groups)
{
// this is a pile of something foul
auto real_group = m_groups[i];
int beginning_row = 0;
for(auto group: m_groups)
if(group == real_group)
break;
beginning_row += group->numRows();
}
QPair<int, int> pos = cat->positionOf(current);
int column = pos.first;
int row = pos.second;
if(m_currentCursorColumn < 0)
{
m_currentCursorColumn = column;
}
switch(cursorAction)
{
case MoveUp:
{
if(group == real_group)
break;
beginning_row += group->numRows();
if(row == 0)
{
int prevgroupindex = group_index-1;
while(prevgroupindex >= 0)
{
auto prevgroup = m_groups[prevgroupindex];
if(prevgroup->collapsed)
{
prevgroupindex--;
continue;
}
int newRow = prevgroup->numRows() - 1;
int newRowSize = prevgroup->rows[newRow].size();
int newColumn = m_currentCursorColumn;
if (m_currentCursorColumn >= newRowSize)
{
newColumn = newRowSize - 1;
}
return prevgroup->rows[newRow][newColumn];
}
}
else
{
int newRow = row - 1;
int newRowSize = cat->rows[newRow].size();
int newColumn = m_currentCursorColumn;
if (m_currentCursorColumn >= newRowSize)
{
newColumn = newRowSize - 1;
}
return cat->rows[newRow][newColumn];
}
return current;
}
qDebug() << "category: " << real_group->text;
QPair<int, int> pos = categoryInternalPosition(current);
int row = beginning_row + pos.second;
qDebug() << "row: " << row;
qDebug() << "column: " << pos.first;
case MoveDown:
{
if(row == cat->rows.size() - 1)
{
int nextgroupindex = group_index+1;
while (nextgroupindex < m_groups.size())
{
auto nextgroup = m_groups[nextgroupindex];
if(nextgroup->collapsed)
{
nextgroupindex++;
continue;
}
int newRowSize = nextgroup->rows[0].size();
int newColumn = m_currentCursorColumn;
if (m_currentCursorColumn >= newRowSize)
{
newColumn = newRowSize - 1;
}
return nextgroup->rows[0][newColumn];
}
}
else
{
int newRow = row + 1;
int newRowSize = cat->rows[newRow].size();
int newColumn = m_currentCursorColumn;
if (m_currentCursorColumn >= newRowSize)
{
newColumn = newRowSize - 1;
}
return cat->rows[newRow][newColumn];
}
return current;
}
case MoveLeft:
{
if(column > 0)
{
m_currentCursorColumn = column - 1;
return cat->rows[row][column - 1];
}
// TODO: moving to previous line
return current;
}
case MoveRight:
{
if(column < cat->rows[row].size() - 1)
{
m_currentCursorColumn = column + 1;
return cat->rows[row][column + 1];
}
// TODO: moving to next line
return current;
}
case MoveHome:
{
m_currentCursorColumn = 0;
return cat->rows[row][0];
}
case MoveEnd:
{
auto last = cat->rows[row].size() - 1;
m_currentCursorColumn = last;
return cat->rows[row][last];
}
default:
break;
}
return current;
}
int GroupView::horizontalOffset() const
{
return horizontalScrollBar()->value();
}
int GroupView::verticalOffset() const
{
return verticalScrollBar()->value();
}
void GroupView::scrollContentsBy(int dx, int dy)
{
scrollDirtyRegion(dx, dy);
viewport()->scroll(dx, dy);
}
void GroupView::scrollTo(const QModelIndex &index, ScrollHint hint)
{
if (!index.isValid())
return;
const QRect rect = visualRect(index);
if (hint == EnsureVisible && viewport()->rect().contains(rect))
{
viewport()->update(rect);
return;
}
verticalScrollBar()->setValue(verticalScrollToValue(index, rect, hint));
}
int GroupView::verticalScrollToValue(const QModelIndex &index, const QRect &rect,
QListView::ScrollHint hint) const
{
const QRect area = viewport()->rect();
const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top());
const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom());
int verticalValue = verticalScrollBar()->value();
QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing());
if (hint == QListView::PositionAtTop || above)
verticalValue += adjusted.top();
else if (hint == QListView::PositionAtBottom || below)
verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1);
else if (hint == QListView::PositionAtCenter)
verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2);
return verticalValue;
}

View File

@@ -3,6 +3,7 @@
#include <QListView>
#include <QLineEdit>
#include <QScrollBar>
#include <QCache>
struct GroupViewRoles
{
@@ -14,7 +15,7 @@ struct GroupViewRoles
};
};
struct Group;
struct VisualGroup;
class GroupView : public QAbstractItemView
{
@@ -35,35 +36,20 @@ public:
void setSelection(const QRect &rect,
const QItemSelectionModel::SelectionFlags commands) override;
virtual int horizontalOffset() const override
{
return horizontalScrollBar()->value();
}
virtual int verticalOffset() const override
{
return verticalScrollBar()->value();
}
virtual void scrollContentsBy(int dx, int dy) override
{
scrollDirtyRegion(dx, dy);
viewport()->scroll(dx, dy);
}
/*
* TODO!
*/
virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override
{
return;
}
virtual int horizontalOffset() const override;
virtual int verticalOffset() const override;
virtual void scrollContentsBy(int dx, int dy) override;
virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override;
virtual QModelIndex moveCursor(CursorAction cursorAction,
Qt::KeyboardModifiers modifiers) override;
virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override;
int spacing() const
{
return m_spacing;
};
protected
slots:
virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
@@ -90,57 +76,50 @@ protected:
void startDrag(Qt::DropActions supportedActions) override;
private:
friend struct Group;
friend struct VisualGroup;
QList<VisualGroup *> m_groups;
QList<Group *> m_groups;
// geometry
int m_leftMargin = 5;
int m_rightMargin = 5;
int m_bottomMargin = 5;
int m_categoryMargin = 5;
int m_spacing = 5;
int m_itemWidth = 100;
int m_currentItemsPerRow = -1;
int m_currentCursorColumn= -1;
mutable QCache<int, QRect> geometryCache;
int m_leftMargin;
int m_rightMargin;
int m_bottomMargin;
int m_categoryMargin;
// bool m_updatesDisabled;
Group *category(const QModelIndex &index) const;
Group *category(const QString &cat) const;
Group *categoryAt(const QPoint &pos) const;
int itemsPerRow() const;
int contentWidth() const;
private:
int itemWidth() const;
int categoryRowHeight(const QModelIndex &index) const;
/*QLineEdit *m_categoryEditor;
Category *m_editedCategory;
void startCategoryEditor(Category *category);
private slots:
void endCategoryEditor();*/
private: /* variables */
/// point where the currently active mouse action started in geometry coordinates
// point where the currently active mouse action started in geometry coordinates
QPoint m_pressedPosition;
QPersistentModelIndex m_pressedIndex;
bool m_pressedAlreadySelected;
Group *m_pressedCategory;
VisualGroup *m_pressedCategory;
QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag;
QPoint m_lastDragPosition;
int m_spacing = 5;
VisualGroup *category(const QModelIndex &index) const;
VisualGroup *category(const QString &cat) const;
VisualGroup *categoryAt(const QPoint &pos) const;
int itemsPerRow() const
{
return m_currentItemsPerRow;
};
int contentWidth() const;
private: /* methods */
QPair<int, int> categoryInternalPosition(const QModelIndex &index) const;
int categoryInternalRowTop(const QModelIndex &index) const;
int itemHeightForCategoryRow(const Group *category, const int internalRow) const;
int itemWidth() const;
int calculateItemsPerRow() const;
int verticalScrollToValue(const QModelIndex &index, const QRect &rect,
QListView::ScrollHint hint) const;
QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const;
QList<QPair<QRect, QModelIndex>> draggablePaintPairs(const QModelIndexList &indices,
QRect *r) const;
bool isDragEventAccepted(QDropEvent *event);
QPair<Group *, int> rowDropPos(const QPoint &pos);
QPair<VisualGroup *, int> rowDropPos(const QPoint &pos);
QPoint offset() const;
};

View File

@@ -1,6 +1,7 @@
#include "GroupedProxyModel.h"
#include "GroupView.h"
#include <QDebug>
GroupedProxyModel::GroupedProxyModel(QObject *parent) : QSortFilterProxyModel(parent)
{
@@ -16,7 +17,13 @@ bool GroupedProxyModel::lessThan(const QModelIndex &left, const QModelIndex &rig
}
else
{
return leftCategory < rightCategory;
// FIXME: real group sorting happens in GroupView::updateGeometries(), see LocaleString
auto result = leftCategory.localeAwareCompare(rightCategory);
if(result == 0)
{
return subSortLessThan(left, right);
}
return result < 0;
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@
#include <QtMath>
#include "GroupView.h"
#include "logic/BaseInstance.h"
#include "logic/lists/InstanceList.h"
#include "BaseInstance.h"
#include "InstanceList.h"
QCache<QString, QPixmap> ListViewDelegate::m_pixmapCache;
@@ -113,14 +113,14 @@ void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItemV4 &option
void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseInstance *instance)
{
QList<QString> pixmaps;
for (auto flag : instance->flags())
const BaseInstance::InstanceFlags flags = instance->flags();
if (flags & BaseInstance::VersionBrokenFlag)
{
switch (flag)
{
case BaseInstance::VersionBrokenFlag:
pixmaps.append("broken");
break;
}
pixmaps.append("broken");
}
if (flags & BaseInstance::UpdateAvailable)
{
pixmaps.append("updateavailable");
}
// begin easter eggs
@@ -158,7 +158,7 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseIns
{
return;
}
const QPixmap pixmap = ListViewDelegate::requestPixmap(it.next()).scaled(
const QPixmap pixmap = ListViewDelegate::requestBadgePixmap(it.next()).scaled(
itemSide, itemSide, Qt::KeepAspectRatio, Qt::FastTransformation);
painter->drawPixmap(option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide,
y * itemSide + qMax(y - 1, 0) * spacing, itemSide, itemSide,
@@ -352,7 +352,7 @@ QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option,
return sz;
}
QPixmap ListViewDelegate::requestPixmap(const QString &key)
QPixmap ListViewDelegate::requestBadgePixmap(const QString &key)
{
if (!m_pixmapCache.contains(key))
{

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,7 @@ class ListViewDelegate : public QStyledItemDelegate
public:
explicit ListViewDelegate(QObject *parent = 0);
static QPixmap requestPixmap(const QString &key);
static QPixmap requestBadgePixmap(const QString &key);
protected:
void paint(QPainter *painter, const QStyleOptionViewItem &option,

View File

@@ -1,4 +1,4 @@
#include "Group.h"
#include "VisualGroup.h"
#include <QModelIndex>
#include <QPainter>
@@ -7,30 +7,83 @@
#include "GroupView.h"
Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false)
VisualGroup::VisualGroup(const QString &text, GroupView *view) : view(view), text(text), collapsed(false)
{
}
Group::Group(const Group *other)
VisualGroup::VisualGroup(const VisualGroup *other)
: view(other->view), text(other->text), collapsed(other->collapsed)
{
}
void Group::update()
void VisualGroup::update()
{
firstItemIndex = firstItem().row();
auto temp_items = items();
auto itemsPerRow = view->itemsPerRow();
rowHeights = QVector<int>(numRows());
for (int i = 0; i < numRows(); ++i)
int numRows = qMax(1, qCeil((qreal)temp_items.size() / (qreal)itemsPerRow));
rows = QVector<VisualRow>(numRows);
int maxRowHeight = 0;
int positionInRow = 0;
int currentRow = 0;
int offsetFromTop = 0;
for (auto item: temp_items)
{
rowHeights[i] = view->categoryRowHeight(
view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0));
if(positionInRow == itemsPerRow)
{
rows[currentRow].height = maxRowHeight;
rows[currentRow].top = offsetFromTop;
currentRow ++;
offsetFromTop += maxRowHeight + 5;
positionInRow = 0;
maxRowHeight = 0;
}
auto itemHeight = view->itemDelegate()->sizeHint(view->viewOptions(), item).height();
if(itemHeight > maxRowHeight)
{
maxRowHeight = itemHeight;
}
rows[currentRow].items.append(item);
positionInRow++;
}
rows[currentRow].height = maxRowHeight;
rows[currentRow].top = offsetFromTop;
}
Group::HitResults Group::hitScan(const QPoint &pos) const
QPair<int, int> VisualGroup::positionOf(const QModelIndex &index) const
{
Group::HitResults results = Group::NoHit;
int x = 0;
int y = 0;
for (auto & row: rows)
{
for(auto x = 0; x < row.items.size(); x++)
{
if(row.items[x] == index)
{
return qMakePair(x,y);
}
}
y++;
}
return qMakePair(x, y);
}
int VisualGroup::rowTopOf(const QModelIndex &index) const
{
auto position = positionOf(index);
return rows[position.second].top;
}
int VisualGroup::rowHeightOf(const QModelIndex &index) const
{
auto position = positionOf(index);
return rows[position.second].height;
}
VisualGroup::HitResults VisualGroup::hitScan(const QPoint &pos) const
{
VisualGroup::HitResults results = VisualGroup::NoHit;
int y_start = verticalPosition();
int body_start = y_start + headerHeight();
int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5?
@@ -38,28 +91,28 @@ Group::HitResults Group::hitScan(const QPoint &pos) const
// int x = pos.x();
if (y < y_start)
{
results = Group::NoHit;
results = VisualGroup::NoHit;
}
else if (y < body_start)
{
results = Group::HeaderHit;
results = VisualGroup::HeaderHit;
int collapseSize = headerHeight() - 4;
// the icon
QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize);
if (iconRect.contains(pos))
{
results |= Group::CheckboxHit;
results |= VisualGroup::CheckboxHit;
}
}
else if (y < body_end)
{
results |= Group::BodyHit;
results |= VisualGroup::BodyHit;
}
return results;
}
void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
void VisualGroup::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
{
painter->setRenderHint(QPainter::Antialiasing);
@@ -190,12 +243,12 @@ void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option)
//END: text
}
int Group::totalHeight() const
int VisualGroup::totalHeight() const
{
return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'?
}
int Group::headerHeight() const
int VisualGroup::headerHeight() const
{
QFont font(QApplication::font());
font.setBold(true);
@@ -213,31 +266,27 @@ int Group::headerHeight() const
*/
}
int Group::contentHeight() const
int VisualGroup::contentHeight() const
{
if (collapsed)
{
return 0;
}
int result = 0;
for (int i = 0; i < rowHeights.size(); ++i)
{
result += rowHeights[i];
}
return result;
auto last = rows[numRows() - 1];
return last.top + last.height;
}
int Group::numRows() const
int VisualGroup::numRows() const
{
return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow()));
return rows.size();
}
int Group::verticalPosition() const
int VisualGroup::verticalPosition() const
{
return m_verticalPosition;
}
QList<QModelIndex> Group::items() const
QList<QModelIndex> VisualGroup::items() const
{
QList<QModelIndex> indices;
for (int i = 0; i < view->model()->rowCount(); ++i)
@@ -250,20 +299,3 @@ QList<QModelIndex> Group::items() const
}
return indices;
}
int Group::numItems() const
{
return items().size();
}
QModelIndex Group::firstItem() const
{
QList<QModelIndex> indices = items();
return indices.isEmpty() ? QModelIndex() : indices.first();
}
QModelIndex Group::lastItem() const
{
QList<QModelIndex> indices = items();
return indices.isEmpty() ? QModelIndex() : indices.last();
}

View File

@@ -9,22 +9,37 @@ class GroupView;
class QPainter;
class QModelIndex;
struct Group
struct VisualRow
{
QList<QModelIndex> items;
int height = 0;
int top = 0;
inline int size() const
{
return items.size();
}
inline QModelIndex &operator[](int i)
{
return items[i];
}
};
struct VisualGroup
{
/* constructors */
Group(const QString &text, GroupView *view);
Group(const Group *other);
VisualGroup(const QString &text, GroupView *view);
VisualGroup(const VisualGroup *other);
/* data */
GroupView *view = nullptr;
QString text;
bool collapsed = false;
QVector<int> rowHeights;
QVector<VisualRow> rows;
int firstItemIndex = 0;
int m_verticalPosition = 0;
/* logic */
/// do stuff. and things. TODO: redo.
/// update the internal list of items and flow them into the rows.
void update();
/// draw the header at y-position.
@@ -42,9 +57,21 @@ struct Group
/// the number of visual rows this group has
int numRows() const;
/// actually calculate the above value
int calculateNumRows() const;
/// the height at which this group starts, in pixels
int verticalPosition() const;
/// relative geometry - top of the row of the given item
int rowTopOf(const QModelIndex &index) const;
/// height of the row of the given item
int rowHeightOf(const QModelIndex &index) const;
/// x/y position of the given item inside the group (in items!)
QPair<int, int> positionOf(const QModelIndex &index) const;
enum HitResult
{
NoHit = 0x0,
@@ -58,12 +85,7 @@ struct Group
/// shoot! BANG! what did we hit?
HitResults hitScan (const QPoint &pos) const;
/// super derpy thing.
QList<QModelIndex> items() const;
/// I don't even
int numItems() const;
QModelIndex firstItem() const;
QModelIndex lastItem() const;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults)
Q_DECLARE_OPERATORS_FOR_FLAGS(VisualGroup::HitResults)

View File

@@ -1,16 +1,16 @@
#include "MultiMC.h"
#include "gui/MainWindow.h"
#include "MainWindow.h"
int main_gui(MultiMC &app)
{
// show main window
QIcon::setThemeName("multimc");
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()));
mainWin.show();
mainWin.checkMigrateLegacyAssets();
mainWin.checkSetDefaultJava();
mainWin.checkInstancePathForProblems();
return app.exec();
}
@@ -22,6 +22,14 @@ int main(int argc, char *argv[])
Q_INIT_RESOURCE(instances);
Q_INIT_RESOURCE(multimc);
Q_INIT_RESOURCE(backgrounds);
Q_INIT_RESOURCE(versions);
Q_INIT_RESOURCE(pe_dark);
Q_INIT_RESOURCE(pe_light);
Q_INIT_RESOURCE(pe_blue);
Q_INIT_RESOURCE(pe_colored);
Q_INIT_RESOURCE(OSX);
Q_INIT_RESOURCE(iOS);
switch (app.status())
{

View File

@@ -37,6 +37,10 @@ if [ "x$DEPS_LIST" = "x" ]; then
# Run MultiMC in valgrind
# 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}" $@
# use callgrind_control -i on/off to profile actions
# Exit with MultiMC's exit code.
exit $?
else

View File

@@ -0,0 +1,62 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "PageDialog.h"
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QKeyEvent>
#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);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(m_container);
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
setLayout(mainLayout);
QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close);
buttons->button(QDialogButtonBox::Close)->setDefault(true);
m_container->addButtons(buttons);
connect(buttons->button(QDialogButtonBox::Close), SIGNAL(clicked()), this, SLOT(close()));
connect(buttons->button(QDialogButtonBox::Help), SIGNAL(clicked()), m_container,
SLOT(help()));
restoreGeometry(
QByteArray::fromBase64(MMC->settings()->get("PagedGeometry").toByteArray()));
}
void PageDialog::closeEvent(QCloseEvent *event)
{
if (m_container->requestClose(event))
{
MMC->settings()->set("PagedGeometry", saveGeometry().toBase64());
QDialog::closeEvent(event);
}
}

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,29 +16,21 @@
#pragma once
#include <QDialog>
#include "pages/BasePageProvider.h"
namespace Ui
{
class LWJGLSelectDialog;
}
class LWJGLSelectDialog : public QDialog
class PageContainer;
class PageDialog : public QDialog
{
Q_OBJECT
public:
explicit LWJGLSelectDialog(QWidget *parent = 0);
~LWJGLSelectDialog();
QString selectedVersion() const;
explicit PageDialog(BasePageProviderPtr pageProvider, QString defaultId = QString(),
QWidget *parent = 0);
virtual ~PageDialog() {}
private
slots:
void on_refreshButton_clicked();
void loadingStateUpdated(bool loading);
void loadingFailed(QString error);
virtual void closeEvent(QCloseEvent *event);
private:
Ui::LWJGLSelectDialog *ui;
PageContainer * m_container;
};

View File

@@ -1,4 +1,4 @@
/* Copyright 2013 MultiMC Contributors
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,19 +14,25 @@
*/
#pragma once
#include <QString>
#include <settingsobject.h>
#include <QIcon>
#include <memory>
#include "BaseInstance_p.h"
#include "ModList.h"
class LegacyInstancePrivate : public BaseInstancePrivate
class BasePage
{
public:
virtual ~LegacyInstancePrivate() {};
std::shared_ptr<ModList> jar_mod_list;
std::shared_ptr<ModList> core_mod_list;
std::shared_ptr<ModList> loader_mod_list;
std::shared_ptr<ModList> texture_pack_list;
virtual ~BasePage() {}
virtual QString id() const = 0;
virtual QString displayName() const = 0;
virtual QIcon icon() const = 0;
virtual bool apply() { return true; }
virtual bool shouldDisplay() const { return true; }
virtual QString helpPage() const { return QString(); }
virtual void opened() {}
virtual void closed() {}
int stackIndex = -1;
int listIndex = -1;
};
typedef std::shared_ptr<BasePage> BasePagePtr;

View File

@@ -0,0 +1,69 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "BasePage.h"
#include <memory>
#include <functional>
class BasePageProvider
{
public:
virtual QList<BasePage *> getPages() = 0;
virtual QString dialogTitle() = 0;
};
class GenericPageProvider : public BasePageProvider
{
typedef std::function<BasePage *()> PageCreator;
public:
explicit GenericPageProvider(const QString &dialogTitle)
: m_dialogTitle(dialogTitle)
{
}
QList<BasePage *> getPages() override
{
QList<BasePage *> pages;
for (PageCreator creator : m_creators)
{
pages.append(creator());
}
return pages;
}
QString dialogTitle() override { return m_dialogTitle; }
void setDialogTitle(const QString &title)
{
m_dialogTitle = title;
}
void addPageCreator(PageCreator page)
{
m_creators.append(page);
}
template<typename PageClass>
void addPage()
{
addPageCreator([](){return new PageClass();});
}
private:
QList<PageCreator> m_creators;
QString m_dialogTitle;
};
typedef std::shared_ptr<BasePageProvider> BasePageProviderPtr;

View File

@@ -0,0 +1,205 @@
#include "InstanceSettingsPage.h"
#include "ui_InstanceSettingsPage.h"
#include <QFileDialog>
#include <QDialog>
#include <QMessageBox>
#include "dialogs/VersionSelectDialog.h"
#include "JavaCommon.h"
#include "MultiMC.h"
#include <java/JavaVersionList.h>
InstanceSettingsPage::InstanceSettingsPage(BaseInstance *inst, QWidget *parent)
: QWidget(parent), ui(new Ui::InstanceSettingsPage), m_instance(inst)
{
m_settings = &(inst->settings());
ui->setupUi(this);
loadSettings();
}
bool InstanceSettingsPage::shouldDisplay() const
{
return !m_instance->isRunning();
}
InstanceSettingsPage::~InstanceSettingsPage()
{
delete ui;
}
bool InstanceSettingsPage::apply()
{
applySettings();
return true;
}
void InstanceSettingsPage::applySettings()
{
// Console
bool console = ui->consoleSettingsBox->isChecked();
m_settings->set("OverrideConsole", console);
if (console)
{
m_settings->set("ShowConsole", ui->showConsoleCheck->isChecked());
m_settings->set("AutoCloseConsole", ui->autoCloseConsoleCheck->isChecked());
}
else
{
m_settings->reset("ShowConsole");
m_settings->reset("AutoCloseConsole");
}
// Window Size
bool window = ui->windowSizeGroupBox->isChecked();
m_settings->set("OverrideWindow", window);
if (window)
{
m_settings->set("LaunchMaximized", ui->maximizedCheckBox->isChecked());
m_settings->set("MinecraftWinWidth", ui->windowWidthSpinBox->value());
m_settings->set("MinecraftWinHeight", ui->windowHeightSpinBox->value());
}
else
{
m_settings->reset("LaunchMaximized");
m_settings->reset("MinecraftWinWidth");
m_settings->reset("MinecraftWinHeight");
}
// Memory
bool memory = ui->memoryGroupBox->isChecked();
m_settings->set("OverrideMemory", memory);
if (memory)
{
m_settings->set("MinMemAlloc", ui->minMemSpinBox->value());
m_settings->set("MaxMemAlloc", ui->maxMemSpinBox->value());
m_settings->set("PermGen", ui->permGenSpinBox->value());
}
else
{
m_settings->reset("MinMemAlloc");
m_settings->reset("MaxMemAlloc");
m_settings->reset("PermGen");
}
// Java Install Settings
bool javaInstall = ui->javaSettingsGroupBox->isChecked();
m_settings->set("OverrideJavaLocation", javaInstall);
if (javaInstall)
{
m_settings->set("JavaPath", ui->javaPathTextBox->text());
}
else
{
m_settings->reset("JavaPath");
}
// Java arguments
bool javaArgs = ui->javaArgumentsGroupBox->isChecked();
m_settings->set("OverrideJavaArgs", javaArgs);
if(javaArgs)
{
m_settings->set("JvmArgs", ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
JavaCommon::checkJVMArgs(m_settings->get("JvmArgs").toString(), this->parentWidget());
}
else
{
m_settings->reset("JvmArgs");
}
// old generic 'override both' is removed.
m_settings->reset("OverrideJava");
// Custom Commands
bool custcmd = ui->customCommandsGroupBox->isChecked();
m_settings->set("OverrideCommands", custcmd);
if (custcmd)
{
m_settings->set("PreLaunchCommand", ui->preLaunchCmdTextBox->text());
m_settings->set("PostExitCommand", ui->postExitCmdTextBox->text());
}
else
{
m_settings->reset("PreLaunchCommand");
m_settings->reset("PostExitCommand");
}
}
void InstanceSettingsPage::loadSettings()
{
// Console
ui->consoleSettingsBox->setChecked(m_settings->get("OverrideConsole").toBool());
ui->showConsoleCheck->setChecked(m_settings->get("ShowConsole").toBool());
ui->autoCloseConsoleCheck->setChecked(m_settings->get("AutoCloseConsole").toBool());
// Window Size
ui->windowSizeGroupBox->setChecked(m_settings->get("OverrideWindow").toBool());
ui->maximizedCheckBox->setChecked(m_settings->get("LaunchMaximized").toBool());
ui->windowWidthSpinBox->setValue(m_settings->get("MinecraftWinWidth").toInt());
ui->windowHeightSpinBox->setValue(m_settings->get("MinecraftWinHeight").toInt());
// Memory
ui->memoryGroupBox->setChecked(m_settings->get("OverrideMemory").toBool());
ui->minMemSpinBox->setValue(m_settings->get("MinMemAlloc").toInt());
ui->maxMemSpinBox->setValue(m_settings->get("MaxMemAlloc").toInt());
ui->permGenSpinBox->setValue(m_settings->get("PermGen").toInt());
// Java Settings
bool overrideJava = m_settings->get("OverrideJava").toBool();
bool overrideLocation = m_settings->get("OverrideJavaLocation").toBool() || overrideJava;
bool overrideArgs = m_settings->get("OverrideJavaArgs").toBool() || overrideJava;
ui->javaSettingsGroupBox->setChecked(overrideLocation);
ui->javaPathTextBox->setText(m_settings->get("JavaPath").toString());
ui->javaArgumentsGroupBox->setChecked(overrideArgs);
ui->jvmArgsTextBox->setPlainText(m_settings->get("JvmArgs").toString());
// Custom Commands
ui->customCommandsGroupBox->setChecked(m_settings->get("OverrideCommands").toBool());
ui->preLaunchCmdTextBox->setText(m_settings->get("PreLaunchCommand").toString());
ui->postExitCmdTextBox->setText(m_settings->get("PostExitCommand").toString());
}
void InstanceSettingsPage::on_javaDetectBtn_clicked()
{
JavaVersionPtr java;
VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true);
vselect.setResizeOn(2);
vselect.exec();
if (vselect.result() == QDialog::Accepted && vselect.selectedVersion())
{
java = std::dynamic_pointer_cast<JavaVersion>(vselect.selectedVersion());
ui->javaPathTextBox->setText(java->path);
}
}
void InstanceSettingsPage::on_javaBrowseBtn_clicked()
{
QString dir = QFileDialog::getOpenFileName(this, tr("Find Java executable"));
if (!dir.isNull())
{
ui->javaPathTextBox->setText(dir);
}
}
void InstanceSettingsPage::on_javaTestBtn_clicked()
{
if(checker)
{
return;
}
checker.reset(new JavaCommon::TestCheck(
this, ui->javaPathTextBox->text(), ui->jvmArgsTextBox->toPlainText().replace("\n", " "),
ui->minMemSpinBox->value(), ui->maxMemSpinBox->value(), ui->permGenSpinBox->value()));
connect(checker.get(), SIGNAL(finished()), SLOT(checkerFinished()));
checker->run();
}
void InstanceSettingsPage::checkerFinished()
{
checker.reset();
}

View File

@@ -0,0 +1,74 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QWidget>
#include "java/JavaChecker.h"
#include "BaseInstance.h"
#include <QObjectPtr.h>
#include "BasePage.h"
#include "JavaCommon.h"
#include "MultiMC.h"
class JavaChecker;
namespace Ui
{
class InstanceSettingsPage;
}
class InstanceSettingsPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit InstanceSettingsPage(BaseInstance *inst, QWidget *parent = 0);
virtual ~InstanceSettingsPage();
virtual QString displayName() const override
{
return tr("Settings");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("instance-settings");
}
virtual QString id() const override
{
return "settings";
}
virtual bool apply();
virtual QString helpPage() const override
{
return "Instance-settings";
}
virtual bool shouldDisplay() const;
private slots:
void on_javaDetectBtn_clicked();
void on_javaTestBtn_clicked();
void on_javaBrowseBtn_clicked();
void applySettings();
void loadSettings();
void checkerFinished();
private:
Ui::InstanceSettingsPage *ui;
BaseInstance *m_instance;
SettingsObject *m_settings;
QObjectPtr<JavaCommon::TestCheck> checker;
};

View File

@@ -1,19 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InstanceSettings</class>
<widget class="QDialog" name="InstanceSettings">
<class>InstanceSettingsPage</class>
<widget class="QWidget" name="InstanceSettingsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>526</width>
<height>637</height>
<width>458</width>
<height>426</height>
</rect>
</property>
<property name="windowTitle">
<string>Instance Settings</string>
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="settingsTabs">
<property name="tabShape">
@@ -23,134 +35,52 @@
<number>0</number>
</property>
<widget class="QWidget" name="minecraftTab">
<attribute name="title">
<string>Minecraft</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="windowSizeGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Window Size</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="maximizedCheckBox">
<property name="text">
<string>Start Minecraft maximized?</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayoutWindowSize">
<item row="1" column="0">
<widget class="QLabel" name="labelWindowHeight">
<property name="text">
<string>Window height:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelWindowWidth">
<property name="text">
<string>Window width:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="windowWidthSpinBox">
<property name="minimum">
<number>854</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>854</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="windowHeightSpinBox">
<property name="minimum">
<number>480</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="value">
<number>480</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="consoleSettingsBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Console Settings</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="showConsoleCheck">
<property name="text">
<string>Show console while the game is running?</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoCloseConsoleCheck">
<property name="text">
<string>Automatically close console when the game quits?</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacerMinecraft">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="javaTab">
<attribute name="title">
<string>Java</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QGroupBox" name="javaSettingsGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Java installation</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="3">
<widget class="QLineEdit" name="javaPathTextBox"/>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="javaDetectBtn">
<property name="text">
<string>Auto-detect...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="javaBrowseBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="javaTestBtn">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="memoryGroupBox">
<property name="enabled">
@@ -257,12 +187,12 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="javaSettingsGroupBox">
<widget class="QGroupBox" name="javaArgumentsGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Java Settings</string>
<string>Java arguments</string>
</property>
<property name="checkable">
<bool>true</bool>
@@ -270,51 +200,157 @@
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="4">
<widget class="QPushButton" name="javaTestBtn">
<layout class="QGridLayout" name="gridLayout_5">
<item row="1" column="1">
<widget class="QPlainTextEdit" name="jvmArgsTextBox"/>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacerMinecraft">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="javaTab">
<attribute name="title">
<string>Game windows</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="windowSizeGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Game Window</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QCheckBox" name="maximizedCheckBox">
<property name="text">
<string>Test</string>
<string>Start Minecraft maximized?</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelJavaPath">
<item>
<layout class="QGridLayout" name="gridLayoutWindowSize">
<item row="1" column="0">
<widget class="QLabel" name="labelWindowHeight">
<property name="text">
<string>Window height:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelWindowWidth">
<property name="text">
<string>Window width:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="windowWidthSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="value">
<number>854</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="windowHeightSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65536</number>
</property>
<property name="value">
<number>480</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="consoleSettingsBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Console Settings</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QCheckBox" name="showConsoleCheck">
<property name="text">
<string>Java path:</string>
<string>Show console while the game is running?</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelJVMArgs">
<item>
<widget class="QCheckBox" name="autoCloseConsoleCheck">
<property name="text">
<string>JVM arguments:</string>
</property>
</widget>
</item>
<item row="3" column="2" colspan="3">
<widget class="QLineEdit" name="jvmArgsTextBox"/>
</item>
<item row="0" column="2" colspan="3">
<widget class="QLineEdit" name="javaPathTextBox"/>
</item>
<item row="2" column="3">
<widget class="QPushButton" name="javaBrowseBtn">
<property name="text">
<string>Browse...</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="javaDetectBtn">
<property name="text">
<string>Auto-detect...</string>
<string>Automatically close console when the game quits?</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacerMinecraft_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>88</width>
<height>125</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Custom commands</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QGroupBox" name="customCommandsGroupBox">
<property name="enabled">
@@ -355,17 +391,8 @@
</item>
<item>
<widget class="QLabel" name="labelCustomCmdsDescription">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Pre-launch command runs before the instance launches and post-exit command runs after it exits. Both will be run in MultiMC's working directory with INST_ID, INST_DIR, and INST_NAME as environment variables.</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Pre-launch command runs before the instance launches and post-exit command runs after it exits.&lt;/p&gt;&lt;p&gt;Both will be run in MultiMC's working directory with extra environment variables:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_NAME - Name of the instance&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_ID - ID of the instance&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_DIR - absolute path of the instance&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_MC_DIR - absolute path of minecraft&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_JAVA - java binary used for launch&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;INST_JAVA_ARGS - command-line parameters used for launch&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
@@ -378,25 +405,38 @@
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacerMinecraft_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>88</width>
<height>186</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>settingsTabs</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>javaSettingsGroupBox</tabstop>
<tabstop>javaPathTextBox</tabstop>
<tabstop>javaDetectBtn</tabstop>
<tabstop>javaBrowseBtn</tabstop>
<tabstop>javaTestBtn</tabstop>
<tabstop>memoryGroupBox</tabstop>
<tabstop>minMemSpinBox</tabstop>
<tabstop>maxMemSpinBox</tabstop>
<tabstop>permGenSpinBox</tabstop>
<tabstop>javaArgumentsGroupBox</tabstop>
<tabstop>jvmArgsTextBox</tabstop>
<tabstop>windowSizeGroupBox</tabstop>
<tabstop>maximizedCheckBox</tabstop>
<tabstop>windowWidthSpinBox</tabstop>
@@ -404,12 +444,6 @@
<tabstop>consoleSettingsBox</tabstop>
<tabstop>showConsoleCheck</tabstop>
<tabstop>autoCloseConsoleCheck</tabstop>
<tabstop>memoryGroupBox</tabstop>
<tabstop>minMemSpinBox</tabstop>
<tabstop>maxMemSpinBox</tabstop>
<tabstop>permGenSpinBox</tabstop>
<tabstop>javaSettingsGroupBox</tabstop>
<tabstop>jvmArgsTextBox</tabstop>
<tabstop>customCommandsGroupBox</tabstop>
<tabstop>preLaunchCmdTextBox</tabstop>
<tabstop>postExitCmdTextBox</tabstop>

View File

@@ -0,0 +1,163 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "LegacyJarModPage.h"
#include "ui_LegacyJarModPage.h"
#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 "MultiMC.h"
#include <GuiUtil.h>
LegacyJarModPage::LegacyJarModPage(LegacyInstance *inst, QWidget *parent)
: QWidget(parent), ui(new Ui::LegacyJarModPage), m_inst(inst)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_jarmods = m_inst->jarModList();
ui->jarModsTreeView->setModel(m_jarmods.get());
ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop);
ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
ui->jarModsTreeView->installEventFilter(this);
m_jarmods->startWatching();
auto smodel = ui->jarModsTreeView->selectionModel();
connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
SLOT(jarCurrent(QModelIndex, QModelIndex)));
}
LegacyJarModPage::~LegacyJarModPage()
{
m_jarmods->stopWatching();
delete ui;
}
bool LegacyJarModPage::shouldDisplay() const
{
return !m_inst->isRunning();
}
bool LegacyJarModPage::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() != QEvent::KeyPress || obj != ui->jarModsTreeView)
{
return QWidget::eventFilter(obj, ev);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
switch (keyEvent->key())
{
case Qt::Key_Up:
{
if (keyEvent->modifiers() & Qt::ControlModifier)
{
on_moveJarUpBtn_clicked();
return true;
}
break;
}
case Qt::Key_Down:
{
if (keyEvent->modifiers() & Qt::ControlModifier)
{
on_moveJarDownBtn_clicked();
return true;
}
break;
}
case Qt::Key_Delete:
on_rmJarBtn_clicked();
return true;
case Qt::Key_Plus:
on_addJarBtn_clicked();
return true;
default:
break;
}
return QWidget::eventFilter(obj, ev);
}
void LegacyJarModPage::on_addJarBtn_clicked()
{
auto list = GuiUtil::BrowseForMods("jarmod", tr("Select jar mods"), tr("Minecraft.jar mods (*.zip *.jar)"), this->parentWidget());
if(!list.empty())
{
m_jarmods->stopWatching();
for (auto filename : list)
{
m_jarmods->installMod(QFileInfo(filename));
}
m_jarmods->startWatching();
}
}
void LegacyJarModPage::on_moveJarDownBtn_clicked()
{
int first, last;
auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
if (!lastfirst(list, first, last))
return;
m_jarmods->moveModsDown(first, last);
}
void LegacyJarModPage::on_moveJarUpBtn_clicked()
{
int first, last;
auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
if (!lastfirst(list, first, last))
return;
m_jarmods->moveModsUp(first, last);
}
void LegacyJarModPage::on_rmJarBtn_clicked()
{
int first, last;
auto list = ui->jarModsTreeView->selectionModel()->selectedRows();
if (!lastfirst(list, first, last))
return;
m_jarmods->stopWatching();
m_jarmods->deleteMods(first, last);
m_jarmods->startWatching();
}
void LegacyJarModPage::on_viewJarBtn_clicked()
{
openDirInDefaultProgram(m_inst->jarModsDir(), true);
}
void LegacyJarModPage::jarCurrent(QModelIndex current, QModelIndex previous)
{
if (!current.isValid())
{
ui->jarMIFrame->clear();
return;
}
int row = current.row();
Mod &m = m_jarmods->operator[](row);
ui->jarMIFrame->updateWithMod(m);
}

View File

@@ -0,0 +1,76 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QWidget>
#include "net/NetJob.h"
#include "BasePage.h"
#include <MultiMC.h>
class ModList;
class LegacyInstance;
namespace Ui
{
class LegacyJarModPage;
}
class LegacyJarModPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit LegacyJarModPage(LegacyInstance *inst, QWidget *parent = 0);
virtual ~LegacyJarModPage();
virtual QString displayName() const
{
return tr("Jar Mods");
}
virtual QIcon icon() const
{
return MMC->getThemedIcon("jarmods");
}
virtual QString id() const
{
return "jarmods";
}
virtual QString helpPage() const override
{
return "Legacy-jar-mods";
}
virtual bool shouldDisplay() const;
private
slots:
void on_addJarBtn_clicked();
void on_rmJarBtn_clicked();
void on_moveJarUpBtn_clicked();
void on_moveJarDownBtn_clicked();
void on_viewJarBtn_clicked();
void jarCurrent(QModelIndex current, QModelIndex previous);
protected:
virtual bool eventFilter(QObject *obj, QEvent *ev) override;
private:
Ui::LegacyJarModPage *ui;
std::shared_ptr<ModList> m_jarmods;
LegacyInstance *m_inst;
NetJobPtr forgeJob;
};

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LegacyJarModPage</class>
<widget class="QWidget" name="LegacyJarModPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>659</width>
<height>593</height>
</rect>
</property>
<property name="windowTitle">
<string>LegacyJarModPage</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="ModListView" name="jarModsTreeView">
<property name="verticalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOn</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="jarModsButtonBox">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Selection</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmJarBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveJarUpBtn">
<property name="text">
<string>Move &amp;Up</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="moveJarDownBtn">
<property name="text">
<string>Move &amp;Down</string>
</property>
</widget>
</item>
<item>
<widget class="LineSeparator" name="separator" native="true"/>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Install</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addJarBtn">
<property name="text">
<string>&amp;Add jar mod</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="viewJarBtn">
<property name="text">
<string>&amp;View Folder</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="MCModInfoFrame" name="jarMIFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModListView</class>
<extends>QTreeView</extends>
<header>widgets/ModListView.h</header>
</customwidget>
<customwidget>
<class>MCModInfoFrame</class>
<extends>QFrame</extends>
<header>widgets/MCModInfoFrame.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>LineSeparator</class>
<extends>QWidget</extends>
<header>widgets/LineSeparator.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,25 @@
#include "LegacyUpgradePage.h"
#include "ui_LegacyUpgradePage.h"
#include "minecraft/LegacyInstance.h"
LegacyUpgradePage::LegacyUpgradePage(LegacyInstance *inst, QWidget *parent)
: QWidget(parent), ui(new Ui::LegacyUpgradePage), m_inst(inst)
{
ui->setupUi(this);
}
LegacyUpgradePage::~LegacyUpgradePage()
{
delete ui;
}
void LegacyUpgradePage::on_upgradeButton_clicked()
{
// now what?
}
bool LegacyUpgradePage::shouldDisplay() const
{
return !m_inst->isRunning();
}

View File

@@ -0,0 +1,60 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QWidget>
#include "minecraft/OneSixInstance.h"
#include "pages/BasePage.h"
#include <MultiMC.h>
namespace Ui
{
class LegacyUpgradePage;
}
class LegacyUpgradePage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit LegacyUpgradePage(LegacyInstance *inst, QWidget *parent = 0);
virtual ~LegacyUpgradePage();
virtual QString displayName() const override
{
return tr("Upgrade");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("checkupdate");
}
virtual QString id() const override
{
return "upgrade";
}
virtual QString helpPage() const override
{
return "Legacy-upgrade";
}
virtual bool shouldDisplay() const;
private
slots:
void on_upgradeButton_clicked();
private:
Ui::LegacyUpgradePage *ui;
LegacyInstance *m_inst;
};

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LegacyUpgradePage</class>
<widget class="QWidget" name="LegacyUpgradePage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>546</width>
<height>405</height>
</rect>
</property>
<property name="windowTitle">
<string>Upgrade</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top: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:18pt; font-weight:600;&quot;&gt;New format is available&lt;/span&gt; &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;MultiMC now supports old Minecraft versions in the new (OneSix) instance format. The old format won't be getting any new features and only the most critical bugfixes. As a consequence, you should upgrade this instance. &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The upgrade will create a new instance with the same contents as the current one, in the new format. The original instance will remain untouched, in case anything goes wrong in the process. &lt;/p&gt;
&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Please report any issues on our &lt;a href=&quot;https://github.com/MultiMC/MultiMC5/issues&quot;&gt;&lt;img src=&quot;:/icons/multimc/22x22/bug.png&quot; /&gt;&lt;/a&gt;&lt;a href=&quot;https://github.com/MultiMC/MultiMC5/issues&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#68a0df;&quot;&gt;github issues page&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCommandLinkButton" name="upgradeButton">
<property name="text">
<string>Start the upgrade! (Not Yet Implemented, Coming Soon™)</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,227 @@
#include "LogPage.h"
#include "ui_LogPage.h"
#include "MultiMC.h"
#include <QIcon>
#include <QScrollBar>
#include <QShortcut>
#include "BaseProcess.h"
#include "GuiUtil.h"
LogPage::LogPage(BaseProcess *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,
SLOT(write(QString, MessageLevel::Enum)));
// create the format and set its font
defaultFormat = new QTextCharFormat(ui->text->currentCharFormat());
QString fontFamily = MMC->settings()->get("ConsoleFont").toString();
bool conversionOk = false;
int fontSize = MMC->settings()->get("ConsoleFontSize").toInt(&conversionOk);
if(!conversionOk)
{
fontSize = 11;
}
defaultFormat->setFont(QFont(fontFamily, fontSize));
auto findShortcut = new QShortcut(QKeySequence(QKeySequence::Find), this);
connect(findShortcut, SIGNAL(activated()), SLOT(findActivated()));
auto findNextShortcut = new QShortcut(QKeySequence(QKeySequence::FindNext), this);
connect(findNextShortcut, SIGNAL(activated()), SLOT(findNextActivated()));
connect(ui->searchBar, SIGNAL(returnPressed()), SLOT(on_findButton_clicked()));
auto findPreviousShortcut = new QShortcut(QKeySequence(QKeySequence::FindPrevious), this);
connect(findPreviousShortcut, SIGNAL(activated()), SLOT(findPreviousActivated()));
}
LogPage::~LogPage()
{
delete ui;
delete defaultFormat;
}
bool LogPage::apply()
{
return true;
}
bool LogPage::shouldDisplay() const
{
return m_process->instance()->isRunning();
}
void LogPage::on_btnPaste_clicked()
{
GuiUtil::uploadPaste(ui->text->toPlainText(), this);
}
void LogPage::on_btnCopy_clicked()
{
GuiUtil::setClipboardText(ui->text->toPlainText());
}
void LogPage::on_btnClear_clicked()
{
ui->text->clear();
}
void LogPage::on_btnBottom_clicked()
{
ui->text->verticalScrollBar()->setSliderPosition(ui->text->verticalScrollBar()->maximum());
}
void LogPage::on_trackLogCheckbox_clicked(bool checked)
{
m_write_active = checked;
}
void LogPage::on_findButton_clicked()
{
auto modifiers = QApplication::keyboardModifiers();
if (modifiers & Qt::ShiftModifier)
{
findPreviousActivated();
}
else
{
findNextActivated();
}
}
void LogPage::findActivated()
{
// focus the search bar if it doesn't have focus
if (!ui->searchBar->hasFocus())
{
auto searchForCursor = ui->text->textCursor();
auto searchForString = searchForCursor.selectedText();
if (searchForString.size())
{
ui->searchBar->setText(searchForString);
}
ui->searchBar->setFocus();
ui->searchBar->selectAll();
}
}
void LogPage::findNextActivated()
{
auto toSearch = ui->searchBar->text();
if (toSearch.size())
{
ui->text->find(toSearch);
}
}
void LogPage::findPreviousActivated()
{
auto toSearch = ui->searchBar->text();
if (toSearch.size())
{
ui->text->find(toSearch, QTextDocument::FindBackward);
}
}
void LogPage::write(QString data, MessageLevel::Enum mode)
{
if (!m_write_active)
{
if (mode != MessageLevel::PrePost && mode != MessageLevel::MultiMC)
{
return;
}
}
// save the cursor so it can be restored.
auto savedCursor = ui->text->cursor();
QScrollBar *bar = ui->text->verticalScrollBar();
int max_bar = bar->maximum();
int val_bar = bar->value();
if (isVisible())
{
if (m_scroll_active)
{
m_scroll_active = (max_bar - val_bar) <= 1;
}
else
{
m_scroll_active = val_bar == max_bar;
}
}
if (data.endsWith('\n'))
data = data.left(data.length() - 1);
QStringList paragraphs = data.split('\n');
QStringList filtered;
for (QString &paragraph : paragraphs)
{
//TODO: implement filtering here.
filtered.append(paragraph);
}
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
}
}
while (iter.hasNext())
{
// append a paragraph/line
auto workCursor = ui->text->textCursor();
workCursor.movePosition(QTextCursor::End);
workCursor.insertText(iter.next(), format);
workCursor.insertBlock();
}
if (isVisible())
{
if (m_scroll_active)
{
bar->setValue(bar->maximum());
}
m_last_scroll_value = bar->value();
}
ui->text->setCursor(savedCursor);
}

View File

@@ -0,0 +1,86 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QWidget>
#include "BaseInstance.h"
#include "BaseProcess.h"
#include "BasePage.h"
#include <MultiMC.h>
namespace Ui
{
class LogPage;
}
class QTextCharFormat;
class LogPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit LogPage(BaseProcess *proc, QWidget *parent = 0);
virtual ~LogPage();
virtual QString displayName() const override
{
return tr("Minecraft Log");
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon("log");
}
virtual QString id() const override
{
return "console";
}
virtual bool apply();
virtual QString helpPage() const override
{
return "Minecraft-Logs";
}
virtual bool shouldDisplay() const;
private slots:
/**
* @brief write a string
* @param data the string
* @param mode the WriteMode
* lines have to be put through this as a whole!
*/
void write(QString data, MessageLevel::Enum level = MessageLevel::MultiMC);
void on_btnPaste_clicked();
void on_btnCopy_clicked();
void on_btnClear_clicked();
void on_btnBottom_clicked();
void on_trackLogCheckbox_clicked(bool checked);
void on_findButton_clicked();
void findActivated();
void findNextActivated();
void findPreviousActivated();
private:
Ui::LogPage *ui;
BaseProcess *m_process;
int m_last_scroll_value = 0;
bool m_scroll_active = true;
int m_saved_offset = 0;
bool m_write_active = true;
QTextCharFormat * defaultFormat;
};

View File

@@ -0,0 +1,167 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>LogPage</class>
<widget class="QWidget" name="LogPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>825</width>
<height>782</height>
</rect>
</property>
<property name="windowTitle">
<string>Log</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0" colspan="5">
<widget class="QPlainTextEdit" name="text">
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="plainText">
<string notr="true"/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
<property name="centerOnScroll">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="5">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="trackLogCheckbox">
<property name="text">
<string>Keep updating</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnCopy">
<property name="toolTip">
<string>Copy the whole log into the clipboard</string>
</property>
<property name="text">
<string>&amp;Copy</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnPaste">
<property name="toolTip">
<string>Upload the log to paste.ee - it will stay online for a month</string>
</property>
<property name="text">
<string>Upload</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnClear">
<property name="toolTip">
<string>Clear the log</string>
</property>
<property name="text">
<string>Clear</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Search:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="findButton">
<property name="text">
<string>Find</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="searchBar"/>
</item>
<item row="2" column="4">
<widget class="QPushButton" name="btnBottom">
<property name="toolTip">
<string>Scroll all the way to bottom</string>
</property>
<property name="text">
<string>Bottom</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>tabWidget</tabstop>
<tabstop>trackLogCheckbox</tabstop>
<tabstop>btnCopy</tabstop>
<tabstop>btnPaste</tabstop>
<tabstop>btnClear</tabstop>
<tabstop>text</tabstop>
<tabstop>searchBar</tabstop>
<tabstop>findButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,178 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ModFolderPage.h"
#include "ui_ModFolderPage.h"
#include <QMessageBox>
#include <QEvent>
#include <QKeyEvent>
#include <QDesktopServices>
#include <QAbstractItemModel>
#include <pathutils.h>
#include "MultiMC.h"
#include "dialogs/CustomMessageBox.h"
#include "dialogs/ModEditDialogCommon.h"
#include <GuiUtil.h>
#include "minecraft/ModList.h"
#include "minecraft/Mod.h"
#include "minecraft/VersionFilterData.h"
ModFolderPage::ModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id,
QString iconName, QString displayName, QString helpPage,
QWidget *parent)
: QWidget(parent), ui(new Ui::ModFolderPage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();
m_inst = inst;
m_mods = mods;
m_id = id;
m_displayName = displayName;
m_iconName = iconName;
m_helpName = helpPage;
m_filter = tr("%1 (*.zip *.jar)");
ui->modTreeView->setModel(m_mods.get());
ui->modTreeView->installEventFilter(this);
auto smodel = ui->modTreeView->selectionModel();
connect(smodel, SIGNAL(currentChanged(QModelIndex, QModelIndex)),
SLOT(modCurrent(QModelIndex, QModelIndex)));
}
void ModFolderPage::opened()
{
m_mods->startWatching();
}
void ModFolderPage::closed()
{
m_mods->stopWatching();
}
CoreModFolderPage::CoreModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods,
QString id, QString iconName, QString displayName,
QString helpPage, QWidget *parent)
: ModFolderPage(inst, mods, id, iconName, displayName, helpPage, parent)
{
}
ModFolderPage::~ModFolderPage()
{
m_mods->stopWatching();
delete ui;
}
bool ModFolderPage::shouldDisplay() const
{
if (m_inst)
return !m_inst->isRunning();
return true;
}
bool CoreModFolderPage::shouldDisplay() const
{
if (ModFolderPage::shouldDisplay())
{
auto inst = dynamic_cast<OneSixInstance *>(m_inst);
if (!inst)
return true;
auto version = inst->getMinecraftProfile();
if (!version)
return true;
if (version->m_releaseTime < g_VersionFilterData.legacyCutoffDate)
{
return true;
}
}
return false;
}
bool ModFolderPage::modListFilter(QKeyEvent *keyEvent)
{
switch (keyEvent->key())
{
case Qt::Key_Delete:
on_rmModBtn_clicked();
return true;
case Qt::Key_Plus:
on_addModBtn_clicked();
return true;
default:
break;
}
return QWidget::eventFilter(ui->modTreeView, keyEvent);
}
bool ModFolderPage::eventFilter(QObject *obj, QEvent *ev)
{
if (ev->type() != QEvent::KeyPress)
{
return QWidget::eventFilter(obj, ev);
}
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev);
if (obj == ui->modTreeView)
return modListFilter(keyEvent);
return QWidget::eventFilter(obj, ev);
}
void ModFolderPage::on_addModBtn_clicked()
{
auto list = GuiUtil::BrowseForMods(
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());
if (!list.empty())
{
m_mods->stopWatching();
for (auto filename : list)
{
m_mods->installMod(QFileInfo(filename));
}
m_mods->startWatching();
}
}
void ModFolderPage::on_rmModBtn_clicked()
{
int first, last;
auto list = ui->modTreeView->selectionModel()->selectedRows();
if (!lastfirst(list, first, last))
return;
m_mods->stopWatching();
m_mods->deleteMods(first, last);
m_mods->startWatching();
}
void ModFolderPage::on_viewModBtn_clicked()
{
openDirInDefaultProgram(m_mods->dir().absolutePath(), true);
}
void ModFolderPage::modCurrent(const QModelIndex &current, const QModelIndex &previous)
{
if (!current.isValid())
{
ui->frame->clear();
return;
}
int row = current.row();
Mod &m = m_mods->operator[](row);
ui->frame->updateWithMod(m);
}

View File

@@ -0,0 +1,102 @@
/* Copyright 2013-2015 MultiMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <QWidget>
#include "minecraft/OneSixInstance.h"
#include "BasePage.h"
#include <MultiMC.h>
class ModList;
namespace Ui
{
class ModFolderPage;
}
class ModFolderPage : public QWidget, public BasePage
{
Q_OBJECT
public:
explicit ModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id,
QString iconName, QString displayName, QString helpPage = "",
QWidget *parent = 0);
virtual ~ModFolderPage();
void setFilter(const QString & filter)
{
m_filter = filter;
}
virtual QString displayName() const override
{
return m_displayName;
}
virtual QIcon icon() const override
{
return MMC->getThemedIcon(m_iconName);
}
virtual QString id() const override
{
return m_id;
}
virtual QString helpPage() const override
{
return m_helpName;
}
virtual bool shouldDisplay() const;
virtual void opened();
virtual void closed();
protected:
bool eventFilter(QObject *obj, QEvent *ev);
bool modListFilter(QKeyEvent *ev);
protected:
BaseInstance *m_inst;
private:
Ui::ModFolderPage *ui;
std::shared_ptr<ModList> m_mods;
QString m_iconName;
QString m_id;
QString m_displayName;
QString m_helpName;
QString m_filter;
public
slots:
void modCurrent(const QModelIndex &current, const QModelIndex &previous);
private
slots:
void on_addModBtn_clicked();
void on_rmModBtn_clicked();
void on_viewModBtn_clicked();
};
class CoreModFolderPage : public ModFolderPage
{
public:
explicit CoreModFolderPage(BaseInstance *inst, std::shared_ptr<ModList> mods, QString id,
QString iconName, QString displayName, QString helpPage = "",
QWidget *parent = 0);
virtual ~CoreModFolderPage()
{
}
virtual bool shouldDisplay() const;
};

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModFolderPage</class>
<widget class="QWidget" name="ModFolderPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>723</width>
<height>532</height>
</rect>
</property>
<property name="windowTitle">
<string>Mods</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="ModListView" name="modTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DropOnly</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="addModBtn">
<property name="text">
<string>&amp;Add</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="rmModBtn">
<property name="text">
<string>&amp;Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="viewModBtn">
<property name="text">
<string>&amp;View Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2">
<widget class="MCModInfoFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ModListView</class>
<extends>QTreeView</extends>
<header>widgets/ModListView.h</header>
</customwidget>
<customwidget>
<class>MCModInfoFrame</class>
<extends>QFrame</extends>
<header>widgets/MCModInfoFrame.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

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