Compare commits

..
Author SHA1 Message Date
Tannin db0347212a - bugfix: installFile for mods was not a relative path if the downloads directory was non-default
- bugfix: crash after removing the last profile in the list (ordered alphabetically)
- improved subdirs ordering to ensure pythonrunner is compiled before pythonproxy
- translation files for plugins are now generated
- set some texts as un-translatable where it made no sense
- staging script now re-builds MO completely before packaging
- updated qt libraries for packaging to 4.8.5
- added python to the package
2013-11-22 17:45:34 +01:00
Tannin 5895a1a92d - bugfix: canceling creation of an ini tweak prompted an error message
- added missing files from earlier commit (wtf?)
2013-11-20 21:54:14 +01:00
Tannin c6101e34e1 - archive.dll now supports querying the crc value of files
- esptk now determines if a esp is a dummy (without records)
- hook.dll no longer creates a log file if noone wrote to it
- nexus id and installtime columns are now hidden by default
- modlist can now be refreshed without saving first (so plugins can replace the modlist.txt as a whole)
- plugins can now query more details about virtualised files
- added style options "plastique" and "cleanlooks"
- "overwrite" is no longer listed with a creation time
- a warning will now be displayed if the user has too many plugins active
- a warning will now be displayed if mods with scripts have an installation order that doesn't match the corresponding esp load order
- nmm importer now has select all/deselect all buttons
- nmm importer no longer tries to unpack missing files from archives (won't work anyway)
- initial support for importing from nmm 0.5 alpha
- removed some broken warning suppresions
- python runner now works with bundled python
- extended qbs build system (still fails to build the main gui application)
- implemented a nsis-based installer
2013-11-18 20:17:14 +01:00
Tannin 75475a7984 - started qbs based project files (not functional yet)
- modlist will now updated immediately after a change throw the modinfo dialog
- bugfix: configurator wasn't able to save after revision 137
2013-11-11 23:19:54 +01:00
Tannin 315ed6e41a Added tag release v1.0.7 for changeset 1973f6e00784 2013-11-06 18:40:09 +01:00
Tannin b2d613e9d1 Added tag release v1.0.6 for changeset 1973f6e00784 2013-11-06 18:40:03 +01:00
Tannin f010c22a3b - diagnosis plugins can now request to be re-evaluated
- main application now contains means for plugins to react on some changes (to be extended)
- plugins can now retrieve more information about a mod
- user-agent sent to nexus now automatically contains the current MO version
- included updated russian translation
- problems dialog now refreshes itself after a fix was applied
- fixed new esp/asset ordering diagnosis. May be fully functional now
- bugfix: restoring locked load order could leave MO in an endless loop (not sure if this fix is correct yet)
- bugfix: plugin list was not visually updated after some changes
- bugfix: nmm importer threw away mod ordering
2013-11-06 18:35:27 +01:00
Tannin d1594798e6 - added a new diagnosis to detect potential problems in regards to esp vs. asset ordering (not fully functional yet)
- new plugin interfaces to the mod list
- plugins can now query the origin (mod) of a esp/esm
- bugfix: potential access to profile before one was activated
- bugfix: regression in previous (not-yet-released) commit prevented changes to bsa list from being saved
2013-11-01 14:59:25 +01:00
Tannin 09bd3dbead - text files encoded in system encoding should now be correctly recognized in the mod info dialog
- archive-list can be user-sorted again (within one mod)
2013-10-26 15:02:31 +02:00
Tannin 8c0b309bde - Option to choose edition of the game. Currently only relevant for FO3 because FO3 GOTY is its own app
- applications that aren't in the executables list can again be started from the command line
- references to missing categories are now removed from mods
- bugfix: integrated fomod installer didn't update description and picture on some constellations of Windows version and theme
2013-10-25 14:49:42 +02:00
Tannin bc7c42dbe8 - directories in data-view are now sorted
- questionbox with selection memory is now exposed to plugins
- the choice to run fnis from the checker can now be memorized
2013-10-20 16:51:40 +02:00
Tannin 9a03783f49 - tooltip on download list now contains the file name
- bugfix: when refreshing the directory tree conflict information wasn't immediately refreshed (including on start)
- bugfix: dataChanged events wasn't emitted when user changed the modlist
- bugfix: file patterns in checkfnis plugin weren't completely correct
2013-10-18 23:35:38 +02:00
Tannin d73b1e38c8 - mod sorting now always uses priority as secondary sorting
- added missing ipluginlist.h
2013-10-17 19:21:22 +02:00
Tannin b47c89661e - plugin (esp/esm) list is now exposed to plugins (read-only functionality right now)
- integrated fomod installer now supports file dependencies
- bugfix: integrated fomod installer crashed if the installer had no optiosn´´ns
2013-10-16 23:04:03 +02:00
Tannin 0b84cc3d45 Merge 2013-10-16 19:34:56 +02:00
Tannin 1bf3cc585e - nmm importer now also transfers category, endorsement state and lastest version information
- nmm importer should now always determine the correct nexus id
- nmm importer will now correctly restore overwritten files for imported mods
2013-10-16 19:32:59 +02:00
Tannin eae275cc84 Added tag release v1.0.5 for changeset c6a9d2ab2d6c 2013-10-15 23:34:16 +02:00
64 changed files with 8636 additions and 11480 deletions
+11 -11
View File
@@ -1,23 +1,23 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = bsatk \
shared \
uibase \
organizer \
hookdll \
archive \
helper \
shared \
uibase \
organizer \
hookdll \
archive \
helper \
plugins \
proxydll \
nxmhandler \
BossDummy \
pythonRunner \
esptk
BossDummy \
pythonRunner \
esptk
plugins.depends = pythonRunner
hookdll.depends = shared
organizer.depends = shared, uibase, plugins
organizer.depends = shared uibase plugins
CONFIG(debug, debug|release) {
DESTDIR = outputd
+4 -2
View File
@@ -75,12 +75,14 @@ QVariant DownloadList::data(const QModelIndex &index, int role) const
if (role == Qt::DisplayRole) {
return index.row();
} else if (role == Qt::ToolTipRole) {
QString text = m_Manager->getFileName(index.row()) + "\n";
if (m_Manager->isInfoIncomplete(index.row())) {
return tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
text += tr("Information missing, please select \"Query Info\" from the context menu to re-retrieve.");
} else {
NexusInfo info = m_Manager->getNexusInfo(index.row());
return QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
text += QString("%1 (ID %2) %3").arg(info.m_ModName).arg(m_Manager->getModID(index.row())).arg(info.m_Version);
}
return text;
} else {
return QVariant();
}
+3 -3
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>315</width>
<height>75</height>
<height>81</height>
</rect>
</property>
<property name="contextMenuPolicy">
@@ -78,14 +78,14 @@
<item>
<widget class="QLabel" name="sizeLabel">
<property name="text">
<string>0</string>
<string notr="true">0</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>KB</string>
<string notr="true">KB</string>
</property>
</widget>
</item>
+1 -3
View File
@@ -506,7 +506,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)
{
@@ -528,7 +527,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),
@@ -649,7 +647,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));
+7
View File
@@ -64,6 +64,12 @@ public:
*/
void setModsDirectory(const QString &modsDirectory) { m_ModsDirectory = modsDirectory; }
/**
* @brief update the directory where downloads are stored
* @param downloadDirectory the download directory
*/
void setDownloadDirectory(const QString &downloadDirectory) { m_DownloadsDirectory = downloadDirectory; }
/**
* @brief install a mod from an archive
*
@@ -191,6 +197,7 @@ private:
QWidget *m_ParentWidget;
QString m_ModsDirectory;
QString m_DownloadsDirectory;
std::vector<MOBase::IPluginInstaller*> m_Installers;
std::set<QString, CaseInsensitive> m_SupportedExtensions;
+23 -2
View File
@@ -60,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>
@@ -140,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;
@@ -156,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");
@@ -400,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;
+100 -17
View File
@@ -59,14 +59,15 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include <appconfig.h>
#include <utility.h>
#include <ipluginproxy.h>
#include <questionboxmemory.h>
#include <map>
#include <ctime>
#include <QTime>
#include <util.h>
#include <wchar.h>
#include <QTime>
#include <QInputDialog>
#include <QSettings>
#include <QWhatsThis>
#include <wchar.h>
#include <sstream>
#include <QProcess>
#include <QMenu>
@@ -162,6 +163,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
ui->actionEndorseMO->setVisible(false);
MOBase::QuestionBoxMemory::init(initSettings.fileName());
updateProblemsButton();
updateToolBar();
@@ -185,8 +188,13 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
ui->modList->header()->installEventFilter(&m_ModList);
if (initSettings.contains("mod_list_state")) {
ui->modList->header()->restoreState(initSettings.value("mod_list_state").toByteArray());
} else {
// hide these columns by default
ui->modList->header()->setSectionHidden(ModList::COL_MODID, true);
ui->modList->header()->setSectionHidden(ModList::COL_INSTALLTIME, true);
}
ui->modList->header()->setSectionHidden(0, false); // prevent the name-column from being hidden
ui->modList->header()->setSectionHidden(ModList::COL_NAME, false); // prevent the name-column from being hidden
ui->modList->installEventFilter(&m_ModList);
// set up plugin list
@@ -202,7 +210,6 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
resizeLists(initSettings.contains("mod_list_state"), initSettings.contains("plugin_list_state"));
QMenu *linkMenu = new QMenu(this);
linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Toolbar"), this, SLOT(linkToolbar()));
linkMenu->addAction(QIcon(":/MO/gui/link"), tr("Desktop"), this, SLOT(linkDesktop()));
@@ -216,6 +223,7 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
NexusInterface::instance()->setNMMVersion(m_Settings.getNMMVersion());
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory());
updateDownloadListDelegate();
@@ -244,6 +252,8 @@ MainWindow::MainWindow(const QString &exeName, QSettings &initSettings, QWidget
connect(ui->espFilterEdit, SIGNAL(textChanged(QString)), this, SLOT(espFilterChanged(QString)));
connect(&m_PluginList, SIGNAL(saveTimer()), this, SLOT(savePluginList()));
connect(ui->bsaList, SIGNAL(itemsMoved()), this, SLOT(bsaList_itemMoved()));
connect(ui->bsaWarning, SIGNAL(linkActivated(QString)), this, SLOT(linkClicked(QString)));
connect(&m_DirectoryRefresher, SIGNAL(refreshed()), this, SLOT(directory_refreshed()));
@@ -483,6 +493,7 @@ void MainWindow::updateProblemsButton()
ui->actionProblems->setIconText(tr("Problems"));
ui->actionProblems->setToolTip(tr("There are potential problems with your setup"));
} else {
ui->actionProblems->setEnabled(false);
ui->actionProblems->setIconText(tr("No Problems"));
ui->actionProblems->setToolTip(tr("Everything seems to be in order"));
}
@@ -678,7 +689,7 @@ bool MainWindow::saveCurrentLists()
reportError(tr("failed to save load order: %1").arg(e.what()));
}
// ensure the archive list exists
// save only if the file doesn't exist at all, changes made in the ui are saved immediately
if (!QFile::exists(m_CurrentProfile->getArchivesFileName())) {
saveArchiveList();
}
@@ -880,8 +891,7 @@ void MainWindow::hideSaveGameInfo()
}
}
bool MainWindow::eventFilter(QObject* object, QEvent *event)
bool MainWindow::eventFilter(QObject *object, QEvent *event)
{
if ((object == ui->savegameList) &&
((event->type() == QEvent::Leave) || (event->type() == QEvent::WindowDeactivate))) {
@@ -980,6 +990,7 @@ bool MainWindow::registerPlugin(QObject *plugin)
IPluginDiagnose *diagnose = qobject_cast<IPluginDiagnose*>(plugin);
if (diagnose != NULL) {
m_DiagnosisPlugins.push_back(diagnose);
diagnose->onInvalidated([&] () { this->updateProblemsButton(); });
}
}
{ // tool plugins
@@ -1264,7 +1275,7 @@ HANDLE MainWindow::spawnBinaryDirect(const QFileInfo &binary, const QString &arg
(m_Settings.getLoadMechanism() == LoadMechanism::LOAD_MODORGANIZER)) {
if (!testForSteam()) {
if (QuestionBoxMemory::query(this->isVisible() ? this : NULL,
m_Settings.directInterface(), "steamQuery", tr("Start Steam?"),
"steamQuery", tr("Start Steam?"),
tr("Steam is required to be running already to correctly start the game. "
"Should MO try to start steam now?"),
QDialogButtonBox::Yes | QDialogButtonBox::No) == QDialogButtonBox::Yes) {
@@ -1359,10 +1370,12 @@ void MainWindow::startExeAction()
}
void MainWindow::refreshModList()
void MainWindow::refreshModList(bool saveChanges)
{
// don't lose changes!
m_CurrentProfile->writeModlistNow(true);
if (saveChanges) {
m_CurrentProfile->writeModlistNow(true);
}
ModInfo::updateFromDisc(m_Settings.getModDirectory(), &m_DirectoryStructure);
m_CurrentProfile->refreshModStatus();
@@ -1423,6 +1436,12 @@ void MainWindow::on_profileBox_currentIndexChanged(int index)
saveCurrentLists();
}
// ensure the new index is valid
if (index < 0 || index >= ui->profileBox->count()) {
qDebug("invalid profile index, using last profile");
ui->profileBox->setCurrentIndex(ui->profileBox->count() - 1);
}
if (ui->profileBox->currentIndex() == 0) {
ProfilesDialog(m_GamePath).exec();
while (!refreshProfiles()) {
@@ -1670,7 +1689,7 @@ void MainWindow::refreshSaveList()
void MainWindow::refreshLists()
{
if (m_DirectoryStructure->isPopulated()) {
if ((m_CurrentProfile != NULL) && m_DirectoryStructure->isPopulated()) {
refreshESPList();
refreshBSAList();
} // no point in refreshing lists if no files have been added to the directory tree
@@ -1799,6 +1818,7 @@ void MainWindow::refreshBSAList()
subItem = items.at(0);
} else {
subItem = new QTreeWidgetItem(QStringList(ToQString(origin.getName())));
subItem->setFlags(subItem->flags() & ~Qt::ItemIsDragEnabled);
ui->bsaList->addTopLevelItem(subItem);
}
subItem->addChild(iter->second);
@@ -2080,7 +2100,7 @@ QStringList MainWindow::findFiles(const QString &path, const std::function<bool(
if (dir != NULL) {
std::vector<FileEntry::Ptr> files = dir->getFiles();
foreach (FileEntry::Ptr file, files) {
if (filter(ToQString(file->getName()))) {
if (filter(ToQString(file->getFullPath()))) {
result.append(ToQString(file->getFullPath()));
}
}
@@ -2090,11 +2110,47 @@ QStringList MainWindow::findFiles(const QString &path, const std::function<bool(
return result;
}
QList<IOrganizer::FileInfo> MainWindow::findFileInfos(const QString &path, const std::function<bool (const IOrganizer::FileInfo &)> &filter) const
{
QList<IOrganizer::FileInfo> result;
DirectoryEntry *dir = m_DirectoryStructure->findSubDirectoryRecursive(ToWString(path));
if (dir != NULL) {
std::vector<FileEntry::Ptr> files = dir->getFiles();
foreach (FileEntry::Ptr file, files) {
FileInfo info;
info.filePath = ToQString(file->getFullPath());
bool fromArchive = false;
info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(file->getOrigin(fromArchive)).getName()));
info.archive = fromArchive ? ToQString(file->getArchive()) : "";
foreach (int idx, file->getAlternatives()) {
info.origins.append(ToQString(m_DirectoryStructure->getOriginByID(idx).getName()));
}
if (filter(info)) {
result.append(info);
}
}
} else {
qWarning("directory %s not found", qPrintable(path));
}
return result;
}
IDownloadManager *MainWindow::downloadManager()
{
return &m_DownloadManager;
}
IPluginList *MainWindow::pluginList()
{
return &m_PluginList;
}
IModList *MainWindow::modList()
{
return &m_ModList;
}
HANDLE MainWindow::startApplication(const QString &executable, const QStringList &args, const QString &cwd, const QString &profile)
{
QFileInfo binary;
@@ -2126,7 +2182,7 @@ HANDLE MainWindow::startApplication(const QString &executable, const QStringList
}
} catch (const std::runtime_error&) {
qWarning("\"%s\" not set up as executable", executable.toUtf8().constData());
return INVALID_HANDLE_VALUE;
binary = QFileInfo(executable);
}
}
@@ -2145,6 +2201,9 @@ std::vector<unsigned int> MainWindow::activeProblems() const
if (m_UnloadedPlugins.size() != 0) {
problems.push_back(PROBLEM_PLUGINSNOTLOADED);
}
if (m_PluginList.enabledCount() > 256) {
problems.push_back(PROBLEM_TOOMANYPLUGINS);
}
return problems;
}
@@ -2154,6 +2213,9 @@ QString MainWindow::shortDescription(unsigned int key) const
case PROBLEM_PLUGINSNOTLOADED: {
return tr("Some plugins could not be loaded");
} break;
case PROBLEM_TOOMANYPLUGINS: {
return tr("Too many esps and esms enabled");
} break;
default: {
return tr("Description missing");
} break;
@@ -2164,13 +2226,17 @@ QString MainWindow::fullDescription(unsigned int key) const
{
switch (key) {
case PROBLEM_PLUGINSNOTLOADED: {
QString result = tr("The following Plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:<ul>");
QString result = tr("The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:") + "<ul>";
foreach (const QString &plugin, m_UnloadedPlugins) {
result += "<li>" + plugin + "</li>";
}
result += "<ul>";
return result;
} break;
case PROBLEM_TOOMANYPLUGINS: {
return tr("The game doesn't allow more than 256 active plugins (including the official ones) to be loaded. You have to disable some unused plugins or "
"merge some plugins into one. You can find a guide here: <a href=\"http://wiki.step-project.com/Guide:Merging_Plugins\">http://wiki.step-project.com/Guide:Merging_Plugins</a>");
} break;
default: {
return tr("Description missing");
} break;
@@ -2493,6 +2559,11 @@ void MainWindow::directory_refreshed()
// some problem-reports may rely on the virtual directory tree so they need to be updated
// now
updateProblemsButton();
for (int i = 0; i < m_ModList.rowCount(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
modInfo->clearCaches();
}
}
@@ -2966,8 +3037,9 @@ void MainWindow::unendorse_clicked()
void MainWindow::overwriteClosed(int)
{
QDialog *dialog = this->findChild<QDialog*>("__overwriteDialog");
OverwriteInfoDialog *dialog = this->findChild<OverwriteInfoDialog*>("__overwriteDialog");
if (dialog != NULL) {
m_ModList.modInfoChanged(dialog->modInfo());
dialog->deleteLater();
}
}
@@ -2975,6 +3047,7 @@ void MainWindow::overwriteClosed(int)
void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index, int tab)
{
m_ModList.modInfoAboutToChange(modInfo);
std::vector<ModInfo::EFlag> flags = modInfo->getFlags();
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end()) {
QDialog *dialog = this->findChild<QDialog*>("__overwriteDialog");
@@ -3000,6 +3073,7 @@ void MainWindow::displayModInformation(ModInfo::Ptr modInfo, unsigned int index,
dialog.exec();
modInfo->saveMeta();
emit modInfoDisplayed();
m_ModList.modInfoChanged(modInfo);
}
if (m_CurrentProfile->modEnabled(index)) {
@@ -3084,7 +3158,7 @@ void MainWindow::testExtractBSA(int modIndex)
QFileInfoList archives = dir.entryInfoList(QStringList("*.bsa"));
if (archives.length() != 0 &&
(QuestionBoxMemory::query(this, m_Settings.directInterface(), "unpackBSA", tr("Extract BSA"),
(QuestionBoxMemory::query(this, "unpackBSA", tr("Extract BSA"),
tr("This mod contains at least one BSA. Do you want to unpack it?\n"
"(This removes the BSA after completion. If you don't know about BSAs, just select no)"),
QDialogButtonBox::Yes | QDialogButtonBox::No, QDialogButtonBox::No) == QMessageBox::Yes)) {
@@ -3171,6 +3245,7 @@ void MainWindow::syncOverwrite()
modInfo->testValid();
refreshDirectoryStructure();
}
}
void MainWindow::createModFromOverwrite()
@@ -3841,6 +3916,7 @@ void MainWindow::on_actionSettings_triggered()
bool proxy = m_Settings.useProxy();
m_Settings.query(this);
m_InstallationManager.setModsDirectory(m_Settings.getModDirectory());
m_InstallationManager.setDownloadDirectory(m_Settings.getDownloadDirectory());
fixCategories();
refreshFilters();
if (QDir::fromNativeSeparators(m_DownloadManager.getOutputDirectory()) != QDir::fromNativeSeparators(m_Settings.getDownloadDirectory())) {
@@ -4636,13 +4712,19 @@ void MainWindow::on_bsaList_customContextMenuRequested(const QPoint &pos)
menu.exec(ui->bsaList->mapToGlobal(pos));
}
void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int)
void MainWindow::bsaList_itemMoved()
{
saveArchiveList();
m_CheckBSATimer.start(500);
}
void MainWindow::on_bsaList_itemChanged(QTreeWidgetItem*, int)
{
saveArchiveList();
m_CheckBSATimer.start(500);
}
void MainWindow::on_actionProblems_triggered()
{
// QString problemDescription;
@@ -4822,3 +4904,4 @@ void MainWindow::on_showHiddenBox_toggled(bool checked)
{
m_DownloadManager.setShowHidden(checked);
}
+9 -5
View File
@@ -60,6 +60,7 @@ class QToolButton;
class ModListSortProxy;
class ModListGroupCategoriesProxy;
class MainWindow : public QMainWindow, public MOBase::IOrganizer, public MOBase::IPluginDiagnose
{
Q_OBJECT
@@ -83,8 +84,6 @@ private:
}
};
public:
typedef boost::signals2::signal<bool (const QString&), SignalCombinerAnd> SignalAboutToRunApplication;
public:
@@ -99,7 +98,6 @@ public:
void refreshBSAList();
void refreshDataTree();
void refreshSaveList();
void refreshModList();
void setExecutablesList(const ExecutablesList &executablesList);
@@ -136,9 +134,14 @@ public:
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;
@@ -180,7 +183,6 @@ signals:
void styleChanged(const QString &styleFile);
void modListDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
protected:
@@ -274,6 +276,7 @@ private:
private:
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
static const unsigned int PROBLEM_TOOMANYPLUGINS = 2;
private:
@@ -511,7 +514,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);
@@ -532,6 +535,7 @@ private slots: // ui slots
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
+65 -14
View File
@@ -31,7 +31,16 @@
<property name="spacing">
<number>4</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -721,11 +730,20 @@ p, li { white-space: pre-wrap; }
<string notr="true">Archives</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="margin">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<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 +760,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 +769,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 +786,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 +800,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>
@@ -800,7 +819,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<string>Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="margin">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -870,7 +898,16 @@ BSAs checked here are loaded in such a way that your installation order is obeye
<string>Saves</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="margin">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
@@ -899,7 +936,16 @@ p, li { white-space: pre-wrap; }
<string>Downloads</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="margin">
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
@@ -1224,6 +1270,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"/>
+21 -10
View File
@@ -19,16 +19,19 @@ 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();
}
@@ -42,11 +45,12 @@ bool MOApplication::setStyleFile(const QString &styleName)
// set new stylesheet or clear it
if (styleName.length() != 0) {
QString styleSheetName = applicationDirPath() + "/" + MOBase::ToQString(AppConfig::stylesheetsPath()) + "/" + styleName;
if (!QFile::exists(styleSheetName)) {
return false;
if (QFile::exists(styleSheetName)) {
m_StyleWatcher.addPath(styleSheetName);
updateStyle(styleSheetName);
} else {
updateStyle(styleName);
}
m_StyleWatcher.addPath(styleSheetName);
updateStyle(styleSheetName);
} else {
setStyleSheet("");
}
@@ -74,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();
}
+2
View File
@@ -43,6 +43,8 @@ private slots:
private:
QFileSystemWatcher m_StyleWatcher;
QString m_DefaultStyle;
};
+20 -1
View File
@@ -324,6 +324,7 @@ ModInfoRegular::ModInfoRegular(const QDir &path, DirectoryEntry **directoryStruc
}
}
QString categoriesString = metaFile.value("category", "").toString();
QStringList categories = categoriesString.split(',', QString::SkipEmptyParts);
@@ -554,6 +555,10 @@ void ModInfoRegular::setNexusDescription(const QString &description)
m_MetaInfoChanged = true;
}
void ModInfoRegular::addNexusCategory(int categoryID)
{
m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID));
}
void ModInfoRegular::setIsEndorsed(bool endorsed)
{
@@ -584,6 +589,11 @@ void ModInfoRegular::endorse(bool doEndorse)
}
}
void ModInfoRegular::clearCaches()
{
m_LastConflictCheck = QTime();
}
QString ModInfoRegular::absolutePath() const
{
@@ -685,7 +695,7 @@ ModInfoRegular::EConflictType ModInfoRegular::isConflicted() const
{
// this is costy so cache the result
QTime now = QTime::currentTime();
if (abs(m_LastConflictCheck.secsTo(now)) > 10) {
if (m_LastConflictCheck.isNull() || (m_LastConflictCheck.secsTo(now) > 10)) {
bool overwrite = false;
bool overwritten = false;
bool regular = false;
@@ -817,6 +827,15 @@ ModInfoOverwrite::ModInfoOverwrite()
}
bool ModInfoOverwrite::isEmpty() const
{
QDirIterator iter(absolutePath(), QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
if (!iter.hasNext()) return true;
iter.next();
if ((iter.fileName() == "meta.ini") && !iter.hasNext()) return true;
return false;
}
QString ModInfoOverwrite::absolutePath() const
{
return QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOverwriteDir()));
+26 -17
View File
@@ -80,23 +80,6 @@ public:
ENDORSED_UNKNOWN,
ENDORSED_NEVER
};
/*
struct NexusFileInfo {
NexusFileInfo(const QString &data);
NexusFileInfo(int id, const QString &name, const QString &url, const QString &version,
const QString &description, int category, int size)
: id(id), name(name), url(url), version(version), description(description),
category(category), size(size) {}
int id;
QString name;
QString url;
QString version;
QString description;
int category;
int size;
QString toString() const;
};*/
public:
@@ -267,6 +250,13 @@ public:
*/
virtual void setNexusDescription(const QString &description) = 0;
/**
* @brief sets the category id from a nexus category id. Conversion to MO id happens internally
* @param categoryID the nexus category id
* @note if a mapping is not possible, the category is set to the default value
*/
virtual void addNexusCategory(int categoryID) = 0;
/**
* update the endorsement state for the mod. This only changes the
* buffered state, it does not sync with Nexus
@@ -292,6 +282,11 @@ public:
*/
virtual void endorse(bool doEndorse) = 0;
/**
* @brief clear all caches held for this mod
*/
virtual void clearCaches() {}
/**
* @brief getter for the mod name
*
@@ -605,6 +600,13 @@ public:
*/
virtual void setNexusDescription(const QString &description);
/**
* @brief sets the category id from a nexus category id. Conversion to MO id happens internally
* @param categoryID the nexus category id
* @note if a mapping is not possible, the category is set to the default value
*/
virtual void addNexusCategory(int categoryID);
/**
* @brief sets the new primary category of the mod
* @param categoryID the category to set
@@ -636,6 +638,11 @@ public:
*/
virtual void endorse(bool doEndorse);
/**
* @brief clear all caches held for this mod
*/
virtual void clearCaches();
/**
* @brief getter for the mod name
*
@@ -854,10 +861,12 @@ public:
virtual void setNewestVersion(const MOBase::VersionInfo&) {}
virtual void ignoreUpdate(bool) {}
virtual void setNexusDescription(const QString&) {}
virtual void addNexusCategory(int) {}
virtual void setIsEndorsed(bool) {}
virtual void setNeverEndorse() {}
virtual bool remove() { return false; }
virtual void endorse(bool) {}
virtual bool isEmpty() const;
virtual QString name() const { return "Overwrite"; }
virtual QString notes() const { return ""; }
virtual QDateTime creationTime() const { return m_StartupTime; }
+22 -14
View File
@@ -254,17 +254,19 @@ void ModInfoDialog::refreshLists()
} else if ((fileName.endsWith(".png", Qt::CaseInsensitive)) ||
(fileName.endsWith(".jpg", Qt::CaseInsensitive))) {
QImage image = QImage(fileName);
if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) {
image = image.scaledToWidth(128);
} else {
image = image.scaledToHeight(96);
}
if (!image.isNull()) {
if (static_cast<float>(image.width()) / static_cast<float>(image.height()) > 1.34) {
image = image.scaledToWidth(128);
} else {
image = image.scaledToHeight(96);
}
QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), "");
thumbnailButton->setIconSize(QSize(image.width(), image.height()));
connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map()));
m_ThumbnailMapper.setMapping(thumbnailButton, fileName);
ui->thumbnailArea->addWidget(thumbnailButton);
QPushButton *thumbnailButton = new QPushButton(QPixmap::fromImage(image), "");
thumbnailButton->setIconSize(QSize(image.width(), image.height()));
connect(thumbnailButton, SIGNAL(clicked()), &m_ThumbnailMapper, SLOT(map()));
m_ThumbnailMapper.setMapping(thumbnailButton, fileName);
ui->thumbnailArea->addWidget(thumbnailButton);
}
}
}
@@ -400,7 +402,13 @@ void ModInfoDialog::openTextFile(const QString &fileName)
textFile.open(QIODevice::ReadOnly);
QByteArray buffer = textFile.readAll();
QTextCodec *codec = QTextCodec::codecForUtfText(buffer, m_UTF8Codec);
ui->textFileView->setText(codec->toUnicode(buffer));
QString text = codec->toUnicode(buffer);
if (codec->fromUnicode(text) != buffer) {
qDebug("conversion failed assuming local encoding");
codec = QTextCodec::codecForLocale();
text = codec->toUnicode(buffer);
}
ui->textFileView->setText(text);
ui->textFileView->setProperty("currentFile", fileName);
ui->textFileView->setProperty("encoding", codec->name());
ui->saveTXTButton->setEnabled(false);
@@ -1169,10 +1177,10 @@ void ModInfoDialog::on_prevButton_clicked()
void ModInfoDialog::createTweak()
{
QString name = QInputDialog::getText(this, tr("Name"), tr("Please enter a name"));
if (!fixDirectoryName(name)) {
QMessageBox::critical(this, tr("Error"), tr("Invalid name. Must be a valid file name"));
if (name.isNull()) {
return;
} else if (name.isEmpty()) {
} else if (!fixDirectoryName(name)) {
QMessageBox::critical(this, tr("Error"), tr("Invalid name. Must be a valid file name"));
return;
} else if (ui->iniTweaksList->findItems(name, Qt::MatchFixedString).count() != 0) {
QMessageBox::critical(this, tr("Error"), tr("A tweak by that name exists"));
+13 -23
View File
@@ -217,11 +217,7 @@
<string>Images located in the mod.</string>
</property>
<property name="whatsThis">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;This lists all the images (.jpg and .png) in the mod directory, like screenshots and such. Click one to get a larger view.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>This lists all the images (.jpg and .png) in the mod directory, like screenshots and such. Click one to get a larger view.</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
@@ -254,14 +250,10 @@ p, li { white-space: pre-wrap; }
<string>List of esps and esms that can not be loaded by the game.</string>
</property>
<property name="whatsThis">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;List of esps and esms contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;They usually contain optional functionality, see the readme.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;&quot;&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;Most mods do not have optional esps, so chances are good you are looking at an empty list.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>List of esps and esms contained in this plugin that currently can not be loaded by the game. They will not even appear in the esp-list in the main MO-window.
They usually contain optional functionality, see the readme.
Most mods do not have optional esps, so chances are good you are looking at an empty list.</string>
</property>
</widget>
</item>
@@ -292,7 +284,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/MO/gui/resources/go-up.png</normaloff>:/MO/gui/resources/go-up.png</iconset>
</property>
<property name="iconSize">
@@ -321,7 +313,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/MO/gui/resources/go-down.png</normaloff>:/MO/gui/resources/go-down.png</iconset>
</property>
<property name="iconSize">
@@ -524,9 +516,9 @@ p, li { white-space: pre-wrap; }
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<widget class="QLabel" name="label_9">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Primary Category</string>
</property>
@@ -541,7 +533,7 @@ p, li { white-space: pre-wrap; }
</widget>
<widget class="QWidget" name="tabNexus_2">
<attribute name="icon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/MO/gui/resources/internet-web-browser.png</normaloff>:/MO/gui/resources/internet-web-browser.png</iconset>
</attribute>
<attribute name="title">
@@ -633,7 +625,7 @@ p, li { white-space: pre-wrap; }
<string/>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/MO/gui/refresh</normaloff>:/MO/gui/refresh</iconset>
</property>
</widget>
@@ -695,7 +687,7 @@ p, li { white-space: pre-wrap; }
<string>Endorse</string>
</property>
<property name="icon">
<iconset resource="resources.qrc">
<iconset>
<normaloff>:/MO/gui/icon_favorite</normaloff>:/MO/gui/icon_favorite</iconset>
</property>
</widget>
@@ -790,8 +782,6 @@ p, li { white-space: pre-wrap; }
</item>
</layout>
</widget>
<resources>
<include location="resources.qrc"/>
</resources>
<resources/>
<connections/>
</ui>
+109 -13
View File
@@ -169,10 +169,16 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
int category = modInfo->getPrimaryCategory();
if (category != -1) {
CategoryFactory &categoryFactory = CategoryFactory::instance();
try {
return categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(category));
} catch (const std::exception &e) {
qCritical("failed to retrieve category name: %s", e.what());
if (categoryFactory.categoryExists(category)) {
try {
return categoryFactory.getCategoryName(categoryFactory.getCategoryIndex(category));
} catch (const std::exception &e) {
qCritical("failed to retrieve category name: %s", e.what());
return QString();
}
} else {
qWarning("category %d doesn't exist (may have been removed)", category);
modInfo->setCategory(category, false);
return QString();
}
} else {
@@ -180,7 +186,12 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return QVariant();
}
} else if (column == COL_INSTALLTIME) {
return modInfo->creationTime();
// display installation time for mods that can be updated
if (modInfo->canBeUpdated()) {
return modInfo->creationTime();
} else {
return QVariant();
}
} else {
return tr("invalid");
}
@@ -373,9 +384,10 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
}
return true;
} else if (role == Qt::EditRole) {
bool res = false;
switch (index.column()) {
case COL_NAME: {
return renameMod(modID, value.toString());
res = renameMod(modID, value.toString());
} break;
case COL_PRIORITY: {
bool ok = false;
@@ -384,9 +396,9 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
m_Profile->setModPriority(modID, newPriority);
emit modlist_changed(index, role);
return true;
res = true;
} else {
return false;
res = false;
}
} break;
case COL_MODID: {
@@ -396,9 +408,9 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
if (ok) {
info->setNexusID(newID);
emit modlist_changed(index, role);
return true;
res = true;
} else {
return false;
res = false;
}
} break;
case COL_VERSION: {
@@ -407,17 +419,21 @@ bool ModList::setData(const QModelIndex &index, const QVariant &value, int role)
VersionInfo version(value.toString(), scheme);
if (version.isValid()) {
info->setVersion(version);
return true;
res = true;
} else {
return false;
res = false;
}
} break;
default: {
qWarning("edit on column \"%s\" not supported",
getColumnName(index.column()).toUtf8().constData());
return false;
res = false;
} break;
}
if (res) {
emit dataChanged(index, index);
}
return res;
} else {
return false;
}
@@ -540,6 +556,86 @@ void ModList::changeModPriority(int sourceIndex, int newPriority)
emit modorder_changed();
}
void ModList::modInfoAboutToChange(ModInfo::Ptr info)
{
m_ChangeInfo.name = info->name();
m_ChangeInfo.state = state(info->name());
}
void ModList::modInfoChanged(ModInfo::Ptr info)
{
if (info->name() == m_ChangeInfo.name) {
IModList::ModStates newState = state(info->name());
if (m_ChangeInfo.state != newState) {
m_ModStateChanged(info->name(), newState);
}
int row = ModInfo::getIndex(info->name());
info->testValid();
emit dataChanged(index(row, 0), index(row, columnCount()));
} else {
qCritical("modInfoChanged not called after modInfoAboutToChange");
}
}
IModList::ModStates ModList::state(const QString &name) const
{
ModStates result;
unsigned int modIndex = ModInfo::getIndex(name);
if (modIndex != UINT_MAX) {
result |= IModList::STATE_EXISTS;
ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex);
if (modInfo->isEmpty()) {
result |= IModList::STATE_EMPTY;
}
if (modInfo->endorsedState() == ModInfo::ENDORSED_TRUE) {
result |= IModList::STATE_ENDORSED;
}
if (modInfo->isValid()) {
result |= IModList::STATE_VALID;
}
if (modInfo->canBeEnabled()) {
if (m_Profile->modEnabled(modIndex)) {
result |= IModList::STATE_ACTIVE;
}
} else {
result |= IModList::STATE_ESSENTIAL;
}
}
return result;
}
int ModList::priority(const QString &name) const
{
unsigned int modIndex = ModInfo::getIndex(name);
if (modIndex == UINT_MAX) {
return -1;
} else {
return m_Profile->getModPriority(modIndex);
}
}
bool ModList::setPriority(const QString &name, int newPriority)
{
if ((newPriority < 0) || (newPriority >= static_cast<int>(m_Profile->numRegularMods()))) {
return false;
}
unsigned int modIndex = ModInfo::getIndex(name);
if (modIndex == UINT_MAX) {
return false;
} else {
m_Profile->setModPriority(modIndex, newPriority);
notifyChange(modIndex);
return true;
}
}
bool ModList::onModStateChanged(const std::function<void (const QString &, IModList::ModStates)> &func)
{
auto conn = m_ModStateChanged.connect(func);
return conn.connected();
}
bool ModList::dropURLs(const QMimeData *mimeData, int row, const QModelIndex &parent)
{
+36 -2
View File
@@ -26,11 +26,13 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "modinfo.h"
#include "profile.h"
#include <imodlist.h>
#include <QFile>
#include <QListWidget>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <boost/signals2.hpp>
#include <set>
#include <vector>
#include <QVector>
@@ -41,7 +43,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
* This is used in a view in the main window of MO. It combines general information about
* the mods from ModInfo with status information from the Profile
**/
class ModList : public QAbstractItemModel
class ModList : public QAbstractItemModel, public MOBase::IModList
{
Q_OBJECT
@@ -59,6 +61,8 @@ public:
COL_LASTCOLUMN = COL_PRIORITY
};
typedef boost::signals2::signal<void (const QString &, ModStates)> SignalModStateChanged;
public:
/**
@@ -92,6 +96,23 @@ public:
void changeModPriority(int sourceIndex, int newPriority);
void modInfoAboutToChange(ModInfo::Ptr info);
void modInfoChanged(ModInfo::Ptr info);
public:
/// \copydoc MOBase::IModList::state
virtual ModStates state(const QString &name) const;
/// \copydoc MOBase::IModList::priority
virtual int priority(const QString &name) const;
/// \copydoc MOBase::IModList::setPriority
virtual bool setPriority(const QString &name, int newPriority);
/// \copydoc MOBase::IModList::onModStateChanged
virtual bool onModStateChanged(const std::function<void (const QString &, ModStates)> &func);
public: // implementation of virtual functions of QAbstractItemModel
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
@@ -232,6 +253,11 @@ private:
unsigned int categoryOrder;
};
struct TModInfoChange {
QString name;
QFlags<IModList::ModStates> state;
};
private:
Profile *m_Profile;
@@ -245,6 +271,14 @@ private:
bool m_DropOnItems;
TModInfoChange m_ChangeInfo;
SignalModStateChanged m_ModStateChanged;
// QAbstractItemModel interface
// IModList interface
};
#endif // MODLIST_H
+38 -13
View File
@@ -137,10 +137,30 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
ModInfo::Ptr rightMod = ModInfo::getByIndex(rightIndex);
bool lt = false;
{
QModelIndex leftPrioIdx = left.sibling(left.row(), ModList::COL_PRIORITY);
QVariant leftPrio = leftPrioIdx.data();
if (!leftPrio.isValid()) leftPrio = left.data(Qt::UserRole);
QModelIndex rightPrioIdx = right.sibling(right.row(), ModList::COL_PRIORITY);
QVariant rightPrio = rightPrioIdx.data();
if (!rightPrio.isValid()) rightPrio = right.data(Qt::UserRole);
lt = leftPrio.toInt() < rightPrio.toInt();
}
switch (left.column()) {
case ModList::COL_FLAGS: lt = leftMod->getFlags().size() < rightMod->getFlags().size(); break;
case ModList::COL_NAME: lt = QString::compare(leftMod->name(), rightMod->name(), Qt::CaseInsensitive) < 0; break;
case ModList::COL_FLAGS: {
if (leftMod->getFlags().size() != rightMod->getFlags().size())
lt = leftMod->getFlags().size() < rightMod->getFlags().size();
} break;
case ModList::COL_NAME: {
int comp = QString::compare(leftMod->name(), rightMod->name(), Qt::CaseInsensitive);
if (comp != 0)
lt = comp < 0;
} break;
case ModList::COL_CATEGORY: {
if (leftMod->getPrimaryCategory() != rightMod->getPrimaryCategory()) {
if (leftMod->getPrimaryCategory() < 0) lt = false;
else if (rightMod->getPrimaryCategory() < 0) lt = true;
else {
@@ -153,19 +173,24 @@ bool ModListSortProxy::lessThan(const QModelIndex &left,
qCritical("failed to compare categories: %s", e.what());
}
}
} break;
case ModList::COL_MODID: lt = leftMod->getNexusID() < rightMod->getNexusID(); break;
case ModList::COL_VERSION: lt = leftMod->getVersion() < rightMod->getVersion(); break;
case ModList::COL_PRIORITY: {
QVariant leftPrio = left.data();
if (!leftPrio.isValid()) leftPrio = left.data(Qt::UserRole);
QVariant rightPrio = right.data();
if (!rightPrio.isValid()) rightPrio = right.data(Qt::UserRole);
return leftPrio.toInt() < rightPrio.toInt();
}
} break;
case ModList::COL_MODID: {
if (leftMod->getNexusID() != rightMod->getNexusID())
lt = leftMod->getNexusID() < rightMod->getNexusID();
} break;
case ModList::COL_VERSION: {
if (leftMod->getVersion() != rightMod->getVersion())
lt = leftMod->getVersion() < rightMod->getVersion();
} break;
case ModList::COL_INSTALLTIME: {
return left.data().toDateTime() < right.data().toDateTime();
QDateTime leftTime = left.data().toDateTime();
QDateTime rightTime = right.data().toDateTime();
if (leftTime != rightTime)
return leftTime < rightTime;
} break;
case ModList::COL_PRIORITY: {
// nop, already compared by priority
} break;
}
return lt;
+13 -2
View File
@@ -22,8 +22,10 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "utility.h"
#include "json.h"
#include "selectiondialog.h"
#include <QApplication>
#include <utility.h>
#include <regex>
#include <util.h>
using QtJson::Json;
@@ -148,6 +150,13 @@ NexusInterface::NexusInterface()
m_DiskCache = new QNetworkDiskCache(this);
connect(m_AccessManager, SIGNAL(requestNXMDownload(QString)), this, SLOT(downloadRequestedNXM(QString)));
VS_FIXEDFILEINFO version = GetFileVersion(ToWString(QApplication::applicationFilePath()));
m_MOVersion = VersionInfo(version.dwFileVersionMS >> 16,
version.dwFileVersionMS & 0xFFFF,
version.dwFileVersionLS >> 16);
}
@@ -396,8 +405,10 @@ void NexusInterface::nextRequest()
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/xml");
#pragma message("automatically insert the correct version number")
request.setRawHeader("User-Agent", QString("Mod Organizer v1.0.5 (compatible to Nexus Client v%1)").arg(m_NMMVersion).toUtf8());
request.setRawHeader("User-Agent",
QString("Mod Organizer v%1 (compatible to Nexus Client v%2)")
.arg(m_MOVersion.displayString())
.arg(m_NMMVersion).toUtf8());
info.m_Reply = m_AccessManager->get(request);
+2 -1
View File
@@ -23,7 +23,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "nxmaccessmanager.h"
#include "utility.h"
#include <utility.h>
#include <gameinfo.h>
#include <versioninfo.h>
#include <imodrepositorybridge.h>
@@ -307,6 +307,7 @@ private:
std::list<NXMRequestInfo> m_ActiveRequest;
QQueue<NXMRequestInfo> m_RequestQueue;
MOBase::VersionInfo m_MOVersion;
QString m_NMMVersion;
};

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