From bc9f286bce224743d244e540d55f26b55affbd4a Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 12 Jul 2019 08:30:40 -0400 Subject: moved the log to a dock widget added a menu item in the view menu for it --- src/mainwindow.ui | 69 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 17 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 70d1cf39..d83c68ca 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1286,23 +1286,6 @@ p, li { white-space: pre-wrap; } - - - Qt::ActionsContextMenu - - - QAbstractItemView::NoSelection - - - true - - - false - - - true - - @@ -1403,6 +1386,7 @@ p, li { white-space: pre-wrap; } + @@ -1417,6 +1401,49 @@ p, li { white-space: pre-wrap; } + + + Log + + + 8 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::NoSelection + + + true + + + false + + + true + + + + + + @@ -1736,6 +1763,14 @@ p, li { white-space: pre-wrap; } Status &bar + + + true + + + Log + + -- cgit v1.3.1 From 54d98e291701f2187174a67c186f1ea762c6b959 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 16 Jul 2019 05:38:32 -0400 Subject: removed unused or redundant stuff in error_report.h renamed log() to vlog() for now extracted console creation to Console class rewrote LogBuffer to work with logging from uibase, renamed to LogModel added fmt dependency --- CMakeLists.txt | 8 +- src/CMakeLists.txt | 6 +- src/logbuffer.cpp | 263 +++++++++++++----------------------------- src/logbuffer.h | 70 +++-------- src/main.cpp | 116 ++++++++++++++++--- src/mainwindow.cpp | 35 ++++-- src/mainwindow.h | 1 + src/mainwindow.ui | 15 ++- src/organizercore.cpp | 1 - src/profile.cpp | 5 +- src/shared/directoryentry.cpp | 12 +- src/shared/error_report.cpp | 45 -------- src/shared/error_report.h | 21 +--- 13 files changed, 254 insertions(+), 344 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/CMakeLists.txt b/CMakeLists.txt index 94c76373..ac9d8fc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) -ADD_COMPILE_OPTIONS($<$:/MP> $<$:$<$:/O2>> $<$:$<$:/O2>>) +ADD_COMPILE_OPTIONS( + $<$:/MP> + $<$:/D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING> + $<$:$<$:/O2>> + $<$:$<$:/O2>>) PROJECT(organizer) @@ -11,9 +15,11 @@ set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) set(CMAKE_INSTALL_MESSAGE NEVER) SET(DEPENDENCIES_DIR CACHE PATH "") + # hint to find qt in dependencies path LIST(APPEND CMAKE_PREFIX_PATH ${QT_ROOT}/lib/cmake) LIST(APPEND CMAKE_PREFIX_PATH ${LZ4_ROOT}/dll) +LIST(APPEND CMAKE_PREFIX_PATH ${FMT_ROOT}/build) ADD_SUBDIRECTORY(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f197211a..a359b8a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -520,6 +520,9 @@ LINK_DIRECTORIES(${Boost_LIBRARY_DIRS}) FIND_PACKAGE(zlib REQUIRED) # TODO FindZlib doesn't find the static zlib library +# fmt +find_package(fmt REQUIRED) + INCLUDE_DIRECTORIES(${project_path}/uibase/src ${project_path}/bsatk/src ${project_path}/esptk/src @@ -551,10 +554,11 @@ ELSE() ENDIF() ADD_EXECUTABLE(ModOrganizer WIN32 ${organizer_HDRS} ${organizer_SRCS} ${organizer_UIS} ${organizer_RCS} ${organizer_QRCS} ${organizer_translations_qm}) + TARGET_LINK_LIBRARIES(ModOrganizer Qt5::Widgets Qt5::WinExtras Qt5::WebEngineWidgets Qt5::Quick Qt5::Qml Qt5::QuickWidgets Qt5::Network Qt5::WebSockets - ${Boost_LIBRARIES} + ${Boost_LIBRARIES} fmt::fmt zlibstatic uibase esptk bsatk githubpp ${usvfs_name} diff --git a/src/logbuffer.cpp b/src/logbuffer.cpp index dfe8f943..9e3cd712 100644 --- a/src/logbuffer.cpp +++ b/src/logbuffer.cpp @@ -20,239 +20,140 @@ along with Mod Organizer. If not, see . #include "logbuffer.h" #include #include +#include #include #include #include #include #include -using MOBase::reportError; +using namespace MOBase; -QScopedPointer LogBuffer::s_Instance; -QMutex LogBuffer::s_Mutex; +static LogModel* g_instance = nullptr; +const std::size_t MaxLines = 1000; -LogBuffer::LogBuffer(int messageCount, QtMsgType minMsgType, - const QString &outputFileName) - : QAbstractItemModel(nullptr) - , m_OutFileName(outputFileName) - , m_ShutDown(false) - , m_MinMsgType(minMsgType) - , m_NumMessages(0) +LogModel::LogModel() { - m_Messages.resize(messageCount); + connect(this, &LogModel::entryAdded, [&](auto&& e){ onEntryAdded(e); }); } -LogBuffer::~LogBuffer() +void LogModel::create() { - qInstallMessageHandler(0); - write(); + g_instance = new LogModel; } -void LogBuffer::logMessage(QtMsgType type, const QString &message) +LogModel& LogModel::instance() { - if (type >= m_MinMsgType) { - QStringList messagelist = message.split("\n"); - for (auto split_message : messagelist) { - Message msg = {type, QTime::currentTime(), split_message}; - if (m_NumMessages < m_Messages.size()) { - beginInsertRows(QModelIndex(), static_cast(m_NumMessages), - static_cast(m_NumMessages) + 1); - } - m_Messages.at(m_NumMessages % m_Messages.size()) = msg; - if (m_NumMessages < m_Messages.size()) { - endInsertRows(); - } else { - emit dataChanged(createIndex(0, 0), - createIndex(static_cast(m_Messages.size()), 0)); - } - ++m_NumMessages; - if (type >= QtCriticalMsg) { - write(); - } - } - } -} - -void LogBuffer::write() const -{ - if (m_NumMessages == 0) { - return; - } - - DWORD lastError = ::GetLastError(); - - QFile file(m_OutFileName); - if (!file.open(QIODevice::WriteOnly)) { - reportError(tr("failed to write log to %1: %2") - .arg(m_OutFileName) - .arg(file.errorString())); - return; - } - - unsigned int i - = (m_NumMessages > m_Messages.size()) - ? static_cast(m_NumMessages - m_Messages.size()) - : 0U; - for (; i < m_NumMessages; ++i) { - file.write(m_Messages.at(i % m_Messages.size()).toString().toUtf8()); - file.write("\r\n"); - } - ::SetLastError(lastError); + return *g_instance; } -void LogBuffer::init(int messageCount, QtMsgType minMsgType, - const QString &outputFileName) +void LogModel::add(MOBase::log::Entry e) { - QMutexLocker guard(&s_Mutex); - - s_Instance.reset(new LogBuffer(messageCount, minMsgType, outputFileName)); - qInstallMessageHandler(LogBuffer::log); + emit entryAdded(std::move(e)); } -char LogBuffer::msgTypeID(QtMsgType type) +void LogModel::onEntryAdded(MOBase::log::Entry e) { - switch (type) { - case QtDebugMsg: - return 'D'; - case QtInfoMsg: - return 'I'; - case QtWarningMsg: - return 'W'; - case QtCriticalMsg: - return 'C'; - case QtFatalMsg: - return 'F'; - default: - return '?'; + bool full = false; + if (m_messages.size() > MaxLines) { + m_messages.pop_front(); + full = true; } -} -void LogBuffer::log(QtMsgType type, const QMessageLogContext &context, - const QString &message) -{ - // QMutexLocker doesn't support timeout... - if (!s_Mutex.tryLock(100)) { - fprintf(stderr, "failed to log: %s", qUtf8Printable(message)); - return; - } - ON_BLOCK_EXIT([]() { s_Mutex.unlock(); }); + const int row = static_cast(m_messages.size()); - if (!s_Instance.isNull()) { - s_Instance->logMessage(type, message); + if (!full) { + beginInsertRows(QModelIndex(), row, row + 1); } - if (type == QtDebugMsg) { - fprintf(stdout, "%s [%c] %s\n", qUtf8Printable(QTime::currentTime().toString()), - msgTypeID(type), qUtf8Printable(message)); + m_messages.emplace_back(std::move(e)); + + if (!full) { + endInsertRows(); } else { - if (context.line != 0) { - fprintf(stdout, "%s [%c] (%s:%u) %s\n", - qUtf8Printable(QTime::currentTime().toString()), msgTypeID(type), - context.file, context.line, qUtf8Printable(message)); - } else { - fprintf(stdout, "%s [%c] %s\n", - qUtf8Printable(QTime::currentTime().toString()), msgTypeID(type), - qUtf8Printable(message)); - } + emit dataChanged( + createIndex(row, 0), + createIndex(row + 1, columnCount({}))); } - fflush(stdout); } -QModelIndex LogBuffer::index(int row, int column, const QModelIndex &) const +QModelIndex LogModel::index(int row, int column, const QModelIndex&) const { return createIndex(row, column, row); } -QModelIndex LogBuffer::parent(const QModelIndex &) const +QModelIndex LogModel::parent(const QModelIndex&) const { return QModelIndex(); } -int LogBuffer::rowCount(const QModelIndex &parent) const +int LogModel::rowCount(const QModelIndex& parent) const { if (parent.isValid()) return 0; else - return static_cast(std::min(m_NumMessages, m_Messages.size())); + return static_cast(m_messages.size()); } -int LogBuffer::columnCount(const QModelIndex &) const +int LogModel::columnCount(const QModelIndex&) const { - return 2; + return 3; } -QVariant LogBuffer::data(const QModelIndex &index, int role) const +QVariant LogModel::data(const QModelIndex& index, int role) const { - unsigned int offset - = m_NumMessages < m_Messages.size() - ? 0 - : static_cast(m_NumMessages - m_Messages.size()); - unsigned int msgIndex = (offset + index.row() + 1) % m_Messages.size(); - switch (role) { - case Qt::DisplayRole: { - if (index.column() == 0) { - return m_Messages[msgIndex].time.toString("H: mm: ss"); - } else if (index.column() == 1) { - const QString &msg = m_Messages[msgIndex].message; - if (msg.length() < 200) { - return msg; - } else { - return msg.mid(0, 200) + "..."; - } - } - } break; - case Qt::DecorationRole: { - if (index.column() == 1) { - switch (m_Messages[msgIndex].type) { - case QtDebugMsg: - case QtInfoMsg: - return QIcon(":/MO/gui/information"); - case QtWarningMsg: - return QIcon(":/MO/gui/warning"); - case QtCriticalMsg: - return QIcon(":/MO/gui/important"); - case QtFatalMsg: - return QIcon(":/MO/gui/problem"); - } - } - } break; - case Qt::UserRole: { - if (index.column() == 1) { - switch (m_Messages[msgIndex].type) { - case QtDebugMsg: - return "D"; - case QtInfoMsg: - return "I"; - case QtWarningMsg: - return "W"; - case QtCriticalMsg: - return "C"; - case QtFatalMsg: - return "F"; - } - } - } break; + using namespace std::chrono; + + const auto row = static_cast(index.row()); + if (row >= m_messages.size()) { + return {}; } - return QVariant(); -} -void LogBuffer::writeNow() -{ - QMutexLocker guard(&s_Mutex); - if (!s_Instance.isNull()) { - s_Instance->write(); + const auto& e = m_messages[row]; + + if (role == Qt::DisplayRole) { + if (index.column() == 1) { + const auto ms = duration_cast(e.time.time_since_epoch()); + const auto s = duration_cast(ms); + + const std::time_t t = s.count(); + const std::size_t frac = ms.count() % 1000; + + auto time = QDateTime::fromTime_t(t).time(); + time = time.addMSecs(frac); + + return time.toString("hh:mm:ss.zzz"); + } else if (index.column() == 2) { + return QString::fromStdString(e.message); + } + } + + if (role == Qt::DecorationRole) { + if (index.column() == 0) { + switch (e.level) { + case log::Warning: + return QIcon(":/MO/gui/warning"); + + case log::Error: + return QIcon(":/MO/gui/problem"); + + case log::Debug: // fall-through + case log::Info: + default: + return {}; + } + } } + + return QVariant(); } -void LogBuffer::cleanQuit() +QVariant LogModel::headerData(int, Qt::Orientation, int) const { - QMutexLocker guard(&s_Mutex); - if (!s_Instance.isNull()) { - s_Instance->m_ShutDown = true; - } + return {}; } -void log(const char *format, ...) +void vlog(const char *format, ...) { va_list argList; va_start(argList, format); @@ -268,11 +169,3 @@ void log(const char *format, ...) va_end(argList); } - -QString LogBuffer::Message::toString() const -{ - return QString("%1 [%2] %3") - .arg(time.toString()) - .arg(msgTypeID(type)) - .arg(message); -} diff --git a/src/logbuffer.h b/src/logbuffer.h index 0cfecfa2..1bf8901b 100644 --- a/src/logbuffer.h +++ b/src/logbuffer.h @@ -26,70 +26,36 @@ along with Mod Organizer. If not, see . #include #include #include +#include - -class LogBuffer : public QAbstractItemModel +class LogModel : public QAbstractItemModel { Q_OBJECT public: + static void create(); + static LogModel& instance(); - static void init(int messageCount, QtMsgType minMsgType, const QString &outputFileName); - static void log(QtMsgType type, const QMessageLogContext &context, const QString &message); - - static void writeNow(); - static void cleanQuit(); - - static LogBuffer *instance() { return s_Instance.data(); } - -public: - - virtual ~LogBuffer(); - - void logMessage(QtMsgType type, const QString &message); + void add(MOBase::log::Entry e); - // QAbstractItemModel interface -public: - QModelIndex index(int row, int column, const QModelIndex &parent) const; - QModelIndex parent(const QModelIndex &child) const; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; +protected: + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + QModelIndex parent(const QModelIndex &child) const override; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + + QVariant headerData( + int section, Qt::Orientation ori, int role=Qt::DisplayRole) const override; signals: - -public slots: + void entryAdded(MOBase::log::Entry e); private: + std::deque m_messages; - explicit LogBuffer(int messageCount, QtMsgType minMsgType, const QString &outputFileName); - LogBuffer(const LogBuffer &reference); // not implemented - LogBuffer &operator=(const LogBuffer &reference); // not implemented - - void write() const; - - static char msgTypeID(QtMsgType type); - -private: - - struct Message { - QtMsgType type; - QTime time; - QString message; - QString toString() const; - }; - -private: - - static QScopedPointer s_Instance; - static QMutex s_Mutex; - - QString m_OutFileName; - bool m_ShutDown; - QtMsgType m_MinMsgType; - size_t m_NumMessages; - std::vector m_Messages; - + LogModel(); + void onEntryAdded(MOBase::log::Entry e); }; #endif // LOGBUFFER_H diff --git a/src/main.cpp b/src/main.cpp index db0c8f93..23ea234a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -51,6 +51,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include #include @@ -731,18 +732,46 @@ int runApplication(MOApplication &application, SingleInstance &instance, } } -int doCoreDump(env::CoreDumpTypes type) +class Console { - // open a console - AllocConsole(); +public: + Console() + { + // open a console + AllocConsole(); - // redirect stdin, stdout and stderr to it - FILE* in=nullptr; - FILE* out=nullptr; - FILE* err=nullptr; - freopen_s(&in, "CONIN$", "r", stdin); - freopen_s(&out, "CONOUT$", "w", stdout); - freopen_s(&err, "CONOUT$", "w", stderr); + // redirect stdin, stdout and stderr to it + freopen_s(&m_in, "CONIN$", "r", stdin); + freopen_s(&m_out, "CONOUT$", "w", stdout); + freopen_s(&m_err, "CONOUT$", "w", stderr); + } + + ~Console() + { + // close redirected handles + std::fclose(m_err); + std::fclose(m_out); + std::fclose(m_in); + + // close console + FreeConsole(); + + // redirect stdin, stdout and stderr to NUL, don't bother closing the + // handles + freopen_s(&m_in, "NUL", "r", stdin); + freopen_s(&m_out, "NUL", "w", stdout); + freopen_s(&m_err, "NUL", "w", stderr); + } + +private: + FILE* m_in = nullptr; + FILE* m_out = nullptr; + FILE* m_err = nullptr; +}; + +int doCoreDump(env::CoreDumpTypes type) +{ + Console c; // dump const auto b = env::coredumpOther(type); @@ -753,15 +782,66 @@ int doCoreDump(env::CoreDumpTypes type) std::wcerr << L"Press enter to continue..."; std::wcin.get(); - // close redirected handles - std::fclose(err); - std::fclose(out); - std::fclose(in); + return (b ? 0 : 1); +} + +log::Levels convertQtLevel(QtMsgType t) +{ + switch (t) + { + case QtDebugMsg: + return log::Debug; - // close console - FreeConsole(); + case QtWarningMsg: + return log::Warning; - return (b ? 0 : 1); + case QtCriticalMsg: // fall-through + case QtFatalMsg: + return log::Error; + + case QtInfoMsg: // fall-through + default: + return log::Info; + } +} + +void qtLogCallback( + QtMsgType type, const QMessageLogContext& context, const QString& message) +{ + std::string_view file = ""; + + if (type != QtDebugMsg) { + if (context.file) { + file = context.file; + + const auto lastSep = file.find_last_of("/\\"); + if (lastSep != std::string_view::npos) { + file = {context.file + lastSep + 1}; + } + } + } + + if (file.empty()) { + log::log( + convertQtLevel(type), "{}", + message.toStdString()); + } else { + log::log( + convertQtLevel(type), "[{}:{}] {}", + file, context.line, message.toStdString()); + } +} + +void initLogging(const QString& logFile) +{ + LogModel::create(); + + log::init( + true, MOBase::log::File::rotating(logFile.toStdWString(), 5*1024*1024, 5), + MOBase::log::Debug, "%^[%m-%d %H:%M:%S.%e %L] %v%$", + [](log::Entry e){ LogModel::instance().add(e); }); + + qInstallMessageHandler(qtLogCallback); } int main(int argc, char *argv[]) @@ -839,7 +919,7 @@ int main(int argc, char *argv[]) // initialize dump collection only after "dataPath" since the crashes are stored under it prevUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); - LogBuffer::init(1000000, QtDebugMsg, qApp->property("dataPath").toString() + "/logs/mo_interface.log"); + initLogging(qApp->property("dataPath").toString() + "/logs/mo_interface.log"); QString splash = dataPath + "/splash.png"; if (!QFile::exists(dataPath + "/splash.png")) { diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4b5ef9ed..cd224414 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -357,17 +357,10 @@ MainWindow::MainWindow(QSettings &initSettings m_CategoryFactory.loadCategories(); - ui->logList->setModel(LogBuffer::instance()); - ui->logList->setColumnWidth(0, 100); - ui->logList->setAutoScroll(true); - ui->logList->scrollToBottom(); - ui->logList->addAction(ui->actionCopy_Log_to_Clipboard); + setupLogList(); + int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value ui->topLevelSplitter->setSizes(QList() << splitterSize - 100 << 100); - connect(ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - ui->logList, SLOT(scrollToBottom())); - connect(ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), - ui->logList, SLOT(scrollToBottom())); updateProblemsButton(); @@ -593,6 +586,30 @@ MainWindow::MainWindow(QSettings &initSettings updateModCount(); } +void MainWindow::setupLogList() +{ + ui->logList->setModel(&LogModel::instance()); + + const int timestampWidth = + QFontMetrics(ui->logList->font()).width("00:00:00.000"); + + ui->logList->header()->setMinimumSectionSize(0); + ui->logList->header()->resizeSection(0, 20); + ui->logList->header()->resizeSection(1, timestampWidth + 8); + + ui->logList->setAutoScroll(true); + ui->logList->scrollToBottom(); + ui->logList->addAction(ui->actionCopy_Log_to_Clipboard); + + connect( + ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + ui->logList, SLOT(scrollToBottom())); + + connect( + ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + ui->logList, SLOT(scrollToBottom())); +} + void MainWindow::resetActionIcons() { // this is a bit of a hack diff --git a/src/mainwindow.h b/src/mainwindow.h index d7dbfd90..9ca3e5c3 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -636,6 +636,7 @@ private slots: void resetActionIcons(); void updateModCount(); void updatePluginCount(); + void setupLogList(); private slots: // ui slots // actions diff --git a/src/mainwindow.ui b/src/mainwindow.ui index d83c68ca..5a45b3b4 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1402,6 +1402,9 @@ p, li { white-space: pre-wrap; } + + QDockWidget::AllDockWidgetFeatures + Log @@ -1428,17 +1431,17 @@ p, li { white-space: pre-wrap; } Qt::ActionsContextMenu - QAbstractItemView::NoSelection - - - true + QAbstractItemView::ExtendedSelection - + false - + true + + false + diff --git a/src/organizercore.cpp b/src/organizercore.cpp index eeb69e61..5f5c3afe 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -342,7 +342,6 @@ OrganizerCore::~OrganizerCore() m_CurrentProfile = nullptr; ModInfo::clear(); - LogBuffer::cleanQuit(); m_ModList.setProfile(nullptr); // NexusInterface::instance()->cleanup(); diff --git a/src/profile.cpp b/src/profile.cpp index ef387027..01906903 100644 --- a/src/profile.cpp +++ b/src/profile.cpp @@ -287,8 +287,11 @@ void Profile::createTweakedIniFile() } if (error) { - reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorString().c_str())); + const auto e = ::GetLastError(); + reportError(tr("failed to create tweaked ini: %1") + .arg(formatSystemMessageQ(e))); } + qDebug("%s saved", qUtf8Printable(QDir::toNativeSeparators(tweakedIni))); } diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp index bde515a9..9d9edd85 100644 --- a/src/shared/directoryentry.cpp +++ b/src/shared/directoryentry.cpp @@ -103,7 +103,7 @@ public: m_OriginsNameMap.erase(iter); m_OriginsNameMap[newName] = idx; } else { - log("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str()); + vlog("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str()); } } @@ -714,12 +714,12 @@ void DirectoryEntry::removeFile(FileEntry::Index index) if (iter != m_Files.end()) { m_Files.erase(iter); } else { - log("file \"%ls\" not in directory \"%ls\"", + vlog("file \"%ls\" not in directory \"%ls\"", m_FileRegister->getFile(index)->getName().c_str(), this->getName().c_str()); } } else { - log("file \"%ls\" not in directory \"%ls\", directory empty", + vlog("file \"%ls\" not in directory \"%ls\", directory empty", m_FileRegister->getFile(index)->getName().c_str(), this->getName().c_str()); } @@ -844,7 +844,7 @@ const FileEntry::Ptr DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry *temp = findSubDirectory(pathComponent); if (temp != nullptr) { if (len >= path.size()) { - log("unexpected end of path"); + vlog("unexpected end of path"); return FileEntry::Ptr(); } return temp->searchFile(path.substr(len + 1), directory); @@ -988,7 +988,7 @@ bool FileRegister::removeFile(FileEntry::Index index) m_Files.erase(index); return true; } else { - log("invalid file index for remove: %lu", index); + vlog("invalid file index for remove: %lu", index); return false; } } @@ -1002,7 +1002,7 @@ void FileRegister::removeOrigin(FileEntry::Index index, int originID) m_Files.erase(iter); } } else { - log("invalid file index for remove (for origin): %lu", index); + vlog("invalid file index for remove (for origin): %lu", index); } } diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp index 6d091630..4185b544 100644 --- a/src/shared/error_report.cpp +++ b/src/shared/error_report.cpp @@ -23,7 +23,6 @@ along with Mod Organizer. If not, see . namespace MOShared { - void reportError(LPCSTR format, ...) { char buffer[1025]; @@ -52,48 +51,4 @@ void reportError(LPCWSTR format, ...) MessageBoxW(nullptr, buffer, L"Error", MB_OK | MB_ICONERROR); } - -std::string getCurrentErrorStringA() -{ - LPSTR buffer = nullptr; - - DWORD errorCode = ::GetLastError(); - - if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, nullptr) == 0) { - ::SetLastError(errorCode); - return std::string(); - } else { - LPSTR lastChar = buffer + strlen(buffer) - 2; - *lastChar = '\0'; - - std::string result(buffer); - - LocalFree(buffer); - ::SetLastError(errorCode); - return result; - } -} - -std::wstring getCurrentErrorStringW() -{ - LPWSTR buffer = nullptr; - - DWORD errorCode = ::GetLastError(); - - if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, nullptr) == 0) { - ::SetLastError(errorCode); - return std::wstring(); - } else { - LPWSTR lastChar = buffer + wcslen(buffer) - 2; - *lastChar = '\0'; - - std::wstring result(buffer); - - LocalFree(buffer); - ::SetLastError(errorCode); - return result; - } -} } // namespace MOShared diff --git a/src/shared/error_report.h b/src/shared/error_report.h index c09ad75b..a003ee09 100644 --- a/src/shared/error_report.h +++ b/src/shared/error_report.h @@ -24,28 +24,11 @@ along with Mod Organizer. If not, see . #include #include -namespace std { -#ifdef UNICODE -typedef wstring tstring; -#else -typedef string tstring; -#endif -} - -extern void log(const char* format, ...); - namespace MOShared { void reportError(LPCSTR format, ...); void reportError(LPCWSTR format, ...); -std::string getCurrentErrorStringA(); -std::wstring getCurrentErrorStringW(); - -#ifdef UNICODE -#define getCurrentErrorString getCurrentErrorStringW -#else -#define getCurrentErrorString getCurrentErrorStringA -#endif - } // namespace MOShared + +void vlog(const char* format, ...); -- cgit v1.3.1 From d1b4dec8ad1635738ada3dfbde5907e7f0df3448 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Tue, 16 Jul 2019 05:58:51 -0400 Subject: moved setup to new LogList class --- src/loglist.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++++++------- src/loglist.h | 14 +++++++++++-- src/mainwindow.cpp | 35 ++----------------------------- src/mainwindow.h | 1 - src/mainwindow.ui | 10 +++++---- 5 files changed, 74 insertions(+), 47 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/loglist.cpp b/src/loglist.cpp index cb927272..9e876d37 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -52,21 +52,26 @@ void LogModel::add(MOBase::log::Entry e) emit entryAdded(std::move(e)); } +const std::deque& LogModel::entries() const +{ + return m_entries; +} + void LogModel::onEntryAdded(MOBase::log::Entry e) { bool full = false; - if (m_messages.size() > MaxLines) { - m_messages.pop_front(); + if (m_entries.size() > MaxLines) { + m_entries.pop_front(); full = true; } - const int row = static_cast(m_messages.size()); + const int row = static_cast(m_entries.size()); if (!full) { beginInsertRows(QModelIndex(), row, row + 1); } - m_messages.emplace_back(std::move(e)); + m_entries.emplace_back(std::move(e)); if (!full) { endInsertRows(); @@ -92,7 +97,7 @@ int LogModel::rowCount(const QModelIndex& parent) const if (parent.isValid()) return 0; else - return static_cast(m_messages.size()); + return static_cast(m_entries.size()); } int LogModel::columnCount(const QModelIndex&) const @@ -105,11 +110,11 @@ QVariant LogModel::data(const QModelIndex& index, int role) const using namespace std::chrono; const auto row = static_cast(index.row()); - if (row >= m_messages.size()) { + if (row >= m_entries.size()) { return {}; } - const auto& e = m_messages[row]; + const auto& e = m_entries[row]; if (role == Qt::DisplayRole) { if (index.column() == 1) { @@ -153,6 +158,48 @@ QVariant LogModel::headerData(int, Qt::Orientation, int) const return {}; } + +LogList::LogList(QWidget* parent) + : QTreeView(parent) +{ + setModel(&LogModel::instance()); + + const int timestampWidth = QFontMetrics(font()).width("00:00:00.000"); + + header()->setMinimumSectionSize(0); + header()->resizeSection(0, 20); + header()->resizeSection(1, timestampWidth + 8); + + setAutoScroll(true); + scrollToBottom(); + + connect( + model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), + this, SLOT(scrollToBottom())); + + connect( + model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(scrollToBottom())); +} + +void LogList::copyToClipboard() +{ + std::string s; + + auto* m = static_cast(model()); + for (const auto& e : m->entries()) { + s += e.formattedMessage + "\n"; + } + + if (!s.empty()) { + // last newline + s.pop_back(); + } + + QApplication::clipboard()->setText(QString::fromStdString(s)); +} + + void vlog(const char *format, ...) { va_list argList; diff --git a/src/loglist.h b/src/loglist.h index 1bf8901b..d1f7a2ad 100644 --- a/src/loglist.h +++ b/src/loglist.h @@ -37,6 +37,7 @@ public: static LogModel& instance(); void add(MOBase::log::Entry e); + const std::deque& entries() const; protected: QModelIndex index(int row, int column, const QModelIndex& parent) const override; @@ -44,7 +45,7 @@ protected: int rowCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override; QVariant data(const QModelIndex &index, int role) const override; - + QVariant headerData( int section, Qt::Orientation ori, int role=Qt::DisplayRole) const override; @@ -52,10 +53,19 @@ signals: void entryAdded(MOBase::log::Entry e); private: - std::deque m_messages; + std::deque m_entries; LogModel(); void onEntryAdded(MOBase::log::Entry e); }; + +class LogList : public QTreeView +{ +public: + LogList(QWidget* parent=nullptr); + + void copyToClipboard(); +}; + #endif // LOGBUFFER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 107f3e09..4e91ef9f 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -357,7 +357,7 @@ MainWindow::MainWindow(QSettings &initSettings m_CategoryFactory.loadCategories(); - setupLogList(); + ui->logList->addAction(ui->actionCopy_Log_to_Clipboard); int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value ui->topLevelSplitter->setSizes(QList() << splitterSize - 100 << 100); @@ -586,30 +586,6 @@ MainWindow::MainWindow(QSettings &initSettings updateModCount(); } -void MainWindow::setupLogList() -{ - ui->logList->setModel(&LogModel::instance()); - - const int timestampWidth = - QFontMetrics(ui->logList->font()).width("00:00:00.000"); - - ui->logList->header()->setMinimumSectionSize(0); - ui->logList->header()->resizeSection(0, 20); - ui->logList->header()->resizeSection(1, timestampWidth + 8); - - ui->logList->setAutoScroll(true); - ui->logList->scrollToBottom(); - ui->logList->addAction(ui->actionCopy_Log_to_Clipboard); - - connect( - ui->logList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), - ui->logList, SLOT(scrollToBottom())); - - connect( - ui->logList->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), - ui->logList, SLOT(scrollToBottom())); -} - void MainWindow::resetActionIcons() { // this is a bit of a hack @@ -6837,14 +6813,7 @@ void MainWindow::on_restoreModsButton_clicked() void MainWindow::on_actionCopy_Log_to_Clipboard_triggered() { - QStringList lines; - QAbstractItemModel *model = ui->logList->model(); - for (int i = 0; i < model->rowCount(); ++i) { - lines.append(QString("%1 [%2] %3").arg(model->index(i, 0).data().toString()) - .arg(model->index(i, 1).data(Qt::UserRole).toString()) - .arg(model->index(i, 1).data().toString())); - } - QApplication::clipboard()->setText(lines.join("\n")); + ui->logList->copyToClipboard(); } void MainWindow::on_categoriesAndBtn_toggled(bool checked) diff --git a/src/mainwindow.h b/src/mainwindow.h index 9ca3e5c3..d7dbfd90 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -636,7 +636,6 @@ private slots: void resetActionIcons(); void updateModCount(); void updatePluginCount(); - void setupLogList(); private slots: // ui slots // actions diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 5a45b3b4..c694abd4 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1426,13 +1426,10 @@ p, li { white-space: pre-wrap; } 0 - + Qt::ActionsContextMenu - - QAbstractItemView::ExtendedSelection - false @@ -1807,6 +1804,11 @@ p, li { white-space: pre-wrap; } QWidget
sortabletreewidget.h
+ + LogList + QTreeView +
loglist.h
+
-- cgit v1.3.1 From bca6283311cf1dea4c96f8ee5bf192bdb1640cb3 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 17 Jul 2019 08:56:16 -0400 Subject: use log::Levels instead of ints create log level combobox in code, set selected index based on value instead added log level to context menu in log list --- src/mainwindow.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++--- src/mainwindow.h | 12 +++++++++--- src/mainwindow.ui | 30 +++++++++++++++--------------- src/organizercore.cpp | 4 +++- src/organizercore.h | 5 ++++- src/settings.cpp | 30 ++++++++++++++++++++++++++---- src/settings.h | 10 +++++++++- src/settingsdialog.ui | 20 -------------------- src/usvfsconnector.cpp | 20 +++++++++++--------- src/usvfsconnector.h | 7 ++++++- 10 files changed, 127 insertions(+), 58 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4e91ef9f..f65bf4e1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -357,7 +357,7 @@ MainWindow::MainWindow(QSettings &initSettings m_CategoryFactory.loadCategories(); - ui->logList->addAction(ui->actionCopy_Log_to_Clipboard); + setupLogMenu(); int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value ui->topLevelSplitter->setSizes(QList() << splitterSize - 100 << 100); @@ -812,6 +812,36 @@ void MainWindow::setupActionMenu(QAction* a) tb->setPopupMode(QToolButton::InstantPopup); } +void MainWindow::setupLogMenu() +{ + connect(ui->logList, &QWidget::customContextMenuRequested, [&](auto&& pos){ + auto* menu = new QMenu(ui->logList); + + menu->addAction(tr("Copy& Log"), [&]{ ui->logList->copyToClipboard(); }); + menu->addSeparator(); + + auto* levels = new QMenu(tr("&Level")); + menu->addMenu(levels); + + auto* ag = new QActionGroup(menu); + + auto addAction = [&](auto&& text, auto&& level) { + auto* a = new QAction(text, ag); + a->setCheckable(true); + a->setChecked(log::getDefault().level() == level); + connect(a, &QAction::triggered, [this, level]{ setLogLevel(level); }); + levels->addAction(a); + }; + + addAction(tr("&Errors"), log::Error); + addAction(tr("&Warnings"), log::Warning); + addAction(tr("&Info"), log::Info); + addAction(tr("&Debug"), log::Debug); + + menu->popup(ui->logList->viewport()->mapToGlobal(pos)); + }); +} + void MainWindow::updatePinnedExecutables() { for (auto* a : ui->toolBar->actions()) { @@ -5287,12 +5317,23 @@ void MainWindow::on_actionSettings_triggered() m_statusBar->checkSettings(m_OrganizerCore.settings()); updateDownloadView(); - m_OrganizerCore.updateVFSParams(settings.logLevel(), settings.crashDumpsType(), settings.executablesBlacklist()); + setLogLevel(settings.logLevel()); m_OrganizerCore.cycleDiagnostics(); toggleMO2EndorseState(); } +void MainWindow::setLogLevel(log::Levels level) +{ + auto& s = m_OrganizerCore.settings(); + + s.setLogLevel(level); + + m_OrganizerCore.updateVFSParams( + s.logLevel(), s.crashDumpsType(), s.executablesBlacklist()); + + log::getDefault().setLevel(s.logLevel()); +} void MainWindow::on_actionNexus_triggered() { @@ -6811,7 +6852,7 @@ void MainWindow::on_restoreModsButton_clicked() } } -void MainWindow::on_actionCopy_Log_to_Clipboard_triggered() +void MainWindow::on_actionLogCopy_triggered() { ui->logList->copyToClipboard(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index d7dbfd90..74993667 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -30,6 +30,9 @@ along with Mod Organizer. If not, see . #include "modlistsortproxy.h" #include "savegameinfo.h" #include "tutorialcontrol.h" +#include "plugincontainer.h" //class PluginContainer; +#include "iplugingame.h" //namespace MOBase { class IPluginGame; } +#include //Note the commented headers here can be replaced with forward references, //when I get round to cleaning up main.cpp @@ -38,10 +41,10 @@ class CategoryFactory; class LockedDialogBase; class OrganizerCore; class StatusBar; -#include "plugincontainer.h" //class PluginContainer; + class PluginListSortProxy; namespace BSA { class Archive; } -#include "iplugingame.h" //namespace MOBase { class IPluginGame; } + namespace MOBase { class IPluginModPage; } namespace MOBase { class IPluginTool; } namespace MOBase { class ISaveGame; } @@ -633,10 +636,13 @@ private slots: void search_activated(); void searchClear_activated(); + void setupLogMenu(); void resetActionIcons(); void updateModCount(); void updatePluginCount(); + void setLogLevel(MOBase::log::Levels level); + private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); @@ -690,7 +696,7 @@ private slots: // ui slots void on_restoreButton_clicked(); void on_restoreModsButton_clicked(); void on_saveModsButton_clicked(); - void on_actionCopy_Log_to_Clipboard_triggered(); + void on_actionLogCopy_triggered(); void on_categoriesAndBtn_toggled(bool checked); void on_categoriesOrBtn_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index c694abd4..fc2bcdd3 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1428,7 +1428,7 @@ p, li { white-space: pre-wrap; } - Qt::ActionsContextMenu + Qt::CustomContextMenu false @@ -1645,20 +1645,6 @@ p, li { white-space: pre-wrap; } Endorse Mod Organizer - - - Copy &Log - - - Copy &Log - - - Copy log to clipboard - - - Copy log to clipboard - - @@ -1771,6 +1757,20 @@ p, li { white-space: pre-wrap; } Log + + + Copy &Log + + + Copy &Log + + + Copy log to clipboard + + + Copy log to clipboard + + diff --git a/src/organizercore.cpp b/src/organizercore.cpp index d3cd54ee..25fbc7cd 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -726,7 +726,9 @@ void OrganizerCore::prepareVFS() m_USVFS.updateMapping(fileMapping(m_CurrentProfile->name(), QString())); } -void OrganizerCore::updateVFSParams(int logLevel, int crashDumpsType, QString executableBlacklist) { +void OrganizerCore::updateVFSParams( + log::Levels logLevel, int crashDumpsType, QString executableBlacklist) +{ setGlobalCrashDumpsType(crashDumpsType); m_USVFS.updateParams(logLevel, crashDumpsType, executableBlacklist); } diff --git a/src/organizercore.h b/src/organizercore.h index 99b1c5f2..ef1a4133 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -21,6 +21,7 @@ #include #include #include "executableinfo.h" +#include class ModListSortProxy; class PluginListSortProxy; @@ -191,7 +192,9 @@ public: void prepareVFS(); - void updateVFSParams(int logLevel, int crashDumpsType, QString executableBlacklist); + void updateVFSParams( + MOBase::log::Levels logLevel, int crashDumpsType, + QString executableBlacklist); bool cycleDiagnostics(); diff --git a/src/settings.cpp b/src/settings.cpp index 5cb2524f..e622d632 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -415,9 +415,14 @@ bool Settings::offlineMode() const return m_Settings.value("Settings/offline_mode", false).toBool(); } -int Settings::logLevel() const +log::Levels Settings::logLevel() const { - return m_Settings.value("Settings/log_level", static_cast(LogLevel::Info)).toInt(); + return static_cast(m_Settings.value("Settings/log_level").toInt()); +} + +void Settings::setLogLevel(log::Levels level) +{ + m_Settings.setValue("Settings/log_level", static_cast(level)); } int Settings::crashDumpsType() const @@ -1000,7 +1005,7 @@ Settings::DiagnosticsTab::DiagnosticsTab(Settings *m_parent, SettingsDialog &m_d , m_dumpsMaxEdit(m_dialog.findChild("dumpsMaxEdit")) , m_diagnosticsExplainedLabel(m_dialog.findChild("diagnosticsExplainedLabel")) { - m_logLevelBox->setCurrentIndex(m_parent->logLevel()); + setLevelsBox(); m_dumpsTypeBox->setCurrentIndex(m_parent->crashDumpsType()); m_dumpsMaxEdit->setValue(m_parent->crashDumpsMax()); QString logsPath = qApp->property("dataPath").toString() @@ -1016,11 +1021,28 @@ Settings::DiagnosticsTab::DiagnosticsTab(Settings *m_parent, SettingsDialog &m_d void Settings::DiagnosticsTab::update() { - m_Settings.setValue("Settings/log_level", m_logLevelBox->currentIndex()); + m_Settings.setValue("Settings/log_level", m_logLevelBox->currentData().toInt()); m_Settings.setValue("Settings/crash_dumps_type", m_dumpsTypeBox->currentIndex()); m_Settings.setValue("Settings/crash_dumps_max", m_dumpsMaxEdit->value()); } +void Settings::DiagnosticsTab::setLevelsBox() +{ + m_logLevelBox->clear(); + + m_logLevelBox->addItem(tr("Debug"), log::Debug); + m_logLevelBox->addItem(tr("Info (recommended)"), log::Info); + m_logLevelBox->addItem(tr("Warning"), log::Warning); + m_logLevelBox->addItem(tr("Error"), log::Error); + + for (int i=0; icount(); ++i) { + if (m_logLevelBox->itemData(i) == m_parent->logLevel()) { + m_logLevelBox->setCurrentIndex(i); + break; + } + } +} + Settings::NexusTab::NexusTab(Settings *parent, SettingsDialog &dialog) : Settings::SettingsTab(parent, dialog) , m_offlineBox(dialog.findChild("offlineBox")) diff --git a/src/settings.h b/src/settings.h index bccd1e81..c66eb94c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #define SETTINGS_H #include "loadmechanism.h" +#include #include #include @@ -232,7 +233,12 @@ public: /** * @return the configured log level */ - int logLevel() const; + MOBase::log::Levels logLevel() const; + + /** + * sets the log level setting + */ + void setLogLevel(MOBase::log::Levels level); /** * @return the configured crash dumps type @@ -481,6 +487,8 @@ private: QComboBox *m_dumpsTypeBox; QSpinBox *m_dumpsMaxEdit; QLabel *m_diagnosticsExplainedLabel; + + void setLevelsBox(); }; /** Display/store the configuration in the 'nexus' tab of the settings dialogue */ diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index fccc8be0..1e94bcde 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -1426,26 +1426,6 @@ programs you are intentionally running. "Debug" produces very useful information for finding problems. There is usually no noteworthy performance impact but the file may become rather large. If this is a problem you may prefer the "Info" level for regular use. On the "Error" level the log file usually remains empty. - - - Debug - - - - - Info (recommended) - - - - - Warning - - - - - Error - - diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index b752667d..197955b8 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -32,7 +32,7 @@ along with Mod Organizer. If not, see . #include static const char SHMID[] = "mod_organizer_instance"; - +using namespace MOBase; std::string to_hex(void *bufferIn, size_t bufferSize) { @@ -90,15 +90,16 @@ void LogWorker::exit() m_QuitRequested = true; } -LogLevel logLevel(int level) +LogLevel toUsvfsLogLevel(log::Levels level) { - switch (static_cast(level)) { - case LogLevel::Info: + switch (level) { + case log::Info: return LogLevel::Info; - case LogLevel::Warning: + case log::Warning: return LogLevel::Warning; - case LogLevel::Error: + case log::Error: return LogLevel::Error; + case log::Debug: // fall-through default: return LogLevel::Debug; } @@ -121,7 +122,7 @@ CrashDumpsType crashDumpsType(int type) UsvfsConnector::UsvfsConnector() { USVFSParameters params; - LogLevel level = logLevel(Settings::instance().logLevel()); + LogLevel level = toUsvfsLogLevel(Settings::instance().logLevel()); CrashDumpsType dumpType = crashDumpsType(Settings::instance().crashDumpsType()); std::string dumpPath = MOShared::ToString(OrganizerCore::crashDumpsPath(), true); @@ -205,9 +206,10 @@ void UsvfsConnector::updateMapping(const MappingType &mapping) */ } -void UsvfsConnector::updateParams(int logLevel, int crashDumpsType, QString executableBlacklist) +void UsvfsConnector::updateParams( + MOBase::log::Levels logLevel, int crashDumpsType, QString executableBlacklist) { - USVFSUpdateParams(::logLevel(logLevel), ::crashDumpsType(crashDumpsType)); + USVFSUpdateParams(toUsvfsLogLevel(logLevel), ::crashDumpsType(crashDumpsType)); ClearExecutableBlacklist(); for (auto exec : executableBlacklist.split(";")) { std::wstring buf = exec.toStdWString(); diff --git a/src/usvfsconnector.h b/src/usvfsconnector.h index 8a88bde5..b0bd320c 100644 --- a/src/usvfsconnector.h +++ b/src/usvfsconnector.h @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include #include #include +#include #include "executableinfo.h" @@ -84,7 +85,11 @@ public: ~UsvfsConnector(); void updateMapping(const MappingType &mapping); - void updateParams(int logLevel, int crashDumpsType, QString executableBlacklist); + + void updateParams( + MOBase::log::Levels logLevel, int crashDumpsType, + QString executableBlacklist); + void updateForcedLibraries(const QList &forcedLibraries); private: -- cgit v1.3.1 From 3c7712a32dd5079a9543485b6a85d548460faefd Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 20 Jul 2019 05:34:31 -0400 Subject: moved setLogLevel() to OrganizerCore moved context menu to LogList --- src/loglist.cpp | 61 +++++++++++++++++++++++++++++++++++++++++++-------- src/loglist.h | 16 +++++++++----- src/mainwindow.cpp | 57 ++--------------------------------------------- src/mainwindow.h | 4 ---- src/mainwindow.ui | 14 ------------ src/organizercore.cpp | 12 ++++++++++ src/organizercore.h | 2 ++ 7 files changed, 78 insertions(+), 88 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/loglist.cpp b/src/loglist.cpp index c34ac76e..26aea682 100644 --- a/src/loglist.cpp +++ b/src/loglist.cpp @@ -18,14 +18,7 @@ along with Mod Organizer. If not, see . */ #include "loglist.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include "organizercore.h" using namespace MOBase; @@ -158,7 +151,7 @@ QVariant LogModel::headerData(int, Qt::Orientation, int) const LogList::LogList(QWidget* parent) - : QTreeView(parent) + : QTreeView(parent), m_core(nullptr) { setModel(&LogModel::instance()); @@ -171,6 +164,10 @@ LogList::LogList(QWidget* parent) setAutoScroll(true); scrollToBottom(); + connect( + this, &QWidget::customContextMenuRequested, + [&](auto&& pos){ onContextMenu(pos); }); + connect( model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(scrollToBottom())); @@ -180,6 +177,11 @@ LogList::LogList(QWidget* parent) this, SLOT(scrollToBottom())); } +void LogList::setCore(OrganizerCore& core) +{ + m_core = &core; +} + void LogList::copyToClipboard() { std::string s; @@ -196,3 +198,44 @@ void LogList::copyToClipboard() QApplication::clipboard()->setText(QString::fromStdString(s)); } + +QMenu* LogList::createMenu(QWidget* parent) +{ + auto* menu = new QMenu(parent); + + menu->addAction(tr("Copy& Log"), [&]{ copyToClipboard(); }); + menu->addSeparator(); + + auto* levels = new QMenu(tr("&Level")); + menu->addMenu(levels); + + auto* ag = new QActionGroup(menu); + + auto addAction = [&](auto&& text, auto&& level) { + auto* a = new QAction(text, ag); + + a->setCheckable(true); + a->setChecked(log::getDefault().level() == level); + + connect(a, &QAction::triggered, [this, level]{ + if (m_core) { + m_core->setLogLevel(level); + } + }); + + levels->addAction(a); + }; + + addAction(tr("&Debug"), log::Debug); + addAction(tr("&Info"), log::Info); + addAction(tr("&Warnings"), log::Warning); + addAction(tr("&Errors"), log::Error); + + return menu; +} + +void LogList::onContextMenu(const QPoint& pos) +{ + auto* menu = createMenu(this); + menu->popup(viewport()->mapToGlobal(pos)); +} diff --git a/src/loglist.h b/src/loglist.h index d1f7a2ad..ae827ca7 100644 --- a/src/loglist.h +++ b/src/loglist.h @@ -20,14 +20,11 @@ along with Mod Organizer. If not, see . #ifndef LOGBUFFER_H #define LOGBUFFER_H -#include -#include -#include -#include -#include -#include +#include #include +class OrganizerCore; + class LogModel : public QAbstractItemModel { Q_OBJECT @@ -65,7 +62,14 @@ class LogList : public QTreeView public: LogList(QWidget* parent=nullptr); + void setCore(OrganizerCore& core); + void copyToClipboard(); + QMenu* createMenu(QWidget* parent=nullptr); + +private: + OrganizerCore* m_core; + void onContextMenu(const QPoint& pos); }; #endif // LOGBUFFER_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 8a8a99ef..761e9843 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -59,7 +59,6 @@ along with Mod Organizer. If not, see . #include "installationmanager.h" #include "lockeddialog.h" #include "waitingonclosedialog.h" -#include "loglist.h" #include "downloadlistsortproxy.h" #include "motddialog.h" #include "filedialogmemory.h" @@ -358,7 +357,7 @@ MainWindow::MainWindow(QSettings &initSettings m_CategoryFactory.loadCategories(); - setupLogMenu(); + ui->logList->setCore(m_OrganizerCore); int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value ui->topLevelSplitter->setSizes(QList() << splitterSize - 100 << 100); @@ -813,36 +812,6 @@ void MainWindow::setupActionMenu(QAction* a) tb->setPopupMode(QToolButton::InstantPopup); } -void MainWindow::setupLogMenu() -{ - connect(ui->logList, &QWidget::customContextMenuRequested, [&](auto&& pos){ - auto* menu = new QMenu(ui->logList); - - menu->addAction(tr("Copy& Log"), [&]{ ui->logList->copyToClipboard(); }); - menu->addSeparator(); - - auto* levels = new QMenu(tr("&Level")); - menu->addMenu(levels); - - auto* ag = new QActionGroup(menu); - - auto addAction = [&](auto&& text, auto&& level) { - auto* a = new QAction(text, ag); - a->setCheckable(true); - a->setChecked(log::getDefault().level() == level); - connect(a, &QAction::triggered, [this, level]{ setLogLevel(level); }); - levels->addAction(a); - }; - - addAction(tr("&Debug"), log::Debug); - addAction(tr("&Info"), log::Info); - addAction(tr("&Warnings"), log::Warning); - addAction(tr("&Errors"), log::Error); - - menu->popup(ui->logList->viewport()->mapToGlobal(pos)); - }); -} - void MainWindow::updatePinnedExecutables() { for (auto* a : ui->toolBar->actions()) { @@ -1432,11 +1401,6 @@ bool MainWindow::confirmExit() void MainWindow::cleanup() { - if (ui->logList->model() != nullptr) { - disconnect(ui->logList->model(), nullptr, nullptr, nullptr); - ui->logList->setModel(nullptr); - } - QWebEngineProfile::defaultProfile()->clearAllVisitedLinks(); m_IntegratedBrowser.close(); m_SaveMetaTimer.stop(); @@ -5317,24 +5281,12 @@ void MainWindow::on_actionSettings_triggered() m_statusBar->checkSettings(m_OrganizerCore.settings()); updateDownloadView(); - setLogLevel(settings.logLevel()); + m_OrganizerCore.setLogLevel(settings.logLevel()); m_OrganizerCore.cycleDiagnostics(); toggleMO2EndorseState(); } -void MainWindow::setLogLevel(log::Levels level) -{ - auto& s = m_OrganizerCore.settings(); - - s.setLogLevel(level); - - m_OrganizerCore.updateVFSParams( - s.logLevel(), s.crashDumpsType(), s.executablesBlacklist()); - - log::getDefault().setLevel(s.logLevel()); -} - void MainWindow::on_actionNexus_triggered() { const IPluginGame *game = m_OrganizerCore.managedGame(); @@ -6858,11 +6810,6 @@ void MainWindow::on_restoreModsButton_clicked() } } -void MainWindow::on_actionLogCopy_triggered() -{ - ui->logList->copyToClipboard(); -} - void MainWindow::on_categoriesAndBtn_toggled(bool checked) { if (checked) { diff --git a/src/mainwindow.h b/src/mainwindow.h index 74993667..aa49205d 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -636,13 +636,10 @@ private slots: void search_activated(); void searchClear_activated(); - void setupLogMenu(); void resetActionIcons(); void updateModCount(); void updatePluginCount(); - void setLogLevel(MOBase::log::Levels level); - private slots: // ui slots // actions void on_actionAdd_Profile_triggered(); @@ -696,7 +693,6 @@ private slots: // ui slots void on_restoreButton_clicked(); void on_restoreModsButton_clicked(); void on_saveModsButton_clicked(); - void on_actionLogCopy_triggered(); void on_categoriesAndBtn_toggled(bool checked); void on_categoriesOrBtn_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index fc2bcdd3..6c6d0bca 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1757,20 +1757,6 @@ p, li { white-space: pre-wrap; } Log - - - Copy &Log - - - Copy &Log - - - Copy log to clipboard - - - Copy log to clipboard - - diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 92372d82..1e164525 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -731,6 +731,18 @@ void OrganizerCore::updateVFSParams( m_USVFS.updateParams(logLevel, crashDumpsType, executableBlacklist); } +void OrganizerCore::setLogLevel(log::Levels level) +{ + m_Settings.setLogLevel(level); + + updateVFSParams( + m_Settings.logLevel(), + m_Settings.crashDumpsType(), + m_Settings.executablesBlacklist()); + + log::getDefault().setLevel(m_Settings.logLevel()); +} + bool OrganizerCore::cycleDiagnostics() { if (int maxDumps = settings().crashDumpsMax()) removeOldFiles(QString::fromStdWString(crashDumpsPath()), "*.dmp", maxDumps, QDir::Time|QDir::Reversed); diff --git a/src/organizercore.h b/src/organizercore.h index c368d101..2aa7e707 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -196,6 +196,8 @@ public: MOBase::log::Levels logLevel, int crashDumpsType, QString executableBlacklist); + void setLogLevel(MOBase::log::Levels level); + bool cycleDiagnostics(); static CrashDumpsType getGlobalCrashDumpsType() { return m_globalCrashDumpsType; } -- cgit v1.3.1 From 07f1ac7a96dcf4c91a24bb1d30af92851ecda78f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 3 Aug 2019 01:55:21 -0400 Subject: split into GeometrySettings removed most of storeSettings() from OrganizerCore: QSettings handles saving by itself, no need for that removed topLevelSplitter from ui, unused since the log widget is in a dock removed QSettings from MainWindow::readSettings() replaced return values for some of the new getters in Settings to std::optional --- src/executableslist.cpp | 9 ++- src/executableslist.h | 5 +- src/filedialogmemory.cpp | 8 ++- src/filedialogmemory.h | 5 +- src/iuserinterface.h | 4 +- src/main.cpp | 96 +++++++++++++++---------- src/mainwindow.cpp | 116 ++++++++++++++---------------- src/mainwindow.h | 4 +- src/mainwindow.ui | 5 -- src/organizercore.cpp | 90 +++++------------------- src/organizercore.h | 4 -- src/settings.cpp | 180 +++++++++++++++++++++++++++++++++++++++++++---- src/settings.h | 55 ++++++++++++--- 13 files changed, 359 insertions(+), 222 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/executableslist.cpp b/src/executableslist.cpp index 3f76bb6f..2b3219df 100644 --- a/src/executableslist.cpp +++ b/src/executableslist.cpp @@ -21,6 +21,7 @@ along with Mod Organizer. If not, see . #include "iplugingame.h" #include "utility.h" +#include "settings.h" #include #include @@ -64,7 +65,7 @@ bool ExecutablesList::empty() const return m_Executables.empty(); } -void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings) +void ExecutablesList::load(const MOBase::IPluginGame* game, const Settings& s) { log::debug("loading executables"); @@ -74,6 +75,8 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings) // executables from 2.2.0, see upgradeFromCustom() bool needsUpgrade = false; + auto& settings = const_cast(s.directInterface()); + int numCustomExecutables = settings.beginReadArray("customExecutables"); for (int i = 0; i < numCustomExecutables; ++i) { settings.setArrayIndex(i); @@ -108,8 +111,10 @@ void ExecutablesList::load(const MOBase::IPluginGame* game, QSettings& settings) dump(); } -void ExecutablesList::store(QSettings& settings) +void ExecutablesList::store(Settings& s) { + auto& settings = s.directInterface(); + settings.remove("customExecutables"); settings.beginWriteArray("customExecutables"); diff --git a/src/executableslist.h b/src/executableslist.h index eda2034e..23cf3cfe 100644 --- a/src/executableslist.h +++ b/src/executableslist.h @@ -29,6 +29,7 @@ along with Mod Organizer. If not, see . #include namespace MOBase { class IPluginGame; class ExecutableInfo; } +class Settings; /*! * @brief Information about an executable @@ -103,7 +104,7 @@ public: /** * @brief initializes the list from the settings and the given plugin **/ - void load(const MOBase::IPluginGame* game, QSettings& settings); + void load(const MOBase::IPluginGame* game, const Settings& settings); /** * @brief re-adds all the executables from the plugin and renames existing @@ -114,7 +115,7 @@ public: /** * @brief writes the current list to the settings */ - void store(QSettings& settings); + void store(Settings& settings); /** * @brief get an executable by name diff --git a/src/filedialogmemory.cpp b/src/filedialogmemory.cpp index 308a175e..48828563 100644 --- a/src/filedialogmemory.cpp +++ b/src/filedialogmemory.cpp @@ -27,8 +27,10 @@ FileDialogMemory::FileDialogMemory() } -void FileDialogMemory::save(QSettings &settings) +void FileDialogMemory::save(Settings& s) { + auto& settings = s.directInterface(); + settings.remove("recentDirectories"); settings.beginWriteArray("recentDirectories"); int index = 0; @@ -42,8 +44,10 @@ void FileDialogMemory::save(QSettings &settings) } -void FileDialogMemory::restore(QSettings &settings) +void FileDialogMemory::restore(const Settings& s) { + auto& settings = const_cast(s.directInterface()); + int size = settings.beginReadArray("recentDirectories"); for (int i = 0; i < size; ++i) { settings.setArrayIndex(i); diff --git a/src/filedialogmemory.h b/src/filedialogmemory.h index 1a72b289..d214a8e6 100644 --- a/src/filedialogmemory.h +++ b/src/filedialogmemory.h @@ -23,7 +23,6 @@ along with Mod Organizer. If not, see . #include #include -#include #include class Settings; @@ -31,8 +30,8 @@ class Settings; class FileDialogMemory { public: - static void save(QSettings &settings); - static void restore(QSettings &settings); + static void save(Settings& settings); + static void restore(const Settings& settings); static QString getOpenFileName( const QString &dirID, QWidget *parent = 0, const QString &caption = QString(), diff --git a/src/iuserinterface.h b/src/iuserinterface.h index bba8de2b..7205f982 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -10,13 +10,13 @@ #include -class QSettings; +class Settings; class IUserInterface { public: - virtual void storeSettings(QSettings &settings) = 0; + virtual void storeSettings(Settings &settings) = 0; virtual void registerPluginTool(MOBase::IPluginTool *tool, QString name = QString(), QMenu *menu = nullptr) = 0; virtual void registerPluginTools(std::vector toolPlugins) = 0; diff --git a/src/main.cpp b/src/main.cpp index 720ecbf9..3e26ea17 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -62,7 +62,6 @@ along with Mod Organizer. If not, see . #include #include #include -#include #include #include #include @@ -116,7 +115,7 @@ bool bootstrap() shellDelete(QStringList(backupDirectory)); } - // cycle logfile + // cycle log file removeOldFiles(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::logPath()), "usvfs*.log", 5, QDir::Name); @@ -247,7 +246,7 @@ static bool HaveWriteAccess(const std::wstring &path) QString determineProfile(QStringList &arguments, const Settings &settings) { - QString selectedProfileName = settings.getSelectedProfileName(); + auto selectedProfileName = settings.getSelectedProfileName(); { // see if there is a profile on the command line int profileIndex = arguments.indexOf("-p", 1); @@ -259,14 +258,14 @@ QString determineProfile(QStringList &arguments, const Settings &settings) arguments.removeAt(profileIndex); } - if (selectedProfileName.isEmpty()) { + if (!selectedProfileName) { log::debug("no configured profile"); selectedProfileName = "Default"; } else { - log::debug("configured profile: {}", selectedProfileName); + log::debug("configured profile: {}", *selectedProfileName); } - return selectedProfileName; + return *selectedProfileName; } MOBase::IPluginGame *selectGame( @@ -290,27 +289,27 @@ MOBase::IPluginGame *determineCurrentGame( //user has done something odd. //If the game name has been set up, try to use that. - const QString gameName = settings.getManagedGameName(); - bool gameConfigured = !gameName.isEmpty(); + const auto gameName = settings.getManagedGameName(); + const bool gameConfigured = (gameName.has_value() && *gameName != ""); if (gameConfigured) { - MOBase::IPluginGame *game = plugins.managedGame(gameName); + MOBase::IPluginGame *game = plugins.managedGame(*gameName); if (game == nullptr) { - reportError(QObject::tr("Plugin to handle %1 no longer installed").arg(gameName)); + reportError(QObject::tr("Plugin to handle %1 no longer installed").arg(*gameName)); return nullptr; } - QString gamePath = settings.getManagedGameDirectory(); - if (gamePath == "") { + auto gamePath = settings.getManagedGameDirectory(); + if (!gamePath || *gamePath == "") { gamePath = game->gameDirectory().absolutePath(); } - QDir gameDir(gamePath); + QDir gameDir(*gamePath); QFileInfo directoryInfo(gameDir.path()); if (directoryInfo.isSymLink()) { reportError(QObject::tr("The configured path to the game directory (%1) appears to be a symbolic (or other) link. " - "This setup is incompatible with MO2's VFS and will not run correctly.").arg(gamePath)); + "This setup is incompatible with MO2's VFS and will not run correctly.").arg(*gamePath)); } if (game->looksValid(gameDir)) { @@ -321,17 +320,20 @@ MOBase::IPluginGame *determineCurrentGame( //If we've made it this far and the instance is already configured for a game, something has gone wrong. //Tell the user about it. if (gameConfigured) { - const QString gamePath = settings.getManagedGameDirectory(); - reportError(QObject::tr("Could not use configuration settings for game \"%1\", path \"%2\"."). - arg(gameName).arg(gamePath)); + const auto gamePath = settings.getManagedGameDirectory(); + + reportError( + QObject::tr("Could not use configuration settings for game \"%1\", path \"%2\".") + .arg(*gameName).arg(gamePath ? *gamePath : "")); } - SelectionDialog selection(gameConfigured ? QObject::tr("Please select the installation of %1 to manage").arg(gameName) - : QObject::tr("Please select the game to manage"), nullptr, QSize(32, 32)); + SelectionDialog selection(gameConfigured ? + QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : + QObject::tr("Please select the game to manage"), nullptr, QSize(32, 32)); for (IPluginGame *game : plugins.plugins()) { //If a game is already configured, skip any plugins that are not for that game - if (gameConfigured && gameName.compare(game->gameName(), Qt::CaseInsensitive) != 0) + if (gameConfigured && gameName->compare(game->gameName(), Qt::CaseInsensitive) != 0) continue; //Only add games that are installed @@ -355,9 +357,11 @@ MOBase::IPluginGame *determineCurrentGame( return selectGame(settings, game->gameDirectory(), game); } - gamePath = QFileDialog::getExistingDirectory(nullptr, gameConfigured ? QObject::tr("Please select the installation of %1 to manage").arg(gameName) - : QObject::tr("Please select the game to manage"), - QString(), QFileDialog::ShowDirsOnly); + gamePath = QFileDialog::getExistingDirectory(nullptr, gameConfigured ? + QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : + QObject::tr("Please select the game to manage"), + QString(), QFileDialog::ShowDirsOnly); + if (!gamePath.isEmpty()) { QDir gameDir(gamePath); QFileInfo directoryInfo(gamePath); @@ -368,7 +372,7 @@ MOBase::IPluginGame *determineCurrentGame( QList possibleGames; for (IPluginGame * const game : plugins.plugins()) { //If a game is already configured, skip any plugins that are not for that game - if (gameConfigured && gameName.compare(game->gameName(), Qt::CaseInsensitive) != 0) + if (gameConfigured && gameName->compare(game->gameName(), Qt::CaseInsensitive) != 0) continue; //Only try plugins that look valid for this directory @@ -376,24 +380,31 @@ MOBase::IPluginGame *determineCurrentGame( possibleGames.append(game); } } + if (possibleGames.count() > 1) { - SelectionDialog browseSelection(gameConfigured ? QObject::tr("Please select the installation of %1 to manage").arg(gameName) - : QObject::tr("Please select the game to manage"), - nullptr, QSize(32, 32)); + SelectionDialog browseSelection(gameConfigured ? + QObject::tr("Please select the installation of %1 to manage").arg(*gameName) : + QObject::tr("Please select the game to manage"), + nullptr, QSize(32, 32)); + for (IPluginGame *game : possibleGames) { browseSelection.addChoice(game->gameIcon(), game->gameName(), gamePath, QVariant::fromValue(game)); } + if (browseSelection.exec() == QDialog::Accepted) { return selectGame(settings, gameDir, browseSelection.getChoiceData().value()); } else { - reportError(gameConfigured ? QObject::tr("Canceled finding %1 in \"%2\".").arg(gameName).arg(gamePath) - : QObject::tr("Canceled finding game in \"%1\".").arg(gamePath)); + reportError(gameConfigured ? + QObject::tr("Canceled finding %1 in \"%2\".").arg(*gameName).arg(gamePath) : + QObject::tr("Canceled finding game in \"%1\".").arg(gamePath)); } } else if(possibleGames.count() == 1) { return selectGame(settings, gameDir, possibleGames[0]); } else { if (gameConfigured) { - reportError(QObject::tr("%1 not identified in \"%2\". The directory is required to contain the game binary.").arg(gameName).arg(gamePath)); + reportError( + QObject::tr("%1 not identified in \"%2\". The directory is required to contain the game binary.") + .arg(*gameName).arg(gamePath)); } else { QString supportedGames; @@ -608,7 +619,11 @@ int runApplication(MOApplication &application, SingleInstance &instance, organizer.setManagedGame(game); organizer.createDefaultProfile(); - if (settings.getManagedGameEdition() == "") { + QString edition; + + if (auto v=settings.getManagedGameEdition()) { + edition = *v; + } else { QStringList editions = game->gameVariants(); if (editions.size() > 1) { SelectionDialog selection( @@ -624,12 +639,15 @@ int runApplication(MOApplication &application, SingleInstance &instance, if (selection.exec() == QDialog::Rejected) { return 1; } else { - settings.setManagedGameEdition(selection.getChoiceString()); + edition = selection.getChoiceString(); + settings.setManagedGameEdition(edition); } } } - game->setGameVariant(settings.getManagedGameEdition()); + Q_ASSERT(!edition.isEmpty()); + + game->setGameVariant(edition); log::info("managing game at {}", game->gameDirectory().absolutePath()); @@ -679,10 +697,10 @@ int runApplication(MOApplication &application, SingleInstance &instance, QPixmap pixmap(splashPath); QSplashScreen splash(pixmap); - const int monitor = settings.getMainWindowMonitor(); - if (monitor != -1 && QGuiApplication::screens().size() > monitor) { - QGuiApplication::screens().at(monitor)->geometry().center(); - const QPoint center = QGuiApplication::screens().at(monitor)->geometry().center(); + const auto monitor = settings.geometry().getMainWindowMonitor(); + if (monitor && QGuiApplication::screens().size() > *monitor) { + QGuiApplication::screens().at(*monitor)->geometry().center(); + const QPoint center = QGuiApplication::screens().at(*monitor)->geometry().center(); splash.move(center - splash.rect().center()); } else { const QPoint center = QGuiApplication::primaryScreen()->geometry().center(); @@ -703,7 +721,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, + QString::fromStdWString(AppConfig::tutorialsPath()) + "/", &organizer); - if (!application.setStyleFile(settings.getStyleName())) { + if (!application.setStyleFile(settings.getStyleName().value_or(""))) { // disable invalid stylesheet settings.setStyleName(""); } @@ -726,7 +744,7 @@ int runApplication(MOApplication &application, SingleInstance &instance, // this must be before readSettings(), see DockFixer in mainwindow.cpp splash.finish(&mainWindow); - mainWindow.readSettings(); + mainWindow.readSettings(settings); log::debug("displaying main window"); mainWindow.show(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 7f7ded80..e77d08b1 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -216,26 +216,24 @@ const QSize LargeToolbarSize(42, 36); class DockFixer { public: - static void save(MainWindow* mw, QSettings& settings) + static void save(MainWindow* mw, Settings& settings) { - const auto docks = mw->findChildren(); - // saves the size of each dock - for (int i=0; ifindChildren()) { int size = 0; // save the width for horizontal docks, or the height for vertical - if (orientation(mw, docks[i]) == Qt::Horizontal) { - size = docks[i]->size().width(); + if (orientation(mw, dock) == Qt::Horizontal) { + size = dock->size().width(); } else { - size = docks[i]->size().height(); + size = dock->size().height(); } - settings.setValue(settingName(docks[i]), size); + settings.geometry().setDockSize(dock->objectName(), size); } } - static void restore(MainWindow* mw, const QSettings& settings) + static void restore(MainWindow* mw, const Settings& settings) { struct DockInfo { @@ -246,16 +244,11 @@ public: std::vector dockInfos; - const auto docks = mw->findChildren(); - // for each dock - for (int i=0; ifindChildren()) { + if (auto size=settings.geometry().getDockSize(dock->objectName())) { // remember this dock, its size and orientation - const auto size = settings.value(name).toInt(); - dockInfos.push_back({docks[i], size, orientation(mw, docks[i])}); + dockInfos.push_back({dock, *size, orientation(mw, dock)}); } } @@ -264,30 +257,25 @@ public: // // some people said a single processEvents() call is enough, but it doesn't // look like it - QTimer::singleShot(1, [=] { + QTimer::singleShot(5, [=] { for (const auto& info : dockInfos) { mw->resizeDocks({info.d}, {info.size}, info.ori); } - }); + }); } - static Qt::Orientation orientation(QMainWindow* mw, QDockWidget* d) + static Qt::Orientation orientation(QMainWindow* mw, const QDockWidget* d) { // docks in these areas are horizontal const auto horizontalAreas = Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea; - if (mw->dockWidgetArea(d) & horizontalAreas) { + if (mw->dockWidgetArea(const_cast(d)) & horizontalAreas) { return Qt::Horizontal; } else { return Qt::Vertical; } } - - static QString settingName(QDockWidget* d) - { - return "geometry/" + d->objectName() + "_size"; - } }; @@ -359,9 +347,6 @@ MainWindow::MainWindow(Settings &settings ui->logList->setCore(m_OrganizerCore); - int splitterSize = this->size().height(); // actually total window size, but the splitter doesn't seem to return the true value - ui->topLevelSplitter->setSizes(QList() << splitterSize - 100 << 100); - updateProblemsButton(); setupToolbar(); @@ -540,8 +525,7 @@ MainWindow::MainWindow(Settings &settings connect(&m_SaveMetaTimer, SIGNAL(timeout()), this, SLOT(saveModMetas())); m_SaveMetaTimer.start(5000); - setCategoryListVisible(settings.isCategoryListVisible()); - FileDialogMemory::restore(settings.directInterface()); + FileDialogMemory::restore(settings); fixCategories(); @@ -2247,52 +2231,50 @@ void MainWindow::activateProxy(bool activate) busyDialog.hide(); } -void MainWindow::readSettings() +void MainWindow::readSettings(const Settings& settings) { - QSettings settings(qApp->property("dataPath").toString() + "/" + QString::fromStdWString(AppConfig::iniFileName()), QSettings::IniFormat); - - if (settings.contains("window_geometry")) { - restoreGeometry(settings.value("window_geometry").toByteArray()); + if (auto v=settings.geometry().getMainWindow()) { + restoreGeometry(*v); } - if (settings.contains("window_state")) { - restoreState(settings.value("window_state").toByteArray()); + if (auto v=settings.geometry().getMainWindowState()) { + restoreState(*v); } - if (settings.contains("toolbar_size")) { - setToolbarSize(settings.value("toolbar_size").toSize()); + if (auto v=settings.geometry().getToolbarSize()) { + setToolbarSize(*v); } - if (settings.contains("toolbar_button_style")) { - setToolbarButtonStyle(static_cast( - settings.value("toolbar_button_style").toInt())); + if (auto v=settings.geometry().getToolbarButtonStyle()) { + setToolbarButtonStyle(*v); } - if (settings.contains("menubar_visible")) { - showMenuBar(settings.value("menubar_visible").toBool()); + if (auto v=settings.geometry().getMenubarVisible()) { + showMenuBar(*v); } - if (settings.contains("statusbar_visible")) { - showStatusBar(settings.value("statusbar_visible").toBool()); + if (auto v=settings.geometry().getStatusbarVisible()) { + showStatusBar(*v); } - if (settings.contains("window_split")) { - ui->splitter->restoreState(settings.value("window_split").toByteArray()); + if (auto v=settings.geometry().getMainSplitterState()) { + ui->splitter->restoreState(*v); } - if (settings.contains("log_split")) { - ui->topLevelSplitter->restoreState(settings.value("log_split").toByteArray()); + { + auto v = settings.geometry().getFiltersVisible().value_or(false); + setCategoryListVisible(v); + ui->displayCategoriesBtn->setChecked(v); } - bool filtersVisible = settings.value("filters_visible", false).toBool(); - setCategoryListVisible(filtersVisible); - ui->displayCategoriesBtn->setChecked(filtersVisible); - - int selectedExecutable = settings.value("selected_executable").toInt(); - setExecutableIndex(selectedExecutable); + if (auto v=settings.getSelectedExecutable()) { + setExecutableIndex(*v); + } - if (settings.value("Settings/use_proxy", false).toBool()) { - activateProxy(true); + if (auto v=settings.getUseProxy()) { + if (*v) { + activateProxy(true); + } } DockFixer::restore(this, settings); @@ -2335,6 +2317,12 @@ void MainWindow::processUpdates() { ui->downloadView->header()->hideSection(i); } } + if (lastVersion < QVersionNumber(2, 2, 2)) { + QSettings &instance = Settings::instance().directInterface(); + + // log splitter is gone, it's a dock now + instance.remove("log_split"); + } } if (currentVersion > lastVersion) { @@ -2354,7 +2342,9 @@ void MainWindow::processUpdates() { settings.setValue("version", currentVersion.toString()); } -void MainWindow::storeSettings(QSettings &settings) { +void MainWindow::storeSettings(Settings& s) { + auto& settings = s.directInterface(); + settings.setValue("group_state", ui->groupCombo->currentIndex()); settings.setValue("selected_executable", ui->executablesListBox->currentIndex()); @@ -2367,7 +2357,6 @@ void MainWindow::storeSettings(QSettings &settings) { settings.remove("menubar_visible"); settings.remove("window_split"); settings.remove("window_monitor"); - settings.remove("log_split"); settings.remove("filters_visible"); settings.remove("browser_geometry"); settings.remove("geometry"); @@ -2383,7 +2372,6 @@ void MainWindow::storeSettings(QSettings &settings) { QScreen *screen = this->window()->windowHandle()->screen(); int screenId = QGuiApplication::screens().indexOf(screen); settings.setValue("window_monitor", screenId); - settings.setValue("log_split", ui->topLevelSplitter->saveState()); settings.setValue("browser_geometry", m_IntegratedBrowser.saveGeometry()); settings.setValue("filters_visible", ui->displayCategoriesBtn->isChecked()); @@ -2392,7 +2380,7 @@ void MainWindow::storeSettings(QSettings &settings) { settings.setValue(key, kv.second->saveState()); } - DockFixer::save(this, settings); + DockFixer::save(this, s); } } @@ -5213,7 +5201,7 @@ void MainWindow::on_actionSettings_triggered() QString oldModDirectory(settings.getModDirectory()); QString oldCacheDirectory(settings.getCacheDirectory()); QString oldProfilesDirectory(settings.getProfileDirectory()); - QString oldManagedGameDirectory(settings.getManagedGameDirectory()); + QString oldManagedGameDirectory(settings.getManagedGameDirectory().value_or("")); bool oldDisplayForeign(settings.displayForeign()); bool proxy = settings.useProxy(); DownloadManager *dlManager = m_OrganizerCore.downloadManager(); diff --git a/src/mainwindow.h b/src/mainwindow.h index 7326425a..d4513c0f 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -119,8 +119,8 @@ public: QWidget *parent = 0); ~MainWindow(); - void storeSettings(QSettings &settings) override; - void readSettings(); + void storeSettings(Settings& settings) override; + void readSettings(const Settings& settings); void processUpdates(); virtual ILockedWaitingForProcess* lock() override; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 6c6d0bca..e9910b83 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -47,10 +47,6 @@ 0 - - - Qt::Vertical - @@ -1286,7 +1282,6 @@ p, li { white-space: pre-wrap; } - diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 72c8dab5..a64d93b4 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -94,15 +94,6 @@ static bool isOnline() return false; } -static bool renameFile(const QString &oldName, const QString &newName, - bool overwrite = true) -{ - if (overwrite && QFile::exists(newName)) { - QFile::remove(newName); - } - return QFile::rename(oldName, newName); -} - static std::wstring getProcessName(HANDLE process) { wchar_t buffer[MAX_PATH]; @@ -342,80 +333,37 @@ OrganizerCore::~OrganizerCore() delete m_DirectoryStructure; } -QString OrganizerCore::commitSettings(const QString &iniFile) -{ - if (!shellRename(iniFile + ".new", iniFile, true, qApp->activeWindow())) { - DWORD err = ::GetLastError(); - // make a second attempt using qt functions but if that fails print the - // error from the first attempt - if (!renameFile(iniFile + ".new", iniFile)) { - return QString::fromStdWString(formatSystemMessage(err)); - } - } - return QString(); -} - -QSettings::Status OrganizerCore::storeSettings(const QString &fileName) +void OrganizerCore::storeSettings() { - QSettings settings(fileName, QSettings::IniFormat); - if (m_UserInterface != nullptr) { - m_UserInterface->storeSettings(settings); + m_UserInterface->storeSettings(m_Settings); } if (m_CurrentProfile != nullptr) { - settings.setValue("selected_profile", - m_CurrentProfile->name().toUtf8().constData()); + m_Settings.setSelectedProfileName(m_CurrentProfile->name()); } - m_ExecutablesList.store(settings); - - FileDialogMemory::save(settings); + m_ExecutablesList.store(m_Settings); - settings.sync(); - return settings.status(); -} - -void OrganizerCore::storeSettings() -{ - QString iniFile = qApp->property("dataPath").toString() + "/" - + QString::fromStdWString(AppConfig::iniFileName()); - if (QFileInfo(iniFile).exists()) { - if (!shellCopy(iniFile, iniFile + ".new", true, qApp->activeWindow())) { - const auto e = GetLastError(); - QMessageBox::critical( - qApp->activeWindow(), tr("Failed to write settings"), - tr("An error occurred trying to update MO settings to %1: %2") - .arg(iniFile) - .arg(QString::fromStdWString(formatSystemMessage(e)))); - return; - } - } + FileDialogMemory::save(m_Settings); - QString writeTarget = iniFile + ".new"; + const auto result = m_Settings.sync(); - QSettings::Status result = storeSettings(writeTarget); + if (result != QSettings::NoError) { + QString reason; - if (result == QSettings::NoError) { - QString errMsg = commitSettings(iniFile); - if (!errMsg.isEmpty()) { - log::warn( - "settings file not writable, may be locked by another " - "application, trying direct write"); - writeTarget = iniFile; - result = storeSettings(iniFile); + if (result == QSettings::AccessError) { + reason = tr("File is write protected"); + } else if (result == QSettings::FormatError) { + reason = tr("Invalid file format (probably a bug)"); + } else { + reason = tr("Unknown error %1").arg(result); } - } - if (result != QSettings::NoError) { - QString reason = result == QSettings::AccessError - ? tr("File is write protected") - : result == QSettings::FormatError - ? tr("Invalid file format (probably a bug)") - : tr("Unknown error %1").arg(result); + QMessageBox::critical( - qApp->activeWindow(), tr("Failed to write settings"), - tr("An error occurred trying to write back MO settings to %1: %2") - .arg(writeTarget, reason)); + qApp->activeWindow(), tr("Failed to write settings"), + tr("An error occurred trying to write back MO settings to %1: %2") + .arg(m_Settings.getFilename(), reason)); } } @@ -487,7 +435,7 @@ void OrganizerCore::updateExecutablesList() return; } - m_ExecutablesList.load(managedGame(), m_Settings.directInterface()); + m_ExecutablesList.load(managedGame(), m_Settings); // TODO this has nothing to do with executables list move to an appropriate // function! diff --git a/src/organizercore.h b/src/organizercore.h index 926a21f0..4bcfe745 100644 --- a/src/organizercore.h +++ b/src/organizercore.h @@ -288,10 +288,6 @@ private: void storeSettings(); - QSettings::Status storeSettings(const QString &fileName); - - QString commitSettings(const QString &iniFile); - bool queryApi(QString &apiKey); void updateModActiveState(int index, bool active); diff --git a/src/settings.cpp b/src/settings.cpp index 5d103267..d843a0db 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -26,10 +26,56 @@ along with Mod Organizer. If not, see . using namespace MOBase; +template +T convertVariant(const QVariant& v); + +template <> +QByteArray convertVariant(const QVariant& v) +{ + return v.toByteArray(); +} + +template <> +QString convertVariant(const QVariant& v) +{ + return v.toString(); +} + +template <> +int convertVariant(const QVariant& v) +{ + return v.toInt(); +} + +template <> +bool convertVariant(const QVariant& v) +{ + return v.toBool(); +} + +template <> +QSize convertVariant(const QVariant& v) +{ + return v.toSize(); +} + + + +template +std::optional getOptional(const QSettings& s, const QString& name) +{ + if (s.contains(name)) { + return convertVariant(s.value(name)); + } + + return {}; +} + + Settings *Settings::s_Instance = nullptr; Settings::Settings(const QString& path) - : m_Settings(path, QSettings::IniFormat) + : m_Settings(path, QSettings::IniFormat), m_Geometry(m_Settings) { if (s_Instance != nullptr) { throw std::runtime_error("second instance of \"Settings\" created"); @@ -51,6 +97,11 @@ Settings &Settings::instance() return *s_Instance; } +QString Settings::getFilename() const +{ + return m_Settings.fileName(); +} + void Settings::clearPlugins() { m_Plugins.clear(); @@ -278,9 +329,13 @@ QString Settings::getModDirectory(bool resolve) const return getConfigurablePath("mod_directory", ToQString(AppConfig::modsPath()), resolve); } -QString Settings::getManagedGameDirectory() const +std::optional Settings::getManagedGameDirectory() const { - return QString::fromUtf8(m_Settings.value("gamePath", "").toByteArray()); + if (auto v=getOptional(m_Settings, "gamePath")) { + return QString::fromUtf8(*v); + } + + return {}; } void Settings::setManagedGameDirectory(const QString& path) @@ -288,9 +343,9 @@ void Settings::setManagedGameDirectory(const QString& path) m_Settings.setValue("gamePath", QDir::toNativeSeparators(path).toUtf8()); } -QString Settings::getManagedGameName() const +std::optional Settings::getManagedGameName() const { - return m_Settings.value("gameName", "").toString(); + return getOptional(m_Settings, "gameName"); } void Settings::setManagedGameName(const QString& name) @@ -298,9 +353,9 @@ void Settings::setManagedGameName(const QString& name) m_Settings.setValue("gameName", name); } -QString Settings::getManagedGameEdition() const +std::optional Settings::getManagedGameEdition() const { - return m_Settings.value("game_edition", "").toString(); + return getOptional(m_Settings, "game_edition"); } void Settings::setManagedGameEdition(const QString& name) @@ -308,19 +363,23 @@ void Settings::setManagedGameEdition(const QString& name) m_Settings.setValue("game_edition", name); } -QString Settings::getSelectedProfileName() const +std::optional Settings::getSelectedProfileName() const { - return QString::fromUtf8(m_Settings.value("selected_profile", "").toByteArray()); + if (auto v=getOptional(m_Settings, "selected_profile")) { + return QString::fromUtf8(*v); + } + + return {}; } -int Settings::getMainWindowMonitor() const +void Settings::setSelectedProfileName(const QString& name) { - return m_Settings.value("window_monitor", -1).toInt(); + m_Settings.setValue("selected_profile", name.toUtf8()); } -QString Settings::getStyleName() const +std::optional Settings::getStyleName() const { - return m_Settings.value("Settings/style", "").toString(); + return getOptional(m_Settings, "Settings/style"); } void Settings::setStyleName(const QString& name) @@ -328,9 +387,14 @@ void Settings::setStyleName(const QString& name) m_Settings.setValue("Settings/style", name); } -bool Settings::isCategoryListVisible() const +std::optional Settings::getSelectedExecutable() const { - return m_Settings.value("categorylist_visible", true).toBool(); + return getOptional(m_Settings, "selected_executable"); +} + +std::optional Settings::getUseProxy() const +{ + return getOptional(m_Settings, "Settings/use_proxy"); } QString Settings::getProfileDirectory(bool resolve) const @@ -659,6 +723,22 @@ void Settings::writePluginBlacklist() m_Settings.endArray(); } +GeometrySettings& Settings::geometry() +{ + return m_Geometry; +} + +const GeometrySettings& Settings::geometry() const +{ + return m_Geometry; +} + +QSettings::Status Settings::sync() const +{ + m_Settings.sync(); + return m_Settings.status(); +} + void Settings::dump() const { static const QStringList ignore({ @@ -679,3 +759,73 @@ void Settings::dump() const m_Settings.endGroup(); } + + +GeometrySettings::GeometrySettings(QSettings& s) + : m_Settings(s) +{ +} + +std::optional GeometrySettings::getMainWindow() const +{ + return getOptional(m_Settings, "window_geometry"); +} + +std::optional GeometrySettings::getMainWindowState() const +{ + return getOptional(m_Settings, "window_state"); +} + +std::optional GeometrySettings::getToolbarSize() const +{ + return getOptional(m_Settings, "toolbar_size"); +} + +std::optional GeometrySettings::getToolbarButtonStyle() const +{ + if (auto v=getOptional(m_Settings, "toolbar_button_style")) { + return static_cast(*v); + } + + return {}; +} + +std::optional GeometrySettings::getMenubarVisible() const +{ + return getOptional(m_Settings, "menubar_visible"); +} + +std::optional GeometrySettings::getStatusbarVisible() const +{ + return getOptional(m_Settings, "statusbar_visible"); +} + +std::optional GeometrySettings::getMainSplitterState() const +{ + return getOptional(m_Settings, "window_split"); +} + +std::optional GeometrySettings::getFiltersVisible() const +{ + return getOptional(m_Settings, "filters_visible"); +} + +std::optional GeometrySettings::getMainWindowMonitor() const +{ + return getOptional(m_Settings, "window_monitor"); +} + +void GeometrySettings::setDockSize(const QString& name, int size) +{ + m_Settings.setValue("geometry/" + name + "_size", size); +} + +std::optional GeometrySettings::getDockSize(const QString& name) const +{ + return getOptional(m_Settings, "geometry/" + name + "_size"); +} + +std::optional GeometrySettings::isCategoryListVisible() const +{ + return getOptional(m_Settings, "categorylist_visible"); +} diff --git a/src/settings.h b/src/settings.h index f06aece9..066843c2 100644 --- a/src/settings.h +++ b/src/settings.h @@ -31,13 +31,40 @@ namespace MOBase { class PluginContainer; struct ServerInfo; + +class GeometrySettings +{ +public: + GeometrySettings(QSettings& s); + + std::optional getMainWindow() const; + std::optional getMainWindowState() const; + std::optional getToolbarSize() const; + std::optional getToolbarButtonStyle() const; + std::optional getMenubarVisible() const; + std::optional getStatusbarVisible() const; + std::optional getMainSplitterState() const; + std::optional getFiltersVisible() const; + + std::optional getMainWindowMonitor() const; + void setDockSize(const QString& name, int size); + + std::optional getDockSize(const QString& name) const; + + std::optional isCategoryListVisible() const; + +private: + QSettings& m_Settings; +}; + + /** * manages the settings for Mod Organizer. The settings are not cached * inside the class but read/written directly from/to disc **/ class Settings : public QObject { - Q_OBJECT + Q_OBJECT; public: Settings(const QString& path); @@ -45,6 +72,8 @@ public: static Settings &instance(); + QString getFilename() const; + /** * unregister all plugins from settings */ @@ -122,25 +151,26 @@ public: /** * retrieve the directory where the managed game is stored (with native separators) **/ - QString getManagedGameDirectory() const; + std::optional getManagedGameDirectory() const; void setManagedGameDirectory(const QString& path); - QString getManagedGameName() const; + std::optional getManagedGameName() const; void setManagedGameName(const QString& name); - QString getManagedGameEdition() const; + std::optional getManagedGameEdition() const; void setManagedGameEdition(const QString& name); - QString getSelectedProfileName() const; - - // returns -1 if not set - // - int getMainWindowMonitor() const; + std::optional getSelectedProfileName() const; + void setSelectedProfileName(const QString& name); - QString getStyleName() const; + std::optional getStyleName() const; void setStyleName(const QString& name); - bool isCategoryListVisible() const; + std::optional getSelectedExecutable() const; + std::optional getUseProxy() const; + + GeometrySettings& geometry(); + const GeometrySettings& geometry() const; /** * retrieve the directory where profiles stored (with native separators) @@ -388,6 +418,8 @@ public: MOBase::IPluginGame const *gamePlugin() { return m_GamePlugin; } const LoadMechanism& loadMechanism() const { return m_LoadMechanism; } + QSettings::Status sync() const; + void dump() const; // temp @@ -407,6 +439,7 @@ private: static Settings *s_Instance; MOBase::IPluginGame const *m_GamePlugin; mutable QSettings m_Settings; + GeometrySettings m_Geometry; LoadMechanism m_LoadMechanism; std::vector m_Plugins; -- cgit v1.3.1 From 0374291a3451c464fb27e53077da42ad21c27cd6 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 16 Aug 2019 09:00:31 -0400 Subject: StatusBar now inherits from QStatusBar to handle hide/show events merged settings into saveVisibility() and restoreVisibility() call MainWindow::storeSettings() earlier so widget visibility is still valid --- src/iuserinterface.h | 5 - src/main.cpp | 2 - src/mainwindow.cpp | 78 +- src/mainwindow.h | 15 +- src/mainwindow.ui | 2129 +++++++++++++++++++++++++------------------------ src/organizercore.cpp | 4 - src/settings.cpp | 96 +-- src/settings.h | 13 +- src/statusbar.cpp | 65 +- src/statusbar.h | 14 +- 10 files changed, 1200 insertions(+), 1221 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/iuserinterface.h b/src/iuserinterface.h index 7205f982..a309ed9b 100644 --- a/src/iuserinterface.h +++ b/src/iuserinterface.h @@ -10,14 +10,9 @@ #include -class Settings; - class IUserInterface { public: - - virtual void storeSettings(Settings &settings) = 0; - virtual void registerPluginTool(MOBase::IPluginTool *tool, QString name = QString(), QMenu *menu = nullptr) = 0; virtual void registerPluginTools(std::vector toolPlugins) = 0; virtual void registerModPage(MOBase::IPluginModPage *modPage) = 0; diff --git a/src/main.cpp b/src/main.cpp index 6d4108fa..aa781c19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -736,8 +736,6 @@ int runApplication(MOApplication &application, SingleInstance &instance, // this must be before readSettings(), see DockFixer in mainwindow.cpp splash.finish(&mainWindow); - mainWindow.readSettings(settings); - log::debug("displaying main window"); mainWindow.show(); mainWindow.activateWindow(); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 28e1de2e..7e471d24 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -285,8 +285,6 @@ MainWindow::MainWindow(Settings &settings : QMainWindow(parent) , ui(new Ui::MainWindow) , m_WasVisible(false) - , m_menuBarVisible(true) - , m_statusBarVisible(true) , m_linksSeparator(nullptr) , m_Tutorial(this, "MainWindow") , m_OldProfileIndex(-1) @@ -312,7 +310,7 @@ MainWindow::MainWindow(Settings &settings QWebEngineProfile::defaultProfile()->setPersistentStoragePath(settings.getCacheDirectory()); ui->setupUi(this); - m_statusBar.reset(new StatusBar(statusBar(), ui)); + ui->statusBar->setup(ui); { auto* ni = NexusInterface::instance(&m_PluginContainer); @@ -336,7 +334,7 @@ MainWindow::MainWindow(Settings &settings // in the rare case where the user restarts MO through the settings, this // will correctly pick up the previous values updateWindowTitle(ni->getAPIUserAccount()); - m_statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); + ui->statusBar->setAPI(ni->getAPIStats(), ni->getAPIUserAccount()); } languageChange(settings.language()); @@ -708,7 +706,7 @@ void MainWindow::updateWindowTitle(const APIUserAccount& user) void MainWindow::onRequestsChanged(const APIStats& stats, const APIUserAccount& user) { - m_statusBar->setAPI(stats, user); + ui->statusBar->setAPI(stats, user); } @@ -902,7 +900,7 @@ QMenu* MainWindow::createPopupMenu() void MainWindow::on_actionMainMenuToggle_triggered() { - showMenuBar(!ui->menuBar->isVisible()); + ui->menuBar->setVisible(!ui->menuBar->isVisible()); } void MainWindow::on_actionToolBarMainToggle_triggered() @@ -912,7 +910,7 @@ void MainWindow::on_actionToolBarMainToggle_triggered() void MainWindow::on_actionStatusBarToggle_triggered() { - showStatusBar(!ui->statusBar->isVisible()); + ui->statusBar->setVisible(!ui->statusBar->isVisible()); } void MainWindow::on_actionToolBarSmallIcons_triggered() @@ -964,36 +962,6 @@ void MainWindow::setToolbarButtonStyle(Qt::ToolButtonStyle s) } } -void MainWindow::showMenuBar(bool b) -{ - ui->menuBar->setVisible(b); - m_menuBarVisible = b; -} - -void MainWindow::showStatusBar(bool b) -{ - ui->statusBar->setVisible(b); - m_statusBarVisible = b; - - // the central widget typically has no bottom padding because the status bar - // is more than enough, but when it's hidden, the bottom widget (currently - // the log) touches the bottom border of the window, which looks ugly - // - // when hiding the statusbar, the central widget is given the same border - // margin as it has on the top (which is typically 6, as it's the default from - // the qt designer) - - auto m = ui->centralWidget->layout()->contentsMargins(); - - if (b) { - m.setBottom(0); - } else { - m.setBottom(m.top()); - } - - ui->centralWidget->layout()->setContentsMargins(m); -} - void MainWindow::on_centralWidget_customContextMenuRequested(const QPoint &pos) { // this allows for getting the context menu even if both the menubar and all @@ -1075,8 +1043,8 @@ void MainWindow::updateProblemsButton() } // updating the status bar, may be null very early when MO is starting - if (m_statusBar) { - m_statusBar->setNotifications(numProblems > 0); + if (ui->statusBar) { + ui->statusBar->setNotifications(numProblems > 0); } } @@ -1319,6 +1287,8 @@ void MainWindow::hookUpWindowTutorials() void MainWindow::showEvent(QShowEvent *event) { + readSettings(m_OrganizerCore.settings()); + refreshFilters(); QMainWindow::showEvent(event); @@ -1378,7 +1348,10 @@ void MainWindow::closeEvent(QCloseEvent* event) { if (!confirmExit()) { event->ignore(); + return; } + + storeSettings(m_OrganizerCore.settings()); } bool MainWindow::confirmExit() @@ -2259,17 +2232,12 @@ void MainWindow::readSettings(const Settings& settings) settings.geometry().restoreState(this); settings.geometry().restoreToolbars(this); settings.geometry().restoreState(ui->splitter); - - if (auto v=settings.geometry().getMenubarVisible()) { - showMenuBar(*v); - } - - if (auto v=settings.geometry().getStatusbarVisible()) { - showStatusBar(*v); - } + settings.geometry().restoreVisibility(ui->menuBar); + settings.geometry().restoreVisibility(ui->statusBar); { - auto v = settings.geometry().getFiltersVisible().value_or(false); + settings.geometry().restoreVisibility(ui->categoriesGroup, false); + const auto v = ui->categoriesGroup->isVisible(); setCategoryListVisible(v); ui->displayCategoriesBtn->setChecked(v); } @@ -2339,12 +2307,12 @@ void MainWindow::storeSettings(Settings& s) { s.geometry().saveState(this); s.geometry().saveGeometry(this); - s.geometry().setMenubarVisible(m_menuBarVisible); + s.geometry().saveVisibility(ui->menuBar); + s.geometry().saveVisibility(ui->statusBar); s.geometry().saveToolbars(this); - s.geometry().setStatusbarVisible(m_statusBarVisible); s.geometry().saveState(ui->splitter); s.geometry().saveMainWindowMonitor(this); - s.geometry().setFiltersVisible(ui->displayCategoriesBtn->isChecked()); + s.geometry().saveVisibility(ui->categoriesGroup); s.geometry().saveState(ui->espList->header()); s.geometry().saveState(ui->dataTree->header()); @@ -2606,7 +2574,7 @@ void MainWindow::setESPListSorting(int index) void MainWindow::refresher_progress(int percent) { setEnabled(percent == 100); - m_statusBar->setProgress(percent); + ui->statusBar->setProgress(percent); } void MainWindow::directory_refreshed() @@ -5216,7 +5184,7 @@ void MainWindow::on_actionSettings_triggered() activateProxy(settings.useProxy()); } - m_statusBar->checkSettings(m_OrganizerCore.settings()); + ui->statusBar->checkSettings(m_OrganizerCore.settings()); updateDownloadView(); m_OrganizerCore.setLogLevel(settings.logLevel()); @@ -5525,7 +5493,7 @@ void MainWindow::updateAvailable() { ui->actionUpdate->setEnabled(true); ui->actionUpdate->setToolTip(tr("Update available")); - m_statusBar->setUpdateAvailable(true); + ui->statusBar->setUpdateAvailable(true); } @@ -6858,7 +6826,7 @@ void MainWindow::keyReleaseEvent(QKeyEvent *event) // if the menubar is hidden, pressing Alt will make it visible if (event->key() == Qt::Key_Alt) { if (!ui->menuBar->isVisible()) { - showMenuBar(true); + ui->menuBar->show(); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index 8542dc8a..a905a163 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -40,7 +40,6 @@ class Executable; class CategoryFactory; class LockedDialogBase; class OrganizerCore; -class StatusBar; class PluginListSortProxy; namespace BSA { class Archive; } @@ -118,8 +117,6 @@ public: QWidget *parent = 0); ~MainWindow(); - void storeSettings(Settings& settings) override; - void readSettings(const Settings& settings); void processUpdates(Settings& settings); virtual ILockedWaitingForProcess* lock() override; @@ -331,12 +328,6 @@ private: bool m_WasVisible; - // this has to be remembered because by the time storeSettings() is called, - // the window is closed and the all bars are hidden - bool m_menuBarVisible, m_statusBarVisible; - - std::unique_ptr m_statusBar; - // last separator on the toolbar, used to add spacer for right-alignment and // as an insert point for executables QAction* m_linksSeparator; @@ -685,11 +676,9 @@ private slots: // ui slots void on_categoriesOrBtn_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); + void storeSettings(Settings& settings); + void readSettings(const Settings& settings); void setupModList(); - void showMenuBar(bool b); - void showStatusBar(bool b); }; - - #endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui index e9910b83..02c6dec0 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -48,1239 +48,1239 @@ - + + + - - - - - Categories - - - + + + Categories + + + + 0 + + + 3 + + + 7 + + + 3 + + + 1 + + + + + + 120 + 0 + + + + + 214 + 16777215 + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + 0 - - 3 + + true - - 7 + + false + + + + 1 + + + + + + + + false - - 3 + + + 0 + 0 + - - 1 + + + 0 + 25 + - - - - - 120 - 0 - - - - - 214 - 16777215 - - - - Qt::CustomContextMenu - - - QAbstractItemView::ExtendedSelection - - - 0 - - - true - - - false - - - - 1 - - - - - - - - false - - - - 0 - 0 - - - - - 0 - 25 - - - - Clear - - - true - - - - - - - - 0 - 0 - - - - - - - If checked, only mods that match all selected categories are displayed. - - - And - - - true - - - - - - - If checked, all mods that match at least one of the selected categories are displayed. - - - Or - - - - - - - - - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - 2 - - - - - - - - 0 - 0 - - - - Profile - - - profileBox - - - - - - - Pick a module collection - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 16777215 - 16777215 - - - - Open list options... - - - Refresh list. This is usually not necessary unless you modified data outside the program. - - - - - - - :/MO/gui/settings:/MO/gui/settings - - - - 16 - 16 - - - - - - - - Show Open Folders menu... - - - - - - - :/MO/gui/open_folder:/MO/gui/open_folder - - - + + Clear + + + true + + + + + + + + 0 + 0 + + + - + - Restore Backup... + If checked, only mods that match all selected categories are displayed. - + And - - - :/MO/gui/restore:/MO/gui/restore + + true - + - Create Backup - - - - - - - :/MO/gui/backup:/MO/gui/backup + If checked, all mods that match at least one of the selected categories are displayed. - - - - - Active: - - - - - - - - 0 - 26 - - - - This provides statistics about the mod list. The total number of active mod is normally displayed. Other statistics may be accessed with the tooltip of this counter. - - - QFrame::Sunken - - - 5 - - - QLCDNumber::Flat + Or + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + 2 + + + + + + + + 0 + 0 + + + + Profile + + + profileBox + + - - + + + Pick a module collection + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> + + + + + + + Qt::Horizontal + + - 330 - 400 + 40 + 20 - - Qt::CustomContextMenu + + + + + + + 16777215 + 16777215 + - List of available mods. + Open list options... - This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. + Refresh list. This is usually not necessary unless you modified data outside the program. - + + + + + + :/MO/gui/settings:/MO/gui/settings + + + + 16 + 16 + + + + + + + + Show Open Folders menu... + + + + + + + :/MO/gui/open_folder:/MO/gui/open_folder + + + + + + + Restore Backup... + + - - QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + :/MO/gui/restore:/MO/gui/restore - - true + + + + + + Create Backup - - true + + - - QAbstractItemView::DragDrop + + + :/MO/gui/backup:/MO/gui/backup - - Qt::MoveAction + + + + + + Active: - - true + + + + + + + 0 + 26 + - - QAbstractItemView::ExtendedSelection + + This provides statistics about the mod list. The total number of active mod is normally displayed. Other statistics may be accessed with the tooltip of this counter. - - QAbstractItemView::SelectRows + + QFrame::Sunken - - 20 + + 5 - - true + + QLCDNumber::Flat - - true + + + + + + + + + 330 + 400 + + + + Qt::CustomContextMenu + + + List of available mods. + + + This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. + + + + + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + true + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + 20 + + + true + + + true + + + true + + + false + + + 35 + + + true + + + false + + + + + + + + + + 20 + 16777215 + - - true + + x - - false + + + 20 + 20 + - - 35 - - + true - - + + + + + + + + 0 + 0 + + + + Filter + + + + + + + + 8 + true + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + 0 + 22 + + + + + 95 + 0 + + + false - + + + Qt::RightToLeft + + + border:1px solid #ff0000; + + + Clear all Filters + + + + :/MO/gui/edit_clear:/MO/gui/edit_clear + + + + 12 + 12 + + - - - - - - 20 - 16777215 - - - - x - - - - 20 - 20 - - - - true - - - - - - - - 0 - 0 - - - - Filter - - - - - - - - 8 - true - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 22 - - - - - 95 - 0 - - - - false - - - Qt::RightToLeft - - - border:1px solid #ff0000; - - - Clear all Filters - - - - :/MO/gui/edit_clear:/MO/gui/edit_clear - - - - 12 - 12 - - - + + + + 220 + 0 + + + + Qt::ClickFocus + + + + No groups + - - - - 220 - 0 - - - - Qt::ClickFocus - - - - No groups - - - - - Categories - - - - - Nexus IDs - - - + + Categories + - - - - 220 - 0 - - - - Filter - - + + Nexus IDs + - + - - - - - - + + + + 220 + 0 + + + + Filter + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 40 + + + + + 9 + 75 + true + + + + Pick a program to run. + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> + + + + 32 + 32 + + + + false + + + + + - + - + 0 0 - 0 - 40 + 120 + 0 + + + + + 16777215 + 16777215 - 9 + 10 75 true - Pick a program to run. + Run program <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + + + + + + Run + + + + :/MO/gui/run:/MO/gui/run - 32 - 32 + 36 + 36 - - false - - - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 16777215 - 16777215 - - - - - 10 - 75 - true - - - - Run program - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> - - - - - - Run - - - - :/MO/gui/run:/MO/gui/run - - - - 36 - 36 - - - - - - - - - 0 - 0 - - - - - 140 - 0 - - - - - 16777215 - 16777215 - - - - - 0 - 0 - - - - Create a shortcut in your start menu or on the desktop to the specified program - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + 0 + 0 + + + + + 140 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + Create a shortcut in your start menu or on the desktop to the specified program + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> - - - Shortcut - - - - :/MO/gui/link:/MO/gui/link - - - - + + + Shortcut + + + + :/MO/gui/link:/MO/gui/link + + - - - - - - - 340 - 250 - - - - - 16777215 - 16777215 - + + + + + + + + + 340 + 250 + + + + + 16777215 + 16777215 + + + + Qt::NoContextMenu + + + QTabWidget::Rounded + + + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Plugins + + + + 6 - - Qt::NoContextMenu + + 6 - - QTabWidget::Rounded + + 6 - + 0 - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Plugins - - - - 6 + + + + + + true + + + Sort + + + + :/MO/gui/sort:/MO/gui/sort + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Restore Backup... + + + + + + + :/MO/gui/restore:/MO/gui/restore + + + + 16 + 16 + + + + + + + + Create Backup + + + + + + + :/MO/gui/backup:/MO/gui/backup + + + + + + + Active: + + + + + + + + 0 + 26 + + + + This provides statistics about the plugin list. The total number of active plugins is normally displayed. Other statistics may be accessed with the tooltip of this counter. + + + QFrame::Sunken + + + 4 + + + QLCDNumber::Flat + + + + + + + + + + 250 + 250 + + + + Qt::CustomContextMenu + + + List of available esp/esm files + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This list contains the esps, esms, and esls contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> + + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + true + + + false + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + 0 + + + true - - 6 + + false - - 6 + + true - - 0 + + false + + false + + + + + - - - - - true - - - Sort - - - - :/MO/gui/sort:/MO/gui/sort - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Restore Backup... - - - - - - - :/MO/gui/restore:/MO/gui/restore - - - - 16 - 16 - - - - - - - - Create Backup - - - - - - - :/MO/gui/backup:/MO/gui/backup - - - - - - - Active: - - - - - - - - 0 - 26 - - - - This provides statistics about the plugin list. The total number of active plugins is normally displayed. Other statistics may be accessed with the tooltip of this counter. - - - QFrame::Sunken - - - 4 - - - QLCDNumber::Flat - - - - - - - - - - 250 - 250 - + + + - - Qt::CustomContextMenu + + Filter + + + + + + + + + false + + + Archives + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + - List of available esp/esm files - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This list contains the esps, esms, and esls contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> - - - QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked - - - true - - - false - - - QAbstractItemView::InternalMove - - - Qt::MoveAction - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - 0 - - - true + <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - - false + + <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - + true - - false - - - false - - - - - - - - - - Filter - - - - - - - - - false - - - Archives - - - - 6 + + + + + Qt::CustomContextMenu - - 6 + + List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - - 6 + + BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. + By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! + + BSAs checked here are loaded in such a way that your installation order is obeyed properly. - - 6 + + false + + false + + + false + + + 20 + + + true + + + 1 + + + + + + + + Data + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + refresh data-directory overview + + + Refresh the overview. This may take a moment. + + + Refresh + + + + :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + + + + + - - - - - <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - - - <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - - - true - - - - - - - + Qt::CustomContextMenu - - List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - - BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. - By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! - - BSAs checked here are loaded in such a way that your installation order is obeyed properly. - - - false - - - false - - - false + This is an overview of your data directory as visible to the game (and tools). - - 20 - - + true - - 1 + + true + + 400 + + + + File + + + + + Mod + + - - - - Data - - - - 6 - - - 6 - - - 6 - - - 6 - + + + - + - refresh data-directory overview + Filters the above list so that only conflicts are displayed. - Refresh the overview. This may take a moment. + Filters the above list so that only conflicts are displayed. - Refresh - - - - :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + Show only conflicts - - - - - Qt::CustomContextMenu - - - This is an overview of your data directory as visible to the game (and tools). - - - true - - - true - - - 400 - - - - File - - - - - Mod - - - - - - - - - - - - Filters the above list so that only conflicts are displayed. - - - Filters the above list so that only conflicts are displayed. - - - Show only conflicts - - - - - - - Filters the above list so that files from archives are not shown - - - - - - Filters the above list so that files from archives are not shown - - - Show files from Archives - - - - + + + Filters the above list so that files from archives are not shown + + + + + + Filters the above list so that files from archives are not shown + + + Show files from Archives + + - - - - Saves - - - - 6 + + + + + + Saves + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Qt::CustomContextMenu + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> +<p style="-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;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows - - 6 + + + + + + + Downloads + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Refresh downloads view - - 6 + + Refresh - - 6 + + + :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + + + + - + + + + 320 + 0 + + Qt::CustomContextMenu + + true + - + - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> -<p style="-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;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. + + + Qt::ScrollBarAlwaysOn + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::ScrollPerPixel + + + 0 - - QAbstractItemView::ExtendedSelection + + false - - QAbstractItemView::SelectRows + + true - - - - Downloads - - - - 2 - - - 2 - - - 2 - - - 2 - + + + - - - Refresh downloads view - + - Refresh - - - - :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + Show Hidden - - - - - - 320 - 0 - - - - Qt::CustomContextMenu - - - true - - - - - - This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - - - Qt::ScrollBarAlwaysOn - - - true - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - true - - - QAbstractItemView::ScrollPerPixel - - - 0 - - - false - - - true - - - - + + + Qt::Horizontal + + + + 40 + 20 + + + - - - - - Show Hidden - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Filter - - - - + + + Filter + + - - - - - - - - + + + + + + + + + + @@ -1320,7 +1320,7 @@ p, li { white-space: pre-wrap; } - + @@ -1790,6 +1790,11 @@ p, li { white-space: pre-wrap; } QTreeView
loglist.h
+ + StatusBar + QStatusBar +
statusbar.h
+
diff --git a/src/organizercore.cpp b/src/organizercore.cpp index 2d11dafd..a2b0fd69 100644 --- a/src/organizercore.cpp +++ b/src/organizercore.cpp @@ -335,10 +335,6 @@ OrganizerCore::~OrganizerCore() void OrganizerCore::storeSettings() { - if (m_UserInterface != nullptr) { - m_UserInterface->storeSettings(m_Settings); - } - if (m_CurrentProfile != nullptr) { m_Settings.setSelectedProfileName(m_CurrentProfile->name()); } diff --git a/src/settings.cpp b/src/settings.cpp index db6cecdf..06b4446a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -942,6 +942,12 @@ QString stateSettingName(const Widget* widget) return "geometry/" + widgetName(widget) + "_state"; } +template +QString visibilitySettingName(const Widget* widget) +{ + return "geometry/" + widgetName(widget) + "_visibility"; +} + GeometrySettings::GeometrySettings(QSettings& s) : m_Settings(s), m_Reset(false) @@ -962,18 +968,6 @@ void GeometrySettings::resetIfNeeded() m_Settings.beginGroup("geometry"); m_Settings.remove(""); m_Settings.endGroup(); - - /*settings.remove("window_geometry"); - settings.remove("window_state"); - settings.remove("toolbar_size"); - settings.remove("toolbar_button_style"); - settings.remove("menubar_visible"); - settings.remove("window_split"); - settings.remove("window_monitor"); - settings.remove("filters_visible"); - settings.remove("browser_geometry"); - settings.remove("geometry"); - settings.remove("reset_geometry");*/ } void GeometrySettings::saveGeometry(const QWidget* w) @@ -1036,15 +1030,32 @@ bool GeometrySettings::restoreState(QSplitter* w) const return false; } -bool GeometrySettings::restoreToolbars(QMainWindow* w) const +void GeometrySettings::saveVisibility(const QWidget* w) { - const auto size = getOptional(m_Settings, "toolbar_size"); - const auto style = getOptional(m_Settings, "toolbar_button_style"); + m_Settings.setValue(visibilitySettingName(w), w->isVisible()); +} - if (!size && !style) { - return false; +bool GeometrySettings::restoreVisibility(QWidget* w, std::optional def) const +{ + auto v = getOptional(m_Settings, visibilitySettingName(w)); + if (!v) { + v = def; + } + + if (v) { + w->setVisible(*v); + return true; } + return false; +} + +void GeometrySettings::restoreToolbars(QMainWindow* w) const +{ + // all toolbars have the same size and button style settings + const auto size = getOptional(m_Settings, "toolbar_size"); + const auto style = getOptional(m_Settings, "toolbar_button_style"); + for (auto* tb : w->findChildren()) { if (size) { tb->setIconSize(*size); @@ -1053,53 +1064,28 @@ bool GeometrySettings::restoreToolbars(QMainWindow* w) const if (style) { tb->setToolButtonStyle(static_cast(*style)); } - } - return true; + restoreVisibility(tb); + } } void GeometrySettings::saveToolbars(const QMainWindow* w) { - // all toolbars are identical, just save the first one const auto tbs = w->findChildren(); - if (tbs.isEmpty()) { - return; - } - - const auto* tb = tbs[0]; - - m_Settings.setValue("toolbar_size", tb->iconSize()); - m_Settings.setValue("toolbar_button_style", static_cast(tb->toolButtonStyle())); -} - -std::optional GeometrySettings::getMenubarVisible() const -{ - return getOptional(m_Settings, "menubar_visible"); -} - -void GeometrySettings::setMenubarVisible(bool b) -{ - m_Settings.setValue("menubar_visible", b); -} - -std::optional GeometrySettings::getStatusbarVisible() const -{ - return getOptional(m_Settings, "statusbar_visible"); -} -void GeometrySettings::setStatusbarVisible(bool b) -{ - m_Settings.setValue("statusbar_visible", b); -} + // save visibility for all + for (auto* tb : tbs) { + saveVisibility(tb); + } -std::optional GeometrySettings::getFiltersVisible() const -{ - return getOptional(m_Settings, "filters_visible"); -} + // all toolbars have the same size and button style settings, just save the + // first one + if (!tbs.isEmpty()) { + const auto* tb = tbs[0]; -void GeometrySettings::setFiltersVisible(bool b) -{ - m_Settings.setValue("filters_visible", b); + m_Settings.setValue("toolbar_size", tb->iconSize()); + m_Settings.setValue("toolbar_button_style", static_cast(tb->toolButtonStyle())); + } } QStringList GeometrySettings::getModInfoTabOrder() const diff --git a/src/settings.h b/src/settings.h index 9ae58803..072b4066 100644 --- a/src/settings.h +++ b/src/settings.h @@ -54,6 +54,7 @@ public: void requestReset(); void resetIfNeeded(); + void saveGeometry(const QWidget* w); bool restoreGeometry(QWidget* w) const; @@ -69,17 +70,13 @@ public: void saveState(const QSplitter* splitter); bool restoreState(QSplitter* splitter) const; - std::optional getMenubarVisible() const; - void setMenubarVisible(bool b); - bool restoreToolbars(QMainWindow* w) const; - void saveToolbars(const QMainWindow* w); + void saveVisibility(const QWidget* w); + bool restoreVisibility(QWidget* w, std::optional defaultValue={}) const; - std::optional getStatusbarVisible() const; - void setStatusbarVisible(bool b); - std::optional getFiltersVisible() const; - void setFiltersVisible(bool b); + void saveToolbars(const QMainWindow* w); + void restoreToolbars(QMainWindow* w) const; QStringList getModInfoTabOrder() const; void setModInfoTabOrder(const QString& names); diff --git a/src/statusbar.cpp b/src/statusbar.cpp index e9a6e658..d22010a5 100644 --- a/src/statusbar.cpp +++ b/src/statusbar.cpp @@ -3,26 +3,32 @@ #include "settings.h" #include "ui_mainwindow.h" -StatusBar::StatusBar(QStatusBar* bar, Ui::MainWindow* ui) : - m_bar(bar), m_progress(new QProgressBar), - m_notifications(new StatusBarAction(ui->actionNotifications)), - m_update(new StatusBarAction(ui->actionUpdate)), - m_api(new QLabel) +StatusBar::StatusBar(QWidget* parent) : + QStatusBar(parent), ui(nullptr), m_progress(new QProgressBar), + m_notifications(nullptr), m_update(nullptr), m_api(new QLabel) { +} + +void StatusBar::setup(Ui::MainWindow* mainWindowUI) +{ + ui = mainWindowUI; + m_notifications = new StatusBarAction(ui->actionNotifications); + m_update = new StatusBarAction(ui->actionUpdate); + QWidget* spacer1 = new QWidget; spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); spacer1->setHidden(true); spacer1->setVisible(true); - m_bar->addPermanentWidget(spacer1, 0); - m_bar->addPermanentWidget(m_progress); + addPermanentWidget(spacer1, 0); + addPermanentWidget(m_progress); QWidget* spacer2 = new QWidget; spacer2->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); spacer2->setHidden(true); spacer2->setVisible(true); - m_bar->addPermanentWidget(spacer2,0); - m_bar->addPermanentWidget(m_notifications); - m_bar->addPermanentWidget(m_update); - m_bar->addPermanentWidget(m_api); + addPermanentWidget(spacer2,0); + addPermanentWidget(m_notifications); + addPermanentWidget(m_update); + addPermanentWidget(m_api); m_progress->setTextVisible(true); @@ -42,7 +48,7 @@ StatusBar::StatusBar(QStatusBar* bar, Ui::MainWindow* ui) : "be unable to queue downloads, check updates, parse mod info, or even log " "in. Both pools must be consumed before this happens.")); - m_bar->clearMessage(); + clearMessage(); setProgress(-1); setAPI({}, {}); } @@ -50,10 +56,10 @@ StatusBar::StatusBar(QStatusBar* bar, Ui::MainWindow* ui) : void StatusBar::setProgress(int percent) { if (percent < 0 || percent >= 100) { - m_bar->clearMessage(); + clearMessage(); m_progress->setVisible(false); } else { - m_bar->showMessage(QObject::tr("Loading...")); + showMessage(QObject::tr("Loading...")); m_progress->setVisible(true); m_progress->setValue(percent); } @@ -126,6 +132,37 @@ void StatusBar::checkSettings(const Settings& settings) m_api->setVisible(!settings.hideAPICounter()); } +void StatusBar::showEvent(QShowEvent*) +{ + visibilityChanged(true); +} + +void StatusBar::hideEvent(QHideEvent*) +{ + visibilityChanged(false); +} + +void StatusBar::visibilityChanged(bool visible) +{ + // the central widget typically has no bottom padding because the status bar + // is more than enough, but when it's hidden, the bottom widget (currently + // the log) touches the bottom border of the window, which looks ugly + // + // when hiding the statusbar, the central widget is given the same border + // margin as it has on the top (which is typically 6, as it's the default from + // the qt designer) + + auto m = ui->centralWidget->layout()->contentsMargins(); + + if (visible) { + m.setBottom(0); + } else { + m.setBottom(m.top()); + } + + ui->centralWidget->layout()->setContentsMargins(m); +} + StatusBarAction::StatusBarAction(QAction* action) : m_action(action), m_icon(new QLabel), m_text(new QLabel) diff --git a/src/statusbar.h b/src/statusbar.h index 2baf12ee..442b9acf 100644 --- a/src/statusbar.h +++ b/src/statusbar.h @@ -29,10 +29,12 @@ private: }; -class StatusBar +class StatusBar : public QStatusBar { public: - StatusBar(QStatusBar* bar, Ui::MainWindow* ui); + StatusBar(QWidget* parent=nullptr); + + void setup(Ui::MainWindow* ui); void setProgress(int percent); void setNotifications(bool hasNotifications); @@ -40,12 +42,18 @@ public: void setUpdateAvailable(bool b); void checkSettings(const Settings& settings); +protected: + void showEvent(QShowEvent* e); + void hideEvent(QHideEvent* e); + private: - QStatusBar* m_bar; + Ui::MainWindow* ui; QProgressBar* m_progress; StatusBarAction* m_notifications; StatusBarAction* m_update; QLabel* m_api; + + void visibilityChanged(bool visible); }; #endif // MO_STATUSBAR_H -- cgit v1.3.1 From b389161077675752b6f551e7a6563c4eb0e0ca80 Mon Sep 17 00:00:00 2001 From: Al Date: Wed, 2 Oct 2019 23:06:35 +0200 Subject: Made categories section (filters) resizable via a splitter. Correctly handled hiding and unhiding as well as saving the state between sessions. --- src/mainwindow.cpp | 2 + src/mainwindow.ui | 2215 +++++++++++++++++++++++++-------------------------- src/organizer_en.ts | 857 ++++++++++---------- 3 files changed, 1535 insertions(+), 1539 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 42cbe919..91da786c 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -2139,6 +2139,7 @@ void MainWindow::readSettings(const Settings& settings) settings.geometry().restoreDocks(this); settings.geometry().restoreToolbars(this); settings.geometry().restoreState(ui->splitter); + settings.geometry().restoreState(ui->categoriesSplitter); settings.geometry().restoreVisibility(ui->menuBar); settings.geometry().restoreVisibility(ui->statusBar); @@ -2218,6 +2219,7 @@ void MainWindow::storeSettings(Settings& s) s.geometry().saveVisibility(ui->statusBar); s.geometry().saveToolbars(this); s.geometry().saveState(ui->splitter); + s.geometry().saveState(ui->categoriesSplitter); s.geometry().saveMainWindowMonitor(this); s.geometry().saveVisibility(ui->categoriesGroup); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 02c6dec0..6d36e035 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -33,88 +33,427 @@ Qt::CustomContextMenu - - - 6 - - - 6 - - - 6 - - - 0 - + - - - - + + + Qt::Horizontal + + + + + 500 + 16777215 + + + + Categories + + + + 0 + + + 3 + + + 7 + + + 3 + + + 1 + + + + + + 120 + 0 + + + + Qt::CustomContextMenu + + + QAbstractItemView::ExtendedSelection + + + 0 + + + true + + + false + + + + 1 + + + + + + + + false + + + + 0 + 0 + + + + + 0 + 25 + + + + Clear + + + true + + + - - - Categories + + + + 0 + 0 + - - - 0 + + + + + If checked, only mods that match all selected categories are displayed. + + + And + + + true + + + + + + + If checked, all mods that match at least one of the selected categories are displayed. + + + Or + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + + 2 + + + + + + + + 0 + 0 + + + + Profile + + + profileBox + + + + + + + Pick a module collection + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 16777215 + 16777215 + + + + Open list options... + + + Refresh list. This is usually not necessary unless you modified data outside the program. + + + + + + + :/MO/gui/settings:/MO/gui/settings + + + + 16 + 16 + + + + + + + + Show Open Folders menu... + + + + + + + :/MO/gui/open_folder:/MO/gui/open_folder + + + + + + + Restore Backup... + + + + + + + :/MO/gui/restore:/MO/gui/restore + + + + + + + Create Backup + + + + + + + :/MO/gui/backup:/MO/gui/backup + + + + + + + Active: + + + + + + + + 0 + 26 + + + + This provides statistics about the mod list. The total number of active mod is normally displayed. Other statistics may be accessed with the tooltip of this counter. + + + QFrame::Sunken + + + 5 + + + QLCDNumber::Flat + + + + + + + + + + 330 + 400 + + + + Qt::CustomContextMenu + + + List of available mods. + + + This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. + + + + + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + true + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows - - 3 + + 20 - - 7 + + true - - 3 + + true - - 1 + + true + + false + + + 35 + + + true + + + false + + + + + - - + + - 120 - 0 + 20 + 16777215 - + + x + + - 214 - 16777215 + 20 + 20 - - Qt::CustomContextMenu + + true - - QAbstractItemView::ExtendedSelection + + + + + + + 0 + 0 + - - 0 + + Filter - - true + + + + + + + 8 + true + - - false - - - - 1 - - - - - false + + + Qt::Horizontal + + + + 40 + 20 + + + + + - + 0 0 @@ -122,587 +461,795 @@ 0 - 25 + 22 + + + + + 95 + 0 + + false + + + Qt::RightToLeft + + + border:1px solid #ff0000; + - Clear + Clear all Filters - - true + + + :/MO/gui/edit_clear:/MO/gui/edit_clear + + + + 12 + 12 + - - - - 0 - 0 - + + + + 220 + 0 + + + + Qt::ClickFocus + + + + No groups + + + + + Categories + + + + + Nexus IDs + + + + + + + + + 220 + 0 + + + + Filter - - - - - If checked, only mods that match all selected categories are displayed. - - - And - - - true - - - - - - - If checked, all mods that match at least one of the selected categories are displayed. - - - Or - - - - - - - - - - - - - 0 - 0 - - - - Qt::Horizontal - - - - - 2 - - - + + + + + + + + - + - + 0 0 - - Profile + + + 0 + 40 + - - profileBox + + + 9 + 75 + true + - - - - - Pick a module collection + Pick a program to run. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Create profiles here. Each profile contains its own list of active mods and esps. This way you can quickly switch between setups for different playthroughs.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Please note that right now your esp load order is not kept separate for different profiles.</span></p></body></html> - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 16777215 - 16777215 - - - - Open list options... - - - Refresh list. This is usually not necessary unless you modified data outside the program. - - - - - - - :/MO/gui/settings:/MO/gui/settings - - - - 16 - 16 - - - - - - - - Show Open Folders menu... - - - - - - - :/MO/gui/open_folder:/MO/gui/open_folder - - - - - - - Restore Backup... - - - - - - - :/MO/gui/restore:/MO/gui/restore - - - - - - - Create Backup - - - - - - - :/MO/gui/backup:/MO/gui/backup - - - - - - - Active: - - - - - - - - 0 - 26 - - - - This provides statistics about the mod list. The total number of active mod is normally displayed. Other statistics may be accessed with the tooltip of this counter. - - - QFrame::Sunken - - - 5 - - - QLCDNumber::Flat - - - - - - - - - - 330 - 400 - - - - Qt::CustomContextMenu - - - List of available mods. - - - This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. - - - - - - QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked - - - true - - - true - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - 20 - - - true - - - true - - - true - - - false - - - 35 - - - true - - - false - - - - - - - - - - 20 - 16777215 - - - - x +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> - 20 - 20 + 32 + 32 - - true - - - - - - - - 0 - 0 - - - - Filter - - - - - - - - 8 - true - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 22 - - - - - 95 - 0 - - - + false - - Qt::RightToLeft - - - border:1px solid #ff0000; - - - Clear all Filters - - - - :/MO/gui/edit_clear:/MO/gui/edit_clear - - - - 12 - 12 - - - - - - 220 - 0 - - - - Qt::ClickFocus - + - - No groups - - - - - Categories - + + + + 0 + 0 + + + + + 120 + 0 + + + + + 16777215 + 16777215 + + + + + 10 + 75 + true + + + + Run program + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + + + + + + Run + + + + :/MO/gui/run:/MO/gui/run + + + + 36 + 36 + + + - - Nexus IDs - + + + + 0 + 0 + + + + + 140 + 0 + + + + + 16777215 + 16777215 + + + + + 0 + 0 + + + + Create a shortcut in your start menu or on the desktop to the specified program + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> + + + Shortcut + + + + :/MO/gui/link:/MO/gui/link + + - - - - - - - 220 - 0 - - - - Filter - - + - - - - - - - - + + + + + + + 340 + 250 + + + + + 16777215 + 16777215 + + + + Qt::NoContextMenu + + + QTabWidget::Rounded + + + 4 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Plugins + + + + 6 + + + 6 + + + 6 + + + 0 + - - - - 0 - 0 - - + + + + + false + + + Sort + + + + :/MO/gui/sort:/MO/gui/sort + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Restore Backup... + + + + + + + :/MO/gui/restore:/MO/gui/restore + + + + 16 + 16 + + + + + + + + Create Backup + + + + + + + :/MO/gui/backup:/MO/gui/backup + + + + + + + Active: + + + + + + + + 0 + 26 + + + + This provides statistics about the plugin list. The total number of active plugins is normally displayed. Other statistics may be accessed with the tooltip of this counter. + + + QFrame::Sunken + + + 4 + + + QLCDNumber::Flat + + + + + + + - 0 - 40 + 250 + 250 - - - 9 - 75 - true - + + Qt::CustomContextMenu - Pick a program to run. + List of available esp/esm files <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Choose the program to run. Once you start using ModOrganizer, you should always run your game and tools from here or through shortcuts created here, otherwise mods installed through MO will not be visible.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You can add new Tools to this list, but I can't promise tools I haven't tested will work.</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This list contains the esps, esms, and esls contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> - - - 32 - 32 - + + QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + true + + + false + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + 0 + + + true + + + false + + + true - + false + + false + - + - - - - 0 - 0 - + + + - - - 120 - 0 - + + Filter - - - 16777215 - 16777215 - + + + + + + + + + false + + + Archives + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + + <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - - - 10 - 75 - true - + + <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - - Run program + + true + + + + + + + + + Qt::CustomContextMenu + + + List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. + + + BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. + By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! + + BSAs checked here are loaded in such a way that your installation order is obeyed properly. + + + false + + + false + + + false + + + 20 + + + true + + + 1 + + + + + + + + Data + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + refresh data-directory overview + + + Refresh the overview. This may take a moment. + + + Refresh + + + + :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + + + + + + + + + Qt::CustomContextMenu - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Run the selected program with ModOrganizer enabled.</span></p></body></html> + This is an overview of your data directory as visible to the game (and tools). - - + + true + + + true + + + 400 + + + + File + + + + + Mod + + + + + + + + + + + + Filters the above list so that only conflicts are displayed. + + + Filters the above list so that only conflicts are displayed. - Run + Show only conflicts - - - :/MO/gui/run:/MO/gui/run + + + + + + Filters the above list so that files from archives are not shown - - - 36 - 36 - + + + + + Filters the above list so that files from archives are not shown + + + Show files from Archives + + + + + + + Saves + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Qt::CustomContextMenu + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> +<p style="-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;"></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + + + + + + Downloads + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + Refresh downloads view + + + Refresh + + + + :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png + + + + + - - - - 0 - 0 - - + - 140 + 320 0 - - - 16777215 - 16777215 - + + Qt::CustomContextMenu - - - 0 - 0 - + + true - Create a shortcut in your start menu or on the desktop to the specified program + - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This creates a start menu shortcut that directly starts the selected program with the MO active.</span></p></body></html> + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. + + + Qt::ScrollBarAlwaysOn + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + true + + + QAbstractItemView::ScrollPerPixel + + + 0 + + + false + + + true + + + + + + + + + - Shortcut + Show Hidden - - - :/MO/gui/link:/MO/gui/link + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filter @@ -710,578 +1257,12 @@ p, li { white-space: pre-wrap; } - - - - - - 340 - 250 - - - - - 16777215 - 16777215 - - - - Qt::NoContextMenu - - - QTabWidget::Rounded - - - 0 - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Plugins - - - - 6 - - - 6 - - - 6 - - - 0 - - - - - - - true - - - Sort - - - - :/MO/gui/sort:/MO/gui/sort - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Restore Backup... - - - - - - - :/MO/gui/restore:/MO/gui/restore - - - - 16 - 16 - - - - - - - - Create Backup - - - - - - - :/MO/gui/backup:/MO/gui/backup - - - - - - - Active: - - - - - - - - 0 - 26 - - - - This provides statistics about the plugin list. The total number of active plugins is normally displayed. Other statistics may be accessed with the tooltip of this counter. - - - QFrame::Sunken - - - 4 - - - QLCDNumber::Flat - - - - - - - - - - 250 - 250 - - - - Qt::CustomContextMenu - - - List of available esp/esm files - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This list contains the esps, esms, and esls contained in the active mods. These require their own load order. Use drag&amp;drop to modify this load order. Please note that MO will only save the load order for mods that are active/checked.<br />There is a great tool named &quot;BOSS&quot; to automatically sort these files.</span></p></body></html> - - - QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked - - - true - - - false - - - QAbstractItemView::InternalMove - - - Qt::MoveAction - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - 0 - - - true - - - false - - - true - - - false - - - false - - - - - - - - - - - - Filter - - - - - - - - - - false - - - Archives - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - - <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - - - <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - - - true - - - - - - - - - Qt::CustomContextMenu - - - List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - - - BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. - By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! - - BSAs checked here are loaded in such a way that your installation order is obeyed properly. - - - false - - - false - - - false - - - 20 - - - true - - - 1 - - - - - - - - Data - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - refresh data-directory overview - - - Refresh the overview. This may take a moment. - - - Refresh - - - - :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png - - - - - - - - - Qt::CustomContextMenu - - - This is an overview of your data directory as visible to the game (and tools). - - - true - - - true - - - 400 - - - - File - - - - - Mod - - - - - - - - - - - - Filters the above list so that only conflicts are displayed. - - - Filters the above list so that only conflicts are displayed. - - - Show only conflicts - - - - - - - Filters the above list so that files from archives are not shown - - - - - - Filters the above list so that files from archives are not shown - - - Show files from Archives - - - - - - - - - - Saves - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - Qt::CustomContextMenu - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This is a list of all save games for this game. Hover over a list entry to get detailed information about the save including a list of esps/esms that were used at the time this save was created but aren't active now.</span></p> -<p style="-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;"></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - - - - - - Downloads - - - - 2 - - - 2 - - - 2 - - - 2 - - - - - Refresh downloads view - - - Refresh - - - - :/MO/gui/resources/view-refresh.png:/MO/gui/resources/view-refresh.png - - - - - - - - - - 320 - 0 - - - - Qt::CustomContextMenu - - - true - - - - - - This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - - - Qt::ScrollBarAlwaysOn - - - true - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - true - - - QAbstractItemView::ScrollPerPixel - - - 0 - - - false - - - true - - - - - - - - - - - Show Hidden - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Filter - - - - - - - - - - - + + + - - + diff --git a/src/organizer_en.ts b/src/organizer_en.ts index 465b0c87..e3531cb2 100644 --- a/src/organizer_en.ts +++ b/src/organizer_en.ts @@ -768,194 +768,204 @@ File %3: %4 - + + Hashing download file '%1' + + + + + Cancel + + + + VisitNexus: invalid download index %1 - + Nexus ID for this Mod is unknown - + OpenFile: invalid download index %1 - + OpenFileInDownloadsFolder: invalid download index %1 - + get pending: invalid download index %1 - + get path: invalid download index %1 - + Main - + Update - + Optional - + Old - + Miscellaneous - + Deleted - + Unknown - + display name: invalid download index %1 - + file name: invalid download index %1 - + file time: invalid download index %1 - + file size: invalid download index %1 - + progress: invalid download index %1 - + state: invalid download index %1 - + infocomplete: invalid download index %1 - - + + mod id: invalid download index %1 - + ishidden: invalid download index %1 - + file info: invalid download index %1 - + mark installed: invalid download index %1 - + mark uninstalled: invalid download index %1 - + Memory allocation error (in processing progress event). - + Memory allocation error (in processing downloaded data). - + Information updated - - + + No matching file found on Nexus! Maybe this file is no longer available or it was renamed? - + No file on Nexus matches the selected file by name. Please manually choose the correct one. - + No download server available. Please try again later. - + Failed to request file info from nexus: %1 - + Warning: Content type is: %1 - + Download header content length: %1 downloaded file size: %2 - + Download failed: %1 (%2) - + We were unable to download the file due to errors after four retries. There may be an issue with the Nexus servers. - + failed to re-open %1 - + Unable to write download to drive (return %1). Check the drive's available storage. @@ -1622,48 +1632,48 @@ This is likely due to a corrupted or incompatible download or unrecognized archi MainWindow - - + + Categories - + Clear - + If checked, only mods that match all selected categories are displayed. - + And - + If checked, all mods that match at least one of the selected categories are displayed. - + Or - + Profile - + Pick a module collection - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1673,84 +1683,84 @@ p, li { white-space: pre-wrap; } - + Open list options... - + Refresh list. This is usually not necessary unless you modified data outside the program. - + Show Open Folders menu... - - + + Restore Backup... - - - + + + Create Backup - - + + Active: - + This provides statistics about the mod list. The total number of active mod is normally displayed. Other statistics may be accessed with the tooltip of this counter. - + List of available mods. - + This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. - - - - + + + + Filter - + Clear all Filters - + No groups - + Nexus IDs - + Pick a program to run. - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1760,12 +1770,12 @@ p, li { white-space: pre-wrap; } - + Run program - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1774,17 +1784,17 @@ p, li { white-space: pre-wrap; } - + Run - + Create a shortcut in your start menu or on the desktop to the specified program - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1793,32 +1803,32 @@ p, li { white-space: pre-wrap; } - + Shortcut - + Plugins - + Sort - + This provides statistics about the plugin list. The total number of active plugins is normally displayed. Other statistics may be accessed with the tooltip of this counter. - + List of available esp/esm files - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1827,27 +1837,27 @@ p, li { white-space: pre-wrap; } - + Archives - + <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - + <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> - + List of available BS Archives. Archives not checked here are not managed by MO and ignore installation order. - + BSA files are archives (comparable to .zip files) that contain data assets (meshes, textures, ...) to be used by the game. As such they "compete" with loose files in your data directory over which is loaded. By default, BSAs that share their base name with an enabled ESP (i.e. plugin.esp and plugin.bsa) are automatically loaded and will have precedence over all loose files, the installation order you set up to the left is then ignored! @@ -1855,72 +1865,72 @@ p, li { white-space: pre-wrap; } - + Data - + refresh data-directory overview - + Refresh the overview. This may take a moment. - - - - + + + + Refresh - + This is an overview of your data directory as visible to the game (and tools). - + File - + Mod - - + + Filters the above list so that only conflicts are displayed. - + Show only conflicts - - + + Filters the above list so that files from archives are not shown - + Show files from Archives - + Saves - + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -1931,307 +1941,307 @@ p, li { white-space: pre-wrap; } - + Downloads - + Refresh downloads view - + This is a list of mods you downloaded from Nexus. Double click one to install it. You can also drag an archive into here. - + Show Hidden - + Main ToolBar - + &File - - + + &Tools - - - + + + &Help - + &View - + &Toolbars - + &Run - - + + Log - + Install &Mod... - + Install &Mod - - + + Install a new mod from an archive - + Ctrl+M - + &Profiles... - + &Profiles - - + + Configure profiles - + Ctrl+P - + &Executables... - + &Executables - - + + Configure the executables that can be started through Mod Organizer - + Ctrl+E - + &Tool Plugins - + Tools - + Ctrl+I - + &Settings... - + &Settings - - + + Configure settings and workarounds - + Ctrl+S - - + + Visit &Nexus - - + + Visit the Nexus website in your browser for more mods - + Ctrl+N - - + + &Update Mod Organizer - - + + Mod Organizer is up-to-date - + &Notifications... - - + + Open the notifications dialog - + This button will be highlighted on the toolbar if MO discovered potential problems in your setup and provide tips on how to fix them. - - + + Show help options - + Ctrl+H - - + + &Endorse ModOrganizer - - - + + + Endorse Mod Organizer - + &Change Game... - + &Change Game - - + + Open the Instance selection dialog to manage a different Game - - + + E&xit - - + + Exits Mod Organizer - + M&ain Toolbar - + &Small Icons - + Lar&ge Icons - + &Icons Only - + &Text Only - + I&cons and Text - + M&edium Icons - + &Menu - + Status &bar St&atus bar @@ -2280,8 +2290,8 @@ Error: %1 - - + + Endorse @@ -2401,700 +2411,700 @@ Error: %1 - + Notice: Your current MO version (%1) is lower than the previously used one (%2). The GUI may not downgrade gracefully, so you may experience oddities. However, there should be no serious issues. - + Choose Mod - + Mod Archive - + Start Tutorial? - + You're about to start a tutorial. For technical reasons it's not possible to end the tutorial early. Continue? - + failed to change origin name: %1 - + failed to move "%1" from mod "%2" to "%3": %4 - + <Contains %1> - + <Checked> - + <Unchecked> - + <Update> - + <Mod Backup> - + <Managed by MO> - + <Managed outside MO> - + <No category> - + <Conflicted> - + <Not Endorsed> - + failed to rename mod: %1 - + Overwrite? - + This will replace the existing mod "%1". Continue? - + failed to remove mod "%1" - - - + + + failed to rename "%1" to "%2" - - - - + + + + Confirm - + Remove the following mods?<br><ul>%1</ul> - + failed to remove mod: %1 - - - + + + Failed - + Installation file no longer exists - + Mods installed with old versions of MO can't be reinstalled in this way. - + Failed to create backup. - + Endorsing multiple mods will take a while. Please wait... - + Unendorsing multiple mods will take a while. Please wait... - + Failed to display overwrite dialog: %1 - + Opening Nexus Links - + You are trying to open %1 links to Nexus Mods. Are you sure you want to do this? - + Nexus ID for this mod is unknown - + Opening Web Pages - + You are trying to open %1 Web Pages. Are you sure you want to do this? - + <table cellspacing="5"><tr><th>Type</th><th>All</th><th>Visible</th><tr><td>Enabled mods:&emsp;</td><td align=right>%1 / %2</td><td align=right>%3 / %4</td></tr><tr><td>Unmanaged/DLCs:&emsp;</td><td align=right>%5</td><td align=right>%6</td></tr><tr><td>Mod backups:&emsp;</td><td align=right>%7</td><td align=right>%8</td></tr><tr><td>Separators:&emsp;</td><td align=right>%9</td><td align=right>%10</td></tr></table> - + <table cellspacing="6"><tr><th>Type</th><th>Active </th><th>Total</th></tr><tr><td>All plugins:</td><td align=right>%1 </td><td align=right>%2</td></tr><tr><td>ESMs:</td><td align=right>%3 </td><td align=right>%4</td></tr><tr><td>ESPs:</td><td align=right>%7 </td><td align=right>%8</td></tr><tr><td>ESMs+ESPs:</td><td align=right>%9 </td><td align=right>%10</td></tr><tr><td>ESLs:</td><td align=right>%5 </td><td align=right>%6</td></tr></table> - - - + + + Create Mod... - + This will create an empty mod. Please enter a name: - - + + A mod with this name already exists - + Create Separator... - + This will create a new separator. Please enter a name: - + A separator with this name already exists - + This will move all files from overwrite into a new, regular mod. Please enter a name: - + Move successful. - - + + Are you sure? - + About to recursively delete: - + Continue? - + The versioning scheme decides which version is considered newer than another. This function will guess the versioning scheme under the assumption that the installed version is outdated. - + Sorry - + I don't know a versioning scheme where %1 is newer than %2. - + Really enable all visible mods? - + Really disable all visible mods? - + Export to csv - + CSV (Comma Separated Values) is a format that can be imported in programs like Excel to create a spreadsheet. You can also use online editors and converters instead. - + Select what mods you want export: - + All installed mods - + Only active (checked) mods from your current profile - + All currently visible mods in the mod list - + Choose what Columns to export: - + Mod_Priority - + Mod_Name - + Notes_column - + Mod_Status - + Primary_Category - + Nexus_ID - + Mod_Nexus_URL - + Mod_Version - + Install_Date - + Download_File_Name - + export failed: %1 - + Open Game folder - + Open MyGames folder - + Open INIs folder - + Open Instance folder - + Open Mods folder - + Open Profile folder - + Open Downloads folder - + Open MO2 Install folder - + Open MO2 Plugins folder - + Open MO2 Logs folder - + Install Mod... - + Create empty mod - + Create Separator - + Enable all visible - + Disable all visible - + Check for updates - + Export to csv... - - + + Send to - - + + Top - - + + Bottom - - + + Priority... - + Separator... - + All Mods - + Sync to Mods... - + Move content to Mod... - + Clear Overwrite... - - + + Open in Explorer - + Restore Backup - + Remove Backup... - - + + Change Categories - - + + Primary Category - + Rename Separator... - + Remove Separator... - + Select Color... - + Reset Color - + Change versioning scheme - + Force-check updates - + Un-ignore update - + Ignore update - - + + Enable selected - - + + Disable selected - + Rename Mod... - + Reinstall Mod - + Remove Mod... - + Un-Endorse - + Won't endorse - + Endorsement state unknown - + Start tracking - + Stop tracking - + Tracked state unknown - + Ignore missing data - + Mark as converted/working - + Visit on Nexus - + Visit on %1 - + Information... - - + + Exception: - - + + Unknown exception - + <All> - + <Multiple> - + %1 more - + Are you sure you want to remove the following %n save(s)?<br><ul>%1</ul><br>Removed saves will be sent to the Recycle Bin. @@ -3102,12 +3112,12 @@ You can also use online editors and converters instead. - + Enable Mods... - + Delete %n save(s) @@ -3115,12 +3125,12 @@ You can also use online editors and converters instead. - + Restarting MO - + Changing the managed game directory requires restarting MO. Any pending downloads will be paused. @@ -3128,336 +3138,336 @@ Click OK to restart MO now. - + Can't change download directory while downloads are in progress! - + failed to write to file %1 - + %1 written - + Enter Name - + Please enter a name for the executable - + Not an executable - + This is not a recognized executable. - - + + Replace file? - + There already is a hidden version of this file. Replace it? - - + + File operation failed - - + + Failed to remove "%1". Maybe you lack the required file permissions? - + There already is a visible version of this file. Replace it? - - + + Set Priority - + Set the priority of the selected plugins - + Update available - + Open/Execute - + Add as Executable - + Preview - + Un-Hide - + Hide - + Write To File... - + Do you want to endorse Mod Organizer on %1 now? - + Abstain from Endorsing Mod Organizer - + Are you sure you want to abstain from endorsing Mod Organizer 2? You will have to visit the mod page on the %1 Nexus site to change your mind. - + Thank you for endorsing MO2! :) - + Please reconsider endorsing MO2 on Nexus! - + Thank you! - + Thank you for your endorsement! - + Mod ID %1 no longer seems to be available on Nexus. - + Request to Nexus failed: %1 - - + + failed to read %1: %2 - - + + Error - + failed to extract %1 (errorcode %2) - + Extract BSA - + This archive contains invalid hashes. Some files may be broken. - + Extract... - + This will restart MO, continue? - + Edit Categories... - + Deselect filter - + Remove '%1' from the toolbar - + Enable all - + Disable all - + Unlock load order - + Lock load order - + Open Origin in Explorer - + Open Origin Info... - + depends on missing "%1" - + incompatible with "%1" - + Please wait while LOOT is running - + loot failed. Exit code was: %1 - + failed to start loot - + failed to run loot: %1 - + Errors occurred - + Backup of load order created - + Choose backup to restore - + No Backups - + There are no backups to restore - - + + Restore failed - - + + Failed to restore the backup. Errorcode: %1 - + Backup of mod list created - + A file with the same name has already been downloaded. What would you like to do? - + Overwrite - + Rename new file - + Ignore file - + Set the priority of the selected mods @@ -3662,17 +3672,20 @@ Most mods do not have optional esps, so chances are good you are looking at an e - This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of MO. + This moves a esp to the esp directory so it can be enabled in the main window. Please note that the ESP merely becomes "available", it will not necessarily be loaded! That is configured in the main window of omo. - Make the selected mod in the lower list unavailable. + Make the selected mod in the right list unavailable. + Make the selected mod in the lower list unavailable. - The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + The selected esp (in the right list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. + The selected esp (in the lower list) will be pushed into a subdirectory of the mod and will thus become "invisible" to the game. It can then no longer be activated. @@ -5767,7 +5780,7 @@ If the folder was still in use, restart MO and try again. - + <Manage...> -- cgit v1.3.1 From 17ebbc30c521382009f01e6f2a7dc3a0a06b2fd0 Mon Sep 17 00:00:00 2001 From: Al Date: Wed, 2 Oct 2019 23:35:51 +0200 Subject: Set Plugins tab as default tab again after last commit. --- src/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 6d36e035..cbfed73e 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -715,7 +715,7 @@ p, li { white-space: pre-wrap; } QTabWidget::Rounded - 4 + 0 -- cgit v1.3.1 From 1aa3e1f96c5dc43638a8871afe593d122aed4ed9 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Thu, 10 Oct 2019 11:48:41 -0400 Subject: visible property was false --- src/mainwindow.ui | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index cbfed73e..b8aeeeb5 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -750,9 +750,6 @@ p, li { white-space: pre-wrap; } - - false - Sort -- cgit v1.3.1 From 70e66802a2d46a1ffdac7528b134c9d7741a8797 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 14:10:50 -0500 Subject: changed labels that have links to new LinkLabel, removed hardcoded colors changed the link colors on dark themes to something saner --- src/aboutdialog.ui | 11 +++++++++-- src/mainwindow.ui | 9 +++++++-- src/settingsdialog.ui | 7 ++++++- src/stylesheets/Night Eyes.qss | 4 ++++ src/stylesheets/Paper Dark by 6788.qss | 4 ++++ src/stylesheets/Transparent-Style-101-Green.qss | 5 +++++ src/stylesheets/Transparent-Style-BOS.qss | 4 ++++ src/stylesheets/Transparent-Style-Skyrim.qss | 4 ++++ src/stylesheets/dark.qss | 6 +++++- src/stylesheets/dracula.qss | 4 ++++ src/stylesheets/skyrim.qss | 3 +++ src/stylesheets/vs15 Dark-Green.qss | 3 +++ src/stylesheets/vs15 Dark-Orange.qss | 3 +++ src/stylesheets/vs15 Dark-Purple.qss | 3 +++ src/stylesheets/vs15 Dark-Red.qss | 3 +++ src/stylesheets/vs15 Dark-Yellow.qss | 3 +++ src/stylesheets/vs15 Dark.qss | 3 +++ 17 files changed, 73 insertions(+), 6 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui index 415ce0a7..424a80f9 100644 --- a/src/aboutdialog.ui +++ b/src/aboutdialog.ui @@ -158,9 +158,9 @@ - + - <html><head/><body><p>Source code can be found at <a href="https://github.com/ModOrganizer2/modorganizer"><span style=" text-decoration: underline; color:#007af4;">GitHub</span></a>.</p></body></html> + <html><head/><body><p>Source code can be found at <a href="https://github.com/ModOrganizer2/modorganizer">GitHub</a>.</p></body></html> @@ -536,6 +536,13 @@ + + + LinkLabel + QLabel +
linklabel.h
+
+
diff --git a/src/mainwindow.ui b/src/mainwindow.ui index b8aeeeb5..723b42fe 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -941,12 +941,12 @@ p, li { white-space: pre-wrap; } - + <html><head/><body><p>BSAs / BA2s are bundles of game assets (textures, scripts, etc.). By default, the engine loads these bundles in a separate step from loose files. <p>Their load order is specified by the priority of the corresponding plugin (right pane, plugins tab).</p><p>If there is a matching plugin, the game will load them no matter what.</p></body></html> - <html><head/><body><p>Currently detected archives. (<a href="#"><span style=" text-decoration: underline; color:#0000ff;">What is an archive?</span></a>)</p></body></html> + <html><head/><body><p>Currently detected archives. (<a href="#">What is an archive?</a>)</p></body></html> true @@ -1773,6 +1773,11 @@ p, li { white-space: pre-wrap; } QStatusBar
statusbar.h
+ + LinkLabel + QLabel +
linklabel.h
+
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index e63ca692..c5a1d2ef 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -1410,7 +1410,7 @@ programs you are intentionally running.
- + Hint: right click link and copy link location @@ -1535,6 +1535,11 @@ programs you are intentionally running. QTableWidget
colortable.h
+ + LinkLabel + QLabel +
linklabel.h
+
tabWidget diff --git a/src/stylesheets/Night Eyes.qss b/src/stylesheets/Night Eyes.qss index 0430a8c8..14f7434e 100644 --- a/src/stylesheets/Night Eyes.qss +++ b/src/stylesheets/Night Eyes.qss @@ -49,6 +49,10 @@ QAbstractScrollArea::corner margin: 0px -2px -2px 0px; } +LinkLabel +{ + qproperty-linkColor: #3399FF; +} /* Toolbar -------------------------------------------------------------------- */ diff --git a/src/stylesheets/Paper Dark by 6788.qss b/src/stylesheets/Paper Dark by 6788.qss index 77086c15..6043ae6b 100644 --- a/src/stylesheets/Paper Dark by 6788.qss +++ b/src/stylesheets/Paper Dark by 6788.qss @@ -66,6 +66,10 @@ QSplitter { width: 8px; } +LinkLabel { + qproperty-linkColor: #3399FF; +} + /* Toolbar */ QToolBar { diff --git a/src/stylesheets/Transparent-Style-101-Green.qss b/src/stylesheets/Transparent-Style-101-Green.qss index a4ed2623..3bbdb5db 100644 --- a/src/stylesheets/Transparent-Style-101-Green.qss +++ b/src/stylesheets/Transparent-Style-101-Green.qss @@ -485,6 +485,11 @@ QStatusBar::item {border: None;} font-family: Source Sans Pro; font-size: 14px; } + +LinkLabel { + qproperty-linkColor: #3399FF; +} + QAbstractItemView { color: #cccccc; font-family: Source Sans Pro; diff --git a/src/stylesheets/Transparent-Style-BOS.qss b/src/stylesheets/Transparent-Style-BOS.qss index efad0859..33eed327 100644 --- a/src/stylesheets/Transparent-Style-BOS.qss +++ b/src/stylesheets/Transparent-Style-BOS.qss @@ -743,3 +743,7 @@ QStatusBar::item {border: None;} border-bottom:1px solid #9A9A00; */ } + +LinkLabel { + qproperty-linkColor: #3399FF; +} diff --git a/src/stylesheets/Transparent-Style-Skyrim.qss b/src/stylesheets/Transparent-Style-Skyrim.qss index 89e36c74..308580a5 100644 --- a/src/stylesheets/Transparent-Style-Skyrim.qss +++ b/src/stylesheets/Transparent-Style-Skyrim.qss @@ -743,3 +743,7 @@ QStatusBar::item {border: None;} border-bottom:1px solid #9A9A00; */ } + +LinkLabel { + qproperty-linkColor: #3399FF; +} diff --git a/src/stylesheets/dark.qss b/src/stylesheets/dark.qss index 22cd598c..9d11109d 100644 --- a/src/stylesheets/dark.qss +++ b/src/stylesheets/dark.qss @@ -380,4 +380,8 @@ DownloadListWidget[downloadView=standard]::item { DownloadListWidget[downloadView=compact]::item { padding: 4px; -} \ No newline at end of file +} + +LinkLabel { + qproperty-linkColor: #3399FF; +} diff --git a/src/stylesheets/dracula.qss b/src/stylesheets/dracula.qss index 2a7fbf9e..537ff083 100644 --- a/src/stylesheets/dracula.qss +++ b/src/stylesheets/dracula.qss @@ -30,6 +30,10 @@ QCheckBox, QGroupBox { selection-color: #bbbbbb; } +LinkLabel { + qproperty-linkColor: #3399FF; +} + /* * GroupBox and CheckBox */ diff --git a/src/stylesheets/skyrim.qss b/src/stylesheets/skyrim.qss index 2da5154d..a516f5ff 100644 --- a/src/stylesheets/skyrim.qss +++ b/src/stylesheets/skyrim.qss @@ -113,6 +113,9 @@ QListView::item { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Font size */ QLabel, QTextEdit, diff --git a/src/stylesheets/vs15 Dark-Green.qss b/src/stylesheets/vs15 Dark-Green.qss index 88e7651f..6d95c6cc 100644 --- a/src/stylesheets/vs15 Dark-Green.qss +++ b/src/stylesheets/vs15 Dark-Green.qss @@ -94,6 +94,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } diff --git a/src/stylesheets/vs15 Dark-Orange.qss b/src/stylesheets/vs15 Dark-Orange.qss index 488da3c4..2dd27df4 100644 --- a/src/stylesheets/vs15 Dark-Orange.qss +++ b/src/stylesheets/vs15 Dark-Orange.qss @@ -95,6 +95,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } diff --git a/src/stylesheets/vs15 Dark-Purple.qss b/src/stylesheets/vs15 Dark-Purple.qss index 24c8705a..116aaa7d 100644 --- a/src/stylesheets/vs15 Dark-Purple.qss +++ b/src/stylesheets/vs15 Dark-Purple.qss @@ -95,6 +95,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } diff --git a/src/stylesheets/vs15 Dark-Red.qss b/src/stylesheets/vs15 Dark-Red.qss index 0c0e21a8..60f565a1 100644 --- a/src/stylesheets/vs15 Dark-Red.qss +++ b/src/stylesheets/vs15 Dark-Red.qss @@ -95,6 +95,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } diff --git a/src/stylesheets/vs15 Dark-Yellow.qss b/src/stylesheets/vs15 Dark-Yellow.qss index 2cf1cb2e..bfaa4c94 100644 --- a/src/stylesheets/vs15 Dark-Yellow.qss +++ b/src/stylesheets/vs15 Dark-Yellow.qss @@ -95,6 +95,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } diff --git a/src/stylesheets/vs15 Dark.qss b/src/stylesheets/vs15 Dark.qss index a5781d72..1d27be17 100644 --- a/src/stylesheets/vs15 Dark.qss +++ b/src/stylesheets/vs15 Dark.qss @@ -94,6 +94,9 @@ QTreeView::branch:selected { QLabel { background-color: transparent; } +LinkLabel { + qproperty-linkColor: #3399FF; } + /* Left Pane & File Trees #QTreeView, #QListView*/ QTreeView::branch:closed:has-children { image: url(./vs15/branch-closed.png); } -- cgit v1.3.1 From d5e38fca6b3a8c7bf90c5a3d8ec779752a22c61d Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 17:05:20 -0500 Subject: added not filter, not functional yet fixed no mods being displayed for OR with no conditions --- src/mainwindow.cpp | 5 +++++ src/mainwindow.h | 1 + src/mainwindow.ui | 7 +++++++ src/modlistsortproxy.cpp | 17 ++++++++++++----- src/modlistsortproxy.h | 4 ++-- 5 files changed, 27 insertions(+), 7 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index e134d64a..21606fa9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -6552,6 +6552,11 @@ void MainWindow::on_categoriesOrBtn_toggled(bool checked) } } +void MainWindow::on_categoriesNotBtn_toggled(bool checked) +{ + m_ModListSortProxy->setFilterNot(checked); +} + void MainWindow::on_managedArchiveLabel_linkHovered(const QString&) { QToolTip::showText(QCursor::pos(), diff --git a/src/mainwindow.h b/src/mainwindow.h index 0c96c15d..cbf45635 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -659,6 +659,7 @@ private slots: // ui slots void on_saveModsButton_clicked(); void on_categoriesAndBtn_toggled(bool checked); void on_categoriesOrBtn_toggled(bool checked); + void on_categoriesNotBtn_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); void storeSettings(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 723b42fe..cd9cbc4b 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -152,6 +152,13 @@
+ + + + Not + + +
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index a9ff6463..805e77f4 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -36,10 +36,9 @@ using namespace MOBase; ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) : QSortFilterProxyModel(parent) , m_Profile(profile) - , m_CategoryFilter() - , m_CurrentFilter() , m_FilterActive(false) , m_FilterMode(FILTER_AND) + , m_FilterNot(false) { setDynamicSortFilter(true); // this seems to work without dynamicsortfilter // but I don't know why. This should be necessary @@ -312,8 +311,8 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons if (!info->hasFlag(ModInfo::FLAG_INVALID)) return false; } break; case CategoryFactory::CATEGORY_SPECIAL_NONEXUSID: { - if (!(info->getNexusID() == -1 && !info->hasFlag(ModInfo::FLAG_FOREIGN) && - !info->hasFlag(ModInfo::FLAG_BACKUP) && + if (!(info->getNexusID() == -1 && !info->hasFlag(ModInfo::FLAG_FOREIGN) && + !info->hasFlag(ModInfo::FLAG_BACKUP) && !info->hasFlag(ModInfo::FLAG_SEPARATOR) && !info->hasFlag(ModInfo::FLAG_OVERWRITE))) return false; } break; @@ -381,7 +380,7 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const if (info->hasContent(static_cast(content))) return true; } - return false; + return m_CategoryFilter.empty() && m_ContentFilter.empty(); } bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const @@ -487,6 +486,14 @@ void ModListSortProxy::setFilterMode(ModListSortProxy::FilterMode mode) } } +void ModListSortProxy::setFilterNot(bool b) +{ + if (b != m_FilterNot) { + m_FilterNot = b; + this->invalidate(); + } +} + bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const { if (m_Profile == nullptr) { diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 5fe8d9d6..2e3e5709 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -85,6 +85,7 @@ public: bool isFilterActive() const { return m_FilterActive; } void setFilterMode(FilterMode mode); + void setFilterNot(bool b); /** * @brief tests if the specified index has child nodes @@ -129,9 +130,7 @@ private slots: void postDataChanged(); private: - Profile *m_Profile; - std::vector m_CategoryFilter; std::vector m_ContentFilter; std::bitset m_EnabledColumns; @@ -139,6 +138,7 @@ private: bool m_FilterActive; FilterMode m_FilterMode; + bool m_FilterNot; std::vector m_PreChangeFilters; -- cgit v1.3.1 From 17452071c9b72a48498e7578d65b9b52729f914f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 17:30:30 -0500 Subject: added separators filter changed notendorsed filter to include anything else than true --- src/mainwindow.cpp | 5 +++++ src/mainwindow.h | 1 + src/mainwindow.ui | 7 +++++++ src/modlistsortproxy.cpp | 20 ++++++++++++++++++-- src/modlistsortproxy.h | 2 ++ 5 files changed, 33 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 21606fa9..d5636ab9 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -6557,6 +6557,11 @@ void MainWindow::on_categoriesNotBtn_toggled(bool checked) m_ModListSortProxy->setFilterNot(checked); } +void MainWindow::on_categoriesSeparators_toggled(bool checked) +{ + m_ModListSortProxy->setFilterSeparators(checked); +} + void MainWindow::on_managedArchiveLabel_linkHovered(const QString&) { QToolTip::showText(QCursor::pos(), diff --git a/src/mainwindow.h b/src/mainwindow.h index cbf45635..c99c724b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -660,6 +660,7 @@ private slots: // ui slots void on_categoriesAndBtn_toggled(bool checked); void on_categoriesOrBtn_toggled(bool checked); void on_categoriesNotBtn_toggled(bool checked); + void on_categoriesSeparators_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); void storeSettings(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index cd9cbc4b..9648a586 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -159,6 +159,13 @@ + + + + Separators + + + diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 0984d415..ddae675c 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -39,6 +39,7 @@ ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) , m_FilterActive(false) , m_FilterMode(FILTER_AND) , m_FilterNot(false) + , m_FilterSeparators(false) { setDynamicSortFilter(true); // this seems to work without dynamicsortfilter // but I don't know why. This should be necessary @@ -278,6 +279,10 @@ bool ModListSortProxy::hasConflictFlag(const std::vector &flags) bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const { for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { + if (info->hasFlag(ModInfo::FLAG_SEPARATOR) && !m_FilterSeparators) { + return false; + } + if (!categoryMatchesMod(info, enabled, *iter)) { return false; } @@ -295,6 +300,10 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const { for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { + if (info->hasFlag(ModInfo::FLAG_SEPARATOR) && !m_FilterSeparators) { + return false; + } + if (categoryMatchesMod(info, enabled, *iter)) { return true; } @@ -332,7 +341,7 @@ bool ModListSortProxy::categoryMatchesMod( case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: { ModInfo::EEndorsedState state = info->endorsedState(); - return ((state == ModInfo::ENDORSED_FALSE) || (state == ModInfo::ENDORSED_NEVER)); + return (state != ModInfo::ENDORSED_TRUE); } case CategoryFactory::CATEGORY_SPECIAL_BACKUP: @@ -353,7 +362,6 @@ bool ModListSortProxy::categoryMatchesMod( info->getNexusID() == -1 && !info->hasFlag(ModInfo::FLAG_FOREIGN) && !info->hasFlag(ModInfo::FLAG_BACKUP) && - !info->hasFlag(ModInfo::FLAG_SEPARATOR) && !info->hasFlag(ModInfo::FLAG_OVERWRITE)); } @@ -480,6 +488,14 @@ void ModListSortProxy::setFilterNot(bool b) } } +void ModListSortProxy::setFilterSeparators(bool b) +{ + if (b != m_FilterSeparators) { + m_FilterSeparators = b; + this->invalidate(); + } +} + bool ModListSortProxy::filterAcceptsRow(int row, const QModelIndex &parent) const { if (m_Profile == nullptr) { diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 17888ae6..2ebfbcf0 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -86,6 +86,7 @@ public: void setFilterMode(FilterMode mode); void setFilterNot(bool b); + void setFilterSeparators(bool b); /** * @brief tests if the specified index has child nodes @@ -139,6 +140,7 @@ private: bool m_FilterActive; FilterMode m_FilterMode; bool m_FilterNot; + bool m_FilterSeparators; std::vector m_PreChangeFilters; -- cgit v1.3.1 From 06bb9870dd267215fac1d585c56a1ae0712f140c Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 17:45:52 -0500 Subject: fixed tooltips --- src/mainwindow.ui | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 9648a586..47da0c38 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -132,7 +132,7 @@ - If checked, only mods that match all selected categories are displayed. + Display mods that match all selected categories. And @@ -145,7 +145,7 @@ - If checked, all mods that match at least one of the selected categories are displayed. + Display mods that match at least one of the selected categories Or @@ -154,6 +154,9 @@ + + Invert each selected category + Not @@ -161,6 +164,9 @@ + + Include separators + Separators -- cgit v1.3.1 From 8ea346183fa432e4a16e604ae2fa03366c01c6af Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 27 Nov 2019 17:53:26 -0500 Subject: renamed "Categories" frame to "Filters" --- src/mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 47da0c38..7e11c70e 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -47,7 +47,7 @@ - Categories + Filters -- cgit v1.3.1 From 93318a1474031035da5e61ad199171cad5803c2f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Fri, 29 Nov 2019 23:50:33 -0500 Subject: moved all remaining filter stuff to FilterList renamed some widgets --- src/categories.cpp | 34 ++++++++++++++ src/categories.h | 2 + src/filterlist.cpp | 135 +++++++++++++++++++++++++++++++---------------------- src/filterlist.h | 6 ++- src/mainwindow.cpp | 107 +++++++++++++++++++++++++++++------------- src/mainwindow.h | 7 ++- src/mainwindow.ui | 12 ++--- 7 files changed, 204 insertions(+), 99 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/categories.cpp b/src/categories.cpp index 12b18998..082b4fbc 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -320,6 +320,40 @@ QString CategoryFactory::getCategoryName(unsigned int index) const return m_Categories[index].m_Name; } +QString CategoryFactory::getSpecialCategoryName(int type) const +{ + switch (type) + { + case CATEGORY_SPECIAL_CHECKED: return QObject::tr(""); + case CATEGORY_SPECIAL_UNCHECKED: return QObject::tr(""); + case CATEGORY_SPECIAL_UPDATEAVAILABLE: return QObject::tr(""); + case CATEGORY_SPECIAL_NOCATEGORY: return QObject::tr(""); + case CATEGORY_SPECIAL_CONFLICT: return QObject::tr(""); + case CATEGORY_SPECIAL_NOTENDORSED: return QObject::tr(""); + case CATEGORY_SPECIAL_BACKUP: return QObject::tr(""); + case CATEGORY_SPECIAL_MANAGED: return QObject::tr(""); + case CATEGORY_SPECIAL_UNMANAGED: return QObject::tr(""); + case CATEGORY_SPECIAL_NOGAMEDATA: return QObject::tr(""); + case CATEGORY_SPECIAL_NONEXUSID: return QObject::tr(""); + default: return {}; + } +} + +QString CategoryFactory::getCategoryNameByID(int id) const +{ + auto itor = m_IDMap.find(id); + + if (itor == m_IDMap.end()) { + return getSpecialCategoryName(id); + } else { + const auto index = itor->second; + if (index >= m_Categories.size()) { + return {}; + } + + return m_Categories[index].m_Name; + } +} int CategoryFactory::getCategoryID(unsigned int index) const { diff --git a/src/categories.h b/src/categories.h index 67fee3e7..2041ce1f 100644 --- a/src/categories.h +++ b/src/categories.h @@ -144,6 +144,8 @@ public: * @return QString name of the category **/ QString getCategoryName(unsigned int index) const; + QString getSpecialCategoryName(int type) const; + QString getCategoryNameByID(int id) const; /** * @brief look up the id of a category by its index diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 8ef4b62d..5562736d 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -9,13 +9,33 @@ using namespace MOBase; FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) : ui(ui), m_factory(factory) { - QObject::connect( - ui->categoriesList, &QTreeWidget::customContextMenuRequested, + connect( + ui->filters, &QTreeWidget::customContextMenuRequested, [&](auto&& pos){ onContextMenu(pos); }); - QObject::connect( - ui->categoriesList, &QTreeWidget::itemSelectionChanged, + connect( + ui->filters, &QTreeWidget::itemSelectionChanged, [&]{ onSelection(); }); + + connect( + ui->filtersClear, &QPushButton::clicked, + [&]{ clearSelection(); }); + + connect( + ui->filtersAnd, &QCheckBox::toggled, + [&]{ onCriteriaChanged(); }); + + connect( + ui->filtersOr, &QCheckBox::toggled, + [&]{ onCriteriaChanged(); }); + + connect( + ui->filtersNot, &QCheckBox::toggled, + [&]{ onCriteriaChanged(); }); + + connect( + ui->filtersSeparators, &QCheckBox::toggled, + [&]{ onCriteriaChanged(); }); } QTreeWidgetItem* FilterList::addFilterItem( @@ -29,7 +49,7 @@ QTreeWidgetItem* FilterList::addFilterItem( if (root != nullptr) { root->addChild(item); } else { - ui->categoriesList->addTopLevelItem(item); + ui->filters->addTopLevelItem(item); } return item; } @@ -37,7 +57,9 @@ QTreeWidgetItem* FilterList::addFilterItem( void FilterList::addContentFilters() { for (unsigned i = 0; i < ModInfo::NUM_CONTENT_TYPES; ++i) { - addFilterItem(nullptr, tr("").arg(ModInfo::getContentTypeName(i)), i, ModListSortProxy::TYPE_CONTENT); + addFilterItem( + nullptr, tr("").arg(ModInfo::getContentTypeName(i)), + i, ModListSortProxy::TYPE_CONTENT); } } @@ -59,32 +81,37 @@ void FilterList::addCategoryFilters(QTreeWidgetItem *root, const std::set & } } -void FilterList::refresh() +void FilterList::addSpecialFilterItem(int type) { - QItemSelection currentSelection = ui->modList->selectionModel()->selection(); - - QVariant currentIndexName = ui->modList->currentIndex().data(); - ui->modList->setCurrentIndex(QModelIndex()); + addFilterItem( + nullptr, m_factory.getSpecialCategoryName(type), + type, ModListSortProxy::TYPE_SPECIAL); +} +void FilterList::refresh() +{ QStringList selectedItems; - for (QTreeWidgetItem *item : ui->categoriesList->selectedItems()) { + for (QTreeWidgetItem *item : ui->filters->selectedItems()) { selectedItems.append(item->text(0)); } - ui->categoriesList->clear(); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_CHECKED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNCHECKED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_BACKUP, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_MANAGED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_UNMANAGED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_CONFLICT, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NONEXUSID, ModListSortProxy::TYPE_SPECIAL); - addFilterItem(nullptr, tr(""), CategoryFactory::CATEGORY_SPECIAL_NOGAMEDATA, ModListSortProxy::TYPE_SPECIAL); + ui->filters->clear(); + + using F = CategoryFactory; + addSpecialFilterItem(F::CATEGORY_SPECIAL_CHECKED); + addSpecialFilterItem(F::CATEGORY_SPECIAL_UNCHECKED); + addSpecialFilterItem(F::CATEGORY_SPECIAL_UPDATEAVAILABLE); + addSpecialFilterItem(F::CATEGORY_SPECIAL_BACKUP); + addSpecialFilterItem(F::CATEGORY_SPECIAL_MANAGED); + addSpecialFilterItem(F::CATEGORY_SPECIAL_UNMANAGED); + addSpecialFilterItem(F::CATEGORY_SPECIAL_NOCATEGORY); + addSpecialFilterItem(F::CATEGORY_SPECIAL_CONFLICT); + addSpecialFilterItem(F::CATEGORY_SPECIAL_NOTENDORSED); + addSpecialFilterItem(F::CATEGORY_SPECIAL_NONEXUSID); + addSpecialFilterItem(F::CATEGORY_SPECIAL_NOGAMEDATA); addContentFilters(); + std::set categoriesUsed; for (unsigned int modIdx = 0; modIdx < ModInfo::getNumMods(); ++modIdx) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modIdx); @@ -106,27 +133,20 @@ void FilterList::refresh() addCategoryFilters(nullptr, categoriesUsed, 0); for (const QString &item : selectedItems) { - QList matches = ui->categoriesList->findItems(item, Qt::MatchFixedString | Qt::MatchRecursive); + QList matches = ui->filters->findItems( + item, Qt::MatchFixedString | Qt::MatchRecursive); + if (matches.size() > 0) { matches.at(0)->setSelected(true); } } - ui->modList->selectionModel()->select(currentSelection, QItemSelectionModel::Select); - QModelIndexList matchList; - if (currentIndexName.isValid()) { - matchList = ui->modList->model()->match(ui->modList->model()->index(0, 0), Qt::DisplayRole, currentIndexName); - } - - if (matchList.size() > 0) { - ui->modList->setCurrentIndex(matchList.at(0)); - } } void FilterList::setSelection(std::vector categories) { - for (int i = 0; i < ui->categoriesList->topLevelItemCount(); ++i) { - if (ui->categoriesList->topLevelItem(i)->data(0, Qt::UserRole) == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) { - ui->categoriesList->setCurrentItem(ui->categoriesList->topLevelItem(i)); + for (int i = 0; i < ui->filters->topLevelItemCount(); ++i) { + if (ui->filters->topLevelItem(i)->data(0, Qt::UserRole) == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) { + ui->filters->setCurrentItem(ui->filters->topLevelItem(i)); break; } } @@ -134,40 +154,32 @@ void FilterList::setSelection(std::vector categories) void FilterList::clearSelection() { - ui->categoriesList->clearSelection(); + ui->filters->clearSelection(); } void FilterList::onSelection() { - QModelIndexList indices = ui->categoriesList->selectionModel()->selectedRows(); + QModelIndexList indices = ui->filters->selectionModel()->selectedRows(); std::vector categories; std::vector content; + for (const QModelIndex &index : indices) { - int filterType = index.data(Qt::UserRole + 1).toInt(); - if ((filterType == ModListSortProxy::TYPE_CATEGORY) - || (filterType == ModListSortProxy::TYPE_SPECIAL)) { - int categoryId = index.data(Qt::UserRole).toInt(); + const int filterType = index.data(Qt::UserRole + 1).toInt(); + + if ((filterType == ModListSortProxy::TYPE_CATEGORY) || (filterType == ModListSortProxy::TYPE_SPECIAL)) { + const int categoryId = index.data(Qt::UserRole).toInt(); if (categoryId != CategoryFactory::CATEGORY_NONE) { categories.push_back(categoryId); } } else if (filterType == ModListSortProxy::TYPE_CONTENT) { - int contentId = index.data(Qt::UserRole).toInt(); + const int contentId = index.data(Qt::UserRole).toInt(); content.push_back(contentId); } } - emit changed(categories, content); - - ui->clickBlankButton->setEnabled(categories.size() > 0 || content.size() >0); + ui->filtersClear->setEnabled(categories.size() > 0 || content.size() >0); - if (indices.count() == 0) { - ui->currentCategoryLabel->setText(QString("(%1)").arg(tr(""))); - } else if (indices.count() > 1) { - ui->currentCategoryLabel->setText(QString("(%1)").arg(tr(""))); - } else { - ui->currentCategoryLabel->setText(QString("(%1)").arg(indices.first().data().toString())); - } - ui->modList->reset(); + emit filtersChanged(categories, content); } void FilterList::onContextMenu(const QPoint &pos) @@ -176,7 +188,7 @@ void FilterList::onContextMenu(const QPoint &pos) menu.addAction(tr("Edit Categories..."), [&]{ editCategories(); }); menu.addAction(tr("Deselect filter"), [&]{ clearSelection(); }); - menu.exec(ui->categoriesList->viewport()->mapToGlobal(pos)); + menu.exec(ui->filters->viewport()->mapToGlobal(pos)); } void FilterList::editCategories() @@ -187,3 +199,14 @@ void FilterList::editCategories() dialog.commitChanges(); } } + +void FilterList::onCriteriaChanged() +{ + const auto mode = ui->filtersAnd->isChecked() ? + ModListSortProxy::FILTER_AND : ModListSortProxy::FILTER_OR; + + const bool inverse = ui->filtersNot->isChecked(); + const bool separators = ui->filtersSeparators->isChecked(); + + emit criteriaChanged(mode, inverse, separators); +} diff --git a/src/filterlist.h b/src/filterlist.h index ca74021a..85982392 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -19,7 +19,8 @@ public: void refresh(); signals: - void changed(std::vector categories, std::vector content); + void filtersChanged(std::vector categories, std::vector content); + void criteriaChanged(ModListSortProxy::FilterMode mode, bool inverse, bool separators); private: Ui::MainWindow* ui; @@ -27,6 +28,7 @@ private: void onContextMenu(const QPoint &pos); void onSelection(); + void onCriteriaChanged(); void editCategories(); @@ -37,6 +39,8 @@ private: void addContentFilters(); void addCategoryFilters( QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID); + void addSpecialFilterItem(int type); + }; #endif // MODORGANIZER_CATEGORIESLIST_INCLUDED diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index b63f9211..1319d906 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -262,10 +262,14 @@ MainWindow::MainWindow(Settings &settings m_CategoryFactory.loadCategories(); m_Filters.reset(new FilterList(ui, m_CategoryFactory)); - connect(m_Filters.get(), &FilterList::changed, [&](auto&& cats, auto&& content) { - m_ModListSortProxy->setCategoryFilter(cats); - m_ModListSortProxy->setContentFilter(content); - }); + + connect( + m_Filters.get(), &FilterList::filtersChanged, + [&](auto&& cats, auto&& content) { onFilters(cats, content); }); + + connect( + m_Filters.get(), &FilterList::criteriaChanged, + [&](auto mode, bool inv, bool sep) { onFiltersCriteria(mode, inv, sep); }); ui->logList->setCore(m_OrganizerCore); @@ -1229,7 +1233,7 @@ void MainWindow::showEvent(QShowEvent *event) if (!m_WasVisible) { readSettings(); - m_Filters->refresh(); + refreshFilters(); // this needs to be connected here instead of in the constructor because the // actual changing of the stylesheet is done by MOApplication, which @@ -4025,7 +4029,7 @@ void MainWindow::addRemoveCategories_MenuHandler() { m_OrganizerCore.modList()->notifyChange(m_ContextRow); } - m_Filters->refresh(); + refreshFilters(); } void MainWindow::replaceCategories_MenuHandler() { @@ -4069,7 +4073,7 @@ void MainWindow::replaceCategories_MenuHandler() { m_OrganizerCore.modList()->notifyChange(m_ContextRow); } - m_Filters->refresh(); + refreshFilters(); } void MainWindow::saveArchiveList() @@ -4936,7 +4940,7 @@ void MainWindow::on_actionSettings_triggered() instManager->setDownloadDirectory(settings.paths().downloads()); fixCategories(); - m_Filters->refresh(); + refreshFilters(); if (settings.paths().profiles() != oldProfilesDirectory) { refreshProfiles(); @@ -6083,6 +6087,69 @@ void MainWindow::deselectFilters() m_Filters->clearSelection(); } +void MainWindow::refreshFilters() +{ + QItemSelection currentSelection = ui->modList->selectionModel()->selection(); + + QVariant currentIndexName = ui->modList->currentIndex().data(); + ui->modList->setCurrentIndex(QModelIndex()); + + m_Filters->refresh(); + + ui->modList->selectionModel()->select(currentSelection, QItemSelectionModel::Select); + + QModelIndexList matchList; + if (currentIndexName.isValid()) { + matchList = ui->modList->model()->match( + ui->modList->model()->index(0, 0), + Qt::DisplayRole, + currentIndexName); + } + + if (matchList.size() > 0) { + ui->modList->setCurrentIndex(matchList.at(0)); + } +} + +void MainWindow::onFilters( + const std::vector& categories, const std::vector& content) +{ + m_ModListSortProxy->setCategoryFilter(categories); + m_ModListSortProxy->setContentFilter(content); + + QString label = "?"; + + if ((categories.size() + content.size()) > 1) { + label = tr(""); + } else if (!categories.empty()) { + const int c = categories[0]; + label = m_CategoryFactory.getCategoryNameByID(c); + if (label.isEmpty()) { + log::error("category '{}' not found", c); + } + } else if (!content.empty()) { + const int c = content[0]; + try { + label = ModInfo::getContentTypeName(c); + } + catch(std::exception&) { + log::error("content filter '{}' not found", c); + } + } else { + label = ""; + } + + ui->currentCategoryLabel->setText(label); + ui->modList->reset(); +} + +void MainWindow::onFiltersCriteria( + ModListSortProxy::FilterMode mode, bool inverse, bool separators) +{ + m_ModListSortProxy->setFilterMode(mode); + m_ModListSortProxy->setFilterNot(inverse); + m_ModListSortProxy->setFilterSeparators(separators); +} void MainWindow::updateESPLock(bool locked) { @@ -6385,30 +6452,6 @@ void MainWindow::on_restoreModsButton_clicked() } } -void MainWindow::on_categoriesAndBtn_toggled(bool checked) -{ - if (checked) { - m_ModListSortProxy->setFilterMode(ModListSortProxy::FILTER_AND); - } -} - -void MainWindow::on_categoriesOrBtn_toggled(bool checked) -{ - if (checked) { - m_ModListSortProxy->setFilterMode(ModListSortProxy::FILTER_OR); - } -} - -void MainWindow::on_categoriesNotBtn_toggled(bool checked) -{ - m_ModListSortProxy->setFilterNot(checked); -} - -void MainWindow::on_categoriesSeparators_toggled(bool checked) -{ - m_ModListSortProxy->setFilterSeparators(checked); -} - void MainWindow::on_managedArchiveLabel_linkHovered(const QString&) { QToolTip::showText(QCursor::pos(), diff --git a/src/mainwindow.h b/src/mainwindow.h index 04ba7a35..9837378b 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -513,6 +513,9 @@ private slots: void onRequestsChanged(const APIStats& stats, const APIUserAccount& user); void deselectFilters(); + void refreshFilters(); + void onFilters(const std::vector& categories, const std::vector& content); + void onFiltersCriteria(ModListSortProxy::FilterMode mode, bool inverse, bool separators); void displayModInformation(const QString &modName, ModInfoTabIDs tabID); @@ -651,10 +654,6 @@ private slots: // ui slots void on_restoreButton_clicked(); void on_restoreModsButton_clicked(); void on_saveModsButton_clicked(); - void on_categoriesAndBtn_toggled(bool checked); - void on_categoriesOrBtn_toggled(bool checked); - void on_categoriesNotBtn_toggled(bool checked); - void on_categoriesSeparators_toggled(bool checked); void on_managedArchiveLabel_linkHovered(const QString &link); void storeSettings(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 7e11c70e..ed35f783 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -66,7 +66,7 @@ 1 - + 120 @@ -96,7 +96,7 @@ - + false @@ -130,7 +130,7 @@ - + Display mods that match all selected categories. @@ -143,7 +143,7 @@ - + Display mods that match at least one of the selected categories @@ -153,7 +153,7 @@ - + Invert each selected category @@ -163,7 +163,7 @@ - + Include separators -- cgit v1.3.1 From e99dfe153c62f914ada0605430305fca81a332a9 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 30 Nov 2019 01:53:09 -0500 Subject: renamed filters to criteria merged categories and content, they can be distinguished using the type added a not flag for criteria, not used yet --- src/filterlist.cpp | 116 +++++++++++++++++++++++++-------------------- src/filterlist.h | 14 +++--- src/mainwindow.cpp | 50 +++++++++----------- src/mainwindow.h | 4 +- src/mainwindow.ui | 38 +++++++-------- src/modlistsortproxy.cpp | 120 +++++++++++++++++------------------------------ src/modlistsortproxy.h | 42 +++++++++++------ 7 files changed, 182 insertions(+), 202 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 5562736d..9e5437b3 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -6,6 +6,9 @@ using namespace MOBase; +const int CategoryIDRole = Qt::UserRole; +const int CategoryTypeRole = Qt::UserRole + 1; + FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) : ui(ui), m_factory(factory) { @@ -29,61 +32,76 @@ FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) ui->filtersOr, &QCheckBox::toggled, [&]{ onCriteriaChanged(); }); - connect( - ui->filtersNot, &QCheckBox::toggled, - [&]{ onCriteriaChanged(); }); - connect( ui->filtersSeparators, &QCheckBox::toggled, [&]{ onCriteriaChanged(); }); + + ui->filters->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->filters->header()->resizeSection(1, 50); } -QTreeWidgetItem* FilterList::addFilterItem( +QTreeWidgetItem* FilterList::addCriteriaItem( QTreeWidgetItem *root, const QString &name, int categoryID, - ModListSortProxy::FilterType type) + ModListSortProxy::CriteriaType type) { QTreeWidgetItem *item = new QTreeWidgetItem(QStringList(name)); + item->setData(0, Qt::ToolTipRole, name); - item->setData(0, Qt::UserRole, categoryID); - item->setData(0, Qt::UserRole + 1, type); + item->setData(0, CategoryIDRole, categoryID); + item->setData(0, CategoryTypeRole, type); + if (root != nullptr) { root->addChild(item); } else { ui->filters->addTopLevelItem(item); } + + auto* w = new QWidget; + w->setStyleSheet("background-color: rgba(0,0,0,0)"); + + auto* ly = new QVBoxLayout(w); + ly->setAlignment(Qt::AlignCenter); + ly->setContentsMargins(0, 0, 0, 0); + + auto* cb = new QCheckBox; + connect(cb, &QCheckBox::toggled, [&]{ onSelection(); }); + ly->addWidget(cb); + + ui->filters->setItemWidget(item, 1, w); + return item; } -void FilterList::addContentFilters() +void FilterList::addContentCriteria() { for (unsigned i = 0; i < ModInfo::NUM_CONTENT_TYPES; ++i) { - addFilterItem( + addCriteriaItem( nullptr, tr("").arg(ModInfo::getContentTypeName(i)), i, ModListSortProxy::TYPE_CONTENT); } } -void FilterList::addCategoryFilters(QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID) +void FilterList::addCategoryCriteria(QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID) { - for (unsigned int i = 1; - i < static_cast(m_factory.numCategories()); ++i) { - if ((m_factory.getParentID(i) == targetID)) { + const auto count = static_cast(m_factory.numCategories()); + for (unsigned int i = 1; i < count; ++i) { + if (m_factory.getParentID(i) == targetID) { int categoryID = m_factory.getCategoryID(i); if (categoriesUsed.find(categoryID) != categoriesUsed.end()) { QTreeWidgetItem *item = - addFilterItem(root, m_factory.getCategoryName(i), + addCriteriaItem(root, m_factory.getCategoryName(i), categoryID, ModListSortProxy::TYPE_CATEGORY); if (m_factory.hasChildren(i)) { - addCategoryFilters(item, categoriesUsed, categoryID); + addCategoryCriteria(item, categoriesUsed, categoryID); } } } } } -void FilterList::addSpecialFilterItem(int type) +void FilterList::addSpecialCriteria(int type) { - addFilterItem( + addCriteriaItem( nullptr, m_factory.getSpecialCategoryName(type), type, ModListSortProxy::TYPE_SPECIAL); } @@ -98,19 +116,19 @@ void FilterList::refresh() ui->filters->clear(); using F = CategoryFactory; - addSpecialFilterItem(F::CATEGORY_SPECIAL_CHECKED); - addSpecialFilterItem(F::CATEGORY_SPECIAL_UNCHECKED); - addSpecialFilterItem(F::CATEGORY_SPECIAL_UPDATEAVAILABLE); - addSpecialFilterItem(F::CATEGORY_SPECIAL_BACKUP); - addSpecialFilterItem(F::CATEGORY_SPECIAL_MANAGED); - addSpecialFilterItem(F::CATEGORY_SPECIAL_UNMANAGED); - addSpecialFilterItem(F::CATEGORY_SPECIAL_NOCATEGORY); - addSpecialFilterItem(F::CATEGORY_SPECIAL_CONFLICT); - addSpecialFilterItem(F::CATEGORY_SPECIAL_NOTENDORSED); - addSpecialFilterItem(F::CATEGORY_SPECIAL_NONEXUSID); - addSpecialFilterItem(F::CATEGORY_SPECIAL_NOGAMEDATA); - - addContentFilters(); + addSpecialCriteria(F::CATEGORY_SPECIAL_CHECKED); + addSpecialCriteria(F::CATEGORY_SPECIAL_UNCHECKED); + addSpecialCriteria(F::CATEGORY_SPECIAL_UPDATEAVAILABLE); + addSpecialCriteria(F::CATEGORY_SPECIAL_BACKUP); + addSpecialCriteria(F::CATEGORY_SPECIAL_MANAGED); + addSpecialCriteria(F::CATEGORY_SPECIAL_UNMANAGED); + addSpecialCriteria(F::CATEGORY_SPECIAL_NOCATEGORY); + addSpecialCriteria(F::CATEGORY_SPECIAL_CONFLICT); + addSpecialCriteria(F::CATEGORY_SPECIAL_NOTENDORSED); + addSpecialCriteria(F::CATEGORY_SPECIAL_NONEXUSID); + addSpecialCriteria(F::CATEGORY_SPECIAL_NOGAMEDATA); + + addContentCriteria(); std::set categoriesUsed; for (unsigned int modIdx = 0; modIdx < ModInfo::getNumMods(); ++modIdx) { @@ -130,7 +148,7 @@ void FilterList::refresh() } } - addCategoryFilters(nullptr, categoriesUsed, 0); + addCategoryCriteria(nullptr, categoriesUsed, 0); for (const QString &item : selectedItems) { QList matches = ui->filters->findItems( @@ -145,7 +163,7 @@ void FilterList::refresh() void FilterList::setSelection(std::vector categories) { for (int i = 0; i < ui->filters->topLevelItemCount(); ++i) { - if (ui->filters->topLevelItem(i)->data(0, Qt::UserRole) == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) { + if (ui->filters->topLevelItem(i)->data(0, CategoryIDRole) == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) { ui->filters->setCurrentItem(ui->filters->topLevelItem(i)); break; } @@ -159,27 +177,24 @@ void FilterList::clearSelection() void FilterList::onSelection() { - QModelIndexList indices = ui->filters->selectionModel()->selectedRows(); - std::vector categories; - std::vector content; + const QModelIndexList indices = ui->filters->selectionModel()->selectedRows(); + std::vector criteria; - for (const QModelIndex &index : indices) { - const int filterType = index.data(Qt::UserRole + 1).toInt(); + for (auto* item: ui->filters->selectedItems()) { + const auto type = static_cast( + item->data(0, CategoryTypeRole).toInt()); - if ((filterType == ModListSortProxy::TYPE_CATEGORY) || (filterType == ModListSortProxy::TYPE_SPECIAL)) { - const int categoryId = index.data(Qt::UserRole).toInt(); - if (categoryId != CategoryFactory::CATEGORY_NONE) { - categories.push_back(categoryId); - } - } else if (filterType == ModListSortProxy::TYPE_CONTENT) { - const int contentId = index.data(Qt::UserRole).toInt(); - content.push_back(contentId); - } + const int id = item->data(0, CategoryIDRole).toInt(); + + auto* cb = static_cast(ui->filters->itemWidget(item, 1)); + const bool inverse = cb->isChecked(); + + criteria.push_back({type, id, inverse}); } - ui->filtersClear->setEnabled(categories.size() > 0 || content.size() >0); + ui->filtersClear->setEnabled(!criteria.empty()); - emit filtersChanged(categories, content); + emit criteriaChanged(criteria); } void FilterList::onContextMenu(const QPoint &pos) @@ -205,8 +220,7 @@ void FilterList::onCriteriaChanged() const auto mode = ui->filtersAnd->isChecked() ? ModListSortProxy::FILTER_AND : ModListSortProxy::FILTER_OR; - const bool inverse = ui->filtersNot->isChecked(); const bool separators = ui->filtersSeparators->isChecked(); - emit criteriaChanged(mode, inverse, separators); + emit optionsChanged(mode, separators); } diff --git a/src/filterlist.h b/src/filterlist.h index 85982392..418989e7 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -19,8 +19,8 @@ public: void refresh(); signals: - void filtersChanged(std::vector categories, std::vector content); - void criteriaChanged(ModListSortProxy::FilterMode mode, bool inverse, bool separators); + void criteriaChanged(std::vector criteria); + void optionsChanged(ModListSortProxy::FilterMode mode, bool separators); private: Ui::MainWindow* ui; @@ -32,14 +32,14 @@ private: void editCategories(); - QTreeWidgetItem* addFilterItem( + QTreeWidgetItem* addCriteriaItem( QTreeWidgetItem *root, const QString &name, int categoryID, - ModListSortProxy::FilterType type); + ModListSortProxy::CriteriaType type); - void addContentFilters(); - void addCategoryFilters( + void addContentCriteria(); + void addCategoryCriteria( QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID); - void addSpecialFilterItem(int type); + void addSpecialCriteria(int type); }; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 1319d906..03d61bc6 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -264,12 +264,12 @@ MainWindow::MainWindow(Settings &settings m_Filters.reset(new FilterList(ui, m_CategoryFactory)); connect( - m_Filters.get(), &FilterList::filtersChanged, - [&](auto&& cats, auto&& content) { onFilters(cats, content); }); + m_Filters.get(), &FilterList::criteriaChanged, + [&](auto&& v) { onFiltersCriteria(v); }); connect( - m_Filters.get(), &FilterList::criteriaChanged, - [&](auto mode, bool inv, bool sep) { onFiltersCriteria(mode, inv, sep); }); + m_Filters.get(), &FilterList::optionsChanged, + [&](auto mode, bool sep) { onFiltersOptions(mode, sep); }); ui->logList->setCore(m_OrganizerCore); @@ -4124,7 +4124,12 @@ void MainWindow::checkModsForUpdates() } if (updatesAvailable || checkingModsForUpdate) { - m_ModListSortProxy->setCategoryFilter(boost::assign::list_of(CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE)); + m_ModListSortProxy->setCriteria({{ + ModListSortProxy::TYPE_SPECIAL, + CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, + false} + }); + m_Filters->setSelection({CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE}); } } @@ -6111,44 +6116,31 @@ void MainWindow::refreshFilters() } } -void MainWindow::onFilters( - const std::vector& categories, const std::vector& content) +void MainWindow::onFiltersCriteria(const std::vector& criteria) { - m_ModListSortProxy->setCategoryFilter(categories); - m_ModListSortProxy->setContentFilter(content); + m_ModListSortProxy->setCriteria(criteria); QString label = "?"; - if ((categories.size() + content.size()) > 1) { - label = tr(""); - } else if (!categories.empty()) { - const int c = categories[0]; - label = m_CategoryFactory.getCategoryNameByID(c); + if (criteria.empty()) { + label = ""; + } else if (criteria.size() == 1) { + const auto& c = criteria[0]; + label = m_CategoryFactory.getCategoryNameByID(c.id); if (label.isEmpty()) { - log::error("category '{}' not found", c); - } - } else if (!content.empty()) { - const int c = content[0]; - try { - label = ModInfo::getContentTypeName(c); - } - catch(std::exception&) { - log::error("content filter '{}' not found", c); + log::error("category '{}' not found", c.id); } } else { - label = ""; + label = tr(""); } ui->currentCategoryLabel->setText(label); ui->modList->reset(); } -void MainWindow::onFiltersCriteria( - ModListSortProxy::FilterMode mode, bool inverse, bool separators) +void MainWindow::onFiltersOptions(ModListSortProxy::FilterMode mode, bool separators) { - m_ModListSortProxy->setFilterMode(mode); - m_ModListSortProxy->setFilterNot(inverse); - m_ModListSortProxy->setFilterSeparators(separators); + m_ModListSortProxy->setOptions(mode, separators); } void MainWindow::updateESPLock(bool locked) diff --git a/src/mainwindow.h b/src/mainwindow.h index 9837378b..0b559300 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -514,8 +514,8 @@ private slots: void deselectFilters(); void refreshFilters(); - void onFilters(const std::vector& categories, const std::vector& content); - void onFiltersCriteria(ModListSortProxy::FilterMode mode, bool inverse, bool separators); + void onFiltersCriteria(const std::vector& filters); + void onFiltersOptions(ModListSortProxy::FilterMode mode, bool separators); void displayModInformation(const QString &modName, ModInfoTabIDs tabID); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index ed35f783..7cc7dca4 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -85,12 +85,26 @@ true + + false + + true + + + true + + false - 1 + Category + + + + + Invert @@ -152,16 +166,6 @@ - - - - Invert each selected category - - - Not - - - @@ -351,9 +355,6 @@ p, li { white-space: pre-wrap; } Qt::CustomContextMenu - - List of available mods. - This is a list of installed mods. Use the checkboxes to activate/deactivate mods and drag & drop mods to change their "installation" orders. @@ -448,14 +449,7 @@ p, li { white-space: pre-wrap; } - - - - 8 - true - - - + diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index d2a5e258..646401b9 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -38,7 +38,6 @@ ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) , m_Profile(profile) , m_FilterActive(false) , m_FilterMode(FILTER_AND) - , m_FilterNot(false) , m_FilterSeparators(false) { setDynamicSortFilter(true); // this seems to work without dynamicsortfilter @@ -52,26 +51,20 @@ void ModListSortProxy::setProfile(Profile *profile) void ModListSortProxy::updateFilterActive() { - m_FilterActive = ((m_CategoryFilter.size() > 0) - || (m_ContentFilter.size() > 0) - || !m_CurrentFilter.isEmpty()); + m_FilterActive = (!m_Criteria.empty() || !m_Filter.isEmpty()); emit filterActive(m_FilterActive); } -void ModListSortProxy::setCategoryFilter(const std::vector &categories) +void ModListSortProxy::setCriteria(const std::vector& criteria) { - //avoid refreshing the filter unless we are checking all mods for update. - if (categories != m_CategoryFilter || (!categories.empty() && categories.at(0) == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE)) { - m_CategoryFilter = categories; - updateFilterActive(); - invalidate(); - } -} - -void ModListSortProxy::setContentFilter(const std::vector &content) -{ - if (content != m_ContentFilter) { - m_ContentFilter = content; + // avoid refreshing the filter unless we are checking all mods for update. + const bool changed = (criteria != m_Criteria); + const bool isForUpdates = ( + !criteria.empty() && + criteria[0].id == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE); + + if (changed || isForUpdates) { + m_Criteria = criteria; updateFilterActive(); invalidate(); } @@ -248,12 +241,10 @@ bool ModListSortProxy::lessThan(const QModelIndex &left, return lt; } -void ModListSortProxy::updateFilter(const QString &filter) +void ModListSortProxy::updateFilter(const QString& filter) { - m_CurrentFilter = filter; + m_Filter = filter; updateFilterActive(); - // using invalidateFilter here should be enough but that crashes the application? WTF? - // invalidateFilter(); invalidate(); } @@ -282,14 +273,8 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons return false; } - for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { - if (!categoryMatchesMod(info, enabled, *iter)) { - return false; - } - } - - foreach (int content, m_ContentFilter) { - if (!contentMatchesMod(info, enabled, content)) { + for (auto&& c : m_Criteria) { + if (!criteriaMatchesMod(info, enabled, c)) { return false; } } @@ -303,29 +288,35 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const return false; } - for (auto iter = m_CategoryFilter.begin(); iter != m_CategoryFilter.end(); ++iter) { - if (categoryMatchesMod(info, enabled, *iter)) { + for (auto&& c : m_Criteria) { + if (criteriaMatchesMod(info, enabled, c)) { return true; } } - if (!m_CategoryFilter.empty()) { + if (!m_Criteria.empty()) { // nothing matched return false; } - foreach (int content, m_ContentFilter) { - if (contentMatchesMod(info, enabled, content)) { - return true; - } - } + return true; +} - if (!m_ContentFilter.empty()) { - // nothing matched - return false; - } +bool ModListSortProxy::criteriaMatchesMod( + ModInfo::Ptr info, bool enabled, const Criteria& c) const +{ + switch (c.type) + { + case TYPE_SPECIAL: // fall-through + case TYPE_CATEGORY: + return categoryMatchesMod(info, enabled, c.id); - return true; + case TYPE_CONTENT: + return contentMatchesMod(info, enabled, c.id); + + default: + return false; + } } bool ModListSortProxy::categoryMatchesMod( @@ -414,29 +405,19 @@ bool ModListSortProxy::categoryMatchesMod( } } - if (m_FilterNot) { - b = !b; - } - return b; } bool ModListSortProxy::contentMatchesMod(ModInfo::Ptr info, bool enabled, int content) const { - bool b = info->hasContent(static_cast(content)); - - if (m_FilterNot) { - b = !b; - } - - return b; + return info->hasContent(static_cast(content)); } bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const { - if (!m_CurrentFilter.isEmpty()) { + if (!m_Filter.isEmpty()) { bool display = false; - QString filterCopy = QString(m_CurrentFilter); + QString filterCopy = QString(m_Filter); filterCopy.replace("||", ";").replace("OR", ";").replace("|", ";"); QStringList ORList = filterCopy.split(";", QString::SkipEmptyParts); @@ -527,26 +508,11 @@ void ModListSortProxy::setColumnVisible(int column, bool visible) m_EnabledColumns[column] = visible; } -void ModListSortProxy::setFilterMode(ModListSortProxy::FilterMode mode) +void ModListSortProxy::setOptions(ModListSortProxy::FilterMode mode, bool separators) { - if (m_FilterMode != mode) { + if (m_FilterMode != mode || separators != m_FilterSeparators) { m_FilterMode = mode; - this->invalidate(); - } -} - -void ModListSortProxy::setFilterNot(bool b) -{ - if (b != m_FilterNot) { - m_FilterNot = b; - this->invalidate(); - } -} - -void ModListSortProxy::setFilterSeparators(bool b) -{ - if (b != m_FilterSeparators) { - m_FilterSeparators = b; + m_FilterSeparators = separators; this->invalidate(); } } @@ -623,8 +589,8 @@ void ModListSortProxy::aboutToChangeData() // (at least with some Qt versions) // this may be related to the fact that the item being edited may disappear from the view as a // result of the edit - m_PreChangeFilters = categoryFilter(); - setCategoryFilter(std::vector()); + m_PreChangeCriteria = m_Criteria; + setCriteria({}); } void ModListSortProxy::postDataChanged() @@ -633,8 +599,8 @@ void ModListSortProxy::postDataChanged() // or at least the view continues to think it's being edited. As a result no new editor can be // opened QTimer::singleShot(10, [this] () { - setCategoryFilter(m_PreChangeFilters); - m_PreChangeFilters.clear(); + setCriteria(m_PreChangeCriteria); + m_PreChangeCriteria.clear(); }); } diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 2ebfbcf0..5aeaccce 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -37,22 +37,38 @@ public: FILTER_OR }; - enum FilterType { + enum CriteriaType { TYPE_SPECIAL, TYPE_CATEGORY, TYPE_CONTENT }; + struct Criteria + { + CriteriaType type; + int id; + bool inverse; + + bool operator==(const Criteria& other) const + { + return + (type == other.type) && + (id == other.id) && + (inverse == other.inverse); + } + + bool operator!=(const Criteria& other) const + { + return !(*this == other); + } + }; + public: explicit ModListSortProxy(Profile *profile, QObject *parent = 0); void setProfile(Profile *profile); - void setCategoryFilter(const std::vector &categories); - std::vector categoryFilter() const { return m_CategoryFilter; } - - void setContentFilter(const std::vector &content); virtual Qt::ItemFlags flags(const QModelIndex &modelIndex) const; virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, @@ -84,9 +100,8 @@ public: */ bool isFilterActive() const { return m_FilterActive; } - void setFilterMode(FilterMode mode); - void setFilterNot(bool b); - void setFilterSeparators(bool b); + void setCriteria(const std::vector& criteria); + void setOptions(FilterMode mode, bool separators); /** * @brief tests if the specified index has child nodes @@ -131,19 +146,18 @@ private slots: void postDataChanged(); private: - Profile *m_Profile; - std::vector m_CategoryFilter; - std::vector m_ContentFilter; + Profile* m_Profile; + std::vector m_Criteria; + QString m_Filter; std::bitset m_EnabledColumns; - QString m_CurrentFilter; bool m_FilterActive; FilterMode m_FilterMode; - bool m_FilterNot; bool m_FilterSeparators; - std::vector m_PreChangeFilters; + std::vector m_PreChangeCriteria; + bool criteriaMatchesMod(ModInfo::Ptr info, bool enabled, const Criteria& c) const; bool categoryMatchesMod(ModInfo::Ptr info, bool enabled, int category) const; bool contentMatchesMod(ModInfo::Ptr info, bool enabled, int content) const; }; -- cgit v1.3.1 From a38d1723bffcd20bc7011c0fe635636b936aa78b Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 30 Nov 2019 03:21:23 -0500 Subject: removed redundant categories now that there's a not filter disabled collapsing for filter, there's already a button to hide it --- src/categories.cpp | 24 +++++++++++------------- src/categories.h | 31 +++++++++++++------------------ src/filterlist.cpp | 28 +++++++++++++++------------- src/mainwindow.cpp | 4 ++-- src/mainwindow.ui | 3 +++ src/modlistsortproxy.cpp | 32 ++++++++++---------------------- 6 files changed, 54 insertions(+), 68 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/categories.cpp b/src/categories.cpp index b75efefa..1bd56f7f 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -320,21 +320,19 @@ QString CategoryFactory::getCategoryName(unsigned int index) const return m_Categories[index].m_Name; } -QString CategoryFactory::getSpecialCategoryName(int type) const +QString CategoryFactory::getSpecialCategoryName(SpecialCategories type) const { switch (type) { - case CATEGORY_SPECIAL_CHECKED: return QObject::tr(""); - case CATEGORY_SPECIAL_UNCHECKED: return QObject::tr(""); - case CATEGORY_SPECIAL_UPDATEAVAILABLE: return QObject::tr(""); - case CATEGORY_SPECIAL_NOCATEGORY: return QObject::tr(""); - case CATEGORY_SPECIAL_CONFLICT: return QObject::tr(""); - case CATEGORY_SPECIAL_NOTENDORSED: return QObject::tr(""); - case CATEGORY_SPECIAL_BACKUP: return QObject::tr(""); - case CATEGORY_SPECIAL_MANAGED: return QObject::tr(""); - case CATEGORY_SPECIAL_UNMANAGED: return QObject::tr(""); - case CATEGORY_SPECIAL_NOGAMEDATA: return QObject::tr(""); - case CATEGORY_SPECIAL_NONEXUSID: return QObject::tr(""); + case Checked: return QObject::tr(""); + case UpdateAvailable: return QObject::tr(""); + case HasNoCategory: return QObject::tr(""); + case Conflict: return QObject::tr(""); + case NotEndorsed: return QObject::tr(""); + case Backup: return QObject::tr(""); + case Managed: return QObject::tr(""); + case NoGameData: return QObject::tr(""); + case NoNexusID: return QObject::tr(""); default: return {}; } } @@ -344,7 +342,7 @@ QString CategoryFactory::getCategoryNameByID(int id) const auto itor = m_IDMap.find(id); if (itor == m_IDMap.end()) { - return getSpecialCategoryName(id); + return getSpecialCategoryName(static_cast(id)); } else { const auto index = itor->second; if (index >= m_Categories.size()) { diff --git a/src/categories.h b/src/categories.h index 2041ce1f..296e7711 100644 --- a/src/categories.h +++ b/src/categories.h @@ -37,25 +37,20 @@ class CategoryFactory { friend class CategoriesDialog; public: - - static const int CATEGORY_NONE = 0; - - static const int CATEGORY_SPECIAL_FIRST = 10000; - static const int CATEGORY_SPECIAL_CHECKED = CATEGORY_SPECIAL_FIRST; - static const int CATEGORY_SPECIAL_UNCHECKED = 10001; - static const int CATEGORY_SPECIAL_UPDATEAVAILABLE = 10002; - static const int CATEGORY_SPECIAL_NOCATEGORY = 10003; - static const int CATEGORY_SPECIAL_CONFLICT = 10004; - static const int CATEGORY_SPECIAL_NOTENDORSED = 10005; - static const int CATEGORY_SPECIAL_BACKUP = 10006; - static const int CATEGORY_SPECIAL_MANAGED = 10007; - static const int CATEGORY_SPECIAL_UNMANAGED = 10008; - static const int CATEGORY_SPECIAL_NOGAMEDATA = 10009; - static const int CATEGORY_SPECIAL_NONEXUSID = 10010; - + enum SpecialCategories + { + Checked = 10000, + UpdateAvailable, + HasNoCategory, + Conflict, + NotEndorsed, + Backup, + Managed, + NoGameData, + NoNexusID + }; public: - struct Category { Category(int sortValue, int id, const QString &name, const std::vector &nexusIDs, int parentID) : m_SortValue(sortValue), m_ID(id), m_Name(name), m_HasChildren(false), @@ -144,7 +139,7 @@ public: * @return QString name of the category **/ QString getCategoryName(unsigned int index) const; - QString getSpecialCategoryName(int type) const; + QString getSpecialCategoryName(SpecialCategories type) const; QString getCategoryNameByID(int id) const; /** diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 8f297af6..36cdacd0 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -94,6 +94,8 @@ FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) ui->filters->header()->setSectionResizeMode(0, QHeaderView::Stretch); ui->filters->header()->resizeSection(1, 50); + ui->categoriesSplitter->setCollapsible(0, false); + ui->categoriesSplitter->setCollapsible(1, false); } QTreeWidgetItem* FilterList::addCriteriaItem( @@ -142,8 +144,10 @@ void FilterList::addCategoryCriteria(QTreeWidgetItem *root, const std::set void FilterList::addSpecialCriteria(int type) { + const auto sc = static_cast(type); + addCriteriaItem( - nullptr, m_factory.getSpecialCategoryName(type), + nullptr, m_factory.getSpecialCategoryName(sc), type, ModListSortProxy::TYPE_SPECIAL); } @@ -157,17 +161,15 @@ void FilterList::refresh() ui->filters->clear(); using F = CategoryFactory; - addSpecialCriteria(F::CATEGORY_SPECIAL_CHECKED); - addSpecialCriteria(F::CATEGORY_SPECIAL_UNCHECKED); - addSpecialCriteria(F::CATEGORY_SPECIAL_UPDATEAVAILABLE); - addSpecialCriteria(F::CATEGORY_SPECIAL_BACKUP); - addSpecialCriteria(F::CATEGORY_SPECIAL_MANAGED); - addSpecialCriteria(F::CATEGORY_SPECIAL_UNMANAGED); - addSpecialCriteria(F::CATEGORY_SPECIAL_NOCATEGORY); - addSpecialCriteria(F::CATEGORY_SPECIAL_CONFLICT); - addSpecialCriteria(F::CATEGORY_SPECIAL_NOTENDORSED); - addSpecialCriteria(F::CATEGORY_SPECIAL_NONEXUSID); - addSpecialCriteria(F::CATEGORY_SPECIAL_NOGAMEDATA); + addSpecialCriteria(F::Checked); + addSpecialCriteria(F::UpdateAvailable); + addSpecialCriteria(F::Backup); + addSpecialCriteria(F::Managed); + addSpecialCriteria(F::HasNoCategory); + addSpecialCriteria(F::Conflict); + addSpecialCriteria(F::NotEndorsed); + addSpecialCriteria(F::NoNexusID); + addSpecialCriteria(F::NoGameData); addContentCriteria(); @@ -211,7 +213,7 @@ void FilterList::setSelection(std::vector categories) continue; } - if (item->id() == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE) { + if (item->id() == CategoryFactory::UpdateAvailable) { ui->filters->setCurrentItem(ui->filters->topLevelItem(i)); break; } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 03d61bc6..0ad57803 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4126,11 +4126,11 @@ void MainWindow::checkModsForUpdates() if (updatesAvailable || checkingModsForUpdate) { m_ModListSortProxy->setCriteria({{ ModListSortProxy::TYPE_SPECIAL, - CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE, + CategoryFactory::UpdateAvailable, false} }); - m_Filters->setSelection({CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE}); + m_Filters->setSelection({CategoryFactory::UpdateAvailable}); } } diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 7cc7dca4..6c35d239 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -39,6 +39,9 @@ Qt::Horizontal + + false + diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 3bb02c0f..36dcae59 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -61,7 +61,7 @@ void ModListSortProxy::setCriteria(const std::vector& criteria) const bool changed = (criteria != m_Criteria); const bool isForUpdates = ( !criteria.empty() && - criteria[0].id == CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE); + criteria[0].id == CategoryFactory::UpdateAvailable); if (changed || isForUpdates) { m_Criteria = criteria; @@ -343,68 +343,56 @@ bool ModListSortProxy::categoryMatchesMod( switch (category) { - case CategoryFactory::CATEGORY_SPECIAL_CHECKED: + case CategoryFactory::Checked: { b = (enabled || info->alwaysEnabled()); break; } - case CategoryFactory::CATEGORY_SPECIAL_UNCHECKED: - { - b = (!enabled && !info->alwaysEnabled()); - break; - } - - case CategoryFactory::CATEGORY_SPECIAL_UPDATEAVAILABLE: + case CategoryFactory::UpdateAvailable: { b = (info->updateAvailable() || info->downgradeAvailable()); break; } - case CategoryFactory::CATEGORY_SPECIAL_NOCATEGORY: + case CategoryFactory::HasNoCategory: { b = (info->getCategories().size() == 0); break; } - case CategoryFactory::CATEGORY_SPECIAL_CONFLICT: + case CategoryFactory::Conflict: { b = (hasConflictFlag(info->getFlags())); break; } - case CategoryFactory::CATEGORY_SPECIAL_NOTENDORSED: + case CategoryFactory::NotEndorsed: { ModInfo::EEndorsedState state = info->endorsedState(); b = (state != ModInfo::ENDORSED_TRUE); break; } - case CategoryFactory::CATEGORY_SPECIAL_BACKUP: + case CategoryFactory::Backup: { b = (info->hasFlag(ModInfo::FLAG_BACKUP)); break; } - case CategoryFactory::CATEGORY_SPECIAL_MANAGED: + case CategoryFactory::Managed: { b = (!info->hasFlag(ModInfo::FLAG_FOREIGN)); break; } - case CategoryFactory::CATEGORY_SPECIAL_UNMANAGED: - { - b = (info->hasFlag(ModInfo::FLAG_FOREIGN)); - break; - } - - case CategoryFactory::CATEGORY_SPECIAL_NOGAMEDATA: + case CategoryFactory::NoGameData: { b = (info->hasFlag(ModInfo::FLAG_INVALID)); break; } - case CategoryFactory::CATEGORY_SPECIAL_NONEXUSID: + case CategoryFactory::NoNexusID: { b = ( info->getNexusID() == -1 && -- cgit v1.3.1 From f080e2e25d05eb639cc4168d3c6905041f4dc564 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 30 Nov 2019 03:39:12 -0500 Subject: always enable clear filter button, made it so it also removes not flags removed "deselect filters" context menu, redundant disable not flag menu items without selection --- src/filterlist.cpp | 30 ++++++++++++++++++++++++------ src/filterlist.h | 3 ++- src/mainwindow.ui | 6 ------ 3 files changed, 26 insertions(+), 13 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 36cdacd0..66a9aed0 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -78,7 +78,7 @@ FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) connect( ui->filtersClear, &QPushButton::clicked, - [&]{ clearSelection(); }); + [&]{ clear(); }); connect( ui->filtersAnd, &QCheckBox::toggled, @@ -241,20 +241,23 @@ void FilterList::onSelection() }); } - ui->filtersClear->setEnabled(!criteria.empty()); - emit criteriaChanged(criteria); } void FilterList::onContextMenu(const QPoint &pos) { QMenu menu; - menu.addAction(tr("Deselect filters"), [&]{ clearSelection(); }); - menu.addAction(tr("Set inverted"), [&]{ toggleInverted(true); }); - menu.addAction(tr("Unset inverted"), [&]{ toggleInverted(false); }); + + QAction* set = menu.addAction(tr("Set inverted"), [&]{ toggleInverted(true); }); + QAction* unset = menu.addAction(tr("Unset inverted"), [&]{ toggleInverted(false); }); menu.addSeparator(); menu.addAction(tr("Edit Categories..."), [&]{ editCategories(); }); + if (ui->filters->selectedItems().empty()) { + set->setEnabled(false); + unset->setEnabled(false); + } + menu.exec(ui->filters->viewport()->mapToGlobal(pos)); } @@ -267,6 +270,21 @@ void FilterList::editCategories() } } +void FilterList::clear() +{ + const auto count = ui->filters->topLevelItemCount(); + for (int i=0; i(ui->filters->topLevelItem(i)); + if (!ci) { + continue; + } + + ci->setInverted(false); + } + + clearSelection(); +} + void FilterList::toggleInverted(bool b) { bool changed = false; diff --git a/src/filterlist.h b/src/filterlist.h index 1fd0942a..e98f81a9 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -32,8 +32,9 @@ private: void onSelection(); void onCriteriaChanged(); - void editCategories(); + void clear(); void toggleInverted(bool b); + void editCategories(); QTreeWidgetItem* addCriteriaItem( QTreeWidgetItem *root, const QString &name, int categoryID, diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 6c35d239..5206d797 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -114,9 +114,6 @@ - - false - 0 @@ -132,9 +129,6 @@ Clear - - true - -- cgit v1.3.1 From 7f4fce35f97f262c36e4c00dad55c1b078cf3758 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 30 Nov 2019 05:23:22 -0500 Subject: fixed separators option being used even without filters changed filter list to use tristate items without selection --- src/filterlist.cpp | 232 ++++++++++++++++++++++++++++------------------- src/filterlist.h | 8 +- src/mainwindow.ui | 64 +++++++------ src/modlistsortproxy.cpp | 26 +++++- src/modlistsortproxy.h | 3 +- 5 files changed, 204 insertions(+), 129 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 81f8b670..0dee8544 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -11,28 +11,23 @@ using Criteria = ModListSortProxy::Criteria; class FilterList::CriteriaItem : public QTreeWidgetItem { public: - CriteriaItem(FilterList* list, QString name, CriteriaType type, int id) - : QTreeWidgetItem({name}), m_list(list), m_widget(nullptr), m_checkbox(nullptr) + enum States : int { - setData(0, Qt::ToolTipRole, name); - setData(0, TypeRole, type); - setData(0, IDRole, id); - - m_widget = new QWidget; - m_widget->setStyleSheet("background-color: rgba(0,0,0,0)"); + FirstState = 0, - auto* ly = new QVBoxLayout(m_widget); - ly->setAlignment(Qt::AlignCenter); - ly->setContentsMargins(0, 0, 0, 0); + Inactive = FirstState, + Active, + Inverted, - m_checkbox = new QCheckBox; - QObject::connect(m_checkbox, &QCheckBox::toggled, [&]{ m_list->onSelection(); }); - ly->addWidget(m_checkbox); - } + LastState = Inverted + }; - QWidget* widget() + CriteriaItem(FilterList* list, QString name, CriteriaType type, int id) + : QTreeWidgetItem({"", name}), m_list(list), m_state(Inactive) { - return m_widget; + setData(0, Qt::ToolTipRole, name); + setData(0, TypeRole, type); + setData(0, IDRole, id); } CriteriaType type() const @@ -45,14 +40,37 @@ public: return data(0, IDRole).toInt(); } - bool inverse() const + States state() const + { + return m_state; + } + + void setState(States s) { - return m_checkbox->isChecked(); + if (m_state != s) { + m_state = s; + updateState(); + } } - void setInverted(bool b) + void nextState() { - m_checkbox->setChecked(b); + m_state = static_cast(m_state + 1); + if (m_state > LastState) { + m_state = FirstState; + } + + updateState(); + } + + void previousState() + { + m_state = static_cast(m_state - 1); + if (m_state < FirstState) { + m_state = LastState; + } + + updateState(); } private: @@ -60,40 +78,91 @@ private: const int TypeRole = Qt::UserRole + 1; FilterList* m_list; - QWidget* m_widget; - QCheckBox* m_checkbox; + States m_state; + + void updateState() + { + QString s; + + switch (m_state) + { + case Inactive: + { + break; + } + + case Active: + { + // U+2713 CHECK MARK + s = QString::fromUtf8("\xe2\x9c\x93"); + break; + } + + case Inverted: + { + s = tr("Not"); + break; + } + } + + setText(0, s); + } +}; + + +class ClickFilter : public QObject +{ +public: + ClickFilter(std::function f) + : m_f(std::move(f)) + { + } + + bool eventFilter(QObject* o, QEvent* e) override + { + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) { + if (m_f) { + return m_f(static_cast(e)); + } + } + + return QObject::eventFilter(o, e);; + } + +private: + std::function m_f; }; FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) : ui(ui), m_factory(factory) { - connect( - ui->filters, &QTreeWidget::customContextMenuRequested, - [&](auto&& pos){ onContextMenu(pos); }); + ui->filters->viewport()->installEventFilter( + new ClickFilter([&](auto* e){ return onClick(e); })); connect( - ui->filters, &QTreeWidget::itemSelectionChanged, - [&]{ onSelection(); }); + ui->filtersClear, &QPushButton::clicked, + [&]{ clearSelection(); }); connect( - ui->filtersClear, &QPushButton::clicked, - [&]{ clear(); }); + ui->filtersEdit, &QPushButton::clicked, + [&]{ editCategories(); }); connect( ui->filtersAnd, &QCheckBox::toggled, - [&]{ onCriteriaChanged(); }); + [&]{ onOptionsChanged(); }); connect( ui->filtersOr, &QCheckBox::toggled, - [&]{ onCriteriaChanged(); }); + [&]{ onOptionsChanged(); }); connect( ui->filtersSeparators, &QCheckBox::toggled, - [&]{ onCriteriaChanged(); }); + [&]{ onOptionsChanged(); }); - ui->filters->header()->setSectionResizeMode(0, QHeaderView::Stretch); - ui->filters->header()->resizeSection(1, 50); + ui->filters->header()->setMinimumSectionSize(0); + ui->filters->header()->setSectionResizeMode(0, QHeaderView::Fixed); + ui->filters->header()->resizeSection(0, 30); ui->categoriesSplitter->setCollapsible(0, false); ui->categoriesSplitter->setCollapsible(1, false); } @@ -110,7 +179,7 @@ QTreeWidgetItem* FilterList::addCriteriaItem( ui->filters->addTopLevelItem(item); } - ui->filters->setItemWidget(item, 1, item->widget()); + item->setTextAlignment(0, Qt::AlignCenter); return item; } @@ -224,91 +293,72 @@ void FilterList::setSelection(const std::vector& criteria) void FilterList::clearSelection() { - ui->filters->clearSelection(); -} - -void FilterList::onSelection() -{ - const QModelIndexList indices = ui->filters->selectionModel()->selectedRows(); - std::vector criteria; - - for (auto* item : ui->filters->selectedItems()) { - const auto* ci = dynamic_cast(item); + for (int i=0; ifilters->topLevelItemCount(); ++i) { + auto* ci = dynamic_cast(ui->filters->topLevelItem(i)); if (!ci) { continue; } - criteria.push_back({ - ci->type(), ci->id(), ci->inverse() - }); + ci->setState(CriteriaItem::Inactive); } - emit criteriaChanged(criteria); + checkCriteria(); } -void FilterList::onContextMenu(const QPoint &pos) +bool FilterList::onClick(QMouseEvent* e) { - QMenu menu; + auto* item = ui->filters->itemAt(e->pos()); + if (!item) { + return false; + } - QAction* set = menu.addAction(tr("Set inverted"), [&]{ toggleInverted(true); }); - QAction* unset = menu.addAction(tr("Unset inverted"), [&]{ toggleInverted(false); }); - menu.addSeparator(); - menu.addAction(tr("Edit Categories..."), [&]{ editCategories(); }); + auto* ci = dynamic_cast(item); + if (!ci) { + return false; + } - if (ui->filters->selectedItems().empty()) { - set->setEnabled(false); - unset->setEnabled(false); + if (e->button() == Qt::LeftButton) { + ci->nextState(); + } else if (e->button() == Qt::RightButton) { + ci->previousState(); + } else { + return false; } - menu.exec(ui->filters->viewport()->mapToGlobal(pos)); + checkCriteria(); + return true; } -void FilterList::editCategories() +void FilterList::checkCriteria() { - CategoriesDialog dialog(qApp->activeWindow()); - - if (dialog.exec() == QDialog::Accepted) { - dialog.commitChanges(); - } -} + std::vector criteria; -void FilterList::clear() -{ - const auto count = ui->filters->topLevelItemCount(); - for (int i=0; i(ui->filters->topLevelItem(i)); + for (int i=0; ifilters->topLevelItemCount(); ++i) { + const auto* ci = dynamic_cast(ui->filters->topLevelItem(i)); if (!ci) { continue; } - ci->setInverted(false); + if (ci->state() != CriteriaItem::Inactive) { + criteria.push_back({ + ci->type(), ci->id(), (ci->state() == CriteriaItem::Inverted) + }); + } } - clearSelection(); + emit criteriaChanged(criteria); } -void FilterList::toggleInverted(bool b) +void FilterList::editCategories() { - bool changed = false; - - for (auto* item : ui->filters->selectedItems()) { - auto* ci = dynamic_cast(item); - if (!ci) { - continue; - } - - if (ci->inverse() != b) { - ci->setInverted(b); - changed = true; - } - } + CategoriesDialog dialog(qApp->activeWindow()); - if (changed) { - onSelection(); + if (dialog.exec() == QDialog::Accepted) { + dialog.commitChanges(); } } -void FilterList::onCriteriaChanged() +void FilterList::onOptionsChanged() { const auto mode = ui->filtersAnd->isChecked() ? ModListSortProxy::FILTER_AND : ModListSortProxy::FILTER_OR; diff --git a/src/filterlist.h b/src/filterlist.h index 52b90ea7..fac1d683 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -28,13 +28,11 @@ private: Ui::MainWindow* ui; CategoryFactory& m_factory; - void onContextMenu(const QPoint &pos); - void onSelection(); - void onCriteriaChanged(); + bool onClick(QMouseEvent* e); + void onOptionsChanged(); - void clear(); - void toggleInverted(bool b); void editCategories(); + void checkCriteria(); QTreeWidgetItem* addCriteriaItem( QTreeWidgetItem *root, const QString &name, int categoryID, diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 5206d797..1a64dfdd 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -76,59 +76,69 @@ 0 - - Qt::CustomContextMenu - - QAbstractItemView::ExtendedSelection + QAbstractItemView::NoSelection 0 + + false + true - false + true - true + false - true - - false - Category + - Invert + Category - - - - 0 - 0 - - - - - 0 - 25 - - - - Clear - + + + + 0 + + + 2 + + + 0 + + + 0 + + + + + Clear + + + + + + + Edit... + + + + diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index 36dcae59..e6bed49c 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -269,12 +269,12 @@ bool ModListSortProxy::hasConflictFlag(const std::vector &flags) bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const { - if (info->hasFlag(ModInfo::FLAG_SEPARATOR) && !m_FilterSeparators) { + if (!optionsMatchMod(info, enabled)) { return false; } for (auto&& c : m_Criteria) { - if (!criteriaMatchesMod(info, enabled, c)) { + if (!criteriaMatchMod(info, enabled, c)) { return false; } } @@ -284,12 +284,12 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const { - if (info->hasFlag(ModInfo::FLAG_SEPARATOR) && !m_FilterSeparators) { + if (!optionsMatchMod(info, enabled)) { return false; } for (auto&& c : m_Criteria) { - if (criteriaMatchesMod(info, enabled, c)) { + if (criteriaMatchMod(info, enabled, c)) { return true; } } @@ -302,7 +302,23 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const return true; } -bool ModListSortProxy::criteriaMatchesMod( +bool ModListSortProxy::optionsMatchMod(ModInfo::Ptr info, bool) const +{ + // don't check options if there are no filters selected + if (!m_FilterActive) { + return true; + } + + if (!m_FilterSeparators) { + if (info->hasFlag(ModInfo::FLAG_SEPARATOR)) { + return false; + } + } + + return true; +} + +bool ModListSortProxy::criteriaMatchMod( ModInfo::Ptr info, bool enabled, const Criteria& c) const { bool b = false; diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 5aeaccce..9b533492 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -157,7 +157,8 @@ private: std::vector m_PreChangeCriteria; - bool criteriaMatchesMod(ModInfo::Ptr info, bool enabled, const Criteria& c) const; + bool optionsMatchMod(ModInfo::Ptr info, bool enabled) const; + bool criteriaMatchMod(ModInfo::Ptr info, bool enabled, const Criteria& c) const; bool categoryMatchesMod(ModInfo::Ptr info, bool enabled, int category) const; bool contentMatchesMod(ModInfo::Ptr info, bool enabled, int content) const; }; -- cgit v1.3.1 From d2073ef2bd62527034864fd0cacd5537aff33218 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Sat, 30 Nov 2019 05:37:34 -0500 Subject: made all categories positive fixed context menu sometimes appearing --- src/categories.cpp | 16 ++++++++-------- src/categories.h | 8 ++++---- src/filterlist.cpp | 10 +++++----- src/mainwindow.ui | 3 +++ src/modlistsortproxy.cpp | 29 ++++++++++++++++------------- 5 files changed, 36 insertions(+), 30 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/categories.cpp b/src/categories.cpp index 1bd56f7f..5c9a4d55 100644 --- a/src/categories.cpp +++ b/src/categories.cpp @@ -324,15 +324,15 @@ QString CategoryFactory::getSpecialCategoryName(SpecialCategories type) const { switch (type) { - case Checked: return QObject::tr(""); - case UpdateAvailable: return QObject::tr(""); - case HasNoCategory: return QObject::tr(""); + case Checked: return QObject::tr(""); + case UpdateAvailable: return QObject::tr(""); + case HasCategory: return QObject::tr(""); case Conflict: return QObject::tr(""); - case NotEndorsed: return QObject::tr(""); - case Backup: return QObject::tr(""); - case Managed: return QObject::tr(""); - case NoGameData: return QObject::tr(""); - case NoNexusID: return QObject::tr(""); + case Endorsed: return QObject::tr(""); + case Backup: return QObject::tr(""); + case Managed: return QObject::tr(""); + case HasGameData: return QObject::tr(""); + case HasNexusID: return QObject::tr(""); default: return {}; } } diff --git a/src/categories.h b/src/categories.h index 296e7711..02695e4d 100644 --- a/src/categories.h +++ b/src/categories.h @@ -41,13 +41,13 @@ public: { Checked = 10000, UpdateAvailable, - HasNoCategory, + HasCategory, Conflict, - NotEndorsed, + Endorsed, Backup, Managed, - NoGameData, - NoNexusID + HasGameData, + HasNexusID }; public: diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 0dee8544..b65f0f4a 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -11,7 +11,7 @@ using Criteria = ModListSortProxy::Criteria; class FilterList::CriteriaItem : public QTreeWidgetItem { public: - enum States : int + enum States { FirstState = 0, @@ -234,11 +234,11 @@ void FilterList::refresh() addSpecialCriteria(F::UpdateAvailable); addSpecialCriteria(F::Backup); addSpecialCriteria(F::Managed); - addSpecialCriteria(F::HasNoCategory); + addSpecialCriteria(F::HasCategory); addSpecialCriteria(F::Conflict); - addSpecialCriteria(F::NotEndorsed); - addSpecialCriteria(F::NoNexusID); - addSpecialCriteria(F::NoGameData); + addSpecialCriteria(F::Endorsed); + addSpecialCriteria(F::HasNexusID); + addSpecialCriteria(F::HasGameData); addContentCriteria(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 1a64dfdd..92a41c67 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -76,6 +76,9 @@ 0 + + Qt::NoContextMenu + QAbstractItemView::NoSelection diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index e6bed49c..fd3dbc9e 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -371,9 +371,9 @@ bool ModListSortProxy::categoryMatchesMod( break; } - case CategoryFactory::HasNoCategory: + case CategoryFactory::HasCategory: { - b = (info->getCategories().size() == 0); + b = !info->getCategories().empty(); break; } @@ -383,10 +383,9 @@ bool ModListSortProxy::categoryMatchesMod( break; } - case CategoryFactory::NotEndorsed: + case CategoryFactory::Endorsed: { - ModInfo::EEndorsedState state = info->endorsedState(); - b = (state != ModInfo::ENDORSED_TRUE); + b = (info->endorsedState() == ModInfo::ENDORSED_TRUE); break; } @@ -402,20 +401,24 @@ bool ModListSortProxy::categoryMatchesMod( break; } - case CategoryFactory::NoGameData: + case CategoryFactory::HasGameData: { - b = (info->hasFlag(ModInfo::FLAG_INVALID)); + b = !info->hasFlag(ModInfo::FLAG_INVALID); break; } - case CategoryFactory::NoNexusID: + case CategoryFactory::HasNexusID: { - b = ( - info->getNexusID() == -1 && - !info->hasFlag(ModInfo::FLAG_FOREIGN) && - !info->hasFlag(ModInfo::FLAG_BACKUP) && - !info->hasFlag(ModInfo::FLAG_OVERWRITE)); + // never show these + if ( + info->hasFlag(ModInfo::FLAG_FOREIGN) || + info->hasFlag(ModInfo::FLAG_BACKUP) || + info->hasFlag(ModInfo::FLAG_OVERWRITE)) + { + return false; + } + b = (info->getNexusID() > 0); break; } -- cgit v1.3.1 From 3c25117fe163f7fab7afa22ba171ea2d41112f23 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 2 Dec 2019 10:41:52 -0500 Subject: three modes for separators, save state renamed enumerators --- src/filterlist.cpp | 28 ++++++++++++++++----- src/filterlist.h | 7 +++++- src/mainwindow.cpp | 16 +++++++----- src/mainwindow.h | 3 ++- src/mainwindow.ui | 21 ++++++++++++---- src/modlistsortproxy.cpp | 65 +++++++++++++++++++++++++++++------------------- src/modlistsortproxy.h | 25 ++++++++++++------- 7 files changed, 112 insertions(+), 53 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/filterlist.cpp b/src/filterlist.cpp index b65f0f4a..05bff2dd 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -2,6 +2,7 @@ #include "ui_mainwindow.h" #include "categories.h" #include "categoriesdialog.h" +#include "settings.h" #include using namespace MOBase; @@ -157,7 +158,7 @@ FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) [&]{ onOptionsChanged(); }); connect( - ui->filtersSeparators, &QCheckBox::toggled, + ui->filtersSeparators, qOverload(&QComboBox::currentIndexChanged), [&]{ onOptionsChanged(); }); ui->filters->header()->setMinimumSectionSize(0); @@ -165,6 +166,20 @@ FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) ui->filters->header()->resizeSection(0, 30); ui->categoriesSplitter->setCollapsible(0, false); ui->categoriesSplitter->setCollapsible(1, false); + + ui->filtersSeparators->addItem(tr("Filter separators"), ModListSortProxy::SeparatorFilter); + ui->filtersSeparators->addItem(tr("Show separators"), ModListSortProxy::SeparatorShow); + ui->filtersSeparators->addItem(tr("Hide separators"), ModListSortProxy::SeparatorHide); +} + +void FilterList::restoreState(const Settings& s) +{ + s.widgets().restoreIndex(ui->filtersSeparators); +} + +void FilterList::saveState(Settings& s) const +{ + s.widgets().saveIndex(ui->filtersSeparators); } QTreeWidgetItem* FilterList::addCriteriaItem( @@ -189,7 +204,7 @@ void FilterList::addContentCriteria() for (unsigned i = 0; i < ModInfo::NUM_CONTENT_TYPES; ++i) { addCriteriaItem( nullptr, tr("").arg(ModInfo::getContentTypeName(i)), - i, ModListSortProxy::TYPE_CONTENT); + i, ModListSortProxy::TypeContent); } } @@ -202,7 +217,7 @@ void FilterList::addCategoryCriteria(QTreeWidgetItem *root, const std::set if (categoriesUsed.find(categoryID) != categoriesUsed.end()) { QTreeWidgetItem *item = addCriteriaItem(root, m_factory.getCategoryName(i), - categoryID, ModListSortProxy::TYPE_CATEGORY); + categoryID, ModListSortProxy::TypeCategory); if (m_factory.hasChildren(i)) { addCategoryCriteria(item, categoriesUsed, categoryID); } @@ -217,7 +232,7 @@ void FilterList::addSpecialCriteria(int type) addCriteriaItem( nullptr, m_factory.getSpecialCategoryName(sc), - type, ModListSortProxy::TYPE_SPECIAL); + type, ModListSortProxy::TypeSpecial); } void FilterList::refresh() @@ -361,9 +376,10 @@ void FilterList::editCategories() void FilterList::onOptionsChanged() { const auto mode = ui->filtersAnd->isChecked() ? - ModListSortProxy::FILTER_AND : ModListSortProxy::FILTER_OR; + ModListSortProxy::FilterAnd: ModListSortProxy::FilterOr; - const bool separators = ui->filtersSeparators->isChecked(); + const auto separators = static_cast( + ui->filtersSeparators->currentData().toInt()); emit optionsChanged(mode, separators); } diff --git a/src/filterlist.h b/src/filterlist.h index fac1d683..671462d4 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -6,6 +6,7 @@ namespace Ui { class MainWindow; }; class CategoryFactory; +class Settings; class FilterList : public QObject { @@ -14,13 +15,17 @@ class FilterList : public QObject public: FilterList(Ui::MainWindow* ui, CategoryFactory& factory); + void restoreState(const Settings& s); + void saveState(Settings& s) const; + void setSelection(const std::vector& criteria); void clearSelection(); void refresh(); signals: void criteriaChanged(std::vector criteria); - void optionsChanged(ModListSortProxy::FilterMode mode, bool separators); + void optionsChanged( + ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); private: class CriteriaItem; diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 096ea076..b5af9aa5 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -269,7 +269,7 @@ MainWindow::MainWindow(Settings &settings connect( m_Filters.get(), &FilterList::optionsChanged, - [&](auto mode, bool sep) { onFiltersOptions(mode, sep); }); + [&](auto&& mode, auto&& sep) { onFiltersOptions(mode, sep); }); ui->logList->setCore(m_OrganizerCore); @@ -2205,6 +2205,7 @@ void MainWindow::readSettings() } s.widgets().restoreIndex(ui->groupCombo); + m_Filters->restoreState(s); { s.geometry().restoreVisibility(ui->categoriesGroup, false); @@ -2283,6 +2284,8 @@ void MainWindow::storeSettings() s.widgets().saveIndex(ui->groupCombo); s.widgets().saveIndex(ui->executablesListBox); + + m_Filters->saveState(s); } QWidget* MainWindow::qtWidget() @@ -4125,13 +4128,13 @@ void MainWindow::checkModsForUpdates() if (updatesAvailable || checkingModsForUpdate) { m_ModListSortProxy->setCriteria({{ - ModListSortProxy::TYPE_SPECIAL, + ModListSortProxy::TypeSpecial, CategoryFactory::UpdateAvailable, false} }); m_Filters->setSelection({{ - ModListSortProxy::TYPE_SPECIAL, + ModListSortProxy::TypeSpecial, CategoryFactory::UpdateAvailable, false }}); @@ -6131,7 +6134,7 @@ void MainWindow::onFiltersCriteria(const std::vector } else if (criteria.size() == 1) { const auto& c = criteria[0]; - if (c.type == ModListSortProxy::TYPE_CONTENT) { + if (c.type == ModListSortProxy::TypeContent) { label = ModInfo::getContentTypeName(c.id); } else { label = m_CategoryFactory.getCategoryNameByID(c.id); @@ -6148,9 +6151,10 @@ void MainWindow::onFiltersCriteria(const std::vector ui->modList->reset(); } -void MainWindow::onFiltersOptions(ModListSortProxy::FilterMode mode, bool separators) +void MainWindow::onFiltersOptions( + ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep) { - m_ModListSortProxy->setOptions(mode, separators); + m_ModListSortProxy->setOptions(mode, sep); } void MainWindow::updateESPLock(bool locked) diff --git a/src/mainwindow.h b/src/mainwindow.h index 0b559300..69aee073 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -515,7 +515,8 @@ private slots: void deselectFilters(); void refreshFilters(); void onFiltersCriteria(const std::vector& filters); - void onFiltersOptions(ModListSortProxy::FilterMode mode, bool separators); + void onFiltersOptions( + ModListSortProxy::FilterMode mode, ModListSortProxy::SeparatorsMode sep); void displayModInformation(const QString &modName, ModInfoTabIDs tabID); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 92a41c67..85be22b3 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -153,6 +153,18 @@ + + 0 + + + 2 + + + 0 + + + 0 + @@ -177,12 +189,11 @@ - + - Include separators - - - Separators + Filter: only show the separators that match the current filters +Show: always show separators +Hide: never show separators diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp index fd3dbc9e..7ac98f66 100644 --- a/src/modlistsortproxy.cpp +++ b/src/modlistsortproxy.cpp @@ -37,8 +37,8 @@ ModListSortProxy::ModListSortProxy(Profile* profile, QObject *parent) : QSortFilterProxyModel(parent) , m_Profile(profile) , m_FilterActive(false) - , m_FilterMode(FILTER_AND) - , m_FilterSeparators(false) + , m_FilterMode(FilterAnd) + , m_FilterSeparators(SeparatorFilter) { setDynamicSortFilter(true); // this seems to work without dynamicsortfilter // but I don't know why. This should be necessary @@ -269,10 +269,6 @@ bool ModListSortProxy::hasConflictFlag(const std::vector &flags) bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const { - if (!optionsMatchMod(info, enabled)) { - return false; - } - for (auto&& c : m_Criteria) { if (!criteriaMatchMod(info, enabled, c)) { return false; @@ -284,10 +280,6 @@ bool ModListSortProxy::filterMatchesModAnd(ModInfo::Ptr info, bool enabled) cons bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const { - if (!optionsMatchMod(info, enabled)) { - return false; - } - for (auto&& c : m_Criteria) { if (criteriaMatchMod(info, enabled, c)) { return true; @@ -304,16 +296,6 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const bool ModListSortProxy::optionsMatchMod(ModInfo::Ptr info, bool) const { - // don't check options if there are no filters selected - if (!m_FilterActive) { - return true; - } - - if (!m_FilterSeparators) { - if (info->hasFlag(ModInfo::FLAG_SEPARATOR)) { - return false; - } - } return true; } @@ -325,14 +307,14 @@ bool ModListSortProxy::criteriaMatchMod( switch (c.type) { - case TYPE_SPECIAL: // fall-through - case TYPE_CATEGORY: + case TypeSpecial: // fall-through + case TypeCategory: { b = categoryMatchesMod(info, enabled, c.id); break; } - case TYPE_CONTENT: + case TypeContent: { b = contentMatchesMod(info, enabled, c.id); break; @@ -439,6 +421,37 @@ bool ModListSortProxy::contentMatchesMod(ModInfo::Ptr info, bool enabled, int co bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const { + // don't check if there are no filters selected + if (!m_FilterActive) { + return true; + } + + + // special case for separators + if (info->hasFlag(ModInfo::FLAG_SEPARATOR)) { + switch (m_FilterSeparators) + { + case SeparatorFilter: + { + // filter normally + break; + } + + case SeparatorShow: + { + // force visible + return true; + } + + case SeparatorHide: + { + // force hide + return false; + } + } + } + + if (!m_Filter.isEmpty()) { bool display = false; QString filterCopy = QString(m_Filter); @@ -519,7 +532,8 @@ bool ModListSortProxy::filterMatchesMod(ModInfo::Ptr info, bool enabled) const } }//if (!m_CurrentFilter.isEmpty()) - if (m_FilterMode == FILTER_AND) { + + if (m_FilterMode == FilterAnd) { return filterMatchesModAnd(info, enabled); } else { @@ -532,7 +546,8 @@ void ModListSortProxy::setColumnVisible(int column, bool visible) m_EnabledColumns[column] = visible; } -void ModListSortProxy::setOptions(ModListSortProxy::FilterMode mode, bool separators) +void ModListSortProxy::setOptions( + ModListSortProxy::FilterMode mode, SeparatorsMode separators) { if (m_FilterMode != mode || separators != m_FilterSeparators) { m_FilterMode = mode; diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h index 9b533492..46356fe9 100644 --- a/src/modlistsortproxy.h +++ b/src/modlistsortproxy.h @@ -31,16 +31,23 @@ class ModListSortProxy : public QSortFilterProxyModel Q_OBJECT public: - - enum FilterMode { - FILTER_AND, - FILTER_OR + enum FilterMode + { + FilterAnd, + FilterOr }; enum CriteriaType { - TYPE_SPECIAL, - TYPE_CATEGORY, - TYPE_CONTENT + TypeSpecial, + TypeCategory, + TypeContent + }; + + enum SeparatorsMode + { + SeparatorFilter, + SeparatorShow, + SeparatorHide }; struct Criteria @@ -101,7 +108,7 @@ public: bool isFilterActive() const { return m_FilterActive; } void setCriteria(const std::vector& criteria); - void setOptions(FilterMode mode, bool separators); + void setOptions(FilterMode mode, SeparatorsMode separators); /** * @brief tests if the specified index has child nodes @@ -153,7 +160,7 @@ private: bool m_FilterActive; FilterMode m_FilterMode; - bool m_FilterSeparators; + SeparatorsMode m_FilterSeparators; std::vector m_PreChangeCriteria; -- cgit v1.3.1 From 2b4d7929769a91d9de30e1e10319d1c9cf0450af Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 2 Dec 2019 11:06:10 -0500 Subject: don't show the lock overlay for processes that are not hooked starting exe from filetree is now hooked usvfs progress dialog now on top of dialogs --- src/mainwindow.ui | 3 --- src/modinfodialogfiletree.cpp | 5 ++++- src/processrunner.cpp | 9 +++++++++ src/processrunner.h | 5 +++-- src/usvfsconnector.cpp | 3 ++- 5 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 85be22b3..0520db84 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1073,9 +1073,6 @@ p, li { white-space: pre-wrap; } true - - true - 400 diff --git a/src/modinfodialogfiletree.cpp b/src/modinfodialogfiletree.cpp index 79ed4cba..00471a72 100644 --- a/src/modinfodialogfiletree.cpp +++ b/src/modinfodialogfiletree.cpp @@ -140,7 +140,10 @@ void FileTreeTab::onOpen() return; } - shell::Open(m_fs->filePath(selection)); + core().processRunner() + .setFromFile(parentWidget(), m_fs->filePath(selection)) + .setWaitForCompletion() + .run(); } void FileTreeTab::onPreview() diff --git a/src/processrunner.cpp b/src/processrunner.cpp index 89777072..b6167706 100644 --- a/src/processrunner.cpp +++ b/src/processrunner.cpp @@ -498,6 +498,10 @@ ProcessRunner& ProcessRunner::setFromFile(QWidget* parent, const QFileInfo& targ default: { m_shellOpen = targetInfo.absoluteFilePath(); + + // picked up by postRun() + m_sp.hooked = false; + break; } } @@ -722,6 +726,11 @@ ProcessRunner::Results ProcessRunner::postRun() { const bool mustWait = (m_waitFlags & ForceWait); + if (!m_sp.hooked && !mustWait) { + // the process wasn't hooked and there's no force wait, don't lock + return Running; + } + if (mustWait && m_lockReason == UILocker::NoReason) { // never lock the ui without an escape hatch for the user log::debug( diff --git a/src/processrunner.h b/src/processrunner.h index c61d6b70..a5099136 100644 --- a/src/processrunner.h +++ b/src/processrunner.h @@ -42,8 +42,9 @@ public: // the ui will be refreshed once the process has completed Refresh = 0x01, - // the process will be waited for even if locking is disabled - ForceWait = 0x02 + // the process will be waited for even if locking is disabled or the + // process is not hooked + ForceWait = 0x02, }; using WaitFlags = QFlags; diff --git a/src/usvfsconnector.cpp b/src/usvfsconnector.cpp index 3c8c355b..59880754 100644 --- a/src/usvfsconnector.cpp +++ b/src/usvfsconnector.cpp @@ -181,10 +181,11 @@ UsvfsConnector::~UsvfsConnector() void UsvfsConnector::updateMapping(const MappingType &mapping) { - QProgressDialog progress; + QProgressDialog progress(qApp->activeWindow()); progress.setLabelText(tr("Preparing vfs")); progress.setMaximum(static_cast(mapping.size())); progress.show(); + int value = 0; int files = 0; int dirs = 0; -- cgit v1.3.1 From e263d3049acd7e296ad87f2ac91318b37bf33f2f Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Wed, 4 Dec 2019 11:43:36 -0500 Subject: keyboard nav for filter list, alternating row colors --- src/filterlist.cpp | 96 ++++++++++++++++++++++++++++++++++++------------------ src/filterlist.h | 3 +- src/mainwindow.ui | 6 ++++ 3 files changed, 72 insertions(+), 33 deletions(-) (limited to 'src/mainwindow.ui') diff --git a/src/filterlist.cpp b/src/filterlist.cpp index 0a5d0414..b345d21d 100644 --- a/src/filterlist.cpp +++ b/src/filterlist.cpp @@ -111,51 +111,88 @@ private: }; -class ClickFilter : public QObject +class CriteriaItemFilter : public QObject { public: - ClickFilter(std::function f) - : m_f(std::move(f)) + using Callback = std::function; + + CriteriaItemFilter(QTreeWidget* tree, Callback f) + : QObject(tree), m_tree(tree), m_f(std::move(f)) { } bool eventFilter(QObject* o, QEvent* e) override { - if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) { - if (m_f) { - return m_f(static_cast(e)); + // careful: this filter is installed on both the tree and the viewport + // + // no check is currently necessary because mouse events originate from the + // viewport only and keyboard events from the tree only + + if (m_f) { + if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) { + if (handleMouse(static_cast(e))) { + return true; + } + } else if (e->type() == QEvent::KeyPress) { + if (handleKeyboard(static_cast(e))) { + return true; + } } } - return QObject::eventFilter(o, e);; + return QObject::eventFilter(o, e); } private: - std::function m_f; + QTreeWidget* m_tree; + Callback m_f; + + bool handleMouse(QMouseEvent* e) + { + auto* item = m_tree->itemAt(e->pos()); + if (!item) { + return false; + } + + m_tree->setCurrentItem(item); + + const auto dir = (e->button() == Qt::LeftButton ? 1 : - 1); + + return m_f(item, dir); + } + + bool handleKeyboard(QKeyEvent* e) + { + if (e->key() == Qt::Key_Space) { + auto* item = m_tree->currentItem(); + if (!item) { + return false; + } + + const auto shiftPressed = (e->modifiers() & Qt::ShiftModifier); + const auto dir = (shiftPressed ? -1 : 1); + + return m_f(item, dir); + } + + return false; + } }; FilterList::FilterList(Ui::MainWindow* ui, CategoryFactory& factory) : ui(ui), m_factory(factory) { - ui->filters->viewport()->installEventFilter( - new ClickFilter([&](auto* e){ return onClick(e); })); + auto* eventFilter = new CriteriaItemFilter( + ui->filters, [&](auto* item, int dir){ return cycleItem(item, dir); }); - connect( - ui->filtersClear, &QPushButton::clicked, - [&]{ clearSelection(); }); + ui->filters->installEventFilter(eventFilter); + ui->filters->viewport()->installEventFilter(eventFilter); - connect( - ui->filtersEdit, &QPushButton::clicked, - [&]{ editCategories(); }); - - connect( - ui->filtersAnd, &QCheckBox::toggled, - [&]{ onOptionsChanged(); }); - - connect( - ui->filtersOr, &QCheckBox::toggled, - [&]{ onOptionsChanged(); }); + connect(ui->filtersClear, &QPushButton::clicked, [&]{ clearSelection(); }); + connect(ui->filtersEdit, &QPushButton::clicked, [&]{ editCategories(); }); + connect(ui->filtersAnd, &QCheckBox::toggled, [&]{ onOptionsChanged(); }); + connect(ui->filtersOr, &QCheckBox::toggled, [&]{ onOptionsChanged(); }); connect( ui->filtersSeparators, qOverload(&QComboBox::currentIndexChanged), @@ -321,21 +358,16 @@ void FilterList::clearSelection() checkCriteria(); } -bool FilterList::onClick(QMouseEvent* e) +bool FilterList::cycleItem(QTreeWidgetItem* item, int direction) { - auto* item = ui->filters->itemAt(e->pos()); - if (!item) { - return false; - } - auto* ci = dynamic_cast(item); if (!ci) { return false; } - if (e->button() == Qt::LeftButton) { + if (direction > 0) { ci->nextState(); - } else if (e->button() == Qt::RightButton) { + } else if (direction < 0) { ci->previousState(); } else { return false; diff --git a/src/filterlist.h b/src/filterlist.h index 671462d4..72cbe8bf 100644 --- a/src/filterlist.h +++ b/src/filterlist.h @@ -34,10 +34,12 @@ private: CategoryFactory& m_factory; bool onClick(QMouseEvent* e); + void onItemActivated(QTreeWidgetItem* item); void onOptionsChanged(); void editCategories(); void checkCriteria(); + bool cycleItem(QTreeWidgetItem* item, int direction); QTreeWidgetItem* addCriteriaItem( QTreeWidgetItem *root, const QString &name, int categoryID, @@ -47,7 +49,6 @@ private: void addCategoryCriteria( QTreeWidgetItem *root, const std::set &categoriesUsed, int targetID); void addSpecialCriteria(int type); - }; #endif // MODORGANIZER_CATEGORIESLIST_INCLUDED diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 0520db84..309e9f62 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -79,6 +79,9 @@ Qt::NoContextMenu + + true + QAbstractItemView::NoSelection @@ -91,6 +94,9 @@ true + + true + true -- cgit v1.3.1 From a4db0db94ff67b52a7a3b9b43eca5cce5fae5fc3 Mon Sep 17 00:00:00 2001 From: Al Date: Mon, 9 Dec 2019 01:17:27 +0100 Subject: Add alternatingRowColors to saves and data tab. --- src/mainwindow.ui | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/mainwindow.ui') diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 309e9f62..a7e7b937 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -1076,6 +1076,9 @@ p, li { white-space: pre-wrap; } This is an overview of your data directory as visible to the game (and tools). + + true + true @@ -1165,6 +1168,9 @@ p, li { white-space: pre-wrap; } <p style="-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;"></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">If you click &quot;Fix Mods...&quot; in the context menu, MO will try to activate all mods and esps to fix those missing esps. It will not disable anything!</span></p></body></html> + + true + QAbstractItemView::ExtendedSelection -- cgit v1.3.1