diff --git a/CMakeLists.txt b/CMakeLists.txt index 523d55c9..10308e18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ gui/consolewindow.h gui/instancemodel.h gui/instancedelegate.h gui/versionselectdialog.h +gui/lwjglselectdialog.h gui/iconcache.h multimc_pragma.h @@ -203,6 +204,7 @@ gui/consolewindow.cpp gui/instancemodel.cpp gui/instancedelegate.cpp gui/versionselectdialog.cpp +gui/lwjglselectdialog.cpp gui/iconcache.cpp java/javautils.cpp @@ -222,6 +224,7 @@ gui/browserdialog.ui gui/aboutdialog.ui gui/consolewindow.ui gui/versionselectdialog.ui +gui/lwjglselectdialog.ui ) diff --git a/gui/lwjglselectdialog.cpp b/gui/lwjglselectdialog.cpp new file mode 100644 index 00000000..ded299cf --- /dev/null +++ b/gui/lwjglselectdialog.cpp @@ -0,0 +1,68 @@ +/* Copyright 2013 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 "lwjglselectdialog.h" +#include "ui_lwjglselectdialog.h" + +#include "lwjglversionlist.h" + +LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::LWJGLSelectDialog) +{ + ui->setupUi(this); + ui->labelStatus->setVisible(false); + ui->lwjglListView->setModel(&LWJGLVersionList::get()); + + connect(&LWJGLVersionList::get(), SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); + connect(&LWJGLVersionList::get(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); + loadingStateUpdated(LWJGLVersionList::get().isLoading()); +} + +LWJGLSelectDialog::~LWJGLSelectDialog() +{ + delete ui; +} + +QString LWJGLSelectDialog::selectedVersion() const +{ + return LWJGLVersionList::get().data( + ui->lwjglListView->selectionModel()->currentIndex(), + Qt::DisplayRole).toString(); +} + +void LWJGLSelectDialog::on_refreshButton_clicked() +{ + if (!LWJGLVersionList::get().isLoading()) + LWJGLVersionList::get().loadList(); +} + +void LWJGLSelectDialog::loadingStateUpdated(bool loading) +{ + setEnabled(!loading); + if (loading) + { + ui->labelStatus->setText("Loading LWJGL version list..."); + ui->labelStatus->setStyleSheet("QLabel { color: black; }"); + } + ui->labelStatus->setVisible(loading); +} + +void LWJGLSelectDialog::loadingFailed(QString error) +{ + ui->labelStatus->setText(error); + ui->labelStatus->setStyleSheet("QLabel { color: red; }"); + ui->labelStatus->setVisible(true); +} diff --git a/gui/lwjglselectdialog.h b/gui/lwjglselectdialog.h new file mode 100644 index 00000000..1772904a --- /dev/null +++ b/gui/lwjglselectdialog.h @@ -0,0 +1,46 @@ +/* Copyright 2013 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. + */ + +#ifndef LWJGLSELECTDIALOG_H +#define LWJGLSELECTDIALOG_H + +#include + +namespace Ui +{ +class LWJGLSelectDialog; +} + +class LWJGLSelectDialog : public QDialog +{ + Q_OBJECT + +public: + explicit LWJGLSelectDialog(QWidget *parent = 0); + ~LWJGLSelectDialog(); + + QString selectedVersion() const; + +private slots: + void on_refreshButton_clicked(); + + void loadingStateUpdated(bool loading); + void loadingFailed(QString error); + +private: + Ui::LWJGLSelectDialog *ui; +}; + +#endif // LWJGLSELECTDIALOG_H diff --git a/gui/lwjglselectdialog.ui b/gui/lwjglselectdialog.ui new file mode 100644 index 00000000..c715cc07 --- /dev/null +++ b/gui/lwjglselectdialog.ui @@ -0,0 +1,85 @@ + + + LWJGLSelectDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Status label... + + + + + + + + + + + + &Refresh + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + LWJGLSelectDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + LWJGLSelectDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 47753b37..e60155c6 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -40,6 +40,7 @@ #include "gui/browserdialog.h" #include "gui/aboutdialog.h" #include "gui/versionselectdialog.h" +#include "gui/lwjglselectdialog.h" #include "gui/consolewindow.h" #include "kcategorizedview.h" @@ -59,6 +60,7 @@ #include "instancedelegate.h" #include "minecraftversionlist.h" +#include "lwjglversionlist.h" // Opens the given file in the default application. // TODO: Move this somewhere. @@ -140,6 +142,11 @@ MainWindow::MainWindow ( QWidget *parent ) : m_versionLoadTask = MinecraftVersionList::getMainList().getLoadTask(); startTask(m_versionLoadTask); } + + if (!LWJGLVersionList::get().isLoaded()) + { + LWJGLVersionList::get().loadList(); + } } MainWindow::~MainWindow() @@ -460,3 +467,17 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() inst->setIntendedVersion(vselect->selectedVersion()->descriptor()); } } + +void MainWindow::on_actionChangeInstLWJGLVersion_triggered() +{ + Instance *inst = selectedInstance(); + + if (!inst) + return; + + LWJGLSelectDialog *lselect = new LWJGLSelectDialog(this); + if (lselect->exec()) + { + + } +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 192cdbc4..a9a4d395 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -99,6 +99,8 @@ private slots: void taskStart(Task *task); void taskEnd(Task *task); + void on_actionChangeInstLWJGLVersion_triggered(); + public slots: void instanceActivated ( QModelIndex ); diff --git a/libmultimc/CMakeLists.txt b/libmultimc/CMakeLists.txt index 4ffa3173..0209f8a4 100644 --- a/libmultimc/CMakeLists.txt +++ b/libmultimc/CMakeLists.txt @@ -48,6 +48,7 @@ include/loginresponse.h include/version.h include/appsettings.h include/minecraftprocess.h +include/lwjglversionlist.h ) SET(LIBINST_SOURCES @@ -78,6 +79,7 @@ src/loginresponse.cpp src/version.cpp src/appsettings.cpp src/minecraftprocess.cpp +src/lwjglversionlist.cpp ) # Set the include dir path. diff --git a/libmultimc/include/lwjglversionlist.h b/libmultimc/include/lwjglversionlist.h new file mode 100644 index 00000000..700c93d4 --- /dev/null +++ b/libmultimc/include/lwjglversionlist.h @@ -0,0 +1,128 @@ +/* Copyright 2013 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. + */ + +#ifndef LWJGLVERSIONLIST_H +#define LWJGLVERSIONLIST_H + +#include +#include + +#include + +#include +#include + +#include "libmmc_config.h" + +class LIBMULTIMC_EXPORT LWJGLVersion : public QObject +{ + Q_OBJECT + + /*! + * The name of the LWJGL version. + */ + Q_PROPERTY(QString name READ name) + + /*! + * The URL for this version of LWJGL. + */ + Q_PROPERTY(QUrl url READ url) +public: + LWJGLVersion(const QString &name, const QUrl &url, QObject *parent = 0) : + QObject(parent), m_name(name), m_url(url) { } + + LWJGLVersion(const LWJGLVersion &other) : + QObject(other.parent()), m_name(other.name()), m_url(other.url()) { } + + QString name() const { return m_name; } + + QUrl url() const { return m_url; } + +protected: + QString m_name; + QUrl m_url; +}; + +class LIBMULTIMC_EXPORT LWJGLVersionList : public QAbstractListModel +{ + Q_OBJECT +public: + explicit LWJGLVersionList(QObject *parent = 0); + + static LWJGLVersionList &get(); + + bool isLoaded() { return m_vlist.length() > 0; } + + const LWJGLVersion *getVersion(const QString &versionName); + LWJGLVersion &at(int index) { return m_vlist[index]; } + const LWJGLVersion &at(int index) const { return m_vlist[index]; } + + int count() const { return m_vlist.length(); } + + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex &parent) const { return count(); } + virtual int columnCount(const QModelIndex &parent) const; + + virtual bool isLoading() const; + virtual bool errored() const { return m_errored; } + + virtual QString lastErrorMsg() const { return m_lastErrorMsg; } + +public slots: + /*! + * Loads the version list. + * This is done asynchronously. On success, the loadListFinished() signal will + * be emitted. The list model will be reset as well, resulting in the modelReset() + * signal being emitted. Note that the model will be reset before loadListFinished() is emitted. + * If loading the list failed, the loadListFailed(QString msg), + * signal will be emitted. + */ + virtual void loadList(); + +signals: + /*! + * Emitted when the list either starts or finishes loading. + * \param loading Whether or not the list is loading. + */ + void loadingStateUpdated(bool loading); + + void loadListFinished(); + + void loadListFailed(QString msg); + +private: + QList m_vlist; + + QNetworkReply *m_netReply; + + QNetworkAccessManager netMgr; + QNetworkReply *reply; + + bool m_loading; + bool m_errored; + QString m_lastErrorMsg; + + void failed(QString msg); + + void finished(); + + void setLoading(bool loading); + +private slots: + virtual void netRequestComplete(); +}; + +#endif // LWJGLVERSIONLIST_H diff --git a/libmultimc/src/lwjglversionlist.cpp b/libmultimc/src/lwjglversionlist.cpp new file mode 100644 index 00000000..af5cf2f5 --- /dev/null +++ b/libmultimc/src/lwjglversionlist.cpp @@ -0,0 +1,204 @@ +/* Copyright 2013 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 "include/lwjglversionlist.h" + +#include + +#include + +#include + +#define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss" + +LWJGLVersionList mainVersionList; + +LWJGLVersionList &LWJGLVersionList::get() +{ + return mainVersionList; +} + + +LWJGLVersionList::LWJGLVersionList(QObject *parent) : + QAbstractListModel(parent) +{ + setLoading(false); +} + +QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + const LWJGLVersion &version = at(index.row()); + + switch (role) + { + case Qt::DisplayRole: + return version.name(); + + case Qt::ToolTipRole: + return version.url().toString(); + + default: + return QVariant(); + } +} + +QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + return "Version"; + + case Qt::ToolTipRole: + return "LWJGL version name."; + + default: + return QVariant(); + } +} + +int LWJGLVersionList::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +bool LWJGLVersionList::isLoading() const +{ + return m_loading; +} + +void LWJGLVersionList::loadList() +{ + Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)"); + + setLoading(true); + reply = netMgr.get(QNetworkRequest(QUrl(RSS_URL))); + connect(reply, SIGNAL(finished()), SLOT(netRequestComplete())); +} + +inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) +{ + QDomNodeList elementList = parent.elementsByTagName(tagname); + if (elementList.count()) + return elementList.at(0).toElement(); + else + return QDomElement(); +} + +void LWJGLVersionList::netRequestComplete() +{ + if (reply->error() == QNetworkReply::NoError) + { + QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip"); + Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", + "LWJGL regex is invalid"); + + QDomDocument doc; + + QString xmlErrorMsg; + int errorLine; + if (!doc.setContent(reply->readAll(), false, &xmlErrorMsg, &errorLine)) + { + failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + + " at line " + QString::number(errorLine)); + setLoading(false); + return; + } + + QDomNodeList items = doc.elementsByTagName("item"); + + QList tempList; + + for (int i = 0; i < items.length(); i++) + { + Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list", + "XML element isn't an element... wat?"); + + QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link"); + if (linkElement.isNull()) + { + qWarning() << "Link element" << i << "in RSS feed doesn't exist! Skipping."; + continue; + } + + QString link = linkElement.text(); + + // Make sure it's a download link. + if (link.endsWith("/download") && link.contains(lwjglRegex)) + { + QString name = link.mid(lwjglRegex.indexIn(link)); + // Subtract 4 here to remove the .zip file extension. + name = name.left(lwjglRegex.matchedLength() - 4); + + QUrl url(link); + if (!url.isValid()) + { + qWarning() << "LWJGL version URL isn't valid:" << link << "Skipping."; + continue; + } + + tempList.append(LWJGLVersion(name, link)); + } + } + + beginResetModel(); + m_vlist.swap(tempList); + endResetModel(); + + qDebug("Loaded LWJGL list."); + finished(); + } + else + { + failed("Failed to load LWJGL list. Network error: " + reply->errorString()); + } + + setLoading(false); + reply->deleteLater(); +} + +const LWJGLVersion *LWJGLVersionList::getVersion(const QString &versionName) +{ + for (int i = 0; i < count(); i++) + { + if (at(i).name() == versionName) + return &at(i); + } + return NULL; +} + + +void LWJGLVersionList::failed(QString msg) +{ + qWarning() << msg; + emit loadListFailed(msg); +} + +void LWJGLVersionList::finished() +{ + emit loadListFinished(); +} + +void LWJGLVersionList::setLoading(bool loading) +{ + m_loading = loading; + emit loadingStateUpdated(m_loading); +}