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
59 changed files with 8472 additions and 11156 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
+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;
+82 -12
View File
@@ -188,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
@@ -205,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()));
@@ -219,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();
@@ -247,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()));
@@ -486,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"));
}
@@ -681,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();
}
@@ -883,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))) {
@@ -983,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
@@ -1362,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();
@@ -1426,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()) {
@@ -1673,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
@@ -1802,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);
@@ -2093,6 +2110,32 @@ 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;
@@ -2103,6 +2146,11 @@ 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;
@@ -2134,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);
}
}
@@ -2153,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;
}
@@ -2162,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;
@@ -2172,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;
@@ -2979,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();
}
}
@@ -2988,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");
@@ -3013,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)) {
@@ -3184,6 +3245,7 @@ void MainWindow::syncOverwrite()
modInfo->testValid();
refreshDirectoryStructure();
}
}
void MainWindow::createModFromOverwrite()
@@ -3854,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())) {
@@ -4649,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;
@@ -4835,3 +4904,4 @@ void MainWindow::on_showHiddenBox_toggled(bool checked)
{
m_DownloadManager.setShowHidden(checked);
}
+7 -2
View File
@@ -98,7 +98,6 @@ public:
void refreshBSAList();
void refreshDataTree();
void refreshSaveList();
void refreshModList();
void setExecutablesList(const ExecutablesList &executablesList);
@@ -135,10 +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;
@@ -273,6 +276,7 @@ private:
private:
static const unsigned int PROBLEM_PLUGINSNOTLOADED = 1;
static const unsigned int PROBLEM_TOOMANYPLUGINS = 2;
private:
@@ -510,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);
@@ -531,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;
};
+9 -1
View File
@@ -560,7 +560,6 @@ void ModInfoRegular::addNexusCategory(int categoryID)
m_Categories.insert(CategoryFactory::instance().resolveNexusID(categoryID));
}
void ModInfoRegular::setIsEndorsed(bool endorsed)
{
if (m_EndorsedState != ENDORSED_NEVER) {
@@ -828,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()));
+1
View File
@@ -866,6 +866,7 @@ public:
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>
+96 -5
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");
}
@@ -545,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
+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;
};
-4652
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -213,7 +213,7 @@ TRANSLATIONS = organizer_de.ts \
organizer_zh_CN.ts \
organizer_cs.ts \
organizer_tr.ts \
# organizer.en.ts \
organizer_en.ts \
organizer_ru.ts
!isEmpty(TRANSLATIONS) {

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