Compare commits

...
Author SHA1 Message Date
Tannin e43f126b03 - bugfix: crash on overwriting a mod (introduced after the previous release)
- bugfix: ncc installer now brings the installer window to front (again? more reliably? not sure if it really worked before)
2013-12-19 20:30:39 +01:00
Tannin 35c9fe3e98 - bugfix: archive.dll could cause a crash, attempting to close an archive that failed to open
- bugfix: upon changing categories the mappings of deleted categories wasn't cleaned up which could cause an error message
- bugfix: the number of esps/esms that can be loaded is actually 255 not 256 since the save game counts too
- bugfix: "visit on nexus" from the modinfo dialog also lead to the wrong url
2013-12-19 19:40:59 +01:00
Tannin 82f7b09b40 Merge 2013-12-14 17:19:27 +01:00
Tannin f33d24870f - added more information to readme
- 7zip source path is now configurable
2013-12-14 17:19:15 +01:00
Tannin 14ac234daf - plugin list now highlights plugins with attached ini files
- bugfix: opening nexus through the globe icon used the nmm.nexusmods.com url
2013-12-14 13:23:13 +01:00
Tannin 22b8eb7c5a - downgrading a mod (using replace) will now change the version
- bain installer will now accept archives with omod conversion data.
2013-12-11 20:43:30 +01:00
Tannin 9ee90a35dc - MO now applies a minimum to the nmm-compatibility field.
- bugfix: "visit on nexus" directed the browser to the servers meant for nmm
- bugfix: url for "check all for updates" and "enorse/unendorse" were not constructed correctly
2013-12-08 14:16:25 +01:00
Tannin ff78c6a014 - bugfix: extracting bsas did not work with non-ascii characters in the mod name
- another attempt to switch branches...
2013-12-07 17:41:45 +01:00
Tannin 340edcfbca - returned to default branch
- nexus interface will now use the nmm url
- nmm importer no longer cancels on a missing non-essential flag
2013-12-07 17:13:36 +01:00
Tannin c4f0bc39c7 Merge with default 2013-12-07 16:55:19 +01:00
Tannin e71c37df37 - bugfix: drag&drop on archive list didn't always work properly 2013-12-07 16:54:08 +01:00
Tannin b32457a7d2 - minor text fixes
- versions without a subminor version are now displayed without it (1.1 instead of 1.1.0)
- version compares now prefer the decimal comparison over the traditional
2013-12-07 16:07:28 +01:00
Tannin 6e1e8c37eb minor text changes 2013-12-07 14:23:51 +01:00
Tannin 469cc2d945 - download manager now saves the file times on nexus, for potential later use in version check
- nexus interface now supports 301 redirects
- now using the new nexus url format
- bugfix: "visit on nexus" used an outdated url scheme and thus caused unnecessary redirection
2013-12-07 12:48:18 +01:00
Tannin 0e90b9d233 - bugfix: changing nexus id directly via mod list did not cause the change to be saved 2013-12-04 19:54:28 +01:00
Tannin 0e19c84722 Added tag release v1.0.9 for changeset a61a930515ae 2013-12-01 23:52:42 +01:00
Tannin daf4ae44fa - added second context menu for changing categories that applies only changes to
the selected mods instead of replacing the existing set of categories (thanks to Ross!)
- bugfix: category filtering didn't work correctly when grouping was also active
2013-12-01 23:50:24 +01:00
Tannin ac29aa81f2 minor updates to readme and mod info ui 2013-12-01 21:43:16 +01:00
Tannin a0335d0a63 Added tag 1.0.8 for changeset a61a930515ae 2013-11-27 20:25:55 +01:00
Tannin f1515193ca - plugins can now be localized too
- fixes to the installer script
- extended build scripts to fetch translations from pootle server automatically
2013-11-27 20:25:24 +01:00
Tannin db0347212a - bugfix: installFile for mods was not a relative path if the downloads directory was non-default
- bugfix: crash after removing the last profile in the list (ordered alphabetically)
- improved subdirs ordering to ensure pythonrunner is compiled before pythonproxy
- translation files for plugins are now generated
- set some texts as un-translatable where it made no sense
- staging script now re-builds MO completely before packaging
- updated qt libraries for packaging to 4.8.5
- added python to the package
2013-11-22 17:45:34 +01:00
Tannin 5895a1a92d - bugfix: canceling creation of an ini tweak prompted an error message
- added missing files from earlier commit (wtf?)
2013-11-20 21:54:14 +01:00
Tannin c6101e34e1 - archive.dll now supports querying the crc value of files
- esptk now determines if a esp is a dummy (without records)
- hook.dll no longer creates a log file if noone wrote to it
- nexus id and installtime columns are now hidden by default
- modlist can now be refreshed without saving first (so plugins can replace the modlist.txt as a whole)
- plugins can now query more details about virtualised files
- added style options "plastique" and "cleanlooks"
- "overwrite" is no longer listed with a creation time
- a warning will now be displayed if the user has too many plugins active
- a warning will now be displayed if mods with scripts have an installation order that doesn't match the corresponding esp load order
- nmm importer now has select all/deselect all buttons
- nmm importer no longer tries to unpack missing files from archives (won't work anyway)
- initial support for importing from nmm 0.5 alpha
- removed some broken warning suppresions
- python runner now works with bundled python
- extended qbs build system (still fails to build the main gui application)
- implemented a nsis-based installer
2013-11-18 20:17:14 +01:00
Tannin 75475a7984 - started qbs based project files (not functional yet)
- modlist will now updated immediately after a change throw the modinfo dialog
- bugfix: configurator wasn't able to save after revision 137
2013-11-11 23:19:54 +01:00
Tannin 315ed6e41a Added tag release v1.0.7 for changeset 1973f6e00784 2013-11-06 18:40:09 +01:00
Tannin b2d613e9d1 Added tag release v1.0.6 for changeset 1973f6e00784 2013-11-06 18:40:03 +01:00
Tannin f010c22a3b - diagnosis plugins can now request to be re-evaluated
- main application now contains means for plugins to react on some changes (to be extended)
- plugins can now retrieve more information about a mod
- user-agent sent to nexus now automatically contains the current MO version
- included updated russian translation
- problems dialog now refreshes itself after a fix was applied
- fixed new esp/asset ordering diagnosis. May be fully functional now
- bugfix: restoring locked load order could leave MO in an endless loop (not sure if this fix is correct yet)
- bugfix: plugin list was not visually updated after some changes
- bugfix: nmm importer threw away mod ordering
2013-11-06 18:35:27 +01:00
Tannin d1594798e6 - added a new diagnosis to detect potential problems in regards to esp vs. asset ordering (not fully functional yet)
- new plugin interfaces to the mod list
- plugins can now query the origin (mod) of a esp/esm
- bugfix: potential access to profile before one was activated
- bugfix: regression in previous (not-yet-released) commit prevented changes to bsa list from being saved
2013-11-01 14:59:25 +01:00
Tannin 09bd3dbead - text files encoded in system encoding should now be correctly recognized in the mod info dialog
- archive-list can be user-sorted again (within one mod)
2013-10-26 15:02:31 +02:00
Tannin 8c0b309bde - Option to choose edition of the game. Currently only relevant for FO3 because FO3 GOTY is its own app
- applications that aren't in the executables list can again be started from the command line
- references to missing categories are now removed from mods
- bugfix: integrated fomod installer didn't update description and picture on some constellations of Windows version and theme
2013-10-25 14:49:42 +02:00
Tannin bc7c42dbe8 - directories in data-view are now sorted
- questionbox with selection memory is now exposed to plugins
- the choice to run fnis from the checker can now be memorized
2013-10-20 16:51:40 +02:00
Tannin 9a03783f49 - tooltip on download list now contains the file name
- bugfix: when refreshing the directory tree conflict information wasn't immediately refreshed (including on start)
- bugfix: dataChanged events wasn't emitted when user changed the modlist
- bugfix: file patterns in checkfnis plugin weren't completely correct
2013-10-18 23:35:38 +02:00
Tannin d73b1e38c8 - mod sorting now always uses priority as secondary sorting
- added missing ipluginlist.h
2013-10-17 19:21:22 +02:00
Tannin b47c89661e - plugin (esp/esm) list is now exposed to plugins (read-only functionality right now)
- integrated fomod installer now supports file dependencies
- bugfix: integrated fomod installer crashed if the installer had no optiosn´´ns
2013-10-16 23:04:03 +02:00
Tannin 0b84cc3d45 Merge 2013-10-16 19:34:56 +02:00
Tannin 1bf3cc585e - nmm importer now also transfers category, endorsement state and lastest version information
- nmm importer should now always determine the correct nexus id
- nmm importer will now correctly restore overwritten files for imported mods
2013-10-16 19:32:59 +02:00
Tannin eae275cc84 Added tag release v1.0.5 for changeset c6a9d2ab2d6c 2013-10-15 23:34:16 +02:00
Tannin f3cb50057b - bugfixes to last merge
- fnis-check plugin can now be disabled
2013-10-12 13:21:37 +02:00
Tannin 5518309300 Merge 2013-10-12 12:32:37 +02:00
Tannin f22bb9d6a6 Merge 2013-10-11 23:04:44 +02:00
Tannin 382226bb46 - added new plugin to test if fnis needs to be run
- some functionality to the plugin interface to enable them to search for files&directories in the virtual FS (rudimentary atm)
- functionality for plugins to react to application being started from MO
- broken ESPs are no longer reported as popup windows but only in the log file
- bugfix: plugins couldn't store persistent data if they had no user-editable settings
2013-10-11 23:03:49 +02:00
Tannin bdf04f516c - MO now warns if a nxm-link for a wrong game was attempted instead of downloading the wrong file
- bugfix: in nxmhandler adding a new line with different game but same executable didn't work (the change was silently dropped)
2013-10-10 21:23:25 +02:00
Tannin 54c7131a5e - new toggle to display hidden downloads
- hidden downloads can be un-hidden
- the installation manager now more thoroughly cleans up the temporary directory after installation
- added SkyrimLauncher.exe to the list of auto-detected executables
- bugfix: shutting down MO while downloads where active in some occasions didn't work
- bugfix: when canceling the only active download the taskbar icon didn't return to normal
2013-10-10 19:32:01 +02:00
Tannin 15e256ef44 Added tag release v1.0.4 for changeset 7732c5d2e41d 2013-10-10 19:09:29 +02:00
Tannin b23a4885e0 - bugfix: When updating MO, if login is required, the process didn't continue automatically after login 2013-10-06 14:12:25 +02:00
Tannin 823dfd410b - option to ignore/unignore updates is now only shown if there IS an update
- window now shouldn't lose focus on win 8 2 seconds after mod information gets invalidated
- small bugfix related to new version scheme parsing
2013-10-06 13:34:34 +02:00
Tannin ba09054e6b Merge 2013-10-06 12:20:07 +02:00
Tannin 694e3ed279 bugfix: statusbar may have stayed enabled if an error occured on handling the refreshed-event 2013-10-06 12:19:46 +02:00
Tannin cffd9eb4e2 - hook.dll now doesn't inject to certain applications (currently steam, chrome and firefox)
- versioning system improved. Will now report "downgrades" for mods and support a different versioning system (requires manual switch)
- updates can now be ignored until a new version is uploaded
- new splash screen
- bugfix: a few memory leaks (shouldn't account for much)
- bugfix: result of GetModuleHandle wasn't zero-terminated in some cases
2013-10-05 14:20:48 +02:00
Tannin 640b14ef25 diagnosis plugin now warns when nitpick is installed 2013-09-30 18:33:45 +02:00
Tannin 47293827bb - download progress is now visible in task bar
- esp-tooltip now lists all masters, highlighting the missing ones
- python plugin will now report a problem if the path contains a semicolon
- leak detection now (somewaht) works around the fact that we don't always get a stack trace
- bugfix: mod meta-file is now reliably created if it was missing
- bugfix: parser for nxm-links didn't handle numbers in the mod name
- bugfix: small memory leak
2013-09-28 21:13:57 +02:00
Tannin 0a3169b808 - plugin tooltip now only lists the masters that aren't enabled (if any) 2013-09-26 19:10:44 +02:00
Tannin f4528c7aaa - bugfix: esp parsing could crash for broken/unrecognized esps
- bugfix: esp parser didn't handle oblivion esps correctly
2013-09-23 22:39:58 +02:00
Tannin 4f0a6e8b88 Added tag release v1.0.2 for changeset 0aaed2f8f48d 2013-09-22 13:24:53 +02:00
91 changed files with 9865 additions and 11998 deletions
+168 -22
View File
@@ -1,53 +1,125 @@
HOW TO BUILD
============
Intro:
------
Building Mod Organizer can be a rather daunting task especially if you're not very comfortable with C++ development under windows.
Please note that if you only want to work on and build a plugin you can save yourself a lot of trouble
Overview:
---------
As of December 2013 MO consists of the following subprojects:
- organizer: The main userinterface. heavy usage of various libraries
- hookdll: core library of the virtual file system
- uibase: interop between plugins and the main application as well as some reusable functionality
- shared: functionality shared between organizer and hookdll. I'm attempting to get rid of this library over time
- nxmhandler: tool to pass handling of nxm links to MO or other applications
- helper: tool for doing operations requiring elevated priviledges if MO doesn't have them
- proxydll: dll used in the "proxy dll" load mechanism
- esptk: small library containing functionality to work with esps/esms
- bsatk: small library containing functionality to work with bsas. Requires zlib to extract files. Requires boost_thread to provide multi-threaded extraction
- archive: small wrapper library around 7zip for dealing with mod archives. Requires 7zip
- NCC: extension to NMM to provide a binary with command line interface for fomod installation. This is c# code and does not build with the rest of the project. Requires the rest of NMM
- bossdummy: dummy dll that looks like the boss.dll. This is used instead of the real boss dlls in NCC to save some disk space
- pythonrunner: library for embedding python code. Requires boost_python, python 2.7 and pyqt4
And various plugins:
- checkFNIS: Activates each time an application is started from MO and runs fnis if necessary
- diagnoseBasic: Various diagnostic checks on the game
- inieditor: minimalistic file editor for ini files
- installerBain: handles non-scripted bain installers
- installerBCF: no functionality. Supposed to eventually allow installations using bcfs (bain conversion file). python code
- installerBundle: handles installation of archives wrapped in archives
- installerFomod: handles installation of xml fomods
- installerManual: handles installations of archives that aren't supported by any other plugin (or if the user chooses to do the installation manually)
- installerNCC: handles installation of any fomod (requires NCC)
- installerQuick: handles very simple one-click installations
- NMMImport: importer from existing nmm installation
- proxyPython: integrates pythonrunner as a plugin into MO
- pyniEdit: more user-friendly ini editor. python code
Requirements:
-------------
Visual C++ compiler 2010 (VC 10.0) or up
- Included in visual c++ express 2010 or windows sdk 7.0
- Included in visual c++ express 2010 or windows sdk 7.x
- Do install windows sdk too if only for the cdb debugger which is included in that package
Note: If you're having trouble installing the windows sdk, you may be affected by this bug: http://support.microsoft.com/kb/2717426
QtSDK 4.8.x (http://qt-project.org/downloads)
- Qt5 is not yet supported but WIP
Qt Libraries 4.8.x (http://qt-project.org/downloads)
- i.e. "Qt libraries 4.8.5 for Windows (VS 2010, 235 MB)"
- tested: 4.8.5
- Qt5 is not yet supported but WIP. You will see a few conditional qt5 pathes
- Install according to instruction
boost libraries (http://www.boost.org/)
- Compile according to their instructions (using vc++)
- As of time of writing boost_thread is the only compiled boost library used, you can
disable the others to safe yourself the compile time
- tested: 1.49
- Build according to their instructions (using vc++): http://www.boost.org/doc/libs/1_54_0/more/getting_started/windows.html
- A few of the boost libraries need to be built (the rest is header-only). The only compiled libs MO needs (at the time of writing) are
boost_thread (for everything that links agains bsatk) and boost_python (for pythonrunner). You can disable the others to save yourself compile time (even on a modern system compiling boost can easily take an hour)
zlib (http://www.zlib.net/)
- Compile static library according to their instructions
- You should have a "zlibstatic.lib" under "build" afterwards
- Depending on the version of zlib and how you built you should have a file called zlibstatic.lib or zlibstat.lib in "build" or "x86\ZLibStatRelease".
- Please copy that lib file to "build\zlibstatic.lib" (if it doesn't exist) so MO finds it.
7z sourcecode (http://www.7-zip.org/download.html)
- only for "archive" subproject
- no need to compile, will be linked into project
Python 2.7 (32-bit)
- only for pythonrunner
PyQt4 for Python 2.7
- tested: 4.10
- only for pythonrunner
7z.dll (http://www.7-zip.org/download.html)
- Part of the 7-zip program
- Has to be the 32-bit dll! (approx. 893kb)
Recommended:
------------
Qt Creator
- Included in QtSDK but there might be a newer version as a separate download
- http://qt-project.org/downloads#qt-creator
- I.e. "Qt Creator 2.8.1 for Windows (53 MB)"
- With Qt Creator usually the rule is "the newer the better"
- Start Qt Creator and check if things are set up correctly:
1) Go to Tools > Options > Build & Run > Qt Versions
2.1) If the QtSDK 4.8.x you installed earlier is not auto-detected, click "Add" to add it manually
2.2) Navigate to your <your qt4 installation>\bin\qmake.exe
2.3) Restart qt creator
3) Go to Tools > Options > Build & Run > Kits
3.1) If no Kit is auto-detected (or the auto-detected ones have picked up the wrong qt or vc++ installation) click "Add"
3.2) Enter a Name (i.e. "Qt 4.8.5 MSVC2010 32bit")
3.3) In Compiler select the visual studio compiler installed earlier (may show up as "Microsoft Windows SDK for Windows x). Make sure you select the x86 variant
3.4) CDB should be auto-detected
3.5) Select the qt version set up earlier
4) Apply and close the options
Set up (using Qt Creator):
--------------------------
1. Using Qt Creator open source/ModOrganizer.pro from your working copy
2. Open the "Projects" tab, open the "Details" for "Build Environment"
3. Click "Add" to add a variable called "BOOSTPATH" with the path of your boost installation as the value (i.e. C:\code\boost_1_52_0)
4. Click "Add" to add a variable called "ZLIBPATH" with the path of your zlib installation as the value (i.e. C:\code\zlib-1.2.7)
5. Switch the build configuration at the very top of the same page from "debug" to "release" (or vice versa) and repeat steps 3 and 4
6. Compile the configuration(s) you want to use (debug and/or release) (Build All). This should compile correctly.
3a. Click "Add" to add a variable called "BOOSTPATH" with the path of your boost installation as the value (i.e. C:\code\boost_1_49_0)
3b. Click "Add" to add a variable called "ZLIBPATH" with the path of your zlib installation as the value (i.e. C:\code\zlib-1.2.7)
3c. Click "Add" to add a variable called "7ZIPPATH" with the path of your zlib installation as the value (i.e. C:\code\7zip)
3d. Click "Add" to add a variable called "PYTHONPATH" with the path of your zlib installation as the value (i.e. C:\code\python)
4. Switch the build configuration at the very top of the same page from "debug" to "release" (or vice versa) and repeat step 3
5. Compile the configuration(s) you want to use (debug and/or release) (Build All). This should compile correctly.
6. <reserved for future use. Maybe grab a coffee?>
7. return to the "projects" tab and switch to "Run Settings"
8. Determine the folder of the qt binaries in the package you downloaded. This could be "QtSDK\Desktop\Qt\4.8.5\msvc2010\bin" or "Qt4.8.5\bin"
For Release build:
8r. Add a "Run configuration" that points to <your working copy>\output\ModOrganizer.exe
9r. Copy "7z.dll" to <your working copy>\output\dlls
10r. From QtSDK\Desktop\Qt\4.8.0\msvc2010\bin copy the following files to <your working copy>\output\dlls: QtCore4.dll, QtDeclarative4.dll, QtGui4.dll, QtNetwork4.dll, QtScript4.dll, QtSql4.dll, QtWebkit4.dll, QtXml4.dll, QtXmlPatterns4.dll
9r. Add a "Run configuration" that points to <your working copy>\output\ModOrganizer.exe
10r. Copy "7z.dll" to <your working copy>\output\dlls
11r. From the qt binaries directory, copy the following files to <your working copy>\output\dlls: QtCore4.dll, QtDeclarative4.dll, QtGui4.dll, QtNetwork4.dll, QtScript4.dll, QtSql4.dll, QtWebkit4.dll, QtXml4.dll, QtXmlPatterns4.dll
For Debug build:
8d. Add a "Run configuration" that points to <your working copy>\outputd\ModOrganizer.exe
9d. Copy "7z.dll" to <your working copy>\outputd\dlls
10d. From QtSDK\Desktop\Qt\4.8.0\msvc2010\bin copy the following files to <your working copy>\outputd\dlls: QtCored4.dll, QtDeclaratived4.dll, QtGuid4.dll, QtNetworkd4.dll, QtScriptd4.dll, QtSqld4.dll, QtWebkitd4.dll, QtXmld4.dll, QtXmlPatternsd4.dll
9d. Add a "Run configuration" that points to <your working copy>\outputd\ModOrganizer.exe
10d. Copy "7z.dll" to <your working copy>\outputd\dlls
11d. From the qt binaries copy the following files to <your working copy>\outputd\dlls: QtCored4.dll, QtDeclaratived4.dll, QtGuid4.dll, QtNetworkd4.dll, QtScriptd4.dll, QtSqld4.dll, QtWebkitd4.dll, QtXmld4.dll, QtXmlPatternsd4.dll
Now you should be able to compile and run Mod Organizer.
Please note that when you change anything apart from the "organizer" subproject, qt creator may not pick up on the changes
@@ -65,4 +137,78 @@ Set up (without Qt Creator):
I'm not using this workflow so I can't give more detailed instructions.
Please note that the primary project format of MO remains qmake, if you work with visual studio and make changes to the project (like adding
source files) you have to manually transfer those changes to the .pro file.
source files) you have to manually transfer those changes to the .pro file.
-----------------
Troubleshooting (thanks to Ross):
-----------------
1) When I try to build, I am seeing the error "error: LNK1104: cannot open file 'zlibstatic.lib'"
a) Make sure you have the following files
{ZLIBPATH}\build\zconf.h
{ZLIBPATH}\build\zlib.h
{ZLIBPATH}\build\zlibstatic.lib
b) make sure "zlibstat.lib" was renamed to "zlibstatic.lib"
c) currently, there are references to zlibstatic in the following 2 project files:
./bsatk/bsatk.pro
./hookdll/hookdll.pro
2) After building, I try to open {MOPROJECTPATH}\outputd\ModOragnizer.exe and get this error
"The program can't start because QtDeclaratived4.dll is missing from your computer. Try reinstalling the program to fix this problem."
-> When setting up Qt4.8.x, make sure you copied the "d" (debug) versions of the dll files to {MOPROJECTPATH}\outputd\dlls
If you copied the release version or did not copy the dlls at all, you may see this error.
The *.dll files can be found in {QT48xPATH}\bin
Similiar:
http://stackoverflow.com/questions/11196416/cant-start-qt-exe-file-on-windows-7
3) I am getting one or more of the the following compiler warnings...
"{MOPROJECTPATH}\source\bsatk\bsaexception.cpp:32: warning: C4996: 'vsnprintf': This function or variable may be unsafe.
Consider using vsnprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details."
-> Ignore/suppress warning like this and make be ultra-careful using printf-style functions. vsnprintf_s is a visual-c++ only function so using those would make porting (i.e. to mingw) harder
"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\stdio.h:354: see declaration of 'vsnprintf'"
-> see previous
"debug\bsaarchive.obj:-1: warning: LNK4042: object specified more than once; extras ignored"
-> This appears to be an issue when using VC++ 2010 as the compiler. There are some posts on StackOverflow
which suggest fixes when using VC++ as the IDE, but not sure how to apply them to Qt Creator.
http://stackoverflow.com/questions/3729515
http://stackoverflow.com/questions/3695174
-> Also, not sure if this affects VC++ 2012 / VC++ 2013 / GCC via cygwin/mingw.
Not even sure if the project would successfully compile on these.
"{MOPROJECTPATH}\source\organizer\mainwindow.cpp:3995: warning: C4428: universal-character-name encountered in source"
-> very pointless warning.
http://stackoverflow.com/questions/11589571
http://stackoverflow.com/questions/7078013
the best solution I saw was to just disable the warning altogether...
#pragma warning( disable : 4428 )
"warning: LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library"
-> You can always add a #pragma to ignore them :P
http://msdn.microsoft.com/en-us/library/2c8f766e%28v=vs.80%29.aspx
4) I got the following error while building...
"error: LNK1201: error writing to program database
'{MOPROJECTPATH}\{whatever-your-build-directory-name-is}\organizer\debug\ModOrganizer.pdb';
check for insufficient disk space, invalid path, or insufficient privilege"
-> I had this happen after trying to look at something in memory via Process Explorer.
To resolve, I killed and restarted PE. Alternatively, you could probably use the
Unlocker utility. After unlocking, I manually deleted everything in the directory.
Unlocker: http://www.filehippo.com/download_unlocker/ or http://www.emptyloop.com/unlocker/
Warning: With Unlocker v1.9.2, you must select "Advanced" then uncheck everything to
avoid having some Delta toolbar garbage installed...
+11 -11
View File
@@ -1,23 +1,23 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = bsatk \
shared \
uibase \
organizer \
hookdll \
archive \
helper \
shared \
uibase \
organizer \
hookdll \
archive \
helper \
plugins \
proxydll \
nxmhandler \
BossDummy \
pythonRunner \
esptk
BossDummy \
pythonRunner \
esptk
plugins.depends = pythonRunner
hookdll.depends = shared
organizer.depends = shared, uibase, plugins
organizer.depends = shared uibase plugins
CONFIG(debug, debug|release) {
DESTDIR = outputd
+8
View File
@@ -43,6 +43,7 @@ QString CategoryFactory::categoriesFilePath()
CategoryFactory::CategoryFactory()
{
atexit(&cleanup);
reset();
QFile categoryFile(categoriesFilePath());
@@ -102,6 +103,7 @@ CategoryFactory &CategoryFactory::instance()
void CategoryFactory::reset()
{
m_Categories.clear();
m_IDMap.clear();
addCategory(0, "None", MakeVector<int>(2, 28, 87), 0);
}
@@ -124,6 +126,12 @@ void CategoryFactory::setParents()
}
}
void CategoryFactory::cleanup()
{
delete s_Instance;
s_Instance = NULL;
}
void CategoryFactory::saveCategories()
{
+4
View File
@@ -180,6 +180,8 @@ private:
void setParents();
static void cleanup();
private:
static CategoryFactory *s_Instance;
@@ -188,6 +190,8 @@ private:
std::map<int, unsigned int> m_IDMap;
std::map<int, unsigned int> m_NexusMap;
private:
};
+4 -2
View File
@@ -75,12 +75,14 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
return index.row();
} else if (role == Qt::ToolTipRole) {
QString text = m_Manager->getFileName(index.row()) + "\n";
if (m_Manager->isInfoIncomplete(index.row())) {
return tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
} else {
NexusInfo info = m_Manager->getNexusInfo(index.row());
return QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
text += QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
}
return text;
} else {
return QVariant();
}
+16 -4
View File
@@ -208,6 +208,11 @@ void DownloadListWidgetDelegate::issueRemoveFromView()
emit removeDownload(m_ContextRow, false);
}
void DownloadListWidgetDelegate::issueRestoreToView()
{
emit restoreDownload(m_ContextRow);
}
void DownloadListWidgetDelegate::issueCancel()
{
emit cancelDownload(m_ContextRow);
@@ -277,13 +282,18 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *
QMenu menu(m_View);
m_ContextRow = qobject_cast<QSortFilterProxyModel*>(model)->mapToSource(index).row();
DownloadManager::DownloadState state = m_Manager->getState(m_ContextRow);
bool hidden = m_Manager->isHidden(m_ContextRow);
if (state >= DownloadManager::STATE_READY) {
menu.addAction(tr("Install"), this, SLOT(issueInstall()));
if (m_Manager->isInfoIncomplete(m_ContextRow)) {
menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo()));
}
menu.addAction(tr("Delete"), this, SLOT(issueDelete()));
menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView()));
if (hidden) {
menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView()));
} else {
menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView()));
}
} else if (state == DownloadManager::STATE_DOWNLOADING){
menu.addAction(tr("Cancel"), this, SLOT(issueCancel()));
menu.addAction(tr("Pause"), this, SLOT(issuePause()));
@@ -295,9 +305,11 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *
menu.addSeparator();
menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted()));
menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll()));
menu.addSeparator();
menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted()));
menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll()));
if (!hidden) {
menu.addSeparator();
menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted()));
menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll()));
}
menu.exec(mouseEvent->globalPos());
event->accept();
+2
View File
@@ -65,6 +65,7 @@ signals:
void installDownload(int index);
void queryInfo(int index);
void removeDownload(int index, bool deleteFile);
void restoreDownload(int index);
void cancelDownload(int index);
void pauseDownload(int index);
void resumeDownload(int index);
@@ -83,6 +84,7 @@ private slots:
void issueInstall();
void issueDelete();
void issueRemoveFromView();
void issueRestoreToView();
void issueCancel();
void issuePause();
void issueResume();
+3 -3
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>315</width>
<height>75</height>
<height>81</height>
</rect>
</property>
<property name="contextMenuPolicy">
@@ -78,14 +78,14 @@
<item>
<widget class="QLabel" name="sizeLabel">
<property name="text">
<string>0</string>
<string notr="true">0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>KB</string>
<string notr="true">KB</string>
</property>
</widget>
</item>
+16 -4
View File
@@ -195,6 +195,11 @@ void DownloadListWidgetCompactDelegate::issueRemoveFromView()
emit removeDownload(m_ContextIndex.row(), false);
}
void DownloadListWidgetCompactDelegate::issueRestoreToView()
{
emit restoreDownload(m_ContextIndex.row());
}
void DownloadListWidgetCompactDelegate::issueCancel()
{
emit cancelDownload(m_ContextIndex.row());
@@ -265,13 +270,18 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem
QMenu menu;
m_ContextIndex = qobject_cast<QSortFilterProxyModel*>(model)->mapToSource(index);
DownloadManager::DownloadState state = m_Manager->getState(m_ContextIndex.row());
bool hidden = m_Manager->isHidden(m_ContextIndex.row());
if (state >= DownloadManager::STATE_READY) {
menu.addAction(tr("Install"), this, SLOT(issueInstall()));
if (m_Manager->isInfoIncomplete(m_ContextIndex.row())) {
menu.addAction(tr("Query Info"), this, SLOT(issueQueryInfo()));
}
menu.addAction(tr("Delete"), this, SLOT(issueDelete()));
menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView()));
if (hidden) {
menu.addAction(tr("Un-Hide"), this, SLOT(issueRestoreToView()));
} else {
menu.addAction(tr("Remove from View"), this, SLOT(issueRemoveFromView()));
}
} else if (state == DownloadManager::STATE_DOWNLOADING){
menu.addAction(tr("Cancel"), this, SLOT(issueCancel()));
menu.addAction(tr("Pause"), this, SLOT(issuePause()));
@@ -283,9 +293,11 @@ bool DownloadListWidgetCompactDelegate::editorEvent(QEvent *event, QAbstractItem
menu.addSeparator();
menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted()));
menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll()));
menu.addSeparator();
menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted()));
menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll()));
if (!hidden) {
menu.addSeparator();
menu.addAction(tr("Remove Installed..."), this, SLOT(issueRemoveFromViewCompleted()));
menu.addAction(tr("Remove All..."), this, SLOT(issueRemoveFromViewAll()));
}
menu.exec(mouseEvent->globalPos());
event->accept();
+2
View File
@@ -65,6 +65,7 @@ signals:
void installDownload(int index);
void queryInfo(int index);
void removeDownload(int index, bool deleteFile);
void restoreDownload(int index);
void cancelDownload(int index);
void pauseDownload(int index);
void resumeDownload(int index);
@@ -83,6 +84,7 @@ private slots:
void issueInstall();
void issueDelete();
void issueRemoveFromView();
void issueRestoreToView();
void issueCancel();
void issuePause();
void issueResume();
+90 -14
View File
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "nxmurl.h"
#include <gameinfo.h>
#include <nxmurl.h>
#include <taskprogressmanager.h>
#include "utility.h"
#include "json.h"
#include "selectiondialog.h"
@@ -63,18 +64,21 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createNew(const Ne
info->m_CurrentUrl = 0;
info->m_Tries = AUTOMATIC_RETRIES;
info->m_State = STATE_STARTED;
info->m_TaskProgressId = TaskProgressManager::instance().getId();
return info;
}
DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath)
DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(const QString &filePath, bool showHidden)
{
DownloadInfo *info = new DownloadInfo;
QString metaFileName = filePath + ".meta";
QSettings metaFile(metaFileName, QSettings::IniFormat);
if (metaFile.value("removed", false).toBool()) {
if (!showHidden && metaFile.value("removed", false).toBool()) {
return NULL;
} else {
info->m_Hidden = metaFile.value("removed", false).toBool();
}
QString fileName = QFileInfo(filePath).fileName();
@@ -105,6 +109,7 @@ DownloadManager::DownloadInfo *DownloadManager::DownloadInfo::createFromMeta(con
info->m_CurrentUrl = 0;
info->m_Urls = metaFile.value("url", "").toString().split(";");
info->m_Tries = 0;
info->m_TaskProgressId = TaskProgressManager::instance().getId();
info->m_NexusInfo.m_Name = metaFile.value("name", 0).toString();
info->m_NexusInfo.m_ModName = metaFile.value("modName", "").toString();
info->m_NexusInfo.m_Version = metaFile.value("version", 0).toString();
@@ -148,7 +153,8 @@ QString DownloadManager::DownloadInfo::currentURL()
DownloadManager::DownloadManager(NexusInterface *nexusInterface, QObject *parent)
: IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher()
: IDownloadManager(parent), m_NexusInterface(nexusInterface), m_DirWatcher(), m_ShowHidden(false),
m_DateExpression("/Date\\((\\d+)\\)/")
{
connect(&m_DirWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString)));
}
@@ -159,6 +165,7 @@ DownloadManager::~DownloadManager()
for (QVector<DownloadInfo*>::iterator iter = m_ActiveDownloads.begin(); iter != m_ActiveDownloads.end(); ++iter) {
delete *iter;
}
m_ActiveDownloads.clear();
}
@@ -184,13 +191,15 @@ void DownloadManager::pauseAll()
::Sleep(100);
bool done = false;
QTime startTime = QTime::currentTime();
// further loops: busy waiting for all downloads to complete. This could be neater...
while (!done) {
while (!done && (startTime.secsTo(QTime::currentTime()) < 5)) {
QCoreApplication::processEvents();
done = true;
foreach (DownloadInfo *info, m_ActiveDownloads) {
if ((info->m_State < STATE_CANCELED) ||
(info->m_State != STATE_FETCHINGFILEINFO) || (info->m_State != STATE_FETCHINGMODINFO)) {
(info->m_State == STATE_FETCHINGFILEINFO) ||
(info->m_State == STATE_FETCHINGMODINFO)) {
done = false;
break;
}
@@ -226,6 +235,12 @@ void DownloadManager::setSupportedExtensions(const QStringList &extensions)
refreshList();
}
void DownloadManager::setShowHidden(bool showHidden)
{
m_ShowHidden = showHidden;
refreshList();
}
void DownloadManager::refreshList()
{
// remove finished downloads
@@ -263,7 +278,7 @@ void DownloadManager::refreshList()
QString fileName = QDir::fromNativeSeparators(m_OutputDirectory) + "/" + file;
DownloadInfo *info = DownloadInfo::createFromMeta(fileName);
DownloadInfo *info = DownloadInfo::createFromMeta(fileName, m_ShowHidden);
if (info != NULL) {
m_ActiveDownloads.push_front(info);
}
@@ -362,6 +377,15 @@ void DownloadManager::startDownload(QNetworkReply *reply, DownloadInfo *newDownl
void DownloadManager::addNXMDownload(const QString &url)
{
NXMUrl nxmInfo(url);
QString managedGame = ToQString(MOShared::GameInfo::instance().getGameShortName());
if (nxmInfo.game().compare(managedGame, Qt::CaseInsensitive) != 0) {
QMessageBox::information(NULL, tr("Wrong Game"), tr("The download link is for a mod for \"%1\" but this instance of MO "
"has been set up for \"%2\".").arg(nxmInfo.game()).arg(managedGame), QMessageBox::Ok);
return;
}
m_RequestIDs.insert(m_NexusInterface->requestFileInfo(nxmInfo.getModId(), nxmInfo.getFileId(), this, QVariant()));
}
@@ -433,6 +457,22 @@ void DownloadManager::refreshAlphabeticalTranslation()
}
void DownloadManager::restoreDownload(int index)
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
DownloadInfo *download = m_ActiveDownloads.at(index);
download->m_Hidden = false;
QString filePath = m_OutputDirectory + "/" + download->m_FileName;
QSettings metaSettings(filePath.append(".meta"), QSettings::IniFormat);
metaSettings.setValue("removed", false);
}
void DownloadManager::removeDownload(int index, bool deleteFile)
{
try {
@@ -488,9 +528,13 @@ void DownloadManager::pauseDownload(int index)
return;
}
if (m_ActiveDownloads.at(index)->m_State == STATE_DOWNLOADING) {
setState(m_ActiveDownloads.at(index), STATE_PAUSING);
qDebug("pausing %d - %s", index, m_ActiveDownloads[index]->m_FileName.toUtf8().constData());
DownloadInfo *info = m_ActiveDownloads.at(index);
if (info->m_State == STATE_DOWNLOADING) {
setState(info, STATE_PAUSING);
qDebug("pausing %d - %s", index, info->m_FileName.toUtf8().constData());
} else if ((info->m_State == STATE_FETCHINGMODINFO) || (info->m_State == STATE_FETCHINGFILEINFO)) {
setState(info, STATE_READY);
}
}
@@ -667,6 +711,14 @@ int DownloadManager::getModID(int index) const
return m_ActiveDownloads.at(index)->m_ModID;
}
bool DownloadManager::isHidden(int index) const
{
if ((index < 0) || (index >= m_ActiveDownloads.size())) {
throw MyException(tr("invalid index"));
}
return m_ActiveDownloads.at(index)->m_Hidden;
}
NexusInfo DownloadManager::getNexusInfo(int index) const
{
@@ -800,11 +852,11 @@ void DownloadManager::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
setState(info, STATE_PAUSED);
} else {
if (bytesTotal > info->m_TotalSize) {
qDebug("file size %s: %lld", qPrintable(info->m_FileName), bytesTotal);
info->m_TotalSize = bytesTotal;
}
int oldProgress = info->m_Progress;
info->m_Progress = ((info->m_ResumePos + bytesReceived) * 100) / (info->m_ResumePos + bytesTotal);
TaskProgressManager::instance().updateProgress(info->m_TaskProgressId, bytesReceived, bytesTotal);
if (oldProgress != info->m_Progress) {
emit update(index);
}
@@ -831,6 +883,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info)
metaFile.setValue("name", info->m_NexusInfo.m_Name);
metaFile.setValue("modName", info->m_NexusInfo.m_ModName);
metaFile.setValue("version", info->m_NexusInfo.m_Version);
metaFile.setValue("fileTime", info->m_NexusInfo.m_FileTime);
metaFile.setValue("fileCategory", info->m_NexusInfo.m_FileCategory);
metaFile.setValue("newestVersion", info->m_NexusInfo.m_NewestVersion);
metaFile.setValue("category", info->m_NexusInfo.m_Category);
@@ -838,6 +891,7 @@ void DownloadManager::createMetaFile(DownloadInfo *info)
metaFile.setValue("uninstalled", info->m_State == DownloadManager::STATE_UNINSTALLED);
metaFile.setValue("paused", (info->m_State == DownloadManager::STATE_PAUSED) ||
(info->m_State == DownloadManager::STATE_ERROR));
metaFile.setValue("removed", info->m_Hidden);
// slightly hackish...
for (int i = 0; i < m_ActiveDownloads.size(); ++i) {
@@ -861,11 +915,9 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
DownloadInfo *info = downloadInfoByID(userData.toInt());
if (info == NULL) return;
info->m_NexusInfo.m_Category = result["category_id"].toInt();
info->m_NexusInfo.m_ModName = result["name"].toString().trimmed();
info->m_NexusInfo.m_NewestVersion = result["version"].toString();
if (info->m_FileID != 0) {
setState(info, STATE_READY);
} else {
@@ -874,6 +926,17 @@ void DownloadManager::nxmDescriptionAvailable(int, QVariant userData, QVariant r
}
QDateTime DownloadManager::matchDate(const QString &timeString)
{
if (m_DateExpression.exactMatch(timeString)) {
return QDateTime::fromMSecsSinceEpoch(m_DateExpression.cap(1).toLongLong());
} else {
qWarning("date not matched: %s", qPrintable(timeString));
return QDateTime::currentDateTime();
}
}
void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultData, int requestID)
{
std::set<int>::iterator idIter = m_RequestIDs.find(requestID);
@@ -907,7 +970,11 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
(fileNameVariant == info->m_FileName) || (fileNameVariant == alternativeLocalName)) {
info->m_NexusInfo.m_Name = fileInfo["name"].toString();
info->m_NexusInfo.m_Version = fileInfo["version"].toString();
if (info->m_NexusInfo.m_Version.isEmpty()) {
info->m_NexusInfo.m_Version = info->m_NexusInfo.m_NewestVersion;
}
info->m_NexusInfo.m_FileCategory = fileInfo["category_id"].toInt();
info->m_NexusInfo.m_FileTime = matchDate(fileInfo["date"].toString());
info->m_FileID = fileInfo["id"].toInt();
found = true;
break;
@@ -946,7 +1013,7 @@ void DownloadManager::nxmFilesAvailable(int, QVariant userData, QVariant resultD
}
void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant, QVariant resultData, int requestID)
void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant userData, QVariant resultData, int requestID)
{
std::set<int>::iterator idIter = m_RequestIDs.find(requestID);
if (idIter == m_RequestIDs.end()) {
@@ -961,9 +1028,17 @@ void DownloadManager::nxmFileInfoAvailable(int modID, int fileID, QVariant, QVar
info.m_Name = result["name"].toString();
info.m_Version = result["version"].toString();
if (info.m_Version.isEmpty()) {
info.m_Version = info.m_NewestVersion;
}
info.m_FileName = result["uri"].toString();
info.m_FileTime = matchDate(result["date"].toString());
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info)));
if (userData.isValid()) {
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info), userData.toString()));
} else {
m_RequestIDs.insert(m_NexusInterface->requestDownloadURL(modID, fileID, this, qVariantFromValue(info)));
}
}
@@ -1105,6 +1180,7 @@ void DownloadManager::downloadFinished()
QByteArray data = info->m_Reply->readAll();
info->m_Output.write(data);
info->m_Output.close();
TaskProgressManager::instance().forgetMe(info->m_TaskProgressId);
bool error = false;
+31 -2
View File
@@ -46,6 +46,7 @@ struct NexusInfo {
QString m_NewestVersion;
QString m_FileName;
QVariantList m_DownloadMap;
QDateTime m_FileTime;
bool m_Set;
};
Q_DECLARE_METATYPE(NexusInfo)
@@ -98,10 +99,14 @@ private:
int m_Tries;
bool m_ReQueried;
quint32 m_TaskProgressId;
NexusInfo m_NexusInfo;
bool m_Hidden;
static DownloadInfo *createNew(const NexusInfo &nexusInfo, int modID, int fileID, const QStringList &URLs);
static DownloadInfo *createFromMeta(const QString &filePath);
static DownloadInfo *createFromMeta(const QString &filePath, bool showHidden);
/**
* @brief rename the file
@@ -121,7 +126,7 @@ private:
private:
static unsigned int s_NextDownloadID;
private:
DownloadInfo() : m_TotalSize(0), m_ReQueried(false) {}
DownloadInfo() : m_TotalSize(0), m_ReQueried(false), m_Hidden(false) {}
};
public:
@@ -166,6 +171,11 @@ public:
*/
void setSupportedExtensions(const QStringList &extensions);
/**
* @brief sets whether hidden files are to be shown after all
*/
void setShowHidden(bool showHidden);
/**
* @brief download from an already open network connection
*
@@ -261,6 +271,13 @@ public:
**/
int getModID(int index) const;
/**
* @brief determine if the specified file is supposed to be hidden
* @param index index of the file to look up
* @return true if the specified file is supposed to be hidden
*/
bool isHidden(int index) const;
/**
* @brief retrieve all nexus info of the download specified by index
*
@@ -350,6 +367,12 @@ public slots:
**/
void removeDownload(int index, bool deleteFile);
/**
* @brief restores the specified download to view (which was previously hidden
* @param index index of the download to restore
*/
void restoreDownload(int index);
/**
* @brief cancel the specified download. This will lead to the corresponding file to be deleted
*
@@ -415,6 +438,8 @@ private:
DownloadInfo *downloadInfoByID(unsigned int id);
QDateTime matchDate(const QString &timeString);
private:
static const int AUTOMATIC_RETRIES = 3;
@@ -434,6 +459,10 @@ private:
std::map<QString, int> m_DownloadFails;
bool m_ShowHidden;
QRegExp m_DateExpression;
};
#endif // DOWNLOADMANAGER_H
+40 -64
View File
@@ -46,6 +46,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QDateTime>
#include <QDirIterator>
#include <boost/assign.hpp>
#include <boost/scoped_ptr.hpp>
using namespace MOBase;
@@ -183,11 +184,7 @@ bool InstallationManager::unpackSingleFile(const QString &fileName)
QString InstallationManager::extractFile(const QString &fileName)
{
if (unpackSingleFile(fileName)) {
QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName());
m_FilesToDelete.insert(tempFileName);
return tempFileName;
return QDir::tempPath().append("/").append(QFileInfo(fileName).fileName());
} else {
return QString();
}
@@ -429,7 +426,7 @@ QString InstallationManager::generateBackupName(const QString &directoryName) co
}
bool InstallationManager::testOverwrite(GuessedValue<QString> &modName) const
bool InstallationManager::testOverwrite(GuessedValue<QString> &modName, bool *merge) const
{
QString targetDirectory = QDir::fromNativeSeparators(m_ModsDirectory + "\\" + modName);
@@ -443,6 +440,9 @@ bool InstallationManager::testOverwrite(GuessedValue<QString> &modName) const
return false;
}
}
if (merge != nullptr) {
*merge = (overwriteDialog.action() == QueryOverwriteDialog::ACT_MERGE);
}
if (overwriteDialog.action() == QueryOverwriteDialog::ACT_RENAME) {
bool ok = false;
QString name = QInputDialog::getText(m_ParentWidget, tr("Mod Name"), tr("Name"),
@@ -509,7 +509,6 @@ bool InstallationManager::ensureValidModName(GuessedValue<QString> &name) const
return true;
}
bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
const QString &version, const QString &newestVersion, int categoryID)
{
@@ -517,8 +516,9 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
return false;
}
bool merge = false;
// determine target directory
if (!testOverwrite(modName)) {
if (!testOverwrite(modName, &merge)) {
return false;
}
@@ -531,7 +531,6 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
m_InstallationProgress.setValue(0);
m_InstallationProgress.setWindowModality(Qt::WindowModal);
m_InstallationProgress.show();
if (!m_CurrentArchive->extract(ToWString(QDir::toNativeSeparators(targetDirectory)).c_str(),
new MethodCallback<InstallationManager, void, float>(this, &InstallationManager::updateProgress),
new MethodCallback<InstallationManager, void, LPCWSTR>(this, &InstallationManager::updateProgressFile),
@@ -554,7 +553,7 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
}
if (!settingsFile.contains("version") ||
(!version.isEmpty() &&
(VersionInfo(version) >= VersionInfo(settingsFile.value("version").toString())))) {
(!merge || (VersionInfo(version) >= VersionInfo(settingsFile.value("version").toString()))))) {
settingsFile.setValue("version", version);
}
if (!newestVersion.isEmpty() || !settingsFile.contains("newestVersion")) {
@@ -570,52 +569,39 @@ bool InstallationManager::doInstall(GuessedValue<QString> &modName, int modID,
}
void InstallationManager::openFile(const QString &fileName)
{
unpackSingleFile(fileName);
QString tempFileName = QDir::tempPath().append("/").append(QFileInfo(fileName).fileName());
SHELLEXECUTEINFOW execInfo;
memset(&execInfo, 0, sizeof(SHELLEXECUTEINFOW));
execInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
execInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
execInfo.lpVerb = L"open";
std::wstring fileNameW = ToWString(tempFileName);
execInfo.lpFile = fileNameW.c_str();
execInfo.nShow = SW_SHOWNORMAL;
if (!::ShellExecuteExW(&execInfo)) {
qCritical("failed to spawn %s: %d", tempFileName.toUtf8().constData(), ::GetLastError());
}
m_FilesToDelete.insert(tempFileName);
}
// copy and pasted from mo_dll
bool EndsWith(LPCWSTR string, LPCWSTR subString)
{
size_t slen = wcslen(string);
size_t len = wcslen(subString);
if (slen < len) {
return false;
}
for (size_t i = 0; i < len; ++i) {
if (towlower(string[slen - len + i]) != towlower(subString[i])) {
return false;
}
}
return true;
}
bool InstallationManager::wasCancelled()
{
return m_CurrentArchive->getLastError() == Archive::ERROR_EXTRACT_CANCELLED;
}
void InstallationManager::postInstallCleanup() const
{
m_CurrentArchive->close();
// directories we may want to remove. sorted from longest to shortest to ensure we remove subdirectories first.
auto longestFirst = [](const QString &LHS, const QString &RHS) -> bool {
if (LHS.size() != RHS.size()) return LHS.size() > RHS.size();
else return LHS < RHS;
};
std::set<QString, std::function<bool(const QString&, const QString&)>> directoriesToRemove(longestFirst);
// clean up temp files
// TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached
foreach (const QString &tempFile, m_TempFilesToDelete) {
QFileInfo fileInfo(QDir::tempPath() + "/" + tempFile);
QFile::remove(fileInfo.absoluteFilePath());
directoriesToRemove.insert(fileInfo.absolutePath());
}
// try to delete each directory we had temporary files in. the call fails for non-empty directories which is ok
foreach (const QString &dir, directoriesToRemove) {
QDir().rmdir(dir);
}
}
bool InstallationManager::install(const QString &fileName, GuessedValue<QString> &modName, bool &hasIniTweaks)
{
QFileInfo fileInfo(fileName);
@@ -665,7 +651,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
}
m_CurrentFile = fileInfo.absoluteFilePath();
if (fileInfo.dir() == QDir(ToQString(GameInfo::instance().getDownloadDir()))) {
if (fileInfo.dir() == QDir(m_DownloadsDirectory)) {
m_CurrentFile = fileInfo.fileName();
}
qDebug("using mod name \"%s\" (id %d) -> %s", modName->toUtf8().constData(), modID, qPrintable(m_CurrentFile));
@@ -674,12 +660,9 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
bool archiveOpen = m_CurrentArchive->open(ToWString(QDir::toNativeSeparators(fileName)).c_str(),
new MethodCallback<InstallationManager, void, LPSTR>(this, &InstallationManager::queryPassword));
ON_BLOCK_EXIT([this] {
this->m_CurrentArchive->close();
});
DirectoryTree *filesTree = archiveOpen ? createFilesTree() : NULL;
ON_BLOCK_EXIT(std::bind(&InstallationManager::postInstallCleanup, this));
QScopedPointer<DirectoryTree> filesTree(archiveOpen ? createFilesTree() : NULL);
IPluginInstaller::EInstallResult installResult = IPluginInstaller::RESULT_NOTATTEMPTED;
std::sort(m_Installers.begin(), m_Installers.end(), [] (IPluginInstaller *LHS, IPluginInstaller *RHS) {
@@ -704,7 +687,7 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
(filesTree != NULL) && (installer->isArchiveSupported(*filesTree))) {
installResult = installerSimple->install(modName, *filesTree, version, modID);
if (installResult == IPluginInstaller::RESULT_SUCCESS) {
mapToArchive(filesTree);
mapToArchive(filesTree.data());
// the simple installer only prepares the installation, the rest works the same for all installers
if (!doInstall(modName, modID, version, newestVersion, categoryID)) {
installResult = IPluginInstaller::RESULT_FAILED;
@@ -729,13 +712,6 @@ bool InstallationManager::install(const QString &fileName, GuessedValue<QString>
qPrintable(installer->name()), e.what());
}
// clean up temp files
// TODO: this doesn't yet remove directories. Also, the files may be left there if this point isn't reached
foreach (const QString &tempFile, m_TempFilesToDelete) {
QFile::remove(QDir::tempPath() + "/" + tempFile);
}
// act upon the installation result. at this point the files have already been
// extracted to the correct location
switch (installResult) {
+11 -6
View File
@@ -64,6 +64,12 @@ public:
*/
void setModsDirectory(const QString &modsDirectory) { m_ModsDirectory = modsDirectory; }
/**
* @brief update the directory where downloads are stored
* @param downloadDirectory the download directory
*/
void setDownloadDirectory(const QString &downloadDirectory) { m_DownloadsDirectory = downloadDirectory; }
/**
* @brief install a mod from an archive
*
@@ -132,9 +138,10 @@ public:
/**
* @brief test if the specified mod name is free. If not, query the user how to proceed
* @param modName current possible names for the mod
* @param merge if this value is not null, the value will be set to whether the use chose to merge or replace
* @return true if we can proceed with the installation, false if the user canceled or in case of an unrecoverable error
*/
virtual bool testOverwrite(MOBase::GuessedValue<QString> &modName) const;
virtual bool testOverwrite(MOBase::GuessedValue<QString> &modName, bool *merge = NULL) const;
private:
@@ -159,7 +166,7 @@ private:
bool isSimpleArchiveTopLayer(const MOBase::DirectoryTree::Node *node, bool bainStyle);
MOBase::DirectoryTree::Node *getSimpleArchiveBase(MOBase::DirectoryTree *dataTree);
bool testOverwrite(const QString &modsDirectory, MOBase::GuessedValue<QString> &modName);
//bool testOverwrite(const QString &modsDirectory, MOBase::GuessedValue<QString> &modName, bool *merge = NULL);
bool doInstall(MOBase::GuessedValue<QString> &modName,
int modID, const QString &version, const QString &newestVersion, int categoryID);
@@ -168,9 +175,7 @@ private:
bool ensureValidModName(MOBase::GuessedValue<QString> &name) const;
private slots:
void openFile(const QString &fileName);
void postInstallCleanup() const;
private:
@@ -193,6 +198,7 @@ private:
QWidget *m_ParentWidget;
QString m_ModsDirectory;
QString m_DownloadsDirectory;
std::vector<MOBase::IPluginInstaller*> m_Installers;
std::set<QString, CaseInsensitive> m_SupportedExtensions;
@@ -202,7 +208,6 @@ private:
QProgressDialog m_InstallationProgress;
std::set<QString> m_FilesToDelete;
std::set<QString> m_TempFilesToDelete;
};
+79 -46
View File
@@ -18,6 +18,11 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef LEAK_CHECK_WITH_VLD
#include <wchar.h>
#include <vld.h>
#endif // LEAK_CHECK_WITH_VLD
#include <QApplication>
#include <QPushButton>
#include <QListWidget>
@@ -55,7 +60,6 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "selectiondialog.h"
#include "moapplication.h"
#include "tutorialmanager.h"
#include <QLibrary>
#include <iostream>
#include <QMessageBox>
#include <QSharedMemory>
@@ -135,7 +139,7 @@ bool bootstrap()
QObject::tr("The current user account doesn't have the required access rights to run "
"Mod Organizer. The neccessary changes can be made automatically (the MO directory "
"will be made writable for the current user account). You will be asked to run "
"\"helper.exe\" with administrative rights)."),
"\"helper.exe\" with administrative rights."),
QMessageBox::Yes | QMessageBox::Cancel) == QMessageBox::Yes) {
if (!Helper::init(GameInfo::instance().getOrganizerDirectory())) {
return false;
@@ -151,6 +155,7 @@ bool bootstrap()
// verify the hook-dll exists
QString dllName = qApp->applicationDirPath() + "/" + ToQString(AppConfig::hookDLLName());
HMODULE dllMod = ::LoadLibraryW(ToWString(dllName).c_str());
if (dllMod == NULL) {
throw windows_error("hook.dll is missing or invalid");
@@ -395,6 +400,27 @@ int main(int argc, char *argv[])
settings.setValue("gamePath", gamePath.toUtf8().constData());
}
int edition = 0;
if (settings.contains("game_edition")) {
edition = settings.value("game_edition").toInt();
} else {
std::vector<std::wstring> editions = GameInfo::instance().getSteamVariants();
if (editions.size() < 2) {
edition = 0;
} else {
SelectionDialog selection(QObject::tr("Please select the game edition you have (MO can't start the game correctly if this is set incorrectly!)"), NULL);
int index = 0;
for (auto iter = editions.begin(); iter != editions.end(); ++iter) {
selection.addChoice(ToQString(*iter), "", index++);
}
if (selection.exec() == QDialog::Rejected) {
return -1;
} else {
settings.setValue("game_edition", selection.getChoiceData().toInt());
}
}
}
qDebug("managing game at %s", qPrintable(QDir::toNativeSeparators(gamePath)));
ExecutablesList executablesList;
@@ -428,63 +454,70 @@ int main(int argc, char *argv[])
qDebug("initializing tutorials");
TutorialManager::init(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getTutorialDir())).append("/"));
application.setStyleFile(settings.value("Settings/style", "").toString());
if (!application.setStyleFile(settings.value("Settings/style", "").toString())) {
// disable invalid stylesheet
settings.setValue("Settings/style", "");
}
// set up main window and its data structures
MainWindow mainWindow(argv[0], settings);
QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString)));
QObject::connect(&instance, SIGNAL(messageSent(QString)), &mainWindow, SLOT(externalMessage(QString)));
int res = 1;
{ // scope to control lifetime of mainwindow
// set up main window and its data structures
MainWindow mainWindow(argv[0], settings);
QObject::connect(&mainWindow, SIGNAL(styleChanged(QString)), &application, SLOT(setStyleFile(QString)));
QObject::connect(&instance, SIGNAL(messageSent(QString)), &mainWindow, SLOT(externalMessage(QString)));
mainWindow.setExecutablesList(executablesList);
mainWindow.readSettings();
mainWindow.setExecutablesList(executablesList);
mainWindow.readSettings();
QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray());
QString selectedProfileName = QString::fromUtf8(settings.value("selected_profile", "").toByteArray());
{ // see if there is a profile on the command line
int profileIndex = arguments.indexOf("-p", 1);
if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) {
qDebug("profile overwritten on command line");
selectedProfileName = arguments.at(profileIndex + 1);
{ // see if there is a profile on the command line
int profileIndex = arguments.indexOf("-p", 1);
if ((profileIndex != -1) && (profileIndex < arguments.size() - 1)) {
qDebug("profile overwritten on command line");
selectedProfileName = arguments.at(profileIndex + 1);
}
arguments.removeAt(profileIndex);
arguments.removeAt(profileIndex);
}
arguments.removeAt(profileIndex);
arguments.removeAt(profileIndex);
}
qDebug("configured profile: %s", qPrintable(selectedProfileName));
qDebug("configured profile: %s", qPrintable(selectedProfileName));
// if we have a command line parameter, it is either a nxm link or
// a binary to start
if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) {
QString exeName = arguments.at(1);
qDebug("starting %s from command line", qPrintable(exeName));
arguments.removeFirst(); // remove application name (ModOrganizer.exe)
arguments.removeFirst(); // remove binary name
// pass the remaining parameters to the binary
mainWindow.spawnProgram(exeName, arguments.join(" "), selectedProfileName, QDir());
return 0;
}
// if we have a command line parameter, it is either a nxm link or
// a binary to start
if ((arguments.size() > 1) && (!isNxmLink(arguments.at(1)))) {
QString exeName = arguments.at(1);
qDebug("starting %s from command line", qPrintable(exeName));
arguments.removeFirst(); // remove application name (ModOrganizer.exe)
arguments.removeFirst(); // remove binary name
// pass the remaining parameters to the binary
mainWindow.startApplication(exeName, arguments, QString(), selectedProfileName);
return 0;
}
mainWindow.createFirstProfile();
mainWindow.createFirstProfile();
if (selectedProfileName.length() != 0) {
if (!mainWindow.setCurrentProfile(selectedProfileName)) {
if (selectedProfileName.length() != 0) {
if (!mainWindow.setCurrentProfile(selectedProfileName)) {
mainWindow.setCurrentProfile(1);
qWarning("failed to set profile: %s",
selectedProfileName.toUtf8().constData());
}
} else {
mainWindow.setCurrentProfile(1);
qWarning("failed to set profile: %s",
selectedProfileName.toUtf8().constData());
}
} else {
mainWindow.setCurrentProfile(1);
}
qDebug("displaying main window");
mainWindow.show();
qDebug("displaying main window");
mainWindow.show();
if ((arguments.size() > 1) &&
(isNxmLink(arguments.at(1)))) {
qDebug("starting download from command line: %s", qPrintable(arguments.at(1)));
mainWindow.externalMessage(arguments.at(1));
if ((arguments.size() > 1) &&
(isNxmLink(arguments.at(1)))) {
qDebug("starting download from command line: %s", qPrintable(arguments.at(1)));
mainWindow.externalMessage(arguments.at(1));
}
splash.finish(&mainWindow);
res = application.exec();
}
splash.finish(&mainWindow);
return application.exec();
return res;
} catch (const std::exception &e) {
reportError(e.what());
return 1;
+397 -98
View File
File diff suppressed because it is too large Load Diff
+67 -12
View File
@@ -50,6 +50,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "savegameinfowidgetgamebryo.h"
#include <guessedvalue.h>
#include <directoryentry.h>
#include <boost/signals2.hpp>
namespace Ui {
class MainWindow;
@@ -59,11 +60,32 @@ class QToolButton;
class ModListSortProxy;
class ModListGroupCategoriesProxy;
class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose
{
Q_OBJECT
Q_INTERFACES(MOBase::IPluginDiagnose)
private:
struct SignalCombinerAnd
{
typedef bool result_type;
template<typename InputIterator>
bool operator()(InputIterator first, InputIterator last) const
{
while (first != last) {
if (!(*first)) {
return false;
}
++first;
}
return true;
}
};
typedef boost::signals2::signal<bool (const QString&), SignalCombinerAnd> SignalAboutToRunApplication;
public:
explicit MainWindow(const QString &exeName, QSettings &initSettings, QWidget *parent = 0);
~MainWindow();
@@ -76,7 +98,6 @@ public:
void refreshBSAList();
void refreshDataTree();
void refreshSaveList();
void refreshModList();
void setExecutablesList(const ExecutablesList &executablesList);
@@ -89,8 +110,8 @@ public:
void createFirstProfile();
void spawnProgram(const QString &fileName, const QString &argumentsArg,
const QString &profileName, const QDir &currentDirectory);
/* void spawnProgram(const QString &fileName, const QString &argumentsArg,
const QString &profileName, const QDir &currentDirectory);*/
void loadPlugins();
@@ -111,7 +132,16 @@ public:
virtual QString pluginDataPath() const;
virtual void installMod(const QString &fileName);
virtual QString resolvePath(const QString &fileName) const;
virtual QStringList listDirectories(const QString &directoryName) const;
virtual QStringList findFiles(const QString &path, const std::function<bool(const QString &)> &filter) const;
virtual QList<FileInfo> findFileInfos(const QString &path, const std::function<bool(const FileInfo&)> &filter) const;
virtual MOBase::IDownloadManager *downloadManager();
virtual MOBase::IPluginList *pluginList();
virtual MOBase::IModList *modList();
virtual HANDLE startApplication(const QString &executable, const QStringList &args = QStringList(), const QString &cwd = "", const QString &profile = "");
virtual bool onAboutToRun(const std::function<bool(const QString&)> &func);
virtual void refreshModList(bool saveChanges = true);
virtual std::vector<unsigned int> activeProblems() const;
virtual QString shortDescription(unsigned int key) const;
@@ -153,7 +183,6 @@ signals:
void styleChanged(const QString &styleFile);
void modListDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
protected:
@@ -168,7 +197,7 @@ private:
void actionToToolButton(QAction *&sourceAction);
bool verifyPlugin(MOBase::IPlugin *plugin);
void registerPluginTool(MOBase::IPluginTool *tool);
bool registerPlugin(QObject *pluginObj);
bool registerPlugin(QObject *pluginObj, const QString &fileName);
void updateToolBar();
void activateSelectedProfile();
@@ -201,9 +230,23 @@ private:
void refreshFilters();
void saveCategoriesFromMenu(QMenu *menu, int modRow);
/**
* Sets category selections from menu; for multiple mods, this will only apply
* the changes made in the menu (which is the delta between the current menu selection and the reference mod)
* @param menu the menu after editing by the user
* @param modRow index of the mod to edit
*/
void addRemoveCategoriesFromMenu(QMenu *menu, int modRow);
bool addCategories(QMenu *menu, int targetID);
/**
* Sets category selections from menu; for multiple mods, this will completely
* replace the current set of categories on each selected with those selected in the menu
* @param menu the menu after editing by the user
* @param modRow index of the mod to edit
*/
void replaceCategoriesFromMenu(QMenu *menu, int modRow);
bool populateMenuCategories(QMenu *menu, int targetID);
void updateDownloadListDelegate();
@@ -243,10 +286,12 @@ private:
static void setupNetworkProxy(bool activate);
void activateProxy(bool activate);
void installTranslator(const QString &name);
private:
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
static const unsigned int PROBLEM_TOOMANYPLUGINS = 2;
private:
@@ -288,9 +333,6 @@ private:
DownloadManager m_DownloadManager;
InstallationManager m_InstallationManager;
QTranslator *m_Translator;
QTranslator *m_TranslatorQt;
SelfUpdater m_Updater;
CategoryFactory &m_CategoryFactory;
@@ -320,6 +362,11 @@ private:
QFile m_PluginsCheck;
SignalAboutToRunApplication m_AboutToRun;
QString m_CurrentLanguage;
std::vector<QTranslator*> m_Translators;
private slots:
void showMessage(const QString &message);
@@ -399,7 +446,9 @@ private slots:
void originModified(int originID);
void saveCategories();
void addRemoveCategories_MenuHandler();
void replaceCategories_MenuHandler();
void savePrimaryCategory();
void addPrimaryCategoryCandidates();
@@ -466,6 +515,10 @@ private slots:
void removeFromToolbar();
void overwriteClosed(int);
void changeVersioningScheme();
void ignoreUpdate();
void unignoreUpdate();
private slots: // ui slots
// actions
void on_actionAdd_Profile_triggered();
@@ -478,7 +531,7 @@ private slots: // ui slots
void on_actionEndorseMO_triggered();
void on_bsaList_customContextMenuRequested(const QPoint &pos);
void on_bsaList_itemChanged(QTreeWidgetItem *item, int column);
void bsaList_itemMoved();
void on_btnRefreshData_clicked();
void on_categoriesList_customContextMenuRequested(const QPoint &pos);
void on_compactBox_toggled(bool checked);
@@ -498,6 +551,8 @@ private slots: // ui slots
void on_groupCombo_currentIndexChanged(int index);
void on_categoriesList_itemSelectionChanged();
void on_linkButton_pressed();
void on_showHiddenBox_toggled(bool checked);
void on_bsaList_itemChanged(QTreeWidgetItem *item, int column);
};
#endif // MAINWINDOW_H
+23 -10
View File
@@ -725,7 +725,7 @@ p, li { white-space: pre-wrap; }
<number>6</number>
</property>
<item>
<widget class="QTreeWidget" name="bsaList">
<widget class="MOBase::SortableTreeWidget" name="bsaList">
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
@@ -742,7 +742,7 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>false</bool>
@@ -751,10 +751,10 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::NoDragDrop</enum>
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::IgnoreAction</enum>
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
@@ -768,6 +768,12 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<property name="itemsExpandable">
<bool>true</bool>
</property>
<property name="columnCount">
<number>1</number>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
<attribute name="headerDefaultSectionSize">
<number>200</number>
</attribute>
@@ -776,11 +782,6 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<string>File</string>
</property>
</column>
<column>
<property name="text">
<string>Mod</string>
</property>
</column>
</widget>
</item>
<item>
@@ -968,7 +969,7 @@ p, li { white-space: pre-wrap; }
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0,2">
<item>
<widget class="QCheckBox" name="compactBox">
<property name="text">
@@ -976,6 +977,13 @@ p, li { white-space: pre-wrap; }
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showHiddenBox">
<property name="text">
<string>Show Hidden</string>
</property>
</widget>
</item>
<item>
<widget class="MOBase::LineEditClear" name="downloadFilterEdit">
<property name="placeholderText">
@@ -1217,6 +1225,11 @@ Right now this has very limited functionality</string>
<extends>QTreeView</extends>
<header>modlistview.h</header>
</customwidget>
<customwidget>
<class>MOBase::SortableTreeWidget</class>
<extends>QTreeWidget</extends>
<header>sortabletreewidget.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="resources.qrc"/>
+24 -9
View File
@@ -19,20 +19,23 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "moapplication.h"
#include "report.h"
#include "utility.h"
#include <utility.h>
#include <appconfig.h>
#include <QFile>
#include <QStringList>
#include <QPlastiqueStyle>
#include <QCleanlooksStyle>
MOApplication::MOApplication(int argc, char **argv)
: QApplication(argc, argv)
{
connect(&m_StyleWatcher, SIGNAL(fileChanged(QString)), SLOT(updateStyle(QString)));
m_DefaultStyle = style()->objectName();
}
void MOApplication::setStyleFile(const QString &styleName)
bool MOApplication::setStyleFile(const QString &styleName)
{
// remove all files from watch
QStringList currentWatch = m_StyleWatcher.files();
@@ -42,11 +45,16 @@ void MOApplication::setStyleFile(const QString &styleName)
// set new stylesheet or clear it
if (styleName.length() != 0) {
QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName;
m_StyleWatcher.addPath(styleSheetName);
updateStyle(styleSheetName);
if (QFile::exists(styleSheetName)) {
m_StyleWatcher.addPath(styleSheetName);
updateStyle(styleSheetName);
} else {
updateStyle(styleName);
}
} else {
setStyleSheet("");
}
return true;
}
@@ -70,11 +78,18 @@ bool MOApplication::notify(QObject *receiver, QEvent *event)
void MOApplication::updateStyle(const QString &fileName)
{
QFile file(fileName);
if (file.open(QFile::ReadOnly)) {
setStyleSheet(file.readAll());
if (fileName == "Plastique") {
setStyle(new QPlastiqueStyle);
setStyleSheet("");
} else if (fileName == "Cleanlooks") {
setStyle(new QCleanlooksStyle);
setStyleSheet("");
} else {
qDebug("no stylesheet");
setStyle(m_DefaultStyle);
if (QFile::exists(fileName)) {
setStyleSheet(QString("file:///%1").arg(fileName));
} else {
qWarning("invalid stylesheet: %s", qPrintable(fileName));
}
}
file.close();
}
+32 -30
View File
@@ -17,33 +17,35 @@ You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOAPPLICATION_H
#define MOAPPLICATION_H
#include <QApplication>
#include <QFileSystemWatcher>
class MOApplication : public QApplication {
Q_OBJECT
public:
MOApplication(int argc, char **argv);
virtual bool notify (QObject *receiver, QEvent *event);
public slots:
void setStyleFile(const QString &style);
private slots:
void updateStyle(const QString &fileName);
private:
QFileSystemWatcher m_StyleWatcher;
};
#endif // MOAPPLICATION_H
#ifndef MOAPPLICATION_H
#define MOAPPLICATION_H
#include <QApplication>
#include <QFileSystemWatcher>
class MOApplication : public QApplication {
Q_OBJECT
public:
MOApplication(int argc, char **argv);
virtual bool notify (QObject *receiver, QEvent *event);
public slots:
bool setStyleFile(const QString &style);
private slots:
void updateStyle(const QString &fileName);
private:
QFileSystemWatcher m_StyleWatcher;
QString m_DefaultStyle;
};
#endif // MOAPPLICATION_H

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