Compare commits

..
Author SHA1 Message Date
Tannin bc7c42dbe8 - directories in data-view are now sorted
- questionbox with selection memory is now exposed to plugins
- the choice to run fnis from the checker can now be memorized
2013-10-20 16:51:40 +02:00
Tannin 9a03783f49 - tooltip on download list now contains the file name
- bugfix: when refreshing the directory tree conflict information wasn't immediately refreshed (including on start)
- bugfix: dataChanged events wasn't emitted when user changed the modlist
- bugfix: file patterns in checkfnis plugin weren't completely correct
2013-10-18 23:35:38 +02:00
Tannin d73b1e38c8 - mod sorting now always uses priority as secondary sorting
- added missing ipluginlist.h
2013-10-17 19:21:22 +02:00
Tannin b47c89661e - plugin (esp/esm) list is now exposed to plugins (read-only functionality right now)
- integrated fomod installer now supports file dependencies
- bugfix: integrated fomod installer crashed if the installer had no optiosn´´ns
2013-10-16 23:04:03 +02:00
Tannin 0b84cc3d45 Merge 2013-10-16 19:34:56 +02:00
Tannin 1bf3cc585e - nmm importer now also transfers category, endorsement state and lastest version information
- nmm importer should now always determine the correct nexus id
- nmm importer will now correctly restore overwritten files for imported mods
2013-10-16 19:32:59 +02:00
Tannin eae275cc84 Added tag release v1.0.5 for changeset c6a9d2ab2d6c 2013-10-15 23:34:16 +02:00
Tannin f3cb50057b - bugfixes to last merge
- fnis-check plugin can now be disabled
2013-10-12 13:21:37 +02:00
Tannin 5518309300 Merge 2013-10-12 12:32:37 +02:00
Tannin f22bb9d6a6 Merge 2013-10-11 23:04:44 +02:00
Tannin 382226bb46 - added new plugin to test if fnis needs to be run
- some functionality to the plugin interface to enable them to search for files&directories in the virtual FS (rudimentary atm)
- functionality for plugins to react to application being started from MO
- broken ESPs are no longer reported as popup windows but only in the log file
- bugfix: plugins couldn't store persistent data if they had no user-editable settings
2013-10-11 23:03:49 +02:00
Tannin bdf04f516c - MO now warns if a nxm-link for a wrong game was attempted instead of downloading the wrong file
- bugfix: in nxmhandler adding a new line with different game but same executable didn't work (the change was silently dropped)
2013-10-10 21:23:25 +02:00
Tannin 54c7131a5e - new toggle to display hidden downloads
- hidden downloads can be un-hidden
- the installation manager now more thoroughly cleans up the temporary directory after installation
- added SkyrimLauncher.exe to the list of auto-detected executables
- bugfix: shutting down MO while downloads where active in some occasions didn't work
- bugfix: when canceling the only active download the taskbar icon didn't return to normal
2013-10-10 19:32:01 +02:00
Tannin 15e256ef44 Added tag release v1.0.4 for changeset 7732c5d2e41d 2013-10-10 19:09:29 +02:00
Tannin b23a4885e0 - bugfix: When updating MO, if login is required, the process didn't continue automatically after login 2013-10-06 14:12:25 +02:00
Tannin 823dfd410b - option to ignore/unignore updates is now only shown if there IS an update
- window now shouldn't lose focus on win 8 2 seconds after mod information gets invalidated
- small bugfix related to new version scheme parsing
2013-10-06 13:34:34 +02:00
Tannin ba09054e6b Merge 2013-10-06 12:20:07 +02:00
Tannin 694e3ed279 bugfix: statusbar may have stayed enabled if an error occured on handling the refreshed-event 2013-10-06 12:19:46 +02:00
Tannin cffd9eb4e2 - hook.dll now doesn't inject to certain applications (currently steam, chrome and firefox)
- versioning system improved. Will now report "downgrades" for mods and support a different versioning system (requires manual switch)
- updates can now be ignored until a new version is uploaded
- new splash screen
- bugfix: a few memory leaks (shouldn't account for much)
- bugfix: result of GetModuleHandle wasn't zero-terminated in some cases
2013-10-05 14:20:48 +02:00
Tannin 640b14ef25 diagnosis plugin now warns when nitpick is installed 2013-09-30 18:33:45 +02:00
Tannin 47293827bb - download progress is now visible in task bar
- esp-tooltip now lists all masters, highlighting the missing ones
- python plugin will now report a problem if the path contains a semicolon
- leak detection now (somewaht) works around the fact that we don't always get a stack trace
- bugfix: mod meta-file is now reliably created if it was missing
- bugfix: parser for nxm-links didn't handle numbers in the mod name
- bugfix: small memory leak
2013-09-28 21:13:57 +02:00
Tannin 0a3169b808 - plugin tooltip now only lists the masters that aren't enabled (if any) 2013-09-26 19:10:44 +02:00
Tannin f4528c7aaa - bugfix: esp parsing could crash for broken/unrecognized esps
- bugfix: esp parser didn't handle oblivion esps correctly
2013-09-23 22:39:58 +02:00
Tannin 4f0a6e8b88 Added tag release v1.0.2 for changeset 0aaed2f8f48d 2013-09-22 13:24:53 +02:00
Tannin c57b172204 - removed some obsolete code
- MO will no longer start an application while the directory structure is being refreshed because MO may need to access profile files afterwards
- bugfix: the overwrite info-dialog was not destroyed and could thus keep a lock on files thus preventing those files from being moved/deleted
2013-09-22 13:10:21 +02:00
Tannin dfca8be71b - esp reader now handles invalid files more gracefully
- files moved will now also be treated as "deleted" in the old location so a newly created file with that same name is not created in overwrite
- introduced a mechanism by which MO can recognize if it crashed before when attempting to load a plugin. That plugin can be blacklisted so it doesn't get loaded again
- plugins can now programaticaly change their settings
- plugins can now store data persistently without exposing that data as settings
- requesting an unset-setting from a plugin is no longer treated as a bug
- clarified warning message for when files are in overwrite directory
- the proxyPython plugin will now discover if python initialization crashed MO on a previous session and give the user a chance to fix it or disable the plugin
- bugfix: GetModuleFileName modified the buffer past the zero termination. While this doesn't violate the API documentation it is different from the regular windows implementation
- bugfix: proxy plugins couldn't access the parent widget
- bugfix: when moving a file from overwrite to a mod the in-memory file structure wasn't updated
- bugfix: name input dialog for profiles allowed names that weren't valid directory names
- bugfix: profile dialog wasn't able to delete profiles if the name started or ended in whitespaces
- bugfix: The name-cells for plugin settings could be changed (without effect)
- removed a few obsolete files from the repository
2013-09-21 19:25:33 +02:00
Tannin 08037bf876 Added tag release v1.0.1 for changeset b90a2e9174c0 2013-09-21 18:59:47 +02:00
Tannin 500ab7f706 - bugfix: testing for missing masters at the wrong time seems to have caused crashes
- bugfix: mod list is now written to a temporary file first. Only on success is the original file overwritten
- bugfix: moving a mod priority to just above the overwrite could cause a crash or error message
- bugfix: versions with a release candidate number weren't sorted correctly (woops)
- bugfix: staging script didn't include archive.dll and dlls.manifest
- installation time on overwrite no longer updates constantly
2013-09-16 22:32:42 +02:00
Tannin 08309b71ba Added tag release v1.0.0rc1 for changeset 438584941a82 2013-09-15 16:11:16 +02:00
Tannin dce78b62b8 - bugfix: when installing mods through the nmm importer if no other mods were previously installed
the correct installation directory was never set
- bugfix: the nmm importer didn't "sanitize" mod names and potentially tried to create invalid directories
- bugfix: 0.99.x packages didn't contain qt plugins to handle certain image formats
2013-09-12 21:45:57 +02:00
Tannin af6e1c3ab4 - when installing mods from outside the download directory the absolute path is now stored
- added a context menu to the toolbar buttons so tool icons can be removed directly
- initweaks modinfo tab is now always available and allows new ini tweaks to be created
- fake esms are now treated as masters (as they should)
- MO will now display a warning if not all masters of an esp are enabled. The tooltip gives a list of required masters
- bugfix: path returned by getfullpathname was sometimes not correctly terminated
- bugfix: path after reverse-rerouting was sometimes incorrect, missing a path separator
- bugfix: change of current directory sometimes used a fake directory without need
- bugfix: icons in shortcut menu were not alwayscorrectly updated
2013-09-11 22:54:45 +02:00
Tannin 50325c8fc2 Added tag release v0.99.6 for changeset d9dedb857683 2013-09-05 21:14:19 +02:00
Tannin 7a382eab75 updated versions 2013-09-05 21:14:08 +02:00
Tannin 8e6868bf88 - bugfix: automatically removes a file from old NCC release that was interfering with the current version
- bugfix: fomod installer didn't find fomod files in nested folder
- bugfix: python proxy will now not even try to initialize python if python_dir contains no python.
This is necessary because the python interpreter crashes the application if the path is invalid
2013-09-05 21:04:20 +02:00
Tannin a891146446 Added tag release v0.99.5 for changeset f75473410ed3 2013-09-02 19:28:24 +02:00
Tannin 72f14df8a7 - bugfix: "overwrite" is no longer a localizable string, at least for now, because some pieces of code rely on the name 2013-09-02 19:22:40 +02:00
Tannin 0fa7155eb8 - bugfix: division-by-zero error in the newly introduced server-speed calculation
- bugfix: temp files extracted during fomod installation were not cleaned up. This still doesn't remove directories
- bugfix: fomod installer didn't find the installer xmls because they are in a subdirectory since release 0.99.4
2013-09-02 18:51:17 +02:00
Tannin c6c61ce792 Added tag release v0.99.4 for changeset a2ccfea95ef0 2013-09-02 18:48:52 +02:00
Tannin ef801879ab - separated python proxy into two dlls. One is a wrapper without external
dependencies that fulfills the plugin interface. The other contains the actual python
functionality. This way the outer dll can always be loaded and report issues.
- The build process embeds the second dll into the first, this way only one dll has to be shipped
2013-09-01 18:36:43 +02:00
Tannin 1e6c5f7c25 - added a new column for not-yet-endorsed mods
- set categories menu no longer closes when the mouse cursor leaves the menu
- MO will no longer change the endorsement flag if an update doesn't contain it
- the column selection for the mod list can now only be accessed by mouse,
hotkeys open the context menu of the mod
- now displaying a progress dialog during login. For unknown reasons MO hangs during that time
2013-09-01 13:40:44 +02:00
Tannin 49e1dd23b6 - mod list can now be sorted by install time
- the sorting of download archives wasn't actually by index instead of file time
- bugfix: some of the plugins crashed if they failed to create a mod
2013-08-31 17:11:17 +02:00
Tannin 9c31cfa915 - the download manager now registers download speed. Right now this is only used
to display an average speed on the settings menu
- added a python27.dll compiled with vc100. This can now be bundled without introducing more dependencies
- bugfix: extracting now stops after an error
- bugfix: the way hook.dll caused CREATE_ALWAYS/CREATE_NEW to always write into overwrite could lead to the
file being created when the call should have failed (because the file existed and was protected)
- bugfix: GetPrivateProfileString does NOT properly report files as missing. This means that
the ini-query optimization could optimize away requests that should work
- bugfix: fomod installer couldn't display images because they were unpacked to the wrong temporary location
- bugfix: When disabling local saves and choosing to delete the saves nothing happened
- bugfix: the python plugin couldn't find the pyqt libraries
2013-08-30 20:59:12 +02:00
Tannin 91dd38cb29 Added tag release v0.99.3 for changeset bf57300454f1 2013-08-25 13:32:33 +02:00
Tannin bf0db9c218 - position of splitter in main window is now saved and restored
- confirmation dialog before enabling/disabling all plugins
- bugfix: GetPrivateProfileString-hook potentially accessed buffer that is allowed to be NULL
- bugfix: attempt to extract an archive crashed MO in 0.99.2
- bugfix: archive list wasn't saved correctly in 0.99.2
- bugfix: plugins.txt was incorrectly interpreted as utf-8
2013-08-25 13:15:23 +02:00
Tannin 8fcf21e771 - column sizing is now changeable by the user yet still automatically resizes fit content on first start 2013-08-24 23:11:20 +02:00
Tannin b46b2fe6f2 Merge 2013-08-24 22:50:22 +02:00
Tannin e91eebefbe - download size is now displayed
- multiple esps/mods can now be enabled/disabled at once using space
- bugfix: fomod installer didn't compile because of changes to condition checking
- bugfix: broken inverse virtual name resolution in case of non-default mod directory
2013-08-24 22:50:00 +02:00
Tannin 2e605b275b Added tag release v0.99.2 for changeset 95e629510d01 2013-08-24 14:14:00 +02:00
Tannin 5f2a49b3be - added code for c++ based boss dummy
- fixed staging scripts
2013-08-24 14:12:58 +02:00
Tannin c4955231cf - turned boss dummy into a c++ project (unmanaged exports with c# is a pain)
- updated NCC to current NMM codebase
- updated translation files
2013-08-24 12:08:57 +02:00
Tannin 87f4e2df26 Merge 2013-08-17 08:54:53 +02:00
Tannin 9272013103 - bugfix: download manager will now properly pause all downloads on exiting the application
- bugfix: resumed downloads now get their automatic-retry-count reset
- fiddled with condition tests in fomod (not sure if it works right now)
2013-08-17 08:54:09 +02:00
Tannin 78c4178f2a - plugins that failed to load are now reported as problems
- some wording fixes in the tutorial
- updated proxyPython to a modified interface
2013-08-17 08:49:36 +02:00
Tannin 7d79e3ad1e - bugfix: nameprefix-flag incorrectly interpreted on oblivion-style bsas
- bugfix: optimization for missing inis fixed
- bugfix: fixmods didn't search for esps in overwrite
- bugfix: configurator didn't work for fallout new vegas because of a missing cast
- bugfix: configurator attempted parameter formatting on ini files instead of using the raw data
- bugfix: searching for a path in the directory structure could cause a crash when not using the directory output
- optimization to findfirstfile for cases where a single file is searched
- display of bsas changed. hopefully it is more understandable now
- cache for the test whether a mod is in conflict. Should speed ab scrolling in mod list
- mod list has now an additional column for the installation time
- nmm importer no longer cancels in cases where a data file references an undeclared mod.
- integrated improved settings.json for the configurator contributed by "delta534"
2013-08-15 22:13:18 +02:00
Tannin d0f2c4fcf7 - now avoids a few unnecessary copy operations during generation of the directory structure
- bugfix: circular dependency caused a memory leak
- bugfix: removing a single mod lead to the wrong mod being deleted
2013-07-17 20:58:58 +02:00
Tannin 36ac91e0b7 - basic diagnosis plugin now catches an issue where the users fontconfig.txt refers missing files
- plugins can now resolve relative file names on the virtual directory (no enumeration yet)
- bugfix: dummy plugins were not initialized
2013-07-14 14:59:01 +02:00
Tannin 5c968a1242 - added the ability to connect to the internet through a proxy
- integrated fomod installer now supports the or-operator for page conditions
- integrated fomod installer now caches condition results to speed up tests
2013-07-13 22:11:30 +02:00
Tannin e09cef3909 - minor fixes to ncc build files
- MO will now use system-wide proxy if one is configured
2013-07-13 11:52:40 +02:00
Tannin 8d34ae3413 - some fixes to the refreshing behaviour
- "fix" buttons in problems dialog are now actually functional
- python proxy now has diagnosis functionality to warn user if correct proxy version is not available
- python proxy now has a configurable python path
2013-07-13 10:10:46 +02:00
Tannin 849a0a0ef4 bugfix: nxmhandler in headless calls didn't save changes 2013-07-05 14:34:46 +02:00
Tannin 3b3717a021 - nxmhandler now has a ui to modify the proxy-handler
- mo now registers itself with nxmhandler when launching the browser
- pdbs are now generated for plugins
- reverted configurator to pyqt4
2013-06-29 21:10:53 +02:00
Tannin fe37b48dff - caching of downloadwidgets to fix performance problem, currently broken and disabled
- added new state for downloads "uninstalled" for mods that were at one point installed and then removed
- user-configured server preference is now used
- updated tutorial to account for removal of integrated browser
- reverted to qt 4
- using performance optimised findfirstfile on win vista and up
- bugfix: it was possible to disable all columns of the mod list
- bugfix: hook.dll doesn't load on win xp
2013-06-29 18:04:10 +02:00
Tannin f8c683f700 - some fixes for qt5 compatibility
- hook.dll no longer creates a dump and uninstalls it if an exception is reported that doesn't originate from it
- NCC used read-only transactions again because otherwise solid archives become unusably slow.
- removed the integrated nexus browser
- the mod description and motd are now rendered in QTextBrowser. This (and the above) eliminates the dependency on qtwebkit
- removed the direct file download for mod files
- reduced CPU usage during downloads by invalidating only one column of the download list. This widget still needs to be replaced
- added the complete filename as an option for the modname
- applications that require elevation can now be started by invoking an elevated secondary ModOrganizer instance
- MO will now register nexus file servers and provides a settings dialog to pick preferred servers. (This preference is not used yet)
- worked around 1-2 bugs in QSortFilterProxy
- handling of nxm links is now done by an external application. This allows the registration of different applications depending on the game
- integrated fomod installer now displays the screenshot in a scalable view
- bugfix: integrated fomod installer didn't name output files correctly if the name differs from the source name
- bugfix: a successful login to nexus was (sometimes?) not correctly detected as a success
- bugfix: top-level entries in QtGroupingProxy were sometimes incorrectly displayed as groups
- bugfix: GetPrivateProfile... optimization could cause null-pointer indirection
- bugfix: GetCurrentWorkingDirectory caused buffer overflow in case of pre-flighting (buffer size 0)
- bugfix: configurator plugin now also uses qt5 (it's currently broken though)
2013-06-20 21:55:37 +02:00
Tannin 49cbe03491 - completed Qt5 compatibility
- added GetFileVerisonInfoW hook for Windows 8 compatibility
- fixed BossDummy to compile correctly with current version of the RGiesecke.DllExport assembly
- fixed NCC components compiling with different Framewerk versions
- dropped files-list in Nexus-tab of modinfo dialog
2013-06-15 14:42:20 +02:00
Tannin 6069decb15 - mod list no longer loses selection whenever categories context menu gets hidden 2013-06-08 22:23:25 +02:00
Tannin 30dece9093 Merge 2013-06-07 18:30:26 +02:00
Tannin 50d0523622 - locking/unlocking esps can now be done for the whole selection
- bugfix: MO crashed if the locked index file is borked. This shouldn't happen
anymore so I'm not sure how that file breaks to begin with
2013-06-07 18:16:52 +02:00
Tannin 067f272da3 - bugfix: archive file wasn't closed after installation
- bugfix: batch installer was treated as handler for "manual" installations
- bugfix: dropped include of unused sip interface
2013-06-06 20:19:38 +02:00
Tannin 97111de140 Merge 2013-06-05 10:05:29 +02:00
Tannin 280691ebc3 - skyrim-specific optimization to accelerate startup
- bugfix in SetCurrentDirectory hook
2013-06-05 10:04:56 +02:00
Tannin 1606c1366f - install plugins now get a chance to update name, version and modid of mods
- NCC now makes name, version and modid from the info.xml file available to the installer
- integrated fomod installer also uses version and modid from the info.xml
- mods can now be renamed during installation
- configurator plugin now highlights changed keys and saves changes
2013-06-05 09:51:48 +02:00
Tannin 8077c1ad73 updated .hgignore 2013-05-28 23:17:41 +02:00
Tannin aadd076269 - added hook for ansi version of SHFileOperation, this function may not be routed to the unicode version in every windows variant 2013-05-28 23:14:49 +02:00
Tannin d8cb9a4e37 - bugfixes in hookdll related to path-rerouting
- bugfix: expanding modlist groups after changes to modlist works now
- bugfix: sorting grouped modlist was broken
2013-05-28 22:43:19 +02:00
Tannin 3c21d1884a Merge 2013-05-25 12:26:13 +02:00
Tannin 16bda49e06 - bugfix: mod priority ordering could become incorrect
- bugfix: when grouping is enabled, newly installed mods broke grouping
- bugfix: syncing overwrite didn't clean up emptied directory
2013-05-25 12:24:48 +02:00
Tannin daf8bab136 - bugfix: installation using custom installer always created mod directory
- batch installer now displays progress during mod installation
2013-05-23 20:03:35 +02:00
Tannin 2c4ba2c62c Merge 2013-05-22 20:48:23 +02:00
Tannin 0ae7d4753c - diagnosis plugin now reports file in overwrite as a problem
- bugfix: GetModuleFileName returned the wrong string length
- bugfix: Vector Exception Handler no longer stops hook.dll on recoverable errors
2013-05-22 20:43:08 +02:00
Tannin f0ba6ce95c - added NCC to repository
- deleting files from download manager now moves them to recycle bin instead of deleting permanentyl
- cleanup in installation manager
- ini editor (old) can now be configured to open files in external editor
- bugfix: .net check in ncc installer was broken
- bugfix: mod-overwrite dialog wasn't displayed for complex installers
2013-05-22 18:16:56 +02:00
Tannin cdea0c9da5 Merge 2013-05-19 11:37:03 +02:00
Tannin 0beb88760b - added hook for GetModuleFileName
- downloads are now identifiable by ID
- fixed a bug with the multi-select categories list
- fixed a problem with the nexus-login code breaking support for certain passwords
- fixed a bug where detection of archive invalidation didn't work correctly
- fixed ncc plugin trying to handle archives that aren't actually fomods
2013-05-19 11:36:36 +02:00
Tannin 45f6cd21a2 - extended plugin interface to allow plugins access to download manager
- extended plugin interface to allow installation of mods from files
- improved detection of online state
- download tab now also displays files supported through plugins
- batch installer now has basic functionality (downloads and installes files)
2013-05-17 20:44:40 +02:00
Tannin fc3753dcc5 Merge 2013-05-10 11:00:09 +02:00
Tannin 9bc123e8e2 - initial support for exposing the nexusbridge to python
- started improving usability of nexus bridge
- support for installers implemented in python
2013-05-10 10:57:35 +02:00
Tannin d161e1a0d6 - if no version is known for a mod, the file date is used
- added files for the python proxy
2013-04-14 20:49:18 +02:00
Tannin 1c9018e9fd - bugfix: wrong multibyte to widechar conversion in hookdll breaks internationalization
- bugfix: mod names not checked for validity on rename
- bugfix: mod list wasn't invalidated after rename (regression?)
- problem reports moved to separate dialog
- ncc plugin now does the check for dotNet
- python plugin wrapper started (only supports tools currently)
- new ini editor plugin in python (non-functional currently)
2013-04-13 19:23:18 +02:00
Tannin 4fbfc9aa54 - multi-selection in category window
- profile loading is now slightly more forgiving
2013-04-12 15:31:47 +02:00
Tannin 528338d7bf some code cleanup 2013-04-05 18:24:36 +02:00
Tannin 5b04f38345 added modlist view class (updates drop behaviour on modlist depending on mime type) 2013-04-04 21:51:13 +02:00
Tannin de27ab391f - creating mods from overwrite
- moving files from overwrite to mods
- offline mode
- several fixes to the grouping system
- fix to "duplicate translation" errors
2013-04-04 21:49:44 +02:00
Tannin d9a6dbb916 Merge 2013-03-29 10:26:19 +01:00
Tannin f682f82db5 some improvements to the grouped modlist views 2013-03-29 10:25:38 +01:00
Tannin a47d971788 Merge with default 2013-03-29 10:12:34 +01:00
Tannin 59d6b7840b - post-merge cleanup 2013-03-29 09:41:41 +01:00
Tannin b4220cf2f4 Merge with branch1.0 2013-03-27 21:04:27 +01:00
Tannin e0eb97922d - added hooks for getFileVersion* functions
- automatic donwload retry
- support for storing multiple download urls
- improved "query info" functionality
- some cleanup to download manager code
- external fomod installer dialog are now brought to front
- added shell... functions to have windows handle problematic situations
- added visual clue when filters are active
- esps are now automatically activated when installing a mod
- added option to never endorse a mod
- added "previous" and "next" buttons to mod info dialog
- improved the way messagedialog text is shortened
- coloring in mod info dialog now visible in other color schemes
- plugin list is now saved automatically
- vanilla bsas are now enabled even if they are not listed in the ini file
- bugfix: setting mod to maximum now doesn't try to place the mod below overwrite
2013-03-27 20:48:33 +01:00
Tannin 10485e15e4 - some cleanup to hookdll
- exchanged grouping proxies with existing solution from the kde project
2013-03-27 12:32:23 +01:00
Tannin ec0ea9df8d Merge with default 2013-03-22 18:57:45 +01:00
Tannin 24cc386191 removed obsolete files 2013-03-22 18:54:04 +01:00
Tannin 0a19f04339 - support for grouping filters for mod list (incomplete)
- offering multiple options for mod names during installation
- support for renaming profiles
- updated installer plugins
- minor bugfixes
2013-03-22 18:46:40 +01:00
Tannin 74c75e60d6 Added tag release v0.12.9 for changeset ff711d0a1fa9 2013-03-18 20:03:57 +01:00
Tannin 8b31996525 - bugfix: invalid ini file name when creating profile for oblivion from default settings
- bugfix: archives.txt file was potentially not generated if there were no bsas
- bugfix: static __declspec(thread) declaration causes crashes in windows versions before vista
- logger class in hook.dll now tries alternative names for the log file if it can't access the regular file
- path-environment variable for mo-spawned processes now contains the path to MO, in case the hook.dll needs to find the msvc-dlls
- stack-search in ini-limit hack now starts searching on local stack. This should find the right stack position for the hack more reliable
2013-03-18 20:03:46 +01:00
Tannin b3d0fcb208 - some more safety checks in the ini-limit removal code
- some code cleanup and minor bug fixes based on results from static code analysis
- added naemfilter for the esp list
- bsa changes are now stored automatically but delayed by up to 0.5 seconds (for performance reasons)
- bugfix: buffer overrun when certain functions are called with empty file names
- bugfix: reroute for the ini-limit fix was placed in the data segment
- bugfix: mod list got mixed up when the mod directory was changed externally
- bugfix: plugins.txt reroute on skyrim didn't work on winXP
- bugfix: MO could become unresponsive if the tutorial script couldn't be interpreted
2013-03-13 19:35:20 +01:00
Tannin 93e073c324 Added tag release v0.12.8 for changeset 0f4efc99546f 2013-03-06 19:33:55 +01:00
158 changed files with 23183 additions and 16169 deletions
+11
View File
@@ -2,3 +2,14 @@ syntax: glob
ModOrganizer-build-desktop*
outputd/*
output/*
build-ModOrganizer-*
source/NCC/*/bin
source/NCC/*/obj
source/NCC/bin
*.orig
source/plugins/proxyPython/build
staging/*
source - Copy/*
ModOrganizer-build-*
pdbs/*
source/NCC/BossDummy.x/*
+6 -2
View File
@@ -10,10 +10,14 @@ SUBDIRS = bsatk \
archive \
helper \
plugins \
proxydll
proxydll \
nxmhandler \
BossDummy \
pythonRunner \
esptk
hookdll.depends = shared
organizer.depends = shared, uibase
organizer.depends = shared, uibase, plugins
CONFIG(debug, debug|release) {
DESTDIR = outputd
+78 -83
View File
@@ -17,86 +17,81 @@ 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 "activatemodsdialog.h"
#include "ui_activatemodsdialog.h"
#include <QComboBox>
#include <QLabel>
ActivateModsDialog::ActivateModsDialog(const std::map<QString, std::vector<QString> > &missingPlugins, QWidget *parent)
: TutorableDialog("ActivateMods", parent), ui(new Ui::ActivateModsDialog)
{
ui->setupUi(this);
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
QHeaderView *headerView = modsTable->horizontalHeader();
#if QT_VERSION >= 0x050000
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
headerView->setSectionResizeMode(1, QHeaderView::Interactive);
#else
headerView->setResizeMode(0, QHeaderView::Stretch);
headerView->setResizeMode(1, QHeaderView::Interactive);
#endif
int row = 0;
modsTable->setRowCount(missingPlugins.size());
for (std::map<QString, std::vector<QString> >::const_iterator espIter = missingPlugins.begin();
espIter != missingPlugins.end(); ++espIter, ++row) {
modsTable->setCellWidget(row, 0, new QLabel(espIter->first));
if (espIter->second.size() == 0) {
modsTable->setCellWidget(row, 1, new QLabel(tr("not found")));
} else {
QComboBox* combo = new QComboBox();
for (std::vector<QString>::const_iterator modIter = espIter->second.begin();
modIter != espIter->second.end(); ++modIter) {
combo->addItem(*modIter);
}
modsTable->setCellWidget(row, 1, combo);
}
}
}
ActivateModsDialog::~ActivateModsDialog()
{
delete ui;
}
void ActivateModsDialog::on_buttonBox_accepted()
{
}
std::set<QString> ActivateModsDialog::getModsToActivate()
{
std::set<QString> result;
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
for (int row = 0; row < modsTable->rowCount(); ++row) {
QComboBox *comboBox = dynamic_cast<QComboBox*>(modsTable->cellWidget(row, 1));
if (comboBox != NULL) {
result.insert(comboBox->currentText());
}
}
return result;
}
std::set<QString> ActivateModsDialog::getESPsToActivate()
{
std::set<QString> result;
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
for (int row = 0; row < modsTable->rowCount(); ++row) {
QComboBox *comboBox = dynamic_cast<QComboBox*>(modsTable->cellWidget(row, 1));
if (comboBox != NULL) {
QLabel *espName = dynamic_cast<QLabel*>(modsTable->cellWidget(row, 0));
result.insert(espName->text());
}
}
return result;
}
#include "activatemodsdialog.h"
#include "ui_activatemodsdialog.h"
#include <QComboBox>
#include <QLabel>
ActivateModsDialog::ActivateModsDialog(const std::map<QString, std::vector<QString> > &missingPlugins, QWidget *parent)
: TutorableDialog("ActivateMods", parent), ui(new Ui::ActivateModsDialog)
{
ui->setupUi(this);
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
QHeaderView *headerView = modsTable->horizontalHeader();
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
headerView->setSectionResizeMode(1, QHeaderView::Interactive);
#else
headerView->setResizeMode(0, QHeaderView::Stretch);
headerView->setResizeMode(1, QHeaderView::Interactive);
#endif
int row = 0;
modsTable->setRowCount(missingPlugins.size());
for (std::map<QString, std::vector<QString> >::const_iterator espIter = missingPlugins.begin();
espIter != missingPlugins.end(); ++espIter, ++row) {
modsTable->setCellWidget(row, 0, new QLabel(espIter->first));
if (espIter->second.size() == 0) {
modsTable->setCellWidget(row, 1, new QLabel(tr("not found")));
} else {
QComboBox* combo = new QComboBox();
for (std::vector<QString>::const_iterator modIter = espIter->second.begin();
modIter != espIter->second.end(); ++modIter) {
combo->addItem(*modIter);
}
modsTable->setCellWidget(row, 1, combo);
}
}
}
ActivateModsDialog::~ActivateModsDialog()
{
delete ui;
}
std::set<QString> ActivateModsDialog::getModsToActivate()
{
std::set<QString> result;
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
for (int row = 0; row < modsTable->rowCount(); ++row) {
QComboBox *comboBox = dynamic_cast<QComboBox*>(modsTable->cellWidget(row, 1));
if (comboBox != NULL) {
result.insert(comboBox->currentText());
}
}
return result;
}
std::set<QString> ActivateModsDialog::getESPsToActivate()
{
std::set<QString> result;
QTableWidget *modsTable = findChild<QTableWidget*>("modsTable");
for (int row = 0; row < modsTable->rowCount(); ++row) {
QComboBox *comboBox = dynamic_cast<QComboBox*>(modsTable->cellWidget(row, 1));
if (comboBox != NULL) {
QLabel *espName = dynamic_cast<QLabel*>(modsTable->cellWidget(row, 0));
result.insert(espName->text());
}
}
return result;
}
-1
View File
@@ -62,7 +62,6 @@ public:
std::set<QString> getESPsToActivate();
private slots:
void on_buttonBox_accepted();
private:
Ui::ActivateModsDialog *ui;
-98
View File
@@ -1,98 +0,0 @@
/*
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 "archivetree.h"
#include <QDragMoveEvent>
ArchiveTree::ArchiveTree(QWidget *parent) :
QTreeWidget(parent)
{
}
bool ArchiveTree::testMovePossible(QTreeWidgetItem *source, QTreeWidgetItem *target)
{
if ((target == NULL) ||
(source == NULL)) {
return false;
}
if ((source == target) ||
(source->parent() == target)) {
return false;
}
return true;
}
void ArchiveTree::dragEnterEvent(QDragEnterEvent *event)
{
QTreeWidgetItem *source = this->currentItem();
if ((source == NULL) || (source->parent() == NULL)) {
// can't change top level
event->ignore();
return;
} else {
QTreeWidget::dragEnterEvent(event);
}
}
void ArchiveTree::dragMoveEvent(QDragMoveEvent *event)
{
if (!testMovePossible(this->currentItem(), itemAt(event->pos()))) {
event->ignore();
} else {
QTreeWidget::dragMoveEvent(event);
}
}
void ArchiveTree::dropEvent(QDropEvent *event)
{
event->ignore();
QTreeWidgetItem *target = itemAt(event->pos());
QList<QTreeWidgetItem*> sourceItems = this->selectedItems();
for (QList<QTreeWidgetItem*>::iterator iter = sourceItems.begin();
iter != sourceItems.end(); ++iter) {
QTreeWidgetItem *source = *iter;
if ((source->parent() != NULL) &&
testMovePossible(source, target)) {
source->parent()->removeChild(source);
if (target->data(0, Qt::UserRole).toInt() != 0) {
// target is a file
if (target->parent() == NULL) {
// this should really not happen, how should a
// file get to the top level?
return;
}
int index = target->parent()->indexOfChild(target);
target->parent()->insertChild(index, source);
emit changed();
} else {
// target is a directory
target->insertChild(target->childCount(), source);
emit changed();
}
}
}
}
-55
View File
@@ -1,55 +0,0 @@
/*
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 ARCHIVETREE_H
#define ARCHIVETREE_H
#include <QTreeWidget>
/**
* @brief QT tree widget used to display the content of an archive in the manual installation dialog
**/
class ArchiveTree : public QTreeWidget
{
Q_OBJECT
public:
explicit ArchiveTree(QWidget *parent = 0);
signals:
void changed();
public slots:
protected:
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dragMoveEvent(QDragMoveEvent *event);
virtual void dropEvent(QDropEvent *event);
private:
bool testMovePossible(QTreeWidgetItem *source, QTreeWidgetItem *target);
};
#endif // ARCHIVETREE_H
-124
View File
@@ -1,124 +0,0 @@
/*
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 "baincomplexinstallerdialog.h"
#include "textviewer.h"
#include "ui_baincomplexinstallerdialog.h"
#include <QDir>
using namespace MOBase;
BainComplexInstallerDialog::BainComplexInstallerDialog(DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent)
: TutorableDialog("BainInstaller", parent), ui(new Ui::BainComplexInstallerDialog), m_Manual(false)
{
ui->setupUi(this);
ui->nameEdit->setText(modName);
for (DirectoryTree::const_node_iterator iter = tree->nodesBegin(); iter != tree->nodesEnd(); ++iter) {
const QString &dirName = (*iter)->getData().name;
if ((dirName.compare("fomod", Qt::CaseInsensitive) == 0) ||
(dirName.startsWith("--"))) {
continue;
}
QListWidgetItem *item = new QListWidgetItem(ui->optionsList);
item->setText((*iter)->getData().name);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(item->text().mid(0, 2) == "00" ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, qVariantFromValue((void*)(*iter)));
ui->optionsList->addItem(item);
}
ui->packageBtn->setEnabled(hasPackageTXT);
}
BainComplexInstallerDialog::~BainComplexInstallerDialog()
{
delete ui;
}
QString BainComplexInstallerDialog::getName() const
{
return ui->nameEdit->text();
}
void BainComplexInstallerDialog::moveTreeUp(DirectoryTree *target, DirectoryTree::Node *child)
{
for (DirectoryTree::const_node_iterator iter = child->nodesBegin();
iter != child->nodesEnd();) {
target->addNode(*iter, true);
iter = child->detach(iter);
}
for (DirectoryTree::const_leaf_reverse_iterator iter = child->leafsRBegin();
iter != child->leafsREnd(); ++iter) {
target->addLeaf(*iter);
}
}
DirectoryTree *BainComplexInstallerDialog::updateTree(DirectoryTree *tree)
{
DirectoryTree *newTree = new DirectoryTree;
for (DirectoryTree::const_node_reverse_iterator iter = tree->nodesRBegin();
iter != tree->nodesREnd();) {
QList<QListWidgetItem*> items = ui->optionsList->findItems((*iter)->getData().name, Qt::MatchFixedString);
if ((items.count() == 1) && (items.at(0)->checkState() == Qt::Checked)) {
moveTreeUp(newTree, *iter);
}
iter = tree->erase(iter);
}
return newTree;
}
void BainComplexInstallerDialog::on_okBtn_clicked()
{
this->accept();
}
void BainComplexInstallerDialog::on_cancelBtn_clicked()
{
this->reject();
}
void BainComplexInstallerDialog::on_manualBtn_clicked()
{
m_Manual = true;
this->reject();
}
void BainComplexInstallerDialog::on_packageBtn_clicked()
{
TextViewer viewer("package.txt", this);
viewer.setDescription("");
viewer.addFile(QDir::tempPath().append("/package.txt"), false);
viewer.exec();
}
-95
View File
@@ -1,95 +0,0 @@
/*
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 BAINCOMPLEXINSTALLERDIALOG_H
#define BAINCOMPLEXINSTALLERDIALOG_H
#include "mytree.h"
#include "installdialog.h"
#include "tutorabledialog.h"
namespace Ui {
class BainComplexInstallerDialog;
}
/**
* @brief Dialog to choose from options offered by a (complex) bain package
**/
class BainComplexInstallerDialog : public MOBase::TutorableDialog
{
Q_OBJECT
public:
/**
* @brief Constructor
*
* @param tree the directory tree of the archive. The caller is resonsible to verify this actually qualifies as a bain installer
* @param modName proposed name for the mod. The dialog allows the user to change this
* @param hasPackageTXT set to true if a package.txt is available for this archive. The file has to be unpacked to QDir::tempPath before displaying the dialog!
* @param parent parent widget
**/
explicit BainComplexInstallerDialog(MOBase::DirectoryTree *tree, const QString &modName, bool hasPackageTXT, QWidget *parent);
~BainComplexInstallerDialog();
/**
* @return bool true if the user requested the manual dialog
**/
bool manualRequested() const { return m_Manual; }
/**
* @return QString the (user-modified) name to be used for the mod
**/
QString getName() const;
/**
* @brief retrieve the updated archive tree from the dialog. The caller is responsible to delete the returned tree.
*
* @note This call is destructive on the input tree!
*
* @param tree input tree. (TODO isn't this the same as the tree passed in the constructor?)
* @return DataTree* a new tree with only the selected options and directories arranged correctly. The caller takes custody of this pointer!
**/
MOBase::DirectoryTree *updateTree(MOBase::DirectoryTree *tree);
private slots:
void on_manualBtn_clicked();
void on_okBtn_clicked();
void on_cancelBtn_clicked();
void on_packageBtn_clicked();
private:
void moveTreeUp(MOBase::DirectoryTree *target, MOBase::DirectoryTree::Node *child);
private:
Ui::BainComplexInstallerDialog *ui;
bool m_Manual;
};
#endif // BAINCOMPLEXINSTALLERDIALOG_H
+192 -190
View File
@@ -17,193 +17,195 @@ 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 "bbcode.h"
#include <QRegExp>
#include <map>
#include <algorithm>
namespace BBCode {
class BBCodeMap {
typedef std::map<QString, std::pair<QRegExp, QString> > TagMap;
public:
static BBCodeMap &instance() {
static BBCodeMap s_Instance;
return s_Instance;
}
QString convertTag(const QString &input, int &length)
{
// 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
if (tagName.endsWith('=')) {
tagName.chop(1);
}
QString closeTag = tagName == "*" ? "<br/>"
: QString("[/%1]").arg(tagName);
int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive);
//qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos);
if (closeTagPos > -1) {
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);
} else {
// expression doesn't match. either the input string is invalid
// or the expression is
qWarning("%s doesn't match the expression for %s",
temp.toUtf8().constData(), tagName.toUtf8().constData());
length = 0;
return QString();
}
}
}
// not a recognized tag or tag invalid
length = 0;
return QString();
}
private:
BBCodeMap()
: m_TagNameExp("^[a-zA-Z*]*=?")
{
m_TagMap["b"] = std::make_pair(QRegExp("\\[b\\](.*)\\[/b\\]"),
"<b>\\1</b>");
m_TagMap["i"] = std::make_pair(QRegExp("\\[i\\](.*)\\[/i\\]"),
"<i>\\1</i>");
m_TagMap["u"] = std::make_pair(QRegExp("\\[u\\](.*)\\[/u\\]"),
"<u>\\1</u>");
m_TagMap["s"] = std::make_pair(QRegExp("\\[s\\](.*)\\[/s\\]"),
"<s>\\1</s>");
m_TagMap["sub"] = std::make_pair(QRegExp("\\[sub\\](.*)\\[/sub\\]"),
"<sub>\\1</sub>");
m_TagMap["sup"] = std::make_pair(QRegExp("\\[sup\\](.*)\\[/sup\\]"),
"<sup>\\1</sup>");
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\\]"),
"<div align=\"center\">\\1</div>");
m_TagMap["quote"] = std::make_pair(QRegExp("\\[quote\\](.*)\\[/quote\\]"),
"<blockquote>\"\\1\"</blockquote>");
m_TagMap["quote="] = std::make_pair(QRegExp("\\[quote=([^\\]]*)\\](.*)\\[/quote\\]"),
"<blockquote>\"\\2\"<br/><span>--\\1</span></blockquote></p>");
m_TagMap["code"] = std::make_pair(QRegExp("\\[code\\](.*)\\[/code\\]"),
"<pre>\\1</pre>");
m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"),
"<h2><strong>\\1</strong></h2>");
// lists
m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"),
"<ul>\\1</ul>");
m_TagMap["list="] = std::make_pair(QRegExp("\\[list.*\\](.*)\\[/list\\]"),
"<ol>\\1</ol>");
m_TagMap["ul"] = std::make_pair(QRegExp("\\[ul\\](.*)\\[/ul\\]"),
"<ul>\\1</ul>");
m_TagMap["ol"] = std::make_pair(QRegExp("\\[ol\\](.*)\\[/ol\\]"),
"<ol>\\1</ol>");
m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"),
"<li>\\1</li>");
m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)<br/>"),
"<li>\\1</li>");
// tables
m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"),
"<table>\\1</table>");
m_TagMap["tr"] = std::make_pair(QRegExp("\\[tr\\](.*)\\[/tr\\]"),
"<tr>\\1</tr>");
m_TagMap["th"] = std::make_pair(QRegExp("\\[th\\](.*)\\[/th\\]"),
"<th>\\1</th>");
m_TagMap["td"] = std::make_pair(QRegExp("\\[td\\](.*)\\[/td\\]"),
"<td>\\1</td>");
// web content
m_TagMap["url"] = std::make_pair(QRegExp("\\[url\\](.*)\\[/url\\]"),
"<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["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>");
// 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);
iter->second.first.setMinimal(true);
}
}
private:
QRegExp m_TagNameExp;
TagMap m_TagMap;
};
QString convertToHTML(const QString &inputParam)
{
// this code goes over the input string once and replaces all bbtags
// it encounters. This function is called recursively for every replaced
// string to convert nested tags.
//
// This could be implemented simpler by applying a set of regular expressions
// for each recognized bb-tag one after the other but that would probably be
// very inefficient (O(n^2)).
QString input = inputParam.mid(0).replace("\r\n", "<br/>");
input.replace("\\\"", "\"").replace("\\'", "'");
QString result;
int lastBlock = 0;
int pos = 0;
// iterate over the input buffer
while ((pos = input.indexOf('[', lastBlock)) != -1) {
// append everything between the previous tag-block and the current one
result.append(input.midRef(lastBlock, pos - lastBlock));
// convert the tag and content if necessary
int length = -1;
QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length);
if (length != 0) {
QString temp = convertToHTML(replacement);
result.append(temp);
// length contains the number of characters in the original tag
pos += length;
} else {
// nothing replaced
result.append('[');
++pos;
}
lastBlock = pos;
}
// append the remainder (everything after the last tag)
result.append(input.midRef(lastBlock));
return result;
}
} // namespace BBCode
#include "bbcode.h"
#include <QRegExp>
#include <map>
#include <algorithm>
namespace BBCode {
class BBCodeMap {
typedef std::map<QString, std::pair<QRegExp, QString> > TagMap;
public:
static BBCodeMap &instance() {
static BBCodeMap s_Instance;
return s_Instance;
}
QString convertTag(const QString &input, int &length)
{
// 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
if (tagName.endsWith('=')) {
tagName.chop(1);
}
QString closeTag = tagName == "*" ? "<br/>"
: QString("[/%1]").arg(tagName);
int closeTagPos = input.indexOf(closeTag, 0, Qt::CaseInsensitive);
//qDebug("close tag %s at %d", closeTag.toUtf8().constData(), closeTagPos);
if (closeTagPos > -1) {
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);
} else {
// expression doesn't match. either the input string is invalid
// or the expression is
qWarning("%s doesn't match the expression for %s",
temp.toUtf8().constData(), tagName.toUtf8().constData());
length = 0;
return QString();
}
}
}
// not a recognized tag or tag invalid
length = 0;
return QString();
}
private:
BBCodeMap()
: m_TagNameExp("^[a-zA-Z*]*=?")
{
m_TagMap["b"] = std::make_pair(QRegExp("\\[b\\](.*)\\[/b\\]"),
"<b>\\1</b>");
m_TagMap["i"] = std::make_pair(QRegExp("\\[i\\](.*)\\[/i\\]"),
"<i>\\1</i>");
m_TagMap["u"] = std::make_pair(QRegExp("\\[u\\](.*)\\[/u\\]"),
"<u>\\1</u>");
m_TagMap["s"] = std::make_pair(QRegExp("\\[s\\](.*)\\[/s\\]"),
"<s>\\1</s>");
m_TagMap["sub"] = std::make_pair(QRegExp("\\[sub\\](.*)\\[/sub\\]"),
"<sub>\\1</sub>");
m_TagMap["sup"] = std::make_pair(QRegExp("\\[sup\\](.*)\\[/sup\\]"),
"<sup>\\1</sup>");
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\\]"),
"<div align=\"center\">\\1</div>");
m_TagMap["quote"] = std::make_pair(QRegExp("\\[quote\\](.*)\\[/quote\\]"),
"<blockquote>\"\\1\"</blockquote>");
m_TagMap["quote="] = std::make_pair(QRegExp("\\[quote=([^\\]]*)\\](.*)\\[/quote\\]"),
"<blockquote>\"\\2\"<br/><span>--\\1</span></blockquote></p>");
m_TagMap["code"] = std::make_pair(QRegExp("\\[code\\](.*)\\[/code\\]"),
"<pre>\\1</pre>");
m_TagMap["heading"]= std::make_pair(QRegExp("\\[heading\\](.*)\\[/heading\\]"),
"<h2><strong>\\1</strong></h2>");
// lists
m_TagMap["list"] = std::make_pair(QRegExp("\\[list\\](.*)\\[/list\\]"),
"<ul>\\1</ul>");
m_TagMap["list="] = std::make_pair(QRegExp("\\[list.*\\](.*)\\[/list\\]"),
"<ol>\\1</ol>");
m_TagMap["ul"] = std::make_pair(QRegExp("\\[ul\\](.*)\\[/ul\\]"),
"<ul>\\1</ul>");
m_TagMap["ol"] = std::make_pair(QRegExp("\\[ol\\](.*)\\[/ol\\]"),
"<ol>\\1</ol>");
m_TagMap["li"] = std::make_pair(QRegExp("\\[li\\](.*)\\[/li\\]"),
"<li>\\1</li>");
m_TagMap["*"] = std::make_pair(QRegExp("\\[\\*\\](.*)<br/>"),
"<li>\\1</li>");
// tables
m_TagMap["table"] = std::make_pair(QRegExp("\\[table\\](.*)\\[/table\\]"),
"<table>\\1</table>");
m_TagMap["tr"] = std::make_pair(QRegExp("\\[tr\\](.*)\\[/tr\\]"),
"<tr>\\1</tr>");
m_TagMap["th"] = std::make_pair(QRegExp("\\[th\\](.*)\\[/th\\]"),
"<th>\\1</th>");
m_TagMap["td"] = std::make_pair(QRegExp("\\[td\\](.*)\\[/td\\]"),
"<td>\\1</td>");
// web content
m_TagMap["url"] = std::make_pair(QRegExp("\\[url\\](.*)\\[/url\\]"),
"<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["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>");
// 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);
iter->second.first.setMinimal(true);
}
}
private:
QRegExp m_TagNameExp;
TagMap m_TagMap;
};
QString convertToHTML(const QString &inputParam)
{
// this code goes over the input string once and replaces all bbtags
// it encounters. This function is called recursively for every replaced
// string to convert nested tags.
//
// This could be implemented simpler by applying a set of regular expressions
// for each recognized bb-tag one after the other but that would probably be
// very inefficient (O(n^2)).
QString input = inputParam.mid(0).replace("\r\n", "<br/>");
input.replace("\\\"", "\"").replace("\\'", "'");
QString result;
int lastBlock = 0;
int pos = 0;
// iterate over the input buffer
while ((pos = input.indexOf('[', lastBlock)) != -1) {
// append everything between the previous tag-block and the current one
result.append(input.midRef(lastBlock, pos - lastBlock));
// convert the tag and content if necessary
int length = -1;
QString replacement = BBCodeMap::instance().convertTag(input.mid(pos), length);
if (length != 0) {
QString temp = convertToHTML(replacement);
result.append(temp);
// length contains the number of characters in the original tag
pos += length;
} else {
// nothing replaced
result.append('[');
++pos;
}
lastBlock = pos;
}
// append the remainder (everything after the last tag)
result.append(input.midRef(lastBlock));
return result;
}
} // namespace BBCode
+21 -21
View File
@@ -17,24 +17,24 @@ 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 BBCODE_H
#define BBCODE_H
#include <QString>
namespace BBCode {
/**
* @brief convert a string with BB Code-Tags to HTML
* @param input the input string with BB tags
* @param replaceOccured if not NULL, this parameter will be set to true if any bb tags were replaced
* @return the same string in html representation
**/
QString convertToHTML(const QString &input);
}
#endif // BBCODE_H
#ifndef BBCODE_H
#define BBCODE_H
#include <QString>
namespace BBCode {
/**
* @brief convert a string with BB Code-Tags to HTML
* @param input the input string with BB tags
* @param replaceOccured if not NULL, this parameter will be set to true if any bb tags were replaced
* @return the same string in html representation
**/
QString convertToHTML(const QString &input);
}
#endif // BBCODE_H
+28 -2
View File
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <QFile>
#include <QDir>
#include <QList>
#include <QCoreApplication>
using namespace MOBase;
@@ -34,11 +35,18 @@ using namespace MOShared;
CategoryFactory* CategoryFactory::s_Instance = NULL;
QString CategoryFactory::categoriesFilePath()
{
return QCoreApplication::applicationDirPath() + "/categories.dat";
}
CategoryFactory::CategoryFactory()
{
atexit(&cleanup);
reset();
QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
QFile categoryFile(categoriesFilePath());
if (!categoryFile.open(QIODevice::ReadOnly)) {
loadDefaultCategories();
@@ -117,10 +125,16 @@ void CategoryFactory::setParents()
}
}
void CategoryFactory::cleanup()
{
delete s_Instance;
s_Instance = NULL;
}
void CategoryFactory::saveCategories()
{
QFile categoryFile(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())).append("/categories.dat"));
QFile categoryFile(categoriesFilePath());
if (!categoryFile.open(QIODevice::WriteOnly)) {
reportError(QObject::tr("Failed to save custom categories"));
@@ -144,6 +158,18 @@ void CategoryFactory::saveCategories()
}
unsigned int CategoryFactory::countCategories(std::tr1::function<bool (const Category &category)> filter)
{
unsigned int result = 0;
for (auto iter = m_Categories.begin(); iter != m_Categories.end(); ++iter) {
if (filter(*iter)) {
++result;
}
}
return result;
}
void CategoryFactory::addCategory(int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
{
int index = m_Categories.size();
+179 -161
View File
@@ -17,164 +17,182 @@ 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 CATEGORIES_H
#define CATEGORIES_H
#include <QString>
#include <vector>
#include <map>
/**
* @brief Manage the available mod categories
* @warning member functions of this class currently use a wild mix of ids and indexes to look up categories,
* optimized to where the request comes from. Therefore be very careful which of the two you have available
**/
class CategoryFactory {
friend class CategoriesDialog;
public:
static const int CATEGORY_NONE = 0;
static const int CATEGORY_SPECIAL_FIRST = 10000;
static const int CATEGORY_SPECIAL_CHECKED = CATEGORY_SPECIAL_FIRST;
static const int CATEGORY_SPECIAL_UNCHECKED = 10001;
static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002;
static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
static const int CATEGORY_SPECIAL_CONFLICT = 10004;
public:
/**
* @brief reset the list of categories
**/
void reset();
/**
* @brief save the categories to the categories.dat file
**/
void saveCategories();
/**
* @brief retrieve the number of available categories
*
* @return unsigned int number of categories
**/
unsigned numCategories() const { return m_Categories.size(); }
/**
* @brief get the id of the parent category
*
* @param index the index to look up
* @return int id of the parent category
**/
int getParentID(unsigned int index) const;
/**
* @brief determine if a category exists (by id)
*
* @param id the id to check for existance
* @return true if the category exists, false otherwise
**/
bool categoryExists(int id) const;
/**
* @brief test if a category is child of a second one
* @param id the presumed child id
* @param parentID the parent id to test for
* @return true if id is a child of parentID
**/
bool isDecendantOf(int id, int parentID) const;
/**
* @brief test if the specified category has child categories
*
* @param index index of the category to look up
* @return bool true if the category has child categories
**/
bool hasChildren(unsigned int index) const;
/**
* @brief retrieve the name of a category
*
* @param index index of the category to look up
* @return QString name of the category
**/
QString getCategoryName(unsigned int index) const;
/**
* @brief look up the id of a category by its index
*
* @param index index of the category to look up
* @return int id of the category
**/
int getCategoryID(unsigned int index) const;
/**
* @brief look up the index of a category by its id
*
* @param id index of the category to look up
* @return unsigned int index of the category
**/
int getCategoryIndex(int ID) const;
/**
* @brief retrieve the index of a category by its nexus id
*
* @param nexusID nexus id of the category to look up
* @return unsigned int index of the category or 0 if no category matches
**/
unsigned int resolveNexusID(int nexusID) const;
public:
/**
* @brief retrieve a reference to the singleton instance
*
* @return the reference to the singleton
**/
static CategoryFactory &instance();
private:
struct Category {
Category(int sortValue, int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
: m_SortValue(sortValue), m_ID(id), m_Name(name), m_HasChildren(false),
m_NexusIDs(nexusIDs), m_ParentID(parentID) {}
int m_SortValue;
int m_ID;
int m_ParentID;
bool m_HasChildren;
QString m_Name;
std::vector<int> m_NexusIDs;
friend bool operator<(const Category &LHS, const Category &RHS) {
return LHS.m_SortValue < RHS.m_SortValue;
}
};
private:
CategoryFactory();
void loadDefaultCategories();
void addCategory(int id, const QString &name, const std::vector<int> &nexusID, int parentID);
void setParents();
private:
static CategoryFactory *s_Instance;
std::vector<Category> m_Categories;
std::map<int, unsigned int> m_IDMap;
std::map<int, unsigned int> m_NexusMap;
};
#endif // CATEGORIES_H
#ifndef CATEGORIES_H
#define CATEGORIES_H
#include <QString>
#include <vector>
#include <map>
#include <functional>
/**
* @brief Manage the available mod categories
* @warning member functions of this class currently use a wild mix of ids and indexes to look up categories,
* optimized to where the request comes from. Therefore be very careful which of the two you have available
**/
class CategoryFactory {
friend class CategoriesDialog;
public:
static const int CATEGORY_NONE = 0;
static const int CATEGORY_SPECIAL_FIRST = 10000;
static const int CATEGORY_SPECIAL_CHECKED = CATEGORY_SPECIAL_FIRST;
static const int CATEGORY_SPECIAL_UNCHECKED = 10001;
static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002;
static const int CATEGORY_SPECIAL_NOCATEGORY = 10003;
static const int CATEGORY_SPECIAL_CONFLICT = 10004;
static const int CATEGORY_SPECIAL_NOTENDORSED = 10005;
public:
struct Category {
Category(int sortValue, int id, const QString &name, const std::vector<int> &nexusIDs, int parentID)
: m_SortValue(sortValue), m_ID(id), m_Name(name), m_HasChildren(false),
m_NexusIDs(nexusIDs), m_ParentID(parentID) {}
int m_SortValue;
int m_ID;
int m_ParentID;
bool m_HasChildren;
QString m_Name;
std::vector<int> m_NexusIDs;
friend bool operator<(const Category &LHS, const Category &RHS) {
return LHS.m_SortValue < RHS.m_SortValue;
}
};
public:
/**
* @brief reset the list of categories
**/
void reset();
/**
* @brief save the categories to the categories.dat file
**/
void saveCategories();
/**
* @brief retrieve the number of available categories
*
* @return unsigned int number of categories
**/
unsigned numCategories() const { return m_Categories.size(); }
/**
* @brief count all categories that match a specified filter
* @param filter the filter to test
* @return number of matching categories
*/
unsigned int countCategories(std::tr1::function<bool (const Category &category)> filter);
/**
* @brief get the id of the parent category
*
* @param index the index to look up
* @return int id of the parent category
**/
int getParentID(unsigned int index) const;
/**
* @brief determine if a category exists (by id)
*
* @param id the id to check for existance
* @return true if the category exists, false otherwise
**/
bool categoryExists(int id) const;
/**
* @brief test if a category is child of a second one
* @param id the presumed child id
* @param parentID the parent id to test for
* @return true if id is a child of parentID
**/
bool isDecendantOf(int id, int parentID) const;
/**
* @brief test if the specified category has child categories
*
* @param index index of the category to look up
* @return bool true if the category has child categories
**/
bool hasChildren(unsigned int index) const;
/**
* @brief retrieve the name of a category
*
* @param index index of the category to look up
* @return QString name of the category
**/
QString getCategoryName(unsigned int index) const;
/**
* @brief look up the id of a category by its index
*
* @param index index of the category to look up
* @return int id of the category
**/
int getCategoryID(unsigned int index) const;
/**
* @brief look up the index of a category by its id
*
* @param id index of the category to look up
* @return unsigned int index of the category
**/
int getCategoryIndex(int ID) const;
/**
* @brief retrieve the index of a category by its nexus id
*
* @param nexusID nexus id of the category to look up
* @return unsigned int index of the category or 0 if no category matches
**/
unsigned int resolveNexusID(int nexusID) const;
public:
/**
* @brief retrieve a reference to the singleton instance
*
* @return the reference to the singleton
**/
static CategoryFactory &instance();
/**
* @return path to the file that contains the categories list
*/
static QString categoriesFilePath();
private:
CategoryFactory();
void loadDefaultCategories();
void addCategory(int id, const QString &name, const std::vector<int> &nexusID, int parentID);
void setParents();
static void cleanup();
private:
static CategoryFactory *s_Instance;
std::vector<Category> m_Categories;
std::map<int, unsigned int> m_IDMap;
std::map<int, unsigned int> m_NexusMap;
private:
};
#endif // CATEGORIES_H
+3 -2
View File
@@ -86,7 +86,8 @@ public:
{
QLineEdit *edit = qobject_cast<QLineEdit*>(editor);
int pos = 0;
if (m_Validator->validate(edit->text(), pos) == QValidator::Acceptable) {
QString editText = edit->text();
if (m_Validator->validate(editText, pos) == QValidator::Acceptable) {
QItemDelegate::setModelData(editor, model, index);
}
}
@@ -163,7 +164,7 @@ void CategoriesDialog::fillTable()
CategoryFactory &categories = CategoryFactory::instance();
QTableWidget *table = ui->categoriesTable;
#if QT_VERSION >= 0x050000
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
table->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
table->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
table->horizontalHeader()->setSectionResizeMode(2, QHeaderView::Fixed);
+5
View File
@@ -33,6 +33,11 @@ DirectoryRefresher::DirectoryRefresher()
{
}
DirectoryRefresher::~DirectoryRefresher()
{
delete m_DirectoryStructure;
}
DirectoryEntry *DirectoryRefresher::getDirectoryStructure()
{
QMutexLocker locker(&m_RefreshLock);
+3 -1
View File
@@ -21,10 +21,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#define DIRECTORYREFRESHER_H
#include <QObject>
#include <directoryentry.h>
#include <vector>
#include <QMutex>
#include <tuple>
#include <directoryentry.h>
/**
@@ -43,6 +43,8 @@ public:
**/
DirectoryRefresher();
~DirectoryRefresher();
/**
* @brief retrieve the updated directory structure
*
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="icuin49.dll"/>
<file name="icuuc49.dll"/>
<file name="icudt49.dll"/>
<file name="Qt5Cored.dll"/>
<file name="Qt5Declaratived.dll"/>
<file name="Qt5Guid.dll"/>
<file name="Qt5Networkd.dll"/>
<file name="Qt5Scriptd.dll"/>
<file name="Qt5Sqld.dll"/>
<file name="Qt5Widgetsd.dll"/>
<file name="Qt5XmlPatternsd.dll"/>
</assembly>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="dlls" version="1.0.0.0" processorArchitecture="x86"/>
<file name="icuin49.dll"/>
<file name="icuuc49.dll"/>
<file name="icudt49.dll"/>
<file name="Qt5Core.dll"/>
<file name="Qt5Declarative.dll"/>
<file name="Qt5Gui.dll"/>
<file name="Qt5Network.dll"/>
<file name="Qt5Script.dll"/>
<file name="Qt5Sql.dll"/>
<file name="Qt5Svg.dll"/>
<file name="Qt5Widgets.dll"/>
<file name="Qt5Xml.dll"/>
<file name="Qt5XmlPatterns.dll"/>
</assembly>
+90 -87
View File
@@ -17,90 +17,93 @@ 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 "downloadlist.h"
#include "downloadmanager.h"
#include <QEvent>
#include <QSortFilterProxyModel>
DownloadList::DownloadList(DownloadManager *manager, QObject *parent)
: QAbstractTableModel(parent), m_Manager(manager)
{
connect(m_Manager, SIGNAL(update(int)), this, SLOT(update(int)));
connect(m_Manager, SIGNAL(aboutToUpdate()), this, SLOT(aboutToUpdate()));
}
int DownloadList::rowCount(const QModelIndex&) const
{
return m_Manager->numTotalDownloads();
}
int DownloadList::columnCount(const QModelIndex&) const
{
return 3;
}
QModelIndex DownloadList::index(int row, int column, const QModelIndex&) const
{
return createIndex(row, column, row);
}
QModelIndex DownloadList::parent(const QModelIndex&) const
{
return QModelIndex();
}
QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role == Qt::DisplayRole) &&
(orientation == Qt::Horizontal)) {
switch (section) {
case 0: return tr("Name");
case 1: return tr("Filetime");
default: return tr("Done");
}
} else {
return QAbstractItemModel::headerData(section, orientation, role);
}
}
QVariant DownloadList::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole) {
return index.row();
} else if (role == Qt::ToolTipRole) {
if (m_Manager->isInfoIncomplete(index.row())) {
return tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
} else {
NexusInfo info = m_Manager->getNexusInfo(index.row());
return QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
}
} else {
return QVariant();
}
}
void DownloadList::aboutToUpdate()
{
emit beginResetModel();
}
void DownloadList::update(int row)
{
if (row < 0) {
emit endResetModel();
} else if (row < this->rowCount()) {
emit dataChanged(this->index(row, 0, QModelIndex()), this->index(row, 1, QModelIndex()));
} else {
qCritical("invalid row %d in download list, update failed", row);
}
}
#include "downloadlist.h"
#include "downloadmanager.h"
#include <QEvent>
#include <QSortFilterProxyModel>
DownloadList::DownloadList(DownloadManager *manager, QObject *parent)
: QAbstractTableModel(parent), m_Manager(manager)
{
connect(m_Manager, SIGNAL(update(int)), this, SLOT(update(int)));
connect(m_Manager, SIGNAL(aboutToUpdate()), this, SLOT(aboutToUpdate()));
}
int DownloadList::rowCount(const QModelIndex&) const
{
return m_Manager->numTotalDownloads();
}
int DownloadList::columnCount(const QModelIndex&) const
{
return 3;
}
QModelIndex DownloadList::index(int row, int column, const QModelIndex&) const
{
return createIndex(row, column, row);
}
QModelIndex DownloadList::parent(const QModelIndex&) const
{
return QModelIndex();
}
QVariant DownloadList::headerData(int section, Qt::Orientation orientation, int role) const
{
if ((role == Qt::DisplayRole) &&
(orientation == Qt::Horizontal)) {
switch (section) {
case COL_NAME: return tr("Name");
case COL_FILETIME: return tr("Filetime");
default: return tr("Done");
}
} else {
return QAbstractItemModel::headerData(section, orientation, role);
}
}
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.");
} 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 text;
} else {
return QVariant();
}
}
void DownloadList::aboutToUpdate()
{
emit beginResetModel();
}
void DownloadList::update(int row)
{
if (row < 0) {
emit endResetModel();
} else if (row < this->rowCount()) {
#pragma message("updating only the one column is a hack")
emit dataChanged(this->index(row, 2, QModelIndex()), this->index(row, 2, QModelIndex()));
} else {
qCritical("invalid row %d in download list, update failed", row);
}
}
+77 -73
View File
@@ -17,76 +17,80 @@ 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 DOWNLOADLIST_H
#define DOWNLOADLIST_H
#include <QAbstractTableModel>
class DownloadManager;
/**
* @brief model of the list of active and completed downloads
**/
class DownloadList : public QAbstractTableModel
{
Q_OBJECT
public:
/**
* @brief constructor
*
* @param manager the download manager processing downloads
* @param parent parent object Defaults to 0.
**/
explicit DownloadList(DownloadManager *manager, QObject *parent = 0);
/**
* @brief retrieve the number of rows to display. Invoked by Qt
*
* @param parent not relevant for this implementation
* @return number of rows to display
**/
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
/**
* @brief retrieve the data to display in a specific row. Invoked by Qt
*
* @param index location to look up
* @param role ... Defaults to Qt::DisplayRole.
* @return this implementation only returns the row, the QItemDelegate implementation is expected to fetch its information from the DownloadManager
**/
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
signals:
public slots:
/**
* @brief used to inform the model that data has changed
*
* @param row the row that changed. This can be negative to update the whole view
**/
void update(int row);
void aboutToUpdate();
private:
private:
DownloadManager *m_Manager;
};
#endif // DOWNLOADLIST_H
#ifndef DOWNLOADLIST_H
#define DOWNLOADLIST_H
#include <QAbstractTableModel>
class DownloadManager;
/**
* @brief model of the list of active and completed downloads
**/
class DownloadList : public QAbstractTableModel
{
Q_OBJECT
public:
enum EColumn {
COL_NAME = 0,
COL_FILETIME,
COL_STATUS
};
public:
/**
* @brief constructor
*
* @param manager the download manager processing downloads
* @param parent parent object Defaults to 0.
**/
explicit DownloadList(DownloadManager *manager, QObject *parent = 0);
/**
* @brief retrieve the number of rows to display. Invoked by Qt
*
* @param parent not relevant for this implementation
* @return number of rows to display
**/
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
virtual int columnCount(const QModelIndex &parent) const;
QModelIndex index(int row, int column, const QModelIndex &parent) const;
QModelIndex parent(const QModelIndex &child) const;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
/**
* @brief retrieve the data to display in a specific row. Invoked by Qt
*
* @param index location to look up
* @param role ... Defaults to Qt::DisplayRole.
* @return this implementation only returns the row, the QItemDelegate implementation is expected to fetch its information from the DownloadManager
**/
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
public slots:
/**
* @brief used to inform the model that data has changed
*
* @param row the row that changed. This can be negative to update the whole view
**/
void update(int row);
void aboutToUpdate();
private:
DownloadManager *m_Manager;
};
#endif // DOWNLOADLIST_H
+8 -4
View File
@@ -18,6 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "downloadlistsortproxy.h"
#include "downloadlist.h"
DownloadListSortProxy::DownloadListSortProxy(const DownloadManager *manager, QObject *parent)
: QSortFilterProxyModel(parent), m_Manager(manager), m_CurrentFilter()
@@ -30,18 +31,21 @@ void DownloadListSortProxy::updateFilter(const QString &filter)
invalidateFilter();
}
bool DownloadListSortProxy::lessThan(const QModelIndex &left,
const QModelIndex &right) const
{
int leftIndex = sourceModel()->data(left).toInt();
int rightIndex = sourceModel()->data(right).toInt();
if (left.column() == 0) {
if (left.column() == DownloadList::COL_NAME) {
return m_Manager->getFileName(leftIndex).compare(m_Manager->getFileName(rightIndex), Qt::CaseInsensitive) < 0;
} else if (left.column() == 1) {
return leftIndex < rightIndex;
} else {
} 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;
}
}

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