From b00e63dbe8d0acaae503e63d614ee20b9e9ede2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 6 Sep 2014 21:01:23 +0200 Subject: More sync from quickmods Also a small VersionSelectDialog refactor --- .travis.yml | 2 +- CMakeLists.txt | 17 ++- depends/LogicalGui/CMakeLists.txt | 7 + depends/LogicalGui/LogicalGui.h | 254 ++++++++++++++++++++++++++++++++++++ gui/dialogs/ProgressDialog.cpp | 2 +- gui/dialogs/VersionSelectDialog.cpp | 101 ++++++++++++-- gui/dialogs/VersionSelectDialog.h | 6 +- gui/groupview/InstanceDelegate.cpp | 4 + logic/OneSixFTBInstance.cpp | 12 -- logic/OneSixFTBInstance.h | 1 - 10 files changed, 378 insertions(+), 28 deletions(-) create mode 100644 depends/LogicalGui/CMakeLists.txt create mode 100644 depends/LogicalGui/LogicalGui.h diff --git a/.travis.yml b/.travis.yml index 4ed123de..5d8dd880 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get update -qq install: - - sudo apt-get install -y -qq cmake qt52base qt52svg qt52tools qt52x11extras + - sudo apt-get install -y -qq cmake qt52base qt52svg qt52tools qt52x11extras qt52webkit - sudo apt-get install -y -qq g++-4.8 - if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi before_script: diff --git a/CMakeLists.txt b/CMakeLists.txt index d11d21f9..47c5c9da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,7 @@ find_package(Qt5Network REQUIRED) find_package(Qt5Test REQUIRED) find_package(Qt5Xml REQUIRED) find_package(Qt5LinguistTools REQUIRED) +find_package(Qt5WebKitWidgets REQUIRED) include_directories( ${Qt5Core_INCLUDE_DIRS} @@ -186,6 +187,12 @@ else() set(MultiMC_UPDATER_FORCE_LOCAL_value "false") endif() +#### For QuickMods +option(MultiMC_WEBKIT_INSPECTOR "Enable the QWebInspector for debugging" OFF) +if(MultiMC_WEBKIT_INSPECTOR) + add_definitions(-DWEBKIT_INSPECTOR) +endif() + #### Custom target to just print the version. add_custom_target(version echo "Version: ${MultiMC_VERSION_STRING}") @@ -257,6 +264,10 @@ include_directories(${LIBUTIL_INCLUDE_DIR}) # Add the updater add_subdirectory(mmc_updater) +# Add the GUI -> Logic connection header +add_subdirectory(depends/LogicalGui) +include_directories(${LOGICALGUI_INCLUDE_DIR}) + ################################ FILES ################################ ######## Sources and headers ######## @@ -744,9 +755,9 @@ add_executable(MultiMC MACOSX_BUNDLE WIN32 main.cpp ${MULTIMC_RCS}) # Link target_link_libraries(MultiMC MultiMC_common) -target_link_libraries(MultiMC_common xz-embedded unpack200 quazip libUtil ${MultiMC_LINK_ADDITIONAL_LIBS}) -qt5_use_modules(MultiMC Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) -qt5_use_modules(MultiMC_common Core Widgets Network Xml Concurrent ${MultiMC_QT_ADDITIONAL_MODULES}) +target_link_libraries(MultiMC_common xz-embedded unpack200 quazip libUtil LogicalGui ${MultiMC_LINK_ADDITIONAL_LIBS}) +qt5_use_modules(MultiMC Core Widgets Network Xml Concurrent WebKitWidgets ${MultiMC_QT_ADDITIONAL_MODULES}) +qt5_use_modules(MultiMC_common Core Widgets Network Xml Concurrent WebKitWidgets ${MultiMC_QT_ADDITIONAL_MODULES}) ################################ INSTALLATION AND PACKAGING ################################ diff --git a/depends/LogicalGui/CMakeLists.txt b/depends/LogicalGui/CMakeLists.txt new file mode 100644 index 00000000..9fb03a03 --- /dev/null +++ b/depends/LogicalGui/CMakeLists.txt @@ -0,0 +1,7 @@ +project(LogicalGui) + +# Set the include dir path. +set(LOGICALGUI_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) + +add_library(LogicalGui STATIC LogicalGui.h) +qt5_use_modules(LogicalGui Core) diff --git a/depends/LogicalGui/LogicalGui.h b/depends/LogicalGui/LogicalGui.h new file mode 100644 index 00000000..4034459e --- /dev/null +++ b/depends/LogicalGui/LogicalGui.h @@ -0,0 +1,254 @@ +/* Copyright 2014 Jan Dalheimer + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#if QT_VERSION == QT_VERSION_CHECK(5, 1, 1) +#include <5.1.1/QtCore/private/qobject_p.h> +#elif QT_VERSION == QT_VERSION_CHECK(5, 2, 0) +#include <5.2.0/QtCore/private/qobject_p.h> +#elif QT_VERSION == QT_VERSION_CHECK(5, 2, 1) +#include <5.2.1/QtCore/private/qobject_p.h> +#elif QT_VERSION == QT_VERSION_CHECK(5, 3, 0) +#include <5.3.0/QtCore/private/qobject_p.h> +#elif QT_VERSION == QT_VERSION_CHECK(5, 3, 1) +#include <5.3.1/QtCore/private/qobject_p.h> +#else +#error Please add support for this version of Qt +#endif + +class Bindable +{ + friend class tst_LogicalGui; + +public: + Bindable(Bindable *parent = 0) : m_parent(parent) + { + } + virtual ~Bindable() + { + } + + void setBindableParent(Bindable *parent) + { + m_parent = parent; + } + + void bind(const QString &id, const QObject *receiver, const char *methodSignature) + { + auto mo = receiver->metaObject(); + Q_ASSERT_X(mo, "Bindable::bind", + "Invalid metaobject. Did you forget the QObject macro?"); + const QMetaMethod method = mo->method(mo->indexOfMethod( + QMetaObject::normalizedSignature(methodSignature + 1).constData())); + Q_ASSERT_X(method.isValid(), "Bindable::bind", "Invalid method signature"); + m_bindings.insert(id, Binding(receiver, method)); + } + template + void bind(const QString &id, + const typename QtPrivate::FunctionPointer::Object *receiver, Func slot) + { + typedef QtPrivate::FunctionPointer SlotType; + m_bindings.insert( + id, + Binding(receiver, new QtPrivate::QSlotObject(slot))); + } + template void bind(const QString &id, Func slot) + { + typedef QtPrivate::FunctionPointer SlotType; + m_bindings.insert( + id, + Binding(nullptr, new QtPrivate::QSlotObject(slot))); + } + void unbind(const QString &id) + { + m_bindings.remove(id); + } + +private: + struct Binding + { + Binding(const QObject *receiver, const QMetaMethod &method) + : receiver(receiver), method(method) + { + } + Binding(const QObject *receiver, QtPrivate::QSlotObjectBase *object) + : receiver(receiver), object(object) + { + } + Binding() + { + } + const QObject *receiver; + QMetaMethod method; + QtPrivate::QSlotObjectBase *object = nullptr; + }; + QMap m_bindings; + + Bindable *m_parent; + +private: + inline Qt::ConnectionType connectionType(const QObject *receiver) + { + return receiver == nullptr ? Qt::DirectConnection + : (QThread::currentThread() == receiver->thread() + ? Qt::DirectConnection + : Qt::BlockingQueuedConnection); + } + +protected: + template Ret wait(const QString &id, Params... params) + { + static_assert(!std::is_same::value, "You need to use Bindable::waitVoid"); + + if (!m_bindings.contains(id) && m_parent) + { + return m_parent->wait(id, params...); + } + Q_ASSERT(m_bindings.contains(id)); + const auto binding = m_bindings[id]; + const Qt::ConnectionType type = connectionType(binding.receiver); + Ret ret; + if (binding.object) + { + void *args[] = {&ret, + const_cast(reinterpret_cast(¶ms))...}; + if (type == Qt::BlockingQueuedConnection) + { + QSemaphore semaphore; + QMetaCallEvent *ev = + new QMetaCallEvent(binding.object, nullptr, -1, 0, 0, args, &semaphore); + QCoreApplication::postEvent(const_cast(binding.receiver), ev); + semaphore.acquire(); + } + else + { + binding.object->call(const_cast(binding.receiver), args); + } + } + else + { + const QMetaMethod method = binding.method; + Q_ASSERT_X(method.parameterCount() == sizeof...(params), "Bindable::wait", + qPrintable(QString("Incompatible argument count (expected %1, got %2)") + .arg(method.parameterCount(), sizeof...(params)))); + Q_ASSERT_X( + qMetaTypeId() != QMetaType::UnknownType, "Bindable::wait", + "Requested return type is not registered, please use the Q_DECLARE_METATYPE " + "macro to make it known to Qt's meta-object system"); + Q_ASSERT_X( + method.returnType() == qMetaTypeId() || + QMetaType::hasRegisteredConverterFunction(method.returnType(), + qMetaTypeId()), + "Bindable::wait", + qPrintable( + QString( + "Requested return type (%1) is incompatible method return type (%2)") + .arg(QMetaType::typeName(qMetaTypeId()), + QMetaType::typeName(method.returnType())))); + const auto retArg = QReturnArgument( + QMetaType::typeName(qMetaTypeId()), + ret); // because Q_RETURN_ARG doesn't work with templates... + method.invoke(const_cast(binding.receiver), type, retArg, + Q_ARG(Params, params)...); + } + return ret; + } + template + typename std::enable_if::type waitVoid(const QString &id, + Params... params) + { + if (!m_bindings.contains(id) && m_parent) + { + m_parent->waitVoid(id, params...); + return; + } + Q_ASSERT(m_bindings.contains(id)); + const auto binding = m_bindings[id]; + const Qt::ConnectionType type = connectionType(binding.receiver); + if (binding.object) + { + void *args[] = {0, const_cast(reinterpret_cast(¶ms))...}; + if (type == Qt::BlockingQueuedConnection) + { + QSemaphore semaphore; + QMetaCallEvent *ev = + new QMetaCallEvent(binding.object, nullptr, -1, 0, 0, args, &semaphore); + QCoreApplication::postEvent(const_cast(binding.receiver), ev); + semaphore.acquire(); + } + else + { + binding.object->call(const_cast(binding.receiver), args); + } + } + else + { + const QMetaMethod method = binding.method; + Q_ASSERT_X(method.parameterCount() == sizeof...(params), "Bindable::wait", + qPrintable(QString("Incompatible argument count (expected %1, got %2)") + .arg(method.parameterCount(), sizeof...(params)))); + method.invoke(const_cast(binding.receiver), type, + Q_ARG(Params, params)...); + } + } + void waitVoid(const QString &id) + { + if (!m_bindings.contains(id) && m_parent) + { + m_parent->waitVoid(id); + return; + } + Q_ASSERT(m_bindings.contains(id)); + const auto binding = m_bindings[id]; + const Qt::ConnectionType type = connectionType(binding.receiver); + if (binding.object) + { + void *args[] = {0}; + if (type == Qt::BlockingQueuedConnection) + { + QSemaphore semaphore; + QMetaCallEvent *ev = + new QMetaCallEvent(binding.object, nullptr, -1, 0, 0, args, &semaphore); + QCoreApplication::postEvent(const_cast(binding.receiver), ev); + semaphore.acquire(); + } + else + { + binding.object->call(const_cast(binding.receiver), args); + } + } + else + { + const QMetaMethod method = binding.method; + Q_ASSERT_X(method.parameterCount() == 0, "Bindable::wait", + qPrintable(QString("Incompatible argument count (expected %1, got %2)") + .arg(method.parameterCount(), 0))); + method.invoke(const_cast(binding.receiver), type); + } + } +}; + +// used frequently +Q_DECLARE_METATYPE(bool *) diff --git a/gui/dialogs/ProgressDialog.cpp b/gui/dialogs/ProgressDialog.cpp index ba14cca2..8adaace5 100644 --- a/gui/dialogs/ProgressDialog.cpp +++ b/gui/dialogs/ProgressDialog.cpp @@ -71,7 +71,7 @@ int ProgressDialog::exec(ProgressProvider *task) if(task->isRunning()) return QDialog::exec(); else - return 0; + return QDialog::Accepted; } ProgressProvider *ProgressDialog::getTask() diff --git a/gui/dialogs/VersionSelectDialog.cpp b/gui/dialogs/VersionSelectDialog.cpp index 2745eccc..83a1993f 100644 --- a/gui/dialogs/VersionSelectDialog.cpp +++ b/gui/dialogs/VersionSelectDialog.cpp @@ -24,10 +24,72 @@ #include #include #include +#include +#include "logger/QsLog.h" + +class VersionSelectProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + VersionSelectProxyModel(QObject *parent = 0) : QSortFilterProxyModel(parent) + { + } + + struct Filter + { + QString string; + bool exact = false; + }; + + QHash filters() const + { + return m_filters; + } + void setFilter(const int column, const QString &filter, const bool exact) + { + Filter f; + f.string = filter; + f.exact = exact; + m_filters[column] = f; + invalidateFilter(); + } + void clearFilters() + { + m_filters.clear(); + invalidateFilter(); + } + +protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const + { + for (auto it = m_filters.begin(); it != m_filters.end(); ++it) + { + const QString version = + sourceModel()->index(source_row, it.key()).data().toString(); + + if (it.value().exact) + { + if (version != it.value().string) + { + return false; + } + continue; + } + + if (!Util::versionIsInInterval(version, it.value().string)) + { + return false; + } + } + return true; + } + + QHash m_filters; +}; VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) - : QDialog(parent), ui(new Ui::VersionSelectDialog) + : QDialog(parent), ui(new Ui::VersionSelectDialog), m_useLatest(false) { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); @@ -36,7 +98,7 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, m_vlist = vlist; - m_proxyModel = new QSortFilterProxyModel(this); + m_proxyModel = new VersionSelectProxyModel(this); m_proxyModel->setSourceModel(vlist); ui->listView->setModel(m_proxyModel); @@ -66,18 +128,41 @@ void VersionSelectDialog::setResizeOn(int column) ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); } +void VersionSelectDialog::setUseLatest(const bool useLatest) +{ + m_useLatest = useLatest; +} + int VersionSelectDialog::exec() { QDialog::open(); if (!m_vlist->isLoaded()) + { loadList(); + } + m_proxyModel->invalidate(); + if (m_proxyModel->rowCount() == 0) + { + QLOG_DEBUG() << "No rows in version list"; + return QDialog::Rejected; + } + if (m_proxyModel->rowCount() == 1 || m_useLatest) + { + ui->listView->selectionModel()->setCurrentIndex(m_proxyModel->index(0, 0), + QItemSelectionModel::ClearAndSelect); + return QDialog::Accepted; + } return QDialog::exec(); } void VersionSelectDialog::loadList() { - ProgressDialog *taskDlg = new ProgressDialog(this); Task *loadTask = m_vlist->getLoadTask(); + if (!loadTask) + { + return; + } + ProgressDialog *taskDlg = new ProgressDialog(this); loadTask->setParent(taskDlg); taskDlg->exec(loadTask); delete taskDlg; @@ -97,14 +182,12 @@ void VersionSelectDialog::on_refreshButton_clicked() void VersionSelectDialog::setExactFilter(int column, QString filter) { - m_proxyModel->setFilterKeyColumn(column); - // m_proxyModel->setFilterFixedString(filter); - m_proxyModel->setFilterRegExp(QRegExp(QString("^%1$").arg(filter.replace(".", "\\.")), - Qt::CaseInsensitive, QRegExp::RegExp)); + m_proxyModel->setFilter(column, filter, true); } void VersionSelectDialog::setFuzzyFilter(int column, QString filter) { - m_proxyModel->setFilterKeyColumn(column); - m_proxyModel->setFilterWildcard(filter); + m_proxyModel->setFilter(column, filter, false); } + +#include "VersionSelectDialog.moc" diff --git a/gui/dialogs/VersionSelectDialog.h b/gui/dialogs/VersionSelectDialog.h index 34df4d23..70ee9635 100644 --- a/gui/dialogs/VersionSelectDialog.h +++ b/gui/dialogs/VersionSelectDialog.h @@ -27,6 +27,8 @@ namespace Ui class VersionSelectDialog; } +class VersionSelectProxyModel; + class VersionSelectDialog : public QDialog { Q_OBJECT @@ -47,6 +49,7 @@ public: void setExactFilter(int column, QString filter); void setEmptyString(QString emptyString); void setResizeOn(int column); + void setUseLatest(const bool useLatest); private slots: @@ -57,7 +60,8 @@ private: BaseVersionList *m_vlist; - QSortFilterProxyModel *m_proxyModel; + VersionSelectProxyModel *m_proxyModel; int resizeOnColumn = 0; + bool m_useLatest; }; diff --git a/gui/groupview/InstanceDelegate.cpp b/gui/groupview/InstanceDelegate.cpp index e49e1552..a8e5ee83 100644 --- a/gui/groupview/InstanceDelegate.cpp +++ b/gui/groupview/InstanceDelegate.cpp @@ -118,6 +118,10 @@ void drawBadges(QPainter *painter, const QStyleOptionViewItemV4 &option, BaseIns { pixmaps.append("broken"); } + if (flags & BaseInstance::UpdateAvailable) + { + pixmaps.append("updateavailable"); + } // begin easter eggs if (instance->name().contains("btw", Qt::CaseInsensitive) || diff --git a/logic/OneSixFTBInstance.cpp b/logic/OneSixFTBInstance.cpp index 903c4437..2be74c3c 100644 --- a/logic/OneSixFTBInstance.cpp +++ b/logic/OneSixFTBInstance.cpp @@ -15,18 +15,6 @@ OneSixFTBInstance::OneSixFTBInstance(const QString &rootDir, SettingsObject *set { } -void OneSixFTBInstance::init() -{ - try - { - reloadVersion(); - } - catch(MMCError & e) - { - // QLOG_ERROR() << "Caught exception on instance init: " << e.cause(); - } -} - void OneSixFTBInstance::copy(const QDir &newDir) { QStringList libraryNames; diff --git a/logic/OneSixFTBInstance.h b/logic/OneSixFTBInstance.h index ecfa2231..1f9df8ab 100644 --- a/logic/OneSixFTBInstance.h +++ b/logic/OneSixFTBInstance.h @@ -12,7 +12,6 @@ public: QObject *parent = 0); virtual ~OneSixFTBInstance(){}; - void init() override; void copy(const QDir &newDir) override; virtual QString getStatusbarDescription(); -- cgit v1.2.3