summaryrefslogtreecommitdiff
path: root/archived/projt-launcher/launcher/VersionProxyModel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'archived/projt-launcher/launcher/VersionProxyModel.cpp')
-rw-r--r--archived/projt-launcher/launcher/VersionProxyModel.cpp552
1 files changed, 552 insertions, 0 deletions
diff --git a/archived/projt-launcher/launcher/VersionProxyModel.cpp b/archived/projt-launcher/launcher/VersionProxyModel.cpp
new file mode 100644
index 0000000000..9596f16a21
--- /dev/null
+++ b/archived/projt-launcher/launcher/VersionProxyModel.cpp
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-3.0-only
+// SPDX-FileCopyrightText: 2026 Project Tick
+// SPDX-FileContributor: Project Tick Team
+/*
+ * ProjT Launcher - Minecraft Launcher
+ * Copyright (C) 2026 Project Tick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * === Upstream License Block (Do Not Modify) ==============================
+ *
+ *
+ *
+ * Prism Launcher - Minecraft Launcher
+ * Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net>
+ * Copyright (C) 2023 TheKodeToad <TheKodeToad@proton.me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 2013-2021 MultiMC Contributors
+ *
+ * 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.
+ *
+ * ======================================================================== */
+
+#include "VersionProxyModel.h"
+#include <Version.h>
+#include <meta/VersionList.hpp>
+#include <QIcon>
+#include <QPixmapCache>
+#include <QSortFilterProxyModel>
+
+class VersionFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ public:
+ VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent)
+ {
+ m_parent = parent;
+ setSortRole(BaseVersionList::SortRole);
+ sort(0, Qt::DescendingOrder);
+ }
+
+ bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
+ {
+ const auto& filters = m_parent->filters();
+ const QString& search = m_parent->search();
+ const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
+
+ if (!search.isEmpty()
+ && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
+ return false;
+
+ for (auto it = filters.begin(); it != filters.end(); ++it)
+ {
+ auto data = sourceModel()->data(idx, it.key());
+ auto match = data.toString();
+ if (!it.value()(match))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void filterChanged()
+ {
+ invalidateFilter();
+ }
+
+ private:
+ VersionProxyModel* m_parent;
+};
+
+VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent)
+{
+ filterModel = new VersionFilterModel(this);
+ connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged);
+ connect(filterModel,
+ &QAbstractItemModel::rowsAboutToBeInserted,
+ this,
+ &VersionProxyModel::sourceRowsAboutToBeInserted);
+ connect(filterModel, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted);
+ connect(filterModel,
+ &QAbstractItemModel::rowsAboutToBeRemoved,
+ this,
+ &VersionProxyModel::sourceRowsAboutToBeRemoved);
+ connect(filterModel, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved);
+ connect(filterModel,
+ QOverload<const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint>::of(
+ &QAbstractItemModel::layoutAboutToBeChanged),
+ this,
+ &VersionProxyModel::sourceLayoutAboutToBeChanged);
+
+ connect(filterModel,
+ QOverload<const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint>::of(
+ &QAbstractItemModel::layoutChanged),
+ this,
+ &VersionProxyModel::sourceLayoutChanged);
+
+ connect(filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset);
+ connect(filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset);
+
+ QAbstractProxyModel::setSourceModel(filterModel);
+}
+
+QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if (section < 0 || section >= m_columns.size())
+ return QVariant();
+ if (orientation != Qt::Horizontal)
+ return QVariant();
+ auto column = m_columns[section];
+ if (role == Qt::DisplayRole)
+ {
+ switch (column)
+ {
+ case Name: return tr("Version");
+ case ParentVersion: return m_parentVersionName; // From metadata
+ case Branch: return tr("Branch");
+ case Type: return tr("Type");
+ case CPUArchitecture: return tr("Architecture");
+ case Path: return tr("Path");
+ case JavaName: return tr("Java Name");
+ case JavaMajor: return tr("Major Version");
+ case Time: return tr("Released");
+ }
+ }
+ else if (role == Qt::ToolTipRole)
+ {
+ switch (column)
+ {
+ case Name: return tr("The name of the version.");
+ case ParentVersion: return tr("%1 version").arg(m_parentVersionName); // From metadata
+ case Branch: return tr("The version's branch");
+ case Type: return tr("The version's type");
+ case CPUArchitecture: return tr("CPU Architecture");
+ case Path: return tr("Filesystem path to this version");
+ case JavaName: return tr("The alternative name of the Java version");
+ case JavaMajor: return tr("The Java major version");
+ case Time: return tr("Release date of this version");
+ }
+ }
+ return QVariant();
+}
+
+QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
+{
+ if (!index.isValid())
+ {
+ return QVariant();
+ }
+ auto column = m_columns[index.column()];
+ auto parentIndex = mapToSource(index);
+ switch (role)
+ {
+ case Qt::DisplayRole:
+ {
+ switch (column)
+ {
+ case Name:
+ {
+ QString version = sourceModel()->data(parentIndex, BaseVersionList::VersionRole).toString();
+ if (version == m_currentVersion)
+ {
+ return tr("%1 (installed)").arg(version);
+ }
+ return version;
+ }
+ case ParentVersion: return sourceModel()->data(parentIndex, BaseVersionList::ParentVersionRole);
+ case Branch: return sourceModel()->data(parentIndex, BaseVersionList::BranchRole);
+ case Type: return sourceModel()->data(parentIndex, BaseVersionList::TypeRole);
+ case CPUArchitecture: return sourceModel()->data(parentIndex, BaseVersionList::CPUArchitectureRole);
+ case Path: return sourceModel()->data(parentIndex, BaseVersionList::PathRole);
+ case JavaName: return sourceModel()->data(parentIndex, BaseVersionList::JavaNameRole);
+ case JavaMajor: return sourceModel()->data(parentIndex, BaseVersionList::JavaMajorRole);
+ case Time:
+ return sourceModel()->data(parentIndex, projt::meta::MetaVersionList::TimestampRole).toDate();
+ default: return QVariant();
+ }
+ }
+ case Qt::ToolTipRole:
+ {
+ if (column == Name && hasRecommended)
+ {
+ auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
+ if (value.toBool())
+ {
+ return tr("Recommended");
+ }
+ else if (hasLatest)
+ {
+ auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
+ if (latest.toBool())
+ {
+ return tr("Latest");
+ }
+ }
+ }
+ return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
+ }
+ case Qt::DecorationRole:
+ {
+ if (column == Name && hasRecommended)
+ {
+ auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
+ if (recommenced.toBool())
+ {
+ return QIcon::fromTheme("star");
+ }
+ else if (hasLatest)
+ {
+ auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
+ if (latest.toBool())
+ {
+ return QIcon::fromTheme("bug");
+ }
+ }
+ QPixmap pixmap;
+ QPixmapCache::find("placeholder", &pixmap);
+ if (!pixmap)
+ {
+ QPixmap px(16, 16);
+ px.fill(Qt::transparent);
+ QPixmapCache::insert("placeholder", px);
+ return px;
+ }
+ return pixmap;
+ }
+ return QVariant();
+ }
+ default:
+ {
+ if (roles.contains((BaseVersionList::ModelRoles)role))
+ {
+ return sourceModel()->data(parentIndex, role);
+ }
+ return QVariant();
+ }
+ }
+}
+
+QModelIndex VersionProxyModel::parent([[maybe_unused]] const QModelIndex& child) const
+{
+ return QModelIndex();
+}
+
+QModelIndex VersionProxyModel::mapFromSource(const QModelIndex& sourceIndex) const
+{
+ if (sourceIndex.isValid())
+ {
+ return index(sourceIndex.row(), 0);
+ }
+ return QModelIndex();
+}
+
+QModelIndex VersionProxyModel::mapToSource(const QModelIndex& proxyIndex) const
+{
+ if (proxyIndex.isValid())
+ {
+ return sourceModel()->index(proxyIndex.row(), 0);
+ }
+ return QModelIndex();
+}
+
+QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex& parent) const
+{
+ // no trees here... shoo
+ if (parent.isValid())
+ {
+ return QModelIndex();
+ }
+ if (row < 0 || row >= sourceModel()->rowCount())
+ return QModelIndex();
+ if (column < 0 || column >= columnCount())
+ return QModelIndex();
+ return QAbstractItemModel::createIndex(row, column);
+}
+
+int VersionProxyModel::columnCount(const QModelIndex& parent) const
+{
+ return parent.isValid() ? 0 : m_columns.size();
+}
+
+int VersionProxyModel::rowCount(const QModelIndex& parent) const
+{
+ if (sourceModel())
+ {
+ return sourceModel()->rowCount(parent);
+ }
+ return 0;
+}
+
+void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right)
+{
+ if (source_top_left.parent() != source_bottom_right.parent())
+ return;
+
+ // whole row is getting changed
+ auto topLeft = createIndex(source_top_left.row(), 0);
+ auto bottomRight = createIndex(source_bottom_right.row(), columnCount() - 1);
+ emit dataChanged(topLeft, bottomRight);
+}
+
+void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
+{
+ auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw);
+
+ m_columns.clear();
+ if (!replacing)
+ {
+ roles.clear();
+ filterModel->setSourceModel(replacing);
+ return;
+ }
+
+ // Extract parent version name from metadata if available
+ auto versionList = dynamic_cast<projt::meta::MetaVersionList*>(replacing);
+ if (versionList)
+ {
+ m_parentVersionName = versionList->displayName();
+ }
+ else
+ {
+ m_parentVersionName = tr("Minecraft"); // Fallback
+ }
+
+ roles = replacing->providesRoles();
+ if (roles.contains(BaseVersionList::VersionRole))
+ {
+ m_columns.push_back(Name);
+ }
+ /*
+ if(roles.contains(BaseVersionList::ParentVersionRole))
+ {
+ m_columns.push_back(ParentVersion);
+ }
+ */
+ if (roles.contains(BaseVersionList::CPUArchitectureRole))
+ {
+ m_columns.push_back(CPUArchitecture);
+ }
+ if (roles.contains(BaseVersionList::PathRole))
+ {
+ m_columns.push_back(Path);
+ }
+ if (roles.contains(BaseVersionList::JavaNameRole))
+ {
+ m_columns.push_back(JavaName);
+ }
+ if (roles.contains(BaseVersionList::JavaMajorRole))
+ {
+ m_columns.push_back(JavaMajor);
+ }
+ if (roles.contains(projt::meta::MetaVersionList::TimestampRole))
+ {
+ m_columns.push_back(Time);
+ }
+ if (roles.contains(BaseVersionList::BranchRole))
+ {
+ m_columns.push_back(Branch);
+ }
+ if (roles.contains(BaseVersionList::TypeRole))
+ {
+ m_columns.push_back(Type);
+ }
+ if (roles.contains(BaseVersionList::RecommendedRole))
+ {
+ hasRecommended = true;
+ }
+ if (roles.contains(BaseVersionList::LatestRole))
+ {
+ hasLatest = true;
+ }
+ filterModel->setSourceModel(replacing);
+}
+
+QModelIndex VersionProxyModel::getRecommended() const
+{
+ if (!roles.contains(BaseVersionList::RecommendedRole))
+ {
+ return index(0, 0);
+ }
+ int recommended = 0;
+ for (int i = 0; i < rowCount(); i++)
+ {
+ auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::RecommendedRole);
+ if (value.toBool())
+ {
+ recommended = i;
+ }
+ }
+ return index(recommended, 0);
+}
+
+QModelIndex VersionProxyModel::getVersion(const QString& version) const
+{
+ int found = -1;
+ for (int i = 0; i < rowCount(); i++)
+ {
+ auto value = sourceModel()->data(mapToSource(index(i, 0)), BaseVersionList::VersionRole);
+ if (value.toString() == version)
+ {
+ found = i;
+ }
+ }
+ if (found == -1)
+ {
+ return QModelIndex();
+ }
+ return index(found, 0);
+}
+
+void VersionProxyModel::clearFilters()
+{
+ m_filters.clear();
+ m_search.clear();
+ filterModel->filterChanged();
+}
+
+void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter f)
+{
+ m_filters[column] = std::move(f);
+ filterModel->filterChanged();
+}
+
+void VersionProxyModel::setSearch(const QString& search)
+{
+ m_search = search;
+ filterModel->filterChanged();
+}
+
+const VersionProxyModel::FilterMap& VersionProxyModel::filters() const
+{
+ return m_filters;
+}
+
+const QString& VersionProxyModel::search() const
+{
+ return m_search;
+}
+
+void VersionProxyModel::sourceAboutToBeReset()
+{
+ beginResetModel();
+}
+
+void VersionProxyModel::sourceReset()
+{
+ endResetModel();
+}
+
+void VersionProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& parent, int first, int last)
+{
+ beginInsertRows(parent, first, last);
+}
+
+void VersionProxyModel::sourceRowsInserted([[maybe_unused]] const QModelIndex& parent,
+ [[maybe_unused]] int first,
+ [[maybe_unused]] int last)
+{
+ endInsertRows();
+}
+
+void VersionProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last)
+{
+ beginRemoveRows(parent, first, last);
+}
+
+void VersionProxyModel::sourceRowsRemoved([[maybe_unused]] const QModelIndex& parent,
+ [[maybe_unused]] int first,
+ [[maybe_unused]] int last)
+{
+ endRemoveRows();
+}
+
+void VersionProxyModel::sourceRowsAboutToBeMoved(const QModelIndex& sourceParent,
+ int sourceStart,
+ int sourceEnd,
+ const QModelIndex& destinationParent,
+ int destinationRow)
+{
+ beginMoveRows(sourceParent, sourceStart, sourceEnd, destinationParent, destinationRow);
+}
+
+void VersionProxyModel::sourceRowsMoved(const QModelIndex& sourceParent,
+ int sourceStart,
+ int sourceEnd,
+ const QModelIndex& destinationParent,
+ int destinationRow)
+{
+ Q_UNUSED(sourceParent);
+ Q_UNUSED(sourceStart);
+ Q_UNUSED(sourceEnd);
+ Q_UNUSED(destinationParent);
+ Q_UNUSED(destinationRow);
+ endMoveRows();
+}
+
+void VersionProxyModel::sourceLayoutAboutToBeChanged(const QList<QPersistentModelIndex>& sourceParents,
+ QAbstractItemModel::LayoutChangeHint hint)
+{
+ emit layoutAboutToBeChanged(sourceParents, hint);
+}
+
+void VersionProxyModel::sourceLayoutChanged(const QList<QPersistentModelIndex>& sourceParents,
+ QAbstractItemModel::LayoutChangeHint hint)
+{
+ emit layoutChanged(sourceParents, hint);
+}
+
+void VersionProxyModel::setCurrentVersion(const QString& version)
+{
+ m_currentVersion = version;
+}
+
+#include "VersionProxyModel.moc"