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