Compare commits

...
Author SHA1 Message Date
Tannin 0feb4b702d - loot client now only updates the masterlist once per MO session
- new event to notify plugins of changed mod priority
- overwrite now shows up in the "checked" category instead of "unchecked"
- display of "foreign" mods can now be limited to only official content
- bugfix: bsa extraction dialog showed up even if the plugin was disabled
- bugfix: after detection of foreign mods the priority of the overwrite folder could get messed up
- bugfix: when displaying only the context menu for the mod list as a whole, the menu didn't disappear
- bugfix: MO crashed when trying to download via the integrated browser
2014-06-08 15:10:08 +02:00
Tannin 5a4b6e70fd - plugin-list now displays loot messages 2014-06-02 19:15:23 +02:00
Tannin 18574c2ba8 - removed needless modlist-rewrite 2014-05-31 13:44:26 +02:00
Tannin 1eb783aae3 - added a new mod type that represents files handled externally (i.e. DLCs) as mods in MO
- hashes of file names in bsa files are no longer checked all the time
- author and description is now read from esp files
- rewrote the code that fixes modlists after a rename, should be a bit more robust
- fixes to qt 5 and msvc 2013 compatibility
- started to update the tutorial (not done yet!)
- bugfix: counter for the problems badge wasn't calculated correctly
2014-05-31 13:43:48 +02:00
Tannin 93a19799b8 Merge 2014-05-28 14:23:11 +02:00
Tannin 2a31eb40fb small bugfixes 2014-05-28 14:14:28 +02:00
Tannin cf0a1bc2be - files in bsas are now only displayed in the data tab if they are managed by mo
- number of problems detected by MO is now displayed as a badge on the icon
- rephrased the explanation text on the Archives tab. unchecked plugin-loaded bsas no longer prompt a warning
- bsa extraction is now handled in a plugin
- added a way for plugins to react to mod installation
- re-enabled the automatic fix for asset order problems
- bugfix: In some cases when a download wasn't started successfully the download urls weren't stored in the meta file so no resume was possible
- bugfix: MO tried to resume downloads when it didn't have and download urls
- bugfix: downloads couldn't be paused if the download was already broken on the network layer
- bugfix: download managear did not recognize a file as downloaded if the download completed before signals were hooked up
- bugfix: in-place file replacement was re-broken
2014-05-25 15:39:45 +02:00
Tannin 48c8cca578 - NCC will now report an error if a script tries to extract a non-existent file instead of creating an empty output file
- ncc installer plugin now uses a more reliable method to force the installer window to the foreground
- missing version on TESV.exe will no longer be reported as an error
- bugfix: loot client didn't read list of active mods
- bugfix: invalid free call in error reporting function
2014-05-15 19:11:19 +02:00
Tannin 977b407525 - mod list context menu split into two menus (one for whole list, one for selected mods)
- added option to combine category filters using "or"
- added context menu option for deselecting category filters
- slightly changed ui on the category filters
- added a sample plugin for cpp that can be built without building the rest of MO
- simple installer can now be configured to run without any user interaction
- extended interface for python plugins
- iorganizer implementation moved out of the main window
- nexus requests from plugins will now be identified in the user agent
- bugfix: shortcuts created from MO used the wrong working directory
- bugfix: deactivation of bsas didn't stick
- bugfix: file hiding mechanism wasn't active
- bugfix: executables linked on the toolbar couldn't be removed if the executable was removed first
- bugfix: the endorsement-filter couldn't be combined with other filters
- bugfix: python interface to repository bridge was broken
2014-05-13 21:20:44 +02:00
Tannin f4b1aba61a Added tag release v1.2.1 for changeset 366d0a6ac409 2014-05-06 21:07:34 +02:00
Tannin 0eb1662a0e - very effective optimization to findfirstfile-calls
- several configuration files are now only saved to disk if the content actually changed.
  This should also get rid of a problem where plugins.txt was re-written immediately after starting the game
  (causing a conflict with the game)
- reduced "noise" from hook.dll
- removed some debugging messages
2014-05-05 18:24:15 +02:00
Tannin 164ec25a75 - bugfix: endless loop in detection of mod order problems 2014-05-04 16:13:35 +02:00
Tannin 6fb36d6c02 - main window now has a small view displaying log messages
- mod list will now be highlighted when grouping is active is active
- download tooltip now supports bbcode markup in the description
- bbcode translator will now translate some named colors
- algorithm for detection of mod order problems is now more sophisticated
- exposed more functionality to python plugins
- updated to qt 4.8.6 dlls
- bugfix: plugin list wasn't
- bugfix: state changes in mod list wasn't always reported
- bugfix: loot client will now create necessary directory
- bugfix: NCC sometimes used wrong source path for extracting
- bugfix: removed noisy debug message
2014-05-04 14:50:01 +02:00
Tannin ea1f959ad5 - download tooltip now also includes the file description
- will now display an error message when the ini file can't be updated (in addition to what windows says)
2014-05-01 10:03:40 +02:00
Tannin 7cf3b3455b Added tag release v1.2.0 for changeset cc9f6dd8ee3f 2014-04-25 20:02:53 +02:00
Tannin 84cfa95254 - bugfix: newly installed files could be added hidden
- bugfix: registered bsas in data aren't ordered correctly
2014-04-25 19:15:12 +02:00
Tannin 264da60929 - updated installer
- prettified a few log messages
2014-04-24 22:07:42 +02:00
Tannin 2db33523a1 - added an option to show meta info on downloads instead of filenames
- MO will now cancel if user tries to run MO vfs-injected
- when saving the MO ini file it is now written to a tmp file first and then overwritten
- updated to link against boost python 1.55
2014-04-23 23:59:30 +02:00
Tannin 540747177a - loot client no longer stalls the process while waiting for masterlist update
- configurator now gives better warning messages when encountering an invalid ini file
- bugfix: crash on cleanup up browser dialog
- bugfix: GetCurrentDirectory returned wrong string length
2014-04-16 22:32:59 +02:00
Tannin 739c3424e7 - loot integration now displays the report
- fixes to the previous merge
- bugfix: cli loot didn't handle non-ascii characters correctly
2014-04-05 20:54:08 +02:00
Tannin cabed9b268 Merge with branch1.1 2014-04-05 15:36:42 +02:00
Tannin c017f4a0d5 - added buttons to backup and restore the modlist and pluginlist
- replaced boss integration with loot
2014-04-05 15:14:37 +02:00
Tannin b1f1682790 - bugfix: the add/remove categories function could fail when a filter/grouping on categories was active 2014-03-26 17:23:26 +01:00
Tannin 28301e7486 - improved NCC compatibility
- crude support for multi-volume archives
- updated imageformats plugins
- nxmhandler now puts the exe to the top of the list when registering an MO instance, even if it is already in the list
- bugfix: WritePrivateProfileString hook attempted to access lpKeyName even when it is null
2014-03-26 15:35:59 +01:00
Tannin 2d0fab4482 Merge 2014-03-18 19:18:17 +01:00
Tannin c19c4820d8 - force-enabled game esms no longer break keyboard-navigation in plugin list 2014-03-18 19:18:04 +01:00
Tannin 8ab1b47943 - bugfix: if resuming a download failed with the server sending a textual error message,
MO tried to display the whole file inside the error message
- bugfix: resuming a download didn't trigger a nexus-login when necessary
- bugfix: integrated fomod installer only used the first block of data inside a description
2014-03-18 18:29:32 +01:00
Tannin ff272dcbbd Merge 2014-03-16 20:05:13 +01:00
Tannin 98354cd1e7 some fixes towards qt5 compatibility 2014-03-16 20:04:57 +01:00
Tannin 129b76d7d2 - unpacking bsas during installation no longer overwrites loose files (since the loose files
would have taken precedence)
2014-03-13 21:57:11 +01:00
Tannin a82b7e9949 - "CreateDirectory" will no longer create directories in original data directory
- bain installer will now be less picky about the archives supported
- updated NCC to be compatible with more recent NMM code base
- hack in NCC to allow it to install arbitrary sized files even in 32-bit builds
- updated the python27.dll to one that links to msvcr100.dll to get rid of the dependency of msvcr90.dll
- bugfix: dll search order wasn't set to allow plugins to load the correct qt dlls
2014-03-13 19:00:32 +01:00
Tannin 0f8f7bf777 Added tag releease v1.1.2 for changeset 4276e1188b42 2014-02-26 22:14:06 +01:00
Tannin f4cf992700 Removed tag 1.1.2 2014-02-26 22:13:53 +01:00
Tannin bec644bac3 Added tag 1.1.2 for changeset 4276e1188b42 2014-02-26 22:13:43 +01:00
Tannin bb98bfade6 - MO will now be elevated if needed 2014-02-23 18:53:38 +01:00
Tannin 00f4e1799f - slightly overworked display of icon delegates
- updated installer
2014-02-23 17:18:20 +01:00
Tannin 35fcf1c25b - made the indicator for drag&drop more visible
- message boxes can now be made to not (re-)activate the window
- executable names for starting from the command line are now case-insensitive
2014-02-22 19:22:12 +01:00
Tannin bed3c08a6d - bugfix: on-demand loading of data tree broke filtering on conflicting files 2014-02-18 19:08:12 +01:00
Tannin f9eba9dc15 - ini file changes that would have to overwrite ini tweaks are now stored in a separate file
and actually get used.
- a warning message advices users to clean out that separate file
- slightly improved default main window layout
- bugfix: newly created ini tweaks were added to the list with incorrect  properties
2014-02-17 21:15:20 +01:00
Tannin 9c8e43853d - a few hooks will now somewhat handle file names starting with \\?\
- mod meta information is now (also) saved by a timer to reduce the likelyhood
of a data loss in case of a crash
- mod meta files are now written to a temporary file and then renamed to real name to
reduce chance of breaking the file
- updated minimum compatible nmm version to 0.47.0
- bugfix: defaults for newestVersion, version and installationFile when creating
an empty mod were integers instead of strings
- bugfix: "Plugins" and "Archives" weren't translatable
2014-02-12 21:00:49 +01:00
Tannin 7aadb47637 - archives.txt is now written using the "safe"-write mechanism
- added the whole boss fork to the repository
- bugfix: boss db is now always re-initialised because otherwise there might have been differing results between runs
- bugfix: locked load order was ignored by integrated boss
- bugfix: archive list wasn't written back on all changes that affected it
- bugfix: CreateFile-hook didn't reroute files created with OPEN_ALWAYS to overwrite directory
- bugfix: NtQueryDirectoryFile-hook didn't return the correct status code when searching for a file that doesn't exist
2014-02-07 21:05:48 +01:00
Tannin 76fbe6effe - tabs in the mod info dialog can now be moved around by the user 2014-02-04 19:55:27 +01:00
Tannin 38ee6ccf0a Added tag release v1.1.1 for changeset 73a1f1438658 2014-02-02 15:14:07 +01:00
Tannin 7722a9b6df - main window is now actually disabled while integrated boss is running
- bugfix: integrated boss sorting couldn't recognize plugins specified via regular expression
2014-02-02 14:26:24 +01:00
Tannin 48704877ca - bugfix: upon moving files between mods, an attempt was made to access origins
for both, even if one (or both) mods weren't active
- bugfix: plugin-list should now deal with nested "layoutAboutToBeChanged" calls
gracefully. May be the reason of a bug.
2014-02-02 00:09:03 +01:00
Tannin 48c944c737 - bugfix: elevation dialog didn't offer a "yes" option
- bugfix: python plugins crashed the application when trying to create a nexus bridge
- bugfix: python plugins couldn't register for failed requests correctly
2014-01-29 21:06:55 +01:00
Tannin af50eedbe2 - bugfix: dds preview or transparent textures looked odd
- bugfix: lazy loaded data tree deleted the placeholder
2014-01-29 00:23:16 +01:00
Tannin 52e5dd3c57 Added tag release v1.1.0 for changeset 87c2eea95307 2014-01-29 00:20:47 +01:00
Tannin ccd5d1294f - bugfix: accessing profile without loading one when starting an app through the commandline
- bugfix: locked-dialog was disabled
2014-01-27 21:31:10 +01:00
Tannin de984a4ef8 - information from boss no longer gets lost as soon as the plugin list gets refreshed
- bugfix: modified boss dll missed one file on sorting
2014-01-23 21:14:44 +01:00
Tannin e597823337 - boss integration
- plugin list can now also display multiple flags for a file (like the mod list)
- changed some compiler&linker settings to produce smaller binaries
2014-01-23 00:05:46 +01:00
Tannin e69210b3a7 - when a download server returns a text file, it's assumed to be an error and the text displayed as an error
- save games can now be deleted from inside MO
- bugfix: removing the pending download entry failed if the download-url request failed
- bugfix: download manager didn't stop automatically resuming failed downloads under certain circumstances
- bugfix: uninstalled downloads were treated as not-finished when refreshing the download list
- bugfix: updating the filesystem watcher on the saves directory didn't work correctly
2014-01-18 19:51:58 +01:00
Tannin 40d20ab294 - added an about dialog
- updated json library
2014-01-16 20:43:42 +01:00
Tannin 6ed82866b0 - moved code for file previews to plugins 2014-01-14 21:16:36 +01:00
Tannin a083a2d3b6 - implemented hook for NtQueryDirectoryFile
- saves list is now automatically updated on FS changes
- optimization: data tree widget is no longer filled completely at once but one directory at a time
- bugfix: pending downloads were not removed from list after a failed nxm request
- bugfix: there was still a nmm.nexusmods.com link
- bugfix: the text "alpha" in version strings wasn't interpreted correctly
2014-01-13 19:32:54 +01:00
Tannin db09b806b9 - Mod Organizer can now display most image types (including dds) and txt files from the data
tree, presenting a comparison of variants in case of overwritten files
2014-01-05 18:50:56 +01:00
Tannin 859c0aed98 download manager now removed orphaned meta files 2014-01-04 16:39:38 +01:00
Tannin 0ea7d99b9f Merge 2014-01-04 16:05:39 +01:00
Tannin 6cffbd4f27 using icons + splash from heavydude 2014-01-04 16:05:25 +01:00
Tannin 4dc3538a7d - nxmhandler will now ask before registering itself
- downloads from nexus are now displayed before file information is retrieved
- logging from the ui is now a bit more informative
- download list now scrolls to bottom automatically
2014-01-04 15:57:14 +01:00
Tannin e45b747c82 Added tag release v1.0.12 for changeset 440287834340 2014-01-03 18:43:03 +01:00
Tannin 2d6bacbb6a -removed compiled translations from the repository 2014-01-03 18:41:47 +01:00
Tannin d029e97724 - hook dll now monitors all mod directories, not only overwrite
- dialog for changing executables now warns if changes will be lost
- main window is now locked before activating pre-start hooks
- better responsiveness while main window is locked (or should be)
2014-01-03 18:40:46 +01:00
Tannin a9435f637e Added tag release v1.0.11 for changeset 806fd77aa2de 2013-12-19 20:33:38 +01:00
Tannin e1e35da6fe Added tag release v1.0.10 for changeset e72788d8ac35 2013-12-19 20:31:48 +01:00
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 98e5e57a84 - added support for mod page plugins
- re-introduced the integrated browser
- added a plugin to download from the tes alliance page
- the download list now contains the file description
- nexus interface now stores cookies persistently to reduce number of required log-ins
2013-09-15 15:20:00 +02:00
157 changed files with 19735 additions and 15644 deletions
+7
View File
@@ -13,3 +13,10 @@ source - Copy/*
ModOrganizer-build-*
pdbs/*
source/NCC/BossDummy.x/*
*.ts
staging_prepare/*
staging_trans/*
tools/python_zip/*
Makefile
syntax: regexp
Makefile\.(Debug|Release)
+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...
+12 -11
View File
@@ -1,23 +1,24 @@
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 \
loot_cli
plugins.depends = pythonRunner
hookdll.depends = shared
organizer.depends = shared, uibase, plugins
organizer.depends = shared uibase plugins loot_cli
CONFIG(debug, debug|release) {
DESTDIR = outputd
+83
View File
@@ -0,0 +1,83 @@
/*
Copyright (C) 2014 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "aboutdialog.h"
#include "ui_aboutdialog.h"
#include <utility.h>
AboutDialog::AboutDialog(const QString &version, QWidget *parent)
: QDialog(parent)
, ui(new Ui::AboutDialog)
{
ui->setupUi(this);
m_LicenseFiles[LICENSE_LGPL3] = "lgpl-3.0.txt";
m_LicenseFiles[LICENSE_GPL3] = "gpl-3.0.txt";
m_LicenseFiles[LICENSE_BSD3] = "bsd3.txt";
m_LicenseFiles[LICENSE_BOOST] = "boost.txt";
m_LicenseFiles[LICENSE_CCBY3] = "by-sa3.txt";
m_LicenseFiles[LICENSE_ZLIB] = "zlib.txt";
addLicense("Qt 4.8.5", LICENSE_LGPL3);
addLicense("Qt Json", LICENSE_GPL3);
addLicense("Boost Library", LICENSE_BOOST);
addLicense("Tango Icon Theme", LICENSE_NONE);
addLicense("RRZE Icon Set", LICENSE_CCBY3);
addLicense("7-zip", LICENSE_LGPL3);
addLicense("ZLib", LICENSE_ZLIB);
addLicense("NIF File Format Library", LICENSE_BSD3);
addLicense("BOSS (modified)", LICENSE_GPL3);
addLicense("Alphanum Algorithm", LICENSE_ZLIB);
ui->nameLabel->setText(QString("<span style=\"font-size:12pt; font-weight:600;\">%1 %2</span>").arg(ui->nameLabel->text()).arg(version));
#ifdef HGID
ui->revisionLabel->setText(ui->revisionLabel->text() + " " + HGID);
#else
ui->revisionLabel->setText(ui->revisionLabel->text() + " unknown");
#endif
}
AboutDialog::~AboutDialog()
{
delete ui;
}
void AboutDialog::addLicense(const QString &name, Licenses license)
{
QListWidgetItem *item = new QListWidgetItem(name);
item->setData(Qt::UserRole, license);
ui->creditsList->addItem(item);
}
void AboutDialog::on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem*)
{
auto iter = m_LicenseFiles.find(current->data(Qt::UserRole).toInt());
if (iter != m_LicenseFiles.end()) {
QString filePath = qApp->applicationDirPath() + "/license/" + iter->second;
QString text = MOBase::readFileText(filePath);
ui->licenseText->setText(text);
} else {
ui->licenseText->setText(tr("No license"));
}
}
+69
View File
@@ -0,0 +1,69 @@
#ifndef ABOUTDIALOG_H
/*
Copyright (C) 2014 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#define ABOUTDIALOG_H
#include <QDialog>
#include <QListWidgetItem>
#include <map>
#include <vector>
#include <utility>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(const QString &version, QWidget *parent = 0);
~AboutDialog();
private:
enum Licenses {
LICENSE_NONE,
LICENSE_LGPL3,
LICENSE_GPL3,
LICENSE_BSD3,
LICENSE_BOOST,
LICENSE_CCBY3,
LICENSE_ZLIB
};
private:
void addLicense(const QString &name, Licenses license);
private slots:
void on_creditsList_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous);
private:
Ui::AboutDialog *ui;
std::map<int, QString> m_LicenseFiles;
};
#endif // ABOUTDIALOG_H
+295
View File
@@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>508</width>
<height>335</height>
</rect>
</property>
<property name="windowTitle">
<string>About</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="iconLabel">
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources.qrc">:/MO/gui/mo_icon.ico</pixmap>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<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>
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="about">
<attribute name="title">
<string>About</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="nameLabel">
<property name="text">
<string notr="true">Mod Organizer</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="revisionLabel">
<property name="text">
<string>Revision:</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="QLabel" name="label_3">
<property name="text">
<string notr="true">Copyright 2011-2014 Sebastian Herbord</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.&lt;/p&gt;&lt;p&gt;See the GNU General Public License for more details.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="software">
<attribute name="title">
<string>Used Software</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QListWidget" name="creditsList"/>
</item>
<item>
<widget class="QTextBrowser" name="licenseText"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="credits">
<attribute name="title">
<string>Credits</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Translators</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QListWidget" name="listWidget_2">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<item>
<property name="text">
<string notr="true">pndrev (German)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">DaWul (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Fiama (Spanish)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Alyndiar (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Jlkawaii (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Rigoletto (French)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Scythe1912 (Chinese)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">yc0620shen (Chinese)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">miraclefreak (Czech)</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">tokcdk (Russian)</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Others</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QListWidget" name="listWidget">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<item>
<property name="text">
<string notr="true">blacksol</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">DoubleYou</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">deathneko11</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Bridger</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">GSDFan</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Uhuru</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">Wolverine2710</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">z929669</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="closeButton">
<property name="text">
<string>Close</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<connections>
<connection>
<sender>closeButton</sender>
<signal>clicked()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>460</x>
<y>313</y>
</hint>
<hint type="destinationlabel">
<x>253</x>
<y>167</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+33 -10
View File
@@ -22,6 +22,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QRegExp>
#include <map>
#include <algorithm>
#include <boost/assign.hpp>
namespace BBCode {
@@ -43,7 +44,6 @@ public:
// extract the tag name
m_TagNameExp.indexIn(input, 1, QRegExp::CaretAtOffset);
QString tagName = m_TagNameExp.cap(0).toLower();
//qDebug("tag name %s", tagName.toUtf8().constData());
TagMap::iterator tagIter = m_TagMap.find(tagName);
if (tagIter != m_TagMap.end()) {
// recognized tag
@@ -60,7 +60,21 @@ public:
length = closeTagPos + closeTag.length();
QString temp = input.mid(0, length);
if (tagIter->second.first.indexIn(temp) == 0) {
return temp.replace(tagIter->second.first, tagIter->second.second);
if (tagIter->second.second.isEmpty()) {
if (tagName == "color") {
QString color = tagIter->second.first.cap(1);
QString content = tagIter->second.first.cap(2);
auto colIter = m_ColorMap.find(color.toLower());
if (colIter != m_ColorMap.end()) {
color = colIter->second;
}
return temp.replace(tagIter->second.first, QString("<font style=\"color: #%1;\">%2</font>").arg(color, content));
} else {
qWarning("don't know how to deal with tag %s", qPrintable(tagName));
}
} else {
return temp.replace(tagIter->second.first, tagIter->second.second);
}
} else {
// expression doesn't match. either the input string is invalid
// or the expression is
@@ -96,7 +110,7 @@ private:
m_TagMap["size="] = std::make_pair(QRegExp("\\[size=([^\\]]*)\\](.*)\\[/size\\]"),
"<font size=\"\\1\">\\2</font>");
m_TagMap["color="] = std::make_pair(QRegExp("\\[color=([^\\]]*)\\](.*)\\[/color\\]"),
"<font style=\"color: #\\1;\">\\2</font>");
"");
m_TagMap["font="] = std::make_pair(QRegExp("\\[font=([^\\]]*)\\](.*)\\[/font\\]"),
"<font face=\\1>\\2</font>");
m_TagMap["center"] = std::make_pair(QRegExp("\\[center\\](.*)\\[/center\\]"),
@@ -139,17 +153,25 @@ private:
"<a href=\"\\1\">\\1</a>");
m_TagMap["url="] = std::make_pair(QRegExp("\\[url=([^\\]]*)\\](.*)\\[/url\\]"),
"<a href=\"\\1\">\\2</a>");
/* m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"),
"<img src=\"\\1\"/>");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"),
"<img src=\"\\2\" align=\"\\1\" />");*/
m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), "");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), "");
m_TagMap["img"] = std::make_pair(QRegExp("\\[img\\](.*)\\[/img\\]"), " ");
m_TagMap["img="] = std::make_pair(QRegExp("\\[img=([^\\]]*)\\](.*)\\[/img\\]"), " ");
m_TagMap["email="] = std::make_pair(QRegExp("\\[email=\"?([^\\]]*)\"?\\](.*)\\[/email\\]"),
"<a href=\"mailto:\\1\">\\2</a>");
m_TagMap["youtube"] = std::make_pair(QRegExp("\\[youtube\\](.*)\\[/youtube\\]"),
"<a href=\"http://www.youtube.com/v/\\1\">http://www.youtube.com/v/\\1</a>");
m_ColorMap.insert(std::make_pair<QString, QString>("red", "FF0000"));
m_ColorMap.insert(std::make_pair<QString, QString>("green", "00FF00"));
m_ColorMap.insert(std::make_pair<QString, QString>("blue", "0000FF"));
m_ColorMap.insert(std::make_pair<QString, QString>("black", "000000"));
m_ColorMap.insert(std::make_pair<QString, QString>("gray", "7F7F7F"));
m_ColorMap.insert(std::make_pair<QString, QString>("white", "FFFFFF"));
m_ColorMap.insert(std::make_pair<QString, QString>("yellow", "FFFF00"));
m_ColorMap.insert(std::make_pair<QString, QString>("cyan", "00FFFF"));
m_ColorMap.insert(std::make_pair<QString, QString>("magenta", "FF00FF"));
m_ColorMap.insert(std::make_pair<QString, QString>("brown", "A52A2A"));
m_ColorMap.insert(std::make_pair<QString, QString>("orange", "FFCC00"));
// make all patterns non-greedy and case-insensitive
for (TagMap::iterator iter = m_TagMap.begin(); iter != m_TagMap.end(); ++iter) {
iter->second.first.setCaseSensitivity(Qt::CaseInsensitive);
@@ -157,10 +179,11 @@ private:
}
}
private:
QRegExp m_TagNameExp;
TagMap m_TagMap;
std::map<QString, QString> m_ColorMap;
};
+270
View File
@@ -0,0 +1,270 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "browserdialog.h"
#include "ui_browserdialog.h"
#include "messagedialog.h"
#include "report.h"
#include "json.h"
#include "persistentcookiejar.h"
#include <gameinfo.h>
#include <utility.h>
#include <gameinfo.h>
#include <QNetworkCookieJar>
#include <QNetworkCookie>
#include <QMenu>
#include <QInputDialog>
#include <QWebHistory>
#include <QDir>
#include <QWebFrame>
#include <QDesktopWidget>
BrowserDialog::BrowserDialog(QWidget *parent)
: QDialog(parent)
, ui(new Ui::BrowserDialog)
, m_AccessManager(new QNetworkAccessManager)
{
ui->setupUi(this);
m_AccessManager->setCookieJar(new PersistentCookieJar(
QDir::fromNativeSeparators(MOBase::ToQString(MOShared::GameInfo::instance().getCacheDir())) + "/cookies.dat", this));
Qt::WindowFlags flags = windowFlags() | Qt::WindowMaximizeButtonHint | Qt::WindowMinimizeButtonHint;
Qt::WindowFlags helpFlag = Qt::WindowContextHelpButtonHint;
flags = flags & (~helpFlag);
setWindowFlags(flags);
m_Tabs = this->findChild<QTabWidget*>("browserTabWidget");
connect(m_Tabs, SIGNAL(tabCloseRequested(int)), this, SLOT(tabCloseRequested(int)));
}
BrowserDialog::~BrowserDialog()
{
delete ui;
}
void BrowserDialog::closeEvent(QCloseEvent *event)
{
// m_AccessManager->showCookies();
QDialog::closeEvent(event);
}
void BrowserDialog::initTab(BrowserView *newView)
{
newView->page()->setNetworkAccessManager(m_AccessManager);
newView->page()->setForwardUnsupportedContent(true);
connect(newView, SIGNAL(loadProgress(int)), this, SLOT(progress(int)));
connect(newView, SIGNAL(titleChanged(QString)), this, SLOT(titleChanged(QString)));
connect(newView, SIGNAL(initTab(BrowserView*)), this, SLOT(initTab(BrowserView*)));
connect(newView, SIGNAL(startFind()), this, SLOT(startSearch()));
connect(newView, SIGNAL(urlChanged(QUrl)), this, SLOT(urlChanged(QUrl)));
connect(newView, SIGNAL(openUrlInNewTab(QUrl)), this, SLOT(openInNewTab(QUrl)));
connect(newView->page(), SIGNAL(downloadRequested(QNetworkRequest)), this, SLOT(downloadRequested(QNetworkRequest)));
connect(newView->page(), SIGNAL(unsupportedContent(QNetworkReply*)), this, SLOT(unsupportedContent(QNetworkReply*)));
ui->backBtn->setEnabled(false);
ui->fwdBtn->setEnabled(false);
m_Tabs->addTab(newView, tr("new"));
newView->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
newView->settings()->setAttribute(QWebSettings::AutoLoadImages, true);
}
void BrowserDialog::openInNewTab(const QUrl &url)
{
BrowserView *newView = new BrowserView(this);
initTab(newView);
newView->setUrl(url);
}
BrowserView *BrowserDialog::getCurrentView()
{
return qobject_cast<BrowserView*>(m_Tabs->currentWidget());
}
void BrowserDialog::urlChanged(const QUrl&)
{
BrowserView *currentView = getCurrentView();
if (currentView != NULL) {
ui->backBtn->setEnabled(currentView->history()->canGoBack());
ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
}
}
void BrowserDialog::openUrl(const QUrl &url)
{
if (isHidden()) {
show();
}
openInNewTab(url);
}
void BrowserDialog::maximizeWidth()
{
int viewportWidth = getCurrentView()->page()->viewportSize().width();
int frameWidth = width() - viewportWidth;
int contentWidth = getCurrentView()->page()->mainFrame()->contentsSize().width();
QDesktopWidget screen;
int currentScreen = screen.screenNumber(this);
int screenWidth = screen.screenGeometry(currentScreen).size().width();
int targetWidth = std::min<int>(std::max<int>(viewportWidth, contentWidth) + frameWidth, screenWidth);
this->resize(targetWidth, height());
}
void BrowserDialog::progress(int value)
{
ui->loadProgress->setValue(value);
if (value == 100) {
maximizeWidth();
ui->loadProgress->setVisible(false);
} else {
ui->loadProgress->setVisible(true);
}
}
void BrowserDialog::titleChanged(const QString &title)
{
BrowserView *view = qobject_cast<BrowserView*>(sender());
for (int i = 0; i < m_Tabs->count(); ++i) {
if (m_Tabs->widget(i) == view) {
m_Tabs->setTabText(i, title.mid(0, 15));
m_Tabs->setTabToolTip(i, title);
}
}
}
QString BrowserDialog::guessFileName(const QString &url)
{
QRegExp uploadsExp(QString("http://.+/uploads/([^/]+)$"));
if (uploadsExp.indexIn(url) != -1) {
// these seem to be premium downloads
return uploadsExp.cap(1);
}
QRegExp filesExp(QString("http://.+\\?file=([^&]+)"));
if (filesExp.indexIn(url) != -1) {
// a regular manual download?
return filesExp.cap(1);
}
return "unknown";
}
void BrowserDialog::unsupportedContent(QNetworkReply *reply)
{
try {
QWebPage *page = qobject_cast<QWebPage*>(sender());
if (page == NULL) {
qCritical("sender not a page");
return;
}
BrowserView *view = qobject_cast<BrowserView*>(page->view());
if (view == NULL) {
qCritical("no view?");
return;
}
qDebug("unsupported: %s - %s", view->url().toString().toUtf8().constData(), reply->url().toString().toUtf8().constData());
emit requestDownload(view->url(), reply);
} catch (const std::exception &e) {
if (isVisible()) {
MessageDialog::showMessage(tr("failed to start download"), this);
}
qCritical("exception downloading unsupported content: %s", e.what());
}
}
void BrowserDialog::downloadRequested(const QNetworkRequest &request)
{
qCritical("download request %s ignored", request.url().toString().toUtf8().constData());
}
void BrowserDialog::tabCloseRequested(int index)
{
if (m_Tabs->count() == 1) {
this->close();
} else {
m_Tabs->widget(index)->deleteLater();
m_Tabs->removeTab(index);
}
}
void BrowserDialog::on_backBtn_clicked()
{
BrowserView *currentView = getCurrentView();
if (currentView != NULL) {
currentView->back();
}
}
void BrowserDialog::on_fwdBtn_clicked()
{
BrowserView *currentView = getCurrentView();
if (currentView != NULL) {
currentView->forward();
}
}
void BrowserDialog::startSearch()
{
ui->searchEdit->setFocus();
}
void BrowserDialog::on_searchEdit_returnPressed()
{
BrowserView *currentView = getCurrentView();
if (currentView != NULL) {
currentView->findText(ui->searchEdit->text(), QWebPage::FindWrapsAroundDocument);
}
}
void BrowserDialog::on_refreshBtn_clicked()
{
getCurrentView()->reload();
}
void BrowserDialog::on_browserTabWidget_currentChanged(int index)
{
BrowserView *currentView = qobject_cast<BrowserView*>(ui->browserTabWidget->widget(index));
if (currentView != NULL) {
ui->backBtn->setEnabled(currentView->history()->canGoBack());
ui->fwdBtn->setEnabled(currentView->history()->canGoForward());
}
}
+124
View File
@@ -0,0 +1,124 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 BROWSERDIALOG_H
#define BROWSERDIALOG_H
#include "browserview.h"
#include "tutorialcontrol.h"
#include <QDialog>
#include <QProgressBar>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTimer>
#include <QWebView>
#include <QQueue>
#include <QTabWidget>
#include <QAtomicInt>
namespace Ui {
class BrowserDialog;
}
/**
* @brief a dialog containing a webbrowser that is intended to browse the nexus network
**/
class BrowserDialog : public QDialog
{
Q_OBJECT
public:
/**
* @brief constructor
*
* @param accessManager the access manager to use for network requests
* @param parent parent widget
**/
explicit BrowserDialog(QWidget *parent = 0);
~BrowserDialog();
/**
* @brief set the url to open. If automatic login is enabled, the url is opened after login
*
* @param url the url to open
**/
void openUrl(const QUrl &url);
signals:
/**
* @brief emitted when the user starts a download
* @param pageUrl url of the current web site from which the download was started
* @param reply network reply of the started download
*/
void requestDownload(const QUrl &pageUrl, QNetworkReply *reply);
protected:
virtual void closeEvent(QCloseEvent *);
private slots:
void initTab(BrowserView *newView);
void openInNewTab(const QUrl &url);
void progress(int value);
void titleChanged(const QString &title);
void unsupportedContent(QNetworkReply *reply);
void downloadRequested(const QNetworkRequest &request);
void tabCloseRequested(int index);
void urlChanged(const QUrl &url);
void on_backBtn_clicked();
void on_fwdBtn_clicked();
void on_searchEdit_returnPressed();
void startSearch();
void on_refreshBtn_clicked();
void on_browserTabWidget_currentChanged(int index);
private:
QString guessFileName(const QString &url);
BrowserView *getCurrentView();
void maximizeWidth();
private:
Ui::BrowserDialog *ui;
QNetworkAccessManager *m_AccessManager;
QTabWidget *m_Tabs;
};
#endif // BROWSERDIALOG_H
+289
View File
@@ -0,0 +1,289 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>BrowserDialog</class>
<widget class="QDialog" name="BrowserDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1008</width>
<height>750</height>
</rect>
</property>
<property name="windowTitle">
<string>Some Page</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="toolBar" native="true">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>22</height>
</size>
</property>
<property name="palette">
<palette>
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>226</red>
<green>226</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>106</red>
<green>106</green>
<blue>106</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>199</red>
<green>199</green>
<blue>199</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>81</red>
<green>81</green>
<blue>81</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>226</red>
<green>226</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>106</red>
<green>106</green>
<blue>106</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>199</red>
<green>199</green>
<blue>199</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>81</red>
<green>81</green>
<blue>81</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>106</red>
<green>106</green>
<blue>106</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>120</red>
<green>120</green>
<blue>120</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>81</red>
<green>81</green>
<blue>81</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>81</red>
<green>81</green>
<blue>81</blue>
</color>
</brush>
</colorrole>
</disabled>
</palette>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QPushButton" name="backBtn">
<property name="autoFillBackground">
<bool>false</bool>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/MO/gui/previous</normaloff>:/MO/gui/previous</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="fwdBtn">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/MO/gui/next</normaloff>:/MO/gui/next</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="refreshBtn">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/MO/gui/refresh</normaloff>:/MO/gui/refresh</iconset>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="flat">
<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="QLabel" name="label_2">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchEdit"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="browserTabWidget">
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="tabsClosable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="loadProgress">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<connections/>
</ui>
+76
View File
@@ -0,0 +1,76 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "browserview.h"
#include <QEvent>
#include <QKeyEvent>
#include <QWebFrame>
#include <QWebElement>
#include <QNetworkDiskCache>
#include <QMenu>
#include <Shlwapi.h>
#include "utility.h"
BrowserView::BrowserView(QWidget *parent)
: QWebView(parent)
{
installEventFilter(this);
page()->settings()->setMaximumPagesInCache(10);
}
QWebView *BrowserView::createWindow(QWebPage::WebWindowType)
{
BrowserView *newView = new BrowserView(parentWidget());
emit initTab(newView);
return newView;
}
bool BrowserView::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::ShortcutOverride) {
QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->matches(QKeySequence::Find)) {
emit startFind();
} else if (keyEvent->matches(QKeySequence::FindNext)) {
emit findAgain();
}
} else if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::MidButton) {
mouseEvent->ignore();
return true;
}
} else if (event->type() == QEvent::MouseButtonRelease) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::MidButton) {
QWebHitTestResult hitTest = page()->frameAt(mouseEvent->pos())->hitTestContent(mouseEvent->pos());
if (hitTest.linkUrl().isValid()) {
emit openUrlInNewTab(hitTest.linkUrl());
}
mouseEvent->ignore();
return true;
}
}
return QWebView::eventFilter(obj, event);
}
+80
View File
@@ -0,0 +1,80 @@
/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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 NEXUSVIEW_H
#define NEXUSVIEW_H
#include "finddialog.h"
#include <QWebView>
#include <QWebPage>
#include <QTabWidget>
/**
* @brief web view used to display a nexus page
**/
class BrowserView : public QWebView
{
Q_OBJECT
public:
explicit BrowserView(QWidget *parent = 0);
signals:
/**
* @brief emitted when the user opens a new window to be displayed in another tab
*
* @param newView the view for the newly opened window
**/
void initTab(BrowserView *newView);
/**
* @brief emitted when the user requests a link to be opened in a new tab by middle-clicking
*
* @param url the url to open
*/
void openUrlInNewTab(const QUrl &url);
/**
* @brief Ctrl-f was clicked. The containing dialog should activate its find-facility
*/
void startFind();
/**
* @brief F3 was pressed. The containing dialog should search again
*/
void findAgain();
protected:
virtual QWebView *createWindow(QWebPage::WebWindowType type);
virtual bool eventFilter(QObject *obj, QEvent *event);
private:
QString m_FindPattern;
bool m_MiddleClick;
};
#endif // NEXUSVIEW_H
+1
View File
@@ -103,6 +103,7 @@ CategoryFactory &CategoryFactory::instance()
void CategoryFactory::reset()
{
m_Categories.clear();
m_IDMap.clear();
addCategory(0, "None", MakeVector<int>(2, 28, 87), 0);
}
+60 -15
View File
@@ -20,8 +20,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "directoryrefresher.h"
#include "utility.h"
#include "report.h"
#include "modinfo.h"
#include <gameinfo.h>
#include <QDir>
#include <QString>
using namespace MOBase;
@@ -46,10 +48,19 @@ DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
return result;
}
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods)
void DirectoryRefresher::setMods(const std::vector<std::tuple<QString, QString, int> > &mods
, const std::set<QString> &managedArchives)
{
QMutexLocker locker(&m_RefreshLock);
m_Mods = mods;
m_Mods.clear();
for (auto mod = mods.begin(); mod != mods.end(); ++mod) {
QString name = std::get<0>(*mod);
ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
m_Mods.push_back(EntryInfo(name, std::get<1>(*mod), info->stealFiles(), info->archives(), std::get<2>(*mod)));
}
m_EnabledArchives = managedArchives;
}
@@ -61,21 +72,56 @@ void DirectoryRefresher::cleanStructure(DirectoryEntry *structure)
}
static wchar_t *dirs[] = { L"fomod" };
for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
structure->removeDir(dirs[i]);
for (int i = 0; i < sizeof(dirs) / sizeof(wchar_t*); ++i) {
structure->removeDir(std::wstring(dirs[i]));
}
}
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory)
void DirectoryRefresher::addModToStructure(DirectoryEntry *directoryStructure
, const QString &modName
, int priority
, const QString &directory
, const QStringList &stealFiles
, const QStringList &archives)
{
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
std::wstring modNameW = ToWString(modName);
directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority);
QDir dir(directory);
if (stealFiles.length() > 0) {
// instead of adding all the files of the target directory, we just change the root of the specified
// files to this mod
directoryStructure->createOrigin(modNameW, directoryW, priority);
foreach (const QString &filename, stealFiles) {
QFileInfo fileInfo(filename);
FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName()));
if (file.get() != NULL) {
if (file->getOrigin() == 0) {
// replace data as the origin on this bsa
file->removeOrigin(0);
file->addOrigin(directoryStructure->getOriginByName(modNameW).getID(),
file->getFileTime(), L"");
}
}
}
} else {
directoryStructure->addFromOrigin(modNameW, directoryW, priority);
}
/* QDir dir(directory);
QFileInfoList bsaFiles = dir.entryInfoList(QStringList("*.bsa"), QDir::Files);
foreach (QFileInfo file, bsaFiles) {
directoryStructure->addFromBSA(ToWString(modName), directoryW,
ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
if (m_EnabledArchives.find(file.fileName()) != m_EnabledArchives.end()) {
directoryStructure->addFromBSA(ToWString(modName), directoryW,
ToWString(QDir::toNativeSeparators(file.absoluteFilePath())), priority);
}
}*/
foreach (const QString &archive, archives) {
QFileInfo fileInfo(archive);
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
directoryStructure->addFromBSA(modNameW, directoryW,
ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())), priority);
}
}
}
@@ -87,23 +133,22 @@ void DirectoryRefresher::refresh()
m_DirectoryStructure = new DirectoryEntry(L"data", NULL, 0);
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
// TODO what was the point of having the priority in this tuple? the list is already sorted by priority
std::vector<std::tuple<QString, QString, int> >::const_iterator iter = m_Mods.begin();
auto iter = m_Mods.begin();
//TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but inverted!
for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
QString modName = std::get<0>(*iter);
try {
addModToStructure(m_DirectoryStructure, modName, i, std::get<1>(*iter));
addModToStructure(m_DirectoryStructure, iter->modName, i, iter->absolutePath, iter->stealFiles, iter->archives);
} catch (const std::exception &e) {
emit error(tr("failed to read bsa: %1").arg(e.what()));
}
emit progress((i * 100) / m_Mods.size() + 1);
}
std::wstring dataDirectory = GameInfo::instance().getGameDirectory() + L"\\data";
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
emit progress(100);
cleanStructure(m_DirectoryStructure);
+24 -7
View File
@@ -20,11 +20,13 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef DIRECTORYREFRESHER_H
#define DIRECTORYREFRESHER_H
#include <QObject>
#include <vector>
#include <QMutex>
#include <tuple>
#include <directoryentry.h>
#include <QObject>
#include <QMutex>
#include <QStringList>
#include <vector>
#include <set>
#include <tuple>
/**
@@ -60,7 +62,7 @@ public:
*
* @param mods list of the mods to include
**/
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods);
void setMods(const std::vector<std::tuple<QString, QString, int> > &mods, const std::set<QString> &managedArchives);
/**
* @brief sets up the directory where mods are stored
@@ -82,7 +84,7 @@ public:
* @param directory
* @param priorityDir
*/
static void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory);
void addModToStructure(MOShared::DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles, const QStringList &archives);
public slots:
@@ -99,7 +101,22 @@ signals:
private:
std::vector<std::tuple<QString, QString, int> > m_Mods;
struct EntryInfo {
EntryInfo(const QString &modName, const QString &absolutePath,
const QStringList &stealFiles, const QStringList &archives, int priority)
: modName(modName), absolutePath(absolutePath), stealFiles(stealFiles)
, archives(archives), priority(priority) {}
QString modName;
QString absolutePath;
QStringList stealFiles;
QStringList archives;
int priority;
};
private:
std::vector<EntryInfo> m_Mods;
std::set<QString> m_EnabledArchives;
MOShared::DirectoryEntry *m_DirectoryStructure;
QMutex m_RefreshLock;
+12 -7
View File
@@ -34,9 +34,10 @@ DownloadList::DownloadList(DownloadManager *manager, QObject *parent)
int DownloadList::rowCount(const QModelIndex&) const
{
return m_Manager->numTotalDownloads();
return m_Manager->numTotalDownloads() + m_Manager->numPendingDownloads();
}
int DownloadList::columnCount(const QModelIndex&) const
{
return 3;
@@ -75,14 +76,18 @@ 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())) {
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
if (index.row() < m_Manager->numTotalDownloads()) {
QString text = m_Manager->getFileName(index.row()) + "\n";
if (m_Manager->isInfoIncomplete(index.row())) {
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
} else {
const MOBase::ModRepositoryFileInfo *info = m_Manager->getFileInfo(index.row());
return QString("%1 (ID %2) %3<br><span>%4</span>").arg(info->modName).arg(m_Manager->getModID(index.row())).arg(info->version.canonicalString()).arg(info->description);
}
return text;
} else {
NexusInfo info = m_Manager->getNexusInfo(index.row());
text += QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
return tr("pending download");
}
return text;
} else {
return QVariant();
}
+14 -8
View File
@@ -37,13 +37,16 @@ bool DownloadListSortProxy::lessThan(const QModelIndex &left,
{
int leftIndex = sourceModel()->data(left).toInt();
int rightIndex = sourceModel()->data(right).toInt();
if (left.column() == DownloadList::COL_NAME) {
return m_Manager->getFileName(leftIndex).compare(m_Manager->getFileName(rightIndex), Qt::CaseInsensitive) < 0;
} else if (left.column() == DownloadList::COL_FILETIME) {
return m_Manager->getFileTime(leftIndex) < m_Manager->getFileTime(rightIndex);
} else if (left.column() == DownloadList::COL_STATUS) {
return m_Manager->getState(leftIndex) < m_Manager->getState(rightIndex);
if (leftIndex < m_Manager->numTotalDownloads()) {
if (left.column() == DownloadList::COL_NAME) {
return m_Manager->getFileName(leftIndex).compare(m_Manager->getFileName(rightIndex), Qt::CaseInsensitive) < 0;
} else if (left.column() == DownloadList::COL_FILETIME) {
return m_Manager->getFileTime(leftIndex) < m_Manager->getFileTime(rightIndex);
} else if (left.column() == DownloadList::COL_STATUS) {
return m_Manager->getState(leftIndex) < m_Manager->getState(rightIndex);
} else {
return leftIndex < rightIndex;
}
} else {
return leftIndex < rightIndex;
}
@@ -54,6 +57,9 @@ bool DownloadListSortProxy::filterAcceptsRow(int source_row, const QModelIndex&)
{
if (m_CurrentFilter.length() == 0) {
return true;
} else if (source_row < m_Manager->numTotalDownloads()) {
return m_Manager->getFileName(source_row).contains(m_CurrentFilter, Qt::CaseInsensitive);
} else {
return false;
}
return m_Manager->getFileName(source_row).contains(m_CurrentFilter, Qt::CaseInsensitive);
}
+110 -82
View File
@@ -40,8 +40,13 @@ DownloadListWidget::~DownloadListWidget()
}
DownloadListWidgetDelegate::DownloadListWidgetDelegate(DownloadManager *manager, QTreeView *view, QObject *parent)
: QItemDelegate(parent), m_Manager(manager), m_ItemWidget(new DownloadListWidget), m_ContextRow(0), m_View(view)
DownloadListWidgetDelegate::DownloadListWidgetDelegate(DownloadManager *manager, bool metaDisplay, QTreeView *view, QObject *parent)
: QItemDelegate(parent)
, m_Manager(manager)
, m_MetaDisplay(metaDisplay)
, m_ItemWidget(new DownloadListWidget)
, m_ContextRow(0)
, m_View(view)
{
m_NameLabel = m_ItemWidget->findChild<QLabel*>("nameLabel");
m_SizeLabel = m_ItemWidget->findChild<QLabel*>("sizeLabel");
@@ -82,6 +87,83 @@ void DownloadListWidgetDelegate::drawCache(QPainter *painter, const QStyleOption
}
void DownloadListWidgetDelegate::paintPendingDownload(int downloadIndex) const
{
std::pair<int, int> nexusids = m_Manager->getPendingDownload(downloadIndex);
m_NameLabel->setText(tr("< mod %1 file %2 >").arg(nexusids.first).arg(nexusids.second));
m_SizeLabel->setText("???");
m_InstallLabel->setVisible(true);
m_InstallLabel->setText(tr("Pending"));
m_Progress->setVisible(false);
}
void DownloadListWidgetDelegate::paintRegularDownload(int downloadIndex) const
{
QString name = m_MetaDisplay ? m_Manager->getDisplayName(downloadIndex) : m_Manager->getFileName(downloadIndex);
if (name.length() > 53) {
name.truncate(50);
name.append("...");
}
m_NameLabel->setText(name);
m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1024));
DownloadManager::DownloadState state = m_Manager->getState(downloadIndex);
if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
m_Progress->setVisible(false);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Paused - Double Click to resume", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Paused - Double Click to resume", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkRed);
m_InstallLabel->setPalette(labelPalette);
} else if (state == DownloadManager::STATE_FETCHINGMODINFO) {
m_InstallLabel->setText(tr("Fetching Info 1"));
m_Progress->setVisible(false);
} else if (state == DownloadManager::STATE_FETCHINGFILEINFO) {
m_InstallLabel->setText(tr("Fetching Info 2"));
m_Progress->setVisible(false);
} else if (state >= DownloadManager::STATE_READY) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
m_Progress->setVisible(false);
if (state == DownloadManager::STATE_INSTALLED) {
// the tr-macro doesn't work here, maybe because the translation is actually associated with DownloadListWidget instead
// of DownloadListWidgetDelegate?
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Installed - Double Click to re-install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Installed - Double Click to re-install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkGray);
} else if (state == DownloadManager::STATE_UNINSTALLED) {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::lightGray);
} else {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Done - Double Click to install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Done - Double Click to install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkGreen);
}
m_InstallLabel->setPalette(labelPalette);
if (m_Manager->isInfoIncomplete(downloadIndex)) {
m_NameLabel->setText("<img src=\":/MO/gui/warning_16\" /> " + m_NameLabel->text());
}
} else {
m_InstallLabel->setVisible(false);
m_Progress->setVisible(true);
m_Progress->setValue(m_Manager->getProgress(downloadIndex));
}
}
void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
try {
@@ -95,67 +177,10 @@ void DownloadListWidgetDelegate::paint(QPainter *painter, const QStyleOptionView
int downloadIndex = index.data().toInt();
QString name = m_Manager->getFileName(downloadIndex);
if (name.length() > 53) {
name.truncate(50);
name.append("...");
}
m_NameLabel->setText(name);
m_SizeLabel->setText(QString::number(m_Manager->getFileSize(downloadIndex) / 1024));
DownloadManager::DownloadState state = m_Manager->getState(downloadIndex);
if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
m_Progress->setVisible(false);
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Paused - Double Click to resume", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Paused - Double Click to resume", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkRed);
m_InstallLabel->setPalette(labelPalette);
} else if (state == DownloadManager::STATE_FETCHINGMODINFO) {
m_InstallLabel->setText(tr("Fetching Info 1"));
m_Progress->setVisible(false);
} else if (state == DownloadManager::STATE_FETCHINGFILEINFO) {
m_InstallLabel->setText(tr("Fetching Info 2"));
m_Progress->setVisible(false);
} else if (state >= DownloadManager::STATE_READY) {
QPalette labelPalette;
m_InstallLabel->setVisible(true);
m_Progress->setVisible(false);
if (state == DownloadManager::STATE_INSTALLED) {
// the tr-macro doesn't work here, maybe because the translation is actually associated with DownloadListWidget instead
// of DownloadListWidgetDelegate?
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Installed - Double Click to re-install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Installed - Double Click to re-install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkGray);
} else if (state == DownloadManager::STATE_UNINSTALLED) {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Uninstalled - Double Click to re-install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::lightGray);
} else {
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Done - Double Click to install", 0));
#else
m_InstallLabel->setText(QApplication::translate("DownloadListWidget", "Done - Double Click to install", 0, QApplication::UnicodeUTF8));
#endif
labelPalette.setColor(QPalette::WindowText, Qt::darkGreen);
}
m_InstallLabel->setPalette(labelPalette);
if (m_Manager->isInfoIncomplete(downloadIndex)) {
m_NameLabel->setText("<img src=\":/MO/gui/warning_16\" /> " + m_NameLabel->text());
}
if (downloadIndex >= m_Manager->numTotalDownloads()) {
paintPendingDownload(downloadIndex - m_Manager->numTotalDownloads());
} else {
m_InstallLabel->setVisible(false);
m_Progress->setVisible(true);
m_Progress->setValue(m_Manager->getProgress(downloadIndex));
paintRegularDownload(downloadIndex);
}
#pragma message("caching disabled because changes in the list (including resorting) doesn't work correctly")
@@ -280,29 +305,32 @@ bool DownloadListWidgetDelegate::editorEvent(QEvent *event, QAbstractItemModel *
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
if (mouseEvent->button() == Qt::RightButton) {
QMenu menu(m_View);
bool hidden = false;
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()));
if (m_ContextRow < m_Manager->numTotalDownloads()) {
DownloadManager::DownloadState state = m_Manager->getState(m_ContextRow);
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()));
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()));
} else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
menu.addAction(tr("Remove"), this, SLOT(issueDelete()));
menu.addAction(tr("Resume"), this, SLOT(issueResume()));
}
menu.addAction(tr("Delete"), this, SLOT(issueDelete()));
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()));
} else if ((state == DownloadManager::STATE_PAUSED) || (state == DownloadManager::STATE_ERROR)) {
menu.addAction(tr("Remove"), this, SLOT(issueDelete()));
menu.addAction(tr("Resume"), this, SLOT(issueResume()));
}
menu.addSeparator();
menu.addSeparator();
}
menu.addAction(tr("Delete Installed..."), this, SLOT(issueDeleteCompleted()));
menu.addAction(tr("Delete All..."), this, SLOT(issueDeleteAll()));
if (!hidden) {
+6 -1
View File
@@ -54,12 +54,15 @@ class DownloadListWidgetDelegate : public QItemDelegate
public:
DownloadListWidgetDelegate(DownloadManager *manager, QTreeView *view, QObject *parent = 0);
DownloadListWidgetDelegate(DownloadManager *manager, bool metaDisplay, QTreeView *view, QObject *parent = 0);
~DownloadListWidgetDelegate();
virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void paintPendingDownload(int downloadIndex) const;
void paintRegularDownload(int downloadIndex) const;
signals:
void installDownload(int index);
@@ -102,6 +105,8 @@ private:
DownloadListWidget *m_ItemWidget;
DownloadManager *m_Manager;
bool m_MetaDisplay;
QLabel *m_NameLabel;
QLabel *m_SizeLabel;
QProgressBar *m_Progress;
+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>

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