diff options
Diffstat (limited to 'application')
19 files changed, 295 insertions, 53 deletions
diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index 38bd586b..1a3bd1c3 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -124,8 +124,10 @@ SET(MULTIMC_SOURCES # GUI - platform pages pages/modplatform/VanillaPage.cpp pages/modplatform/VanillaPage.h - pages/modplatform/ftb/FtbModel.cpp - pages/modplatform/ftb/FtbModel.h + pages/modplatform/ftb/FtbFilterModel.cpp + pages/modplatform/ftb/FtbFilterModel.h + pages/modplatform/ftb/FtbListModel.cpp + pages/modplatform/ftb/FtbListModel.h pages/modplatform/ftb/FtbPage.cpp pages/modplatform/ftb/FtbPage.h pages/modplatform/legacy_ftb/Page.cpp diff --git a/application/package/rpm/MultiMC5.spec b/application/package/rpm/MultiMC5.spec new file mode 100644 index 00000000..9dcc01b4 --- /dev/null +++ b/application/package/rpm/MultiMC5.spec @@ -0,0 +1,40 @@ +Name: MultiMC5 +Version: 1.4 +Release: 1%{?dist} +Summary: A local install wrapper for MultiMC + +License: ASL 2.0 +URL: https://multimc.org +BuildArch: x86_64 + +Requires: zenity qt5-qtbase wget +Provides: multimc MultiMC multimc5 + +%description +A local install wrapper for MultiMC + +%prep + + +%build + + +%install +mkdir -p %{buildroot}/opt/multimc +install -m 0644 ../ubuntu/multimc/opt/multimc/icon.svg %{buildroot}/opt/multimc/icon.svg +install -m 0755 ../ubuntu/multimc/opt/multimc/run.sh %{buildroot}/opt/multimc/run.sh +mkdir -p %{buildroot}/%{_datadir}/applications +install -m 0644 ../ubuntu/multimc/usr/share/applications/multimc.desktop %{buildroot}/%{_datadir}/applications/multimc.desktop + + +%files +%dir /opt/multimc +/opt/multimc/icon.svg +/opt/multimc/run.sh +%{_datadir}/applications/multimc.desktop + + + +%changelog +* Wed Nov 25 22:53:59 CET 2020 kb1000 <fedora@kb1000.de> +- Initial version of the RPM package, based on the Ubuntu package diff --git a/application/package/rpm/README.md b/application/package/rpm/README.md new file mode 100644 index 00000000..98b6d5cb --- /dev/null +++ b/application/package/rpm/README.md @@ -0,0 +1,12 @@ +# What is this? +A simple RPM package for MultiMC that contains a script that downloads and installs real MultiMC on Red Hat based systems. + +It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting. + +# How to build this? +You need the `rpm-build` package. Switch into this directory, then run: +``` +rpmbuild --build-in-place -bb MultiMC5.spec +``` + +Replace the version with whatever is appropriate. diff --git a/application/package/ubuntu/readme.md b/application/package/ubuntu/README.md index 5b0d6b27..5c0f4eeb 100644 --- a/application/package/ubuntu/readme.md +++ b/application/package/ubuntu/README.md @@ -1,8 +1,10 @@ # What is this? -A simple ubuntu package for MultiMC that wraps the contains a script that downloads and installs real MultiMC on ubuntu based systems. +A simple Ubuntu package for MultiMC that contains a script that downloads and installs real MultiMC on Ubuntu based systems. It contains a `.desktop` file, an icon, and a simple script that does the heavy lifting. +This is also the source for the files in the [RPM package](../rpm). If you rename, create or delete files here, you'll likely also have to update the RPM spec file there. + # How to build this? You need dpkg utils. Rename the `multimc` folder to `multimc_1.3-1` and then run: ``` diff --git a/application/pages/instance/WorldListPage.cpp b/application/pages/instance/WorldListPage.cpp index 75741d22..4d737025 100644 --- a/application/pages/instance/WorldListPage.cpp +++ b/application/pages/instance/WorldListPage.cpp @@ -170,6 +170,24 @@ void WorldListPage::on_actionView_Folder_triggered() DesktopServices::openDirectory(m_worlds->dir().absolutePath(), true); } +void WorldListPage::on_actionDatapacks_triggered() +{ + QModelIndex index = getSelectedWorld(); + + if (!index.isValid()) + { + return; + } + + if(!worldSafetyNagQuestion()) + return; + + auto fullPath = m_worlds->data(index, WorldList::FolderRole).toString(); + + DesktopServices::openDirectory(FS::PathCombine(fullPath, "datapacks"), true); +} + + void WorldListPage::on_actionReset_Icon_triggered() { auto proxiedIndex = getSelectedWorld(); diff --git a/application/pages/instance/WorldListPage.h b/application/pages/instance/WorldListPage.h index 8ff14819..d19f4937 100644 --- a/application/pages/instance/WorldListPage.h +++ b/application/pages/instance/WorldListPage.h @@ -90,6 +90,7 @@ private slots: void on_actionRename_triggered(); void on_actionRefresh_triggered(); void on_actionView_Folder_triggered(); + void on_actionDatapacks_triggered(); void on_actionReset_Icon_triggered(); void worldChanged(const QModelIndex ¤t, const QModelIndex &previous); void mceditState(LoggedProcess::State state); diff --git a/application/pages/instance/WorldListPage.ui b/application/pages/instance/WorldListPage.ui index 8d00f8f4..ed078d94 100644 --- a/application/pages/instance/WorldListPage.ui +++ b/application/pages/instance/WorldListPage.ui @@ -85,6 +85,7 @@ <addaction name="actionCopy"/> <addaction name="actionRemove"/> <addaction name="actionMCEdit"/> + <addaction name="actionDatapacks"/> <addaction name="actionReset_Icon"/> <addaction name="separator"/> <addaction name="actionCopy_Seed"/> @@ -139,6 +140,14 @@ <string>Remove world icon to make the game re-generate it on next load.</string> </property> </action> + <action name="actionDatapacks"> + <property name="text"> + <string>Datapacks</string> + </property> + <property name="toolTip"> + <string>Manage datapacks inside the world.</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/application/pages/modplatform/ftb/FtbFilterModel.cpp b/application/pages/modplatform/ftb/FtbFilterModel.cpp new file mode 100644 index 00000000..dec3a017 --- /dev/null +++ b/application/pages/modplatform/ftb/FtbFilterModel.cpp @@ -0,0 +1,64 @@ +#include "FtbFilterModel.h" + +#include <QDebug> + +#include "modplatform/modpacksch/FTBPackManifest.h" +#include <MMCStrings.h> + +namespace Ftb { + +FilterModel::FilterModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + currentSorting = Sorting::ByPlays; + sortings.insert(tr("Sort by plays"), Sorting::ByPlays); + sortings.insert(tr("Sort by installs"), Sorting::ByInstalls); + sortings.insert(tr("Sort by name"), Sorting::ByName); +} + +const QMap<QString, FilterModel::Sorting> FilterModel::getAvailableSortings() +{ + return sortings; +} + +QString FilterModel::translateCurrentSorting() +{ + return sortings.key(currentSorting); +} + +void FilterModel::setSorting(Sorting sorting) +{ + currentSorting = sorting; + invalidate(); +} + +FilterModel::Sorting FilterModel::getCurrentSorting() +{ + return currentSorting; +} + +bool FilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + return true; +} + +bool FilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + ModpacksCH::Modpack leftPack = sourceModel()->data(left, Qt::UserRole).value<ModpacksCH::Modpack>(); + ModpacksCH::Modpack rightPack = sourceModel()->data(right, Qt::UserRole).value<ModpacksCH::Modpack>(); + + if (currentSorting == ByPlays) { + return leftPack.plays < rightPack.plays; + } + else if (currentSorting == ByInstalls) { + return leftPack.installs < rightPack.installs; + } + else if (currentSorting == ByName) { + return Strings::naturalCompare(leftPack.name, rightPack.name, Qt::CaseSensitive) >= 0; + } + + // Invalid sorting set, somehow... + qWarning() << "Invalid sorting set!"; + return true; +} + +} diff --git a/application/pages/modplatform/ftb/FtbFilterModel.h b/application/pages/modplatform/ftb/FtbFilterModel.h new file mode 100644 index 00000000..4fe2a274 --- /dev/null +++ b/application/pages/modplatform/ftb/FtbFilterModel.h @@ -0,0 +1,33 @@ +#pragma once + +#include <QtCore/QSortFilterProxyModel> + +namespace Ftb { + +class FilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + FilterModel(QObject* parent = Q_NULLPTR); + enum Sorting { + ByPlays, + ByInstalls, + ByName, + }; + const QMap<QString, Sorting> getAvailableSortings(); + QString translateCurrentSorting(); + void setSorting(Sorting sorting); + Sorting getCurrentSorting(); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + +private: + QMap<QString, Sorting> sortings; + Sorting currentSorting; + +}; + +} diff --git a/application/pages/modplatform/ftb/FtbModel.cpp b/application/pages/modplatform/ftb/FtbListModel.cpp index ecdcb00b..63236827 100644 --- a/application/pages/modplatform/ftb/FtbModel.cpp +++ b/application/pages/modplatform/ftb/FtbListModel.cpp @@ -1,4 +1,4 @@ -#include "FtbModel.h" +#include "FtbListModel.h" #include "BuildConfig.h" #include "Env.h" @@ -79,7 +79,7 @@ void ListModel::performSearch() auto *netJob = new NetJob("Ftb::Search"); QString searchUrl; if(currentSearchTerm.isEmpty()) { - searchUrl = BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/popular/plays/100"; + searchUrl = BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/all"; } else { searchUrl = QString(BuildConfig.MODPACKSCH_API_BASE_URL + "public/modpack/search/25?term=%1") @@ -206,9 +206,18 @@ void ListModel::packRequestFinished() return; } - beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size()); - modpacks.append(pack); - endInsertRows(); + // Since there is no guarantee that packs have a version, this will just + // ignore those "dud" packs. + if (pack.versions.empty()) + { + qWarning() << "FTB Pack " << pack.id << " ignored. reason: lacking any versions"; + } + else + { + beginInsertRows(QModelIndex(), modpacks.size(), modpacks.size()); + modpacks.append(pack); + endInsertRows(); + } if(!remainingPacks.isEmpty()) { currentPack = remainingPacks.at(0); diff --git a/application/pages/modplatform/ftb/FtbModel.h b/application/pages/modplatform/ftb/FtbListModel.h index 9c057d73..9c057d73 100644 --- a/application/pages/modplatform/ftb/FtbModel.h +++ b/application/pages/modplatform/ftb/FtbListModel.h diff --git a/application/pages/modplatform/ftb/FtbPage.cpp b/application/pages/modplatform/ftb/FtbPage.cpp index c82508b3..60294de0 100644 --- a/application/pages/modplatform/ftb/FtbPage.cpp +++ b/application/pages/modplatform/ftb/FtbPage.cpp @@ -10,12 +10,26 @@ FtbPage::FtbPage(NewInstanceDialog* dialog, QWidget *parent) : QWidget(parent), ui(new Ui::FtbPage), dialog(dialog) { ui->setupUi(this); - connect(ui->searchButton, &QPushButton::clicked, this, &FtbPage::triggerSearch); + + filterModel = new Ftb::FilterModel(this); + listModel = new Ftb::ListModel(this); + filterModel->setSourceModel(listModel); + ui->packView->setModel(filterModel); + ui->packView->setSortingEnabled(true); + ui->packView->header()->hide(); + ui->packView->setIndentation(0); + ui->searchEdit->installEventFilter(this); - model = new Ftb::ListModel(this); - ui->packView->setModel(model); - connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged); + for(int i = 0; i < filterModel->getAvailableSortings().size(); i++) + { + ui->sortByBox->addItem(filterModel->getAvailableSortings().keys().at(i)); + } + ui->sortByBox->setCurrentText(filterModel->translateCurrentSorting()); + + connect(ui->searchButton, &QPushButton::clicked, this, &FtbPage::triggerSearch); + connect(ui->sortByBox, &QComboBox::currentTextChanged, this, &FtbPage::onSortingSelectionChanged); + connect(ui->packView->selectionModel(), &QItemSelectionModel::currentChanged, this, &FtbPage::onSelectionChanged); connect(ui->versionSelectionBox, &QComboBox::currentTextChanged, this, &FtbPage::onVersionSelectionChanged); } @@ -59,7 +73,7 @@ void FtbPage::suggestCurrent() QString editedLogoName; editedLogoName = selected.name; - model->getLogo(selected.name, art.url, [this, editedLogoName](QString logo) + listModel->getLogo(selected.name, art.url, [this, editedLogoName](QString logo) { dialog->setSuggestedIconFromFile(logo + ".small", editedLogoName); }); @@ -70,7 +84,13 @@ void FtbPage::suggestCurrent() void FtbPage::triggerSearch() { - model->searchWithTerm(ui->searchEdit->text()); + listModel->searchWithTerm(ui->searchEdit->text()); +} + +void FtbPage::onSortingSelectionChanged(QString data) +{ + auto toSet = filterModel->getAvailableSortings().value(data); + filterModel->setSorting(toSet); } void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second) @@ -86,7 +106,7 @@ void FtbPage::onSelectionChanged(QModelIndex first, QModelIndex second) return; } - selected = model->data(first, Qt::UserRole).value<ModpacksCH::Modpack>(); + selected = filterModel->data(first, Qt::UserRole).value<ModpacksCH::Modpack>(); // reverse foreach, so that the newest versions are first for (auto i = selected.versions.size(); i--;) { diff --git a/application/pages/modplatform/ftb/FtbPage.h b/application/pages/modplatform/ftb/FtbPage.h index 80f152c6..b31e1c9e 100644 --- a/application/pages/modplatform/ftb/FtbPage.h +++ b/application/pages/modplatform/ftb/FtbPage.h @@ -15,7 +15,8 @@ #pragma once -#include "FtbModel.h" +#include "FtbFilterModel.h" +#include "FtbListModel.h" #include <QWidget> @@ -64,13 +65,15 @@ private: private slots: void triggerSearch(); + void onSortingSelectionChanged(QString data); void onSelectionChanged(QModelIndex first, QModelIndex second); void onVersionSelectionChanged(QString data); private: Ui::FtbPage *ui = nullptr; NewInstanceDialog* dialog = nullptr; - Ftb::ListModel* model = nullptr; + Ftb::ListModel* listModel = nullptr; + Ftb::FilterModel* filterModel = nullptr; ModpacksCH::Modpack selected; QString selectedVersion; diff --git a/application/pages/modplatform/ftb/FtbPage.ui b/application/pages/modplatform/ftb/FtbPage.ui index 772b0276..3a2203db 100644 --- a/application/pages/modplatform/ftb/FtbPage.ui +++ b/application/pages/modplatform/ftb/FtbPage.ui @@ -11,22 +11,6 @@ </rect> </property> <layout class="QGridLayout" name="gridLayout"> - <item row="1" column="0" colspan="2"> - <widget class="QListView" name="packView"> - <property name="alternatingRowColors"> - <bool>true</bool> - </property> - <property name="iconSize"> - <size> - <width>48</width> - <height>48</height> - </size> - </property> - <property name="uniformItemSizes"> - <bool>true</bool> - </property> - </widget> - </item> <item row="0" column="1"> <widget class="QPushButton" name="searchButton"> <property name="text"> @@ -38,19 +22,38 @@ <widget class="QLineEdit" name="searchEdit"/> </item> <item row="2" column="0" colspan="2"> - <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0" rowminimumheight="0" columnminimumwidth="0,0"> - <item row="0" column="1"> + <layout class="QGridLayout" name="gridLayout_4" columnstretch="0,0,0" rowminimumheight="0" columnminimumwidth="0,0,0"> + <item row="0" column="2"> <widget class="QComboBox" name="versionSelectionBox"/> </item> - <item row="0" column="0"> + <item row="0" column="1"> <widget class="QLabel" name="label"> <property name="text"> <string>Version selected:</string> </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> </widget> </item> + <item row="0" column="0"> + <widget class="QComboBox" name="sortByBox"/> + </item> </layout> </item> + <item row="1" column="0" colspan="2"> + <widget class="QTreeView" name="packView"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="iconSize"> + <size> + <width>48</width> + <height>48</height> + </size> + </property> + </widget> + </item> </layout> </widget> <tabstops> diff --git a/application/pages/modplatform/technic/TechnicData.h b/application/pages/modplatform/technic/TechnicData.h index 5c746619..c7ddd9ce 100644 --- a/application/pages/modplatform/technic/TechnicData.h +++ b/application/pages/modplatform/technic/TechnicData.h @@ -18,7 +18,6 @@ #include <QList> #include <QString> - namespace Technic { struct Modpack { QString slug; @@ -34,6 +33,9 @@ struct Modpack { QString minecraftVersion; bool metadataLoaded = false; + QString websiteUrl; + QString author; + QString description; }; } diff --git a/application/pages/modplatform/technic/TechnicModel.cpp b/application/pages/modplatform/technic/TechnicModel.cpp index bdc411c3..e1294554 100644 --- a/application/pages/modplatform/technic/TechnicModel.cpp +++ b/application/pages/modplatform/technic/TechnicModel.cpp @@ -20,7 +20,6 @@ #include <QIcon> - Technic::ListModel::ListModel(QObject *parent) : QAbstractListModel(parent) { } diff --git a/application/pages/modplatform/technic/TechnicPage.cpp b/application/pages/modplatform/technic/TechnicPage.cpp index 75efd3ed..f6facd57 100644 --- a/application/pages/modplatform/technic/TechnicPage.cpp +++ b/application/pages/modplatform/technic/TechnicPage.cpp @@ -13,7 +13,6 @@ * limitations under the License. */ - #include "TechnicPage.h" #include "ui_TechnicPage.h" @@ -159,6 +158,9 @@ void TechnicPage::suggestCurrent() } current.minecraftVersion = Json::ensureString(obj, "minecraft", QString(), "__placeholder__"); + current.websiteUrl = Json::ensureString(obj, "platformUrl", QString(), "__placeholder__"); + current.author = Json::ensureString(obj, "user", QString(), "__placeholder__"); + current.description = Json::ensureString(obj, "description", QString(), "__placeholder__"); current.metadataLoaded = true; metadataLoaded(); }); @@ -169,29 +171,22 @@ void TechnicPage::suggestCurrent() // expects current.metadataLoaded to be true void TechnicPage::metadataLoaded() { - /*QString text = ""; + QString text = ""; QString name = current.name; if (current.websiteUrl.isEmpty()) + // This allows injecting HTML here. text = name; else + // URL not properly escaped for inclusion in HTML. The name allows for injecting HTML. text = "<a href=\"" + current.websiteUrl + "\">" + name + "</a>"; - if (!current.authors.empty()) { - auto authorToStr = [](Technic::ModpackAuthor & author) { - if(author.url.isEmpty()) { - return author.name; - } - return QString("<a href=\"%1\">%2</a>").arg(author.url, author.name); - }; - QStringList authorStrs; - for(auto & author: current.authors) { - authorStrs.push_back(authorToStr(author)); - } - text += tr(" by ") + authorStrs.join(", "); + if (!current.author.isEmpty()) { + // This allows injecting HTML here + text += tr(" by ") + current.author; } ui->frame->setModText(text); - ui->frame->setModDescription(current.description);*/ + ui->frame->setModDescription(current.description); if (!current.isSolder) { dialog->setSuggestedPack(current.name, new Technic::SingleZipPackInstallTask(current.url, current.minecraftVersion)); diff --git a/application/pages/modplatform/technic/TechnicPage.ui b/application/pages/modplatform/technic/TechnicPage.ui index be56fa82..36ce2ecf 100644 --- a/application/pages/modplatform/technic/TechnicPage.ui +++ b/application/pages/modplatform/technic/TechnicPage.ui @@ -55,8 +55,37 @@ </property> </widget> </item> + <item> + <widget class="MCModInfoFrame" name="frame"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> </layout> </widget> + <customwidgets> + <customwidget> + <class>MCModInfoFrame</class> + <extends>QFrame</extends> + <header>widgets/MCModInfoFrame.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>searchEdit</tabstop> + <tabstop>searchButton</tabstop> + <tabstop>packView</tabstop> + </tabstops> <resources/> <connections/> </ui> diff --git a/application/widgets/MCModInfoFrame.cpp b/application/widgets/MCModInfoFrame.cpp index 577b32a7..89bb90fd 100644 --- a/application/widgets/MCModInfoFrame.cpp +++ b/application/widgets/MCModInfoFrame.cpp @@ -135,6 +135,7 @@ void MCModInfoFrame::setModDescription(QString text) ui->label_ModDescription->setOpenExternalLinks(false); ui->label_ModDescription->setTextFormat(Qt::TextFormat::RichText); desc = text; + // This allows injecting HTML here. labeltext.append("<html><body>" + finaltext.left(287) + "<a href=\"#mod_desc\">...</a></body></html>"); QObject::connect(ui->label_ModDescription, &QLabel::linkActivated, this, &MCModInfoFrame::modDescEllipsisHandler); } |