Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • app
  • src
checkupdatesdialog.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "checkupdatesdialog.h"
19#include <QDebug>
20#include <QNetworkReply>
21#include <QNetworkAccessManager>
22#include <QHBoxLayout>
23#include <QVBoxLayout>
24#include <QLabel>
25#include <QProgressBar>
26#include <QPushButton>
27#include <QDesktopServices>
28#include <QRegularExpression>
29#include <QVersionNumber>
30#include <QXmlStreamReader>
31
32#include "util.h"
33
34
35CheckUpdatesDialog::CheckUpdatesDialog()
36{
37 Qt::WindowFlags eFlags = Qt::Dialog | Qt::WindowTitleHint | Qt::WindowCloseButtonHint;
38 setWindowFlags(eFlags);
39 setMinimumSize(QSize(400, 150));
40
41 QLabel* logoLabel = new QLabel;
42 logoLabel->setPixmap(QPixmap(":/icons/logo.png"));
43 logoLabel->setFixedSize(QSize(72, 72));
44
45 mTitleLabel = new QLabel;
46 mTitleLabel->setText(tr("Checking for Updates...", "status description in the check-for-update dialog"));
47
48 mDetailLabel = new QLabel;
49 mDetailLabel->setWordWrap(true);
50
51 //If minimum and maximum both are set to 0, the bar shows a busy indicator instead of a percentage of steps.
52 mProgressBar = new QProgressBar;
53 mProgressBar->setMaximum(0);
54 mProgressBar->setMinimum(0);
55 mProgressBar->setValue(0);
56 mProgressBar->setTextVisible(false);
57
58 mDownloadButton = new QPushButton(tr("Download"));
59 mCloseButton = new QPushButton(tr("Close"));
60
61 QHBoxLayout* hButtonLayout = new QHBoxLayout;
62 hButtonLayout->addWidget(mDownloadButton);
63 hButtonLayout->addWidget(mCloseButton);
64
65 QVBoxLayout* vLayout = new QVBoxLayout;
66 vLayout->addWidget(mTitleLabel);
67 vLayout->addWidget(mDetailLabel);
68 vLayout->addWidget(mProgressBar);
69 vLayout->addSpacerItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
70 vLayout->addLayout(hButtonLayout);
71
72 QHBoxLayout* mainLayout = new QHBoxLayout;
73 mainLayout->addWidget(logoLabel);
74 mainLayout->addLayout(vLayout);
75 setLayout(mainLayout);
76
77 mDownloadButton->setDisabled(true);
78
79 connect(mDownloadButton, &QPushButton::clicked, this, &CheckUpdatesDialog::gotoDownloadPage);
80 connect(mCloseButton, &QPushButton::clicked, this, &CheckUpdatesDialog::closeDialog);
81}
82
83CheckUpdatesDialog::~CheckUpdatesDialog()
84{
85 mNetworkManager->deleteLater();
86}
87
88void CheckUpdatesDialog::startChecking()
89{
90 QString version(APP_VERSION);
91 if (version.startsWith("99.0.0")) {
92 nightlyBuildCheck();
93 } else if (version == "0.0.0.0") {
94 // Do nothing
95 } else {
96 regularBuildCheck();
97 }
98}
99
100void CheckUpdatesDialog::regularBuildCheck()
101{
102 mNetworkManager = new QNetworkAccessManager(this);
103 QUrl url("https://github.com/pencil2d/pencil/releases.atom");
104
105 QNetworkRequest req;
106 req.setUrl(url);
107 req.setRawHeader("User-Agent", "Mozilla Firefox");
108
109 mNetworkManager->get(req);
110 connect(mNetworkManager, &QNetworkAccessManager::finished, this, &CheckUpdatesDialog::networkRequestFinished);
111}
112
113void CheckUpdatesDialog::nightlyBuildCheck()
114{
115 mTitleLabel->setText(tr("<b>You are using a Pencil2D nightly build</b>"));
116 mDetailLabel->setText(tr("Please go %1 here %2 to check new nightly builds.")
117 .arg("<a href=\"https://www.pencil2d.org/download/nightly/\">", "</a>"));
118 mDetailLabel->setOpenExternalLinks(true);
119 mProgressBar->setRange(0, 1);
120 mProgressBar->setValue(1);
121 mDownloadButton->setEnabled(false);
122}
123
124void CheckUpdatesDialog::networkErrorHappened()
125{
126 mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
127 mDetailLabel->setText(tr("Please check your internet connection and try again later.", "error msg of check-for-update"));
128 mProgressBar->setRange(0, 1);
129 mProgressBar->setValue(1);
130 mDownloadButton->setEnabled(false);
131}
132
133void CheckUpdatesDialog::networkResponseIsEmpty()
134{
135 mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
136 mDetailLabel->setText(tr("Network response is empty", "error msg of check-for-update"));
137 mProgressBar->setRange(0, 1);
138 mProgressBar->setValue(1);
139 mDownloadButton->setEnabled(false);
140}
141
142void CheckUpdatesDialog::invalidReleaseXml()
143{
144 mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
145 mDetailLabel->setText(tr("Couldn't retrieve the version information", "error msg of check-for-update"));
146 mProgressBar->setRange(0, 1);
147 mProgressBar->setValue(1);
148 mDownloadButton->setEnabled(false);
149}
150
151void CheckUpdatesDialog::networkRequestFinished(QNetworkReply* reply)
152{
153 reply->deleteLater();
154
155 QNetworkReply::NetworkError errorCode = reply->error();
156 if (errorCode != QNetworkReply::NoError)
157 {
158 // show error message
159 qDebug() << reply->errorString();
160 networkErrorHappened();
161 return;
162 }
163
164 auto releasesAtom = QString::fromUtf8(reply->readAll()).trimmed();
165 if (releasesAtom.isEmpty())
166 {
167 networkResponseIsEmpty();
168 return;
169 }
170
171 QString latestVersionString = getVersionNumberFromXml(releasesAtom);
172 if (latestVersionString == "0.0.1")
173 {
174 invalidReleaseXml();
175 return;
176 }
177
178 bool isNewVersionAvailable = compareVersion(APP_VERSION, latestVersionString);
179 if (isNewVersionAvailable)
180 {
181 mTitleLabel->setText(tr("<b>A new version of Pencil2D is available!</b>"));
182 mDetailLabel->setText(tr("Pencil2D %1 is now available -- you have %2. Would you like to download it?")
183 .arg(latestVersionString, APP_VERSION));
184 mProgressBar->hide();
185 mDownloadButton->setEnabled(true);
186 }
187 else
188 {
189 mTitleLabel->setText(tr("<b>Pencil2D is up to date</b>"));
190 mDetailLabel->setText(tr("Version %1").arg(APP_VERSION));
191 mProgressBar->setRange(0, 1);
192 mProgressBar->setValue(1);
193 mDownloadButton->setEnabled(false);
194 }
195}
196
197bool CheckUpdatesDialog::compareVersion(QString currentVersion, QString latestVersion)
198{
199 return QVersionNumber::fromString(currentVersion) < QVersionNumber::fromString(latestVersion);
200}
201
202QString CheckUpdatesDialog::getVersionNumberFromXml(QString xml)
203{
204 // XML source: https://github.com/pencil2d/pencil/releases.atom
205
206 QXmlStreamReader xmlReader(xml);
207
208 while (!xmlReader.atEnd() && !xmlReader.hasError())
209 {
210 QXmlStreamReader::TokenType tokenType = xmlReader.readNext();
211 if (tokenType == QXmlStreamReader::StartElement && xmlReader.name() == QLatin1String("entry"))
212 {
213 while (!xmlReader.atEnd() && !xmlReader.hasError())
214 {
215 xmlReader.readNext();
216 if (xmlReader.name() == QLatin1String("title"))
217 {
218 QString title = xmlReader.readElementText();
219 title = title.remove(QRegularExpression("^v")); // remove the leading 'v'
220 // Ignore release candidate versions
221 if (!title.contains("Release Candidate")) {
222 return title;
223 }
224 }
225 }
226 }
227 }
228 if (xmlReader.error() != QXmlStreamReader::NoError)
229 {
230 qDebug() << xmlReader.errorString();
231 return "0.0.1";
232 }
233 return "0.0.1";
234}
235
236void CheckUpdatesDialog::gotoDownloadPage()
237{
238 QString url = "https://pencil2d.org/download";
239 QDesktopServices::openUrl(QUrl(url));
240}
241
242void CheckUpdatesDialog::closeDialog()
243{
244 done(QDialog::Accepted);
245}
QAbstractButton::clicked
void clicked(bool checked)
QBoxLayout::addLayout
void addLayout(QLayout *layout, int stretch)
QBoxLayout::addSpacerItem
void addSpacerItem(QSpacerItem *spacerItem)
QBoxLayout::addWidget
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QDesktopServices::openUrl
bool openUrl(const QUrl &url)
QDialog::Accepted
Accepted
QDialog::done
virtual void done(int r)
QHBoxLayout
QIODevice::errorString
QString errorString() const const
QIODevice::readAll
QByteArray readAll()
QLabel
QLabel::setOpenExternalLinks
void setOpenExternalLinks(bool open)
QLabel::setPixmap
void setPixmap(const QPixmap &)
QLabel::setText
void setText(const QString &)
QLabel::setWordWrap
void setWordWrap(bool on)
QLatin1String
QNetworkAccessManager
QNetworkAccessManager::finished
void finished(QNetworkReply *reply)
QNetworkAccessManager::get
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply
QNetworkReply::NetworkError
NetworkError
QNetworkReply::error
QNetworkReply::NetworkError error() const const
QNetworkRequest
QNetworkRequest::setRawHeader
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
QNetworkRequest::setUrl
void setUrl(const QUrl &url)
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::deleteLater
void deleteLater()
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QPixmap
QProgressBar
QProgressBar::setMaximum
void setMaximum(int maximum)
QProgressBar::setMinimum
void setMinimum(int minimum)
QProgressBar::setRange
void setRange(int minimum, int maximum)
QProgressBar::setTextVisible
void setTextVisible(bool visible)
QProgressBar::setValue
void setValue(int value)
QPushButton
QRegularExpression
QSize
QSizePolicy::Minimum
Minimum
QSpacerItem
QString
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::remove
QString & remove(int position, int n)
QString::trimmed
QString trimmed() const const
Qt::WindowFlags
typedef WindowFlags
QUrl
QVBoxLayout
QVersionNumber::fromString
QVersionNumber fromString(const QString &string, int *suffixIndex)
QWidget::setEnabled
void setEnabled(bool)
QWidget::hide
void hide()
QWidget::setMinimumSize
void setMinimumSize(const QSize &)
QWidget::setDisabled
void setDisabled(bool disable)
QWidget::setFixedSize
void setFixedSize(const QSize &s)
QWidget::setLayout
void setLayout(QLayout *layout)
QWidget::setWindowFlags
void setWindowFlags(Qt::WindowFlags type)
QXmlStreamReader
QXmlStreamReader::NoError
NoError
QXmlStreamReader::TokenType
TokenType
Generated on Fri Feb 27 2026 07:33:49 for Pencil2D by doxygen 1.9.6 based on revision e1e2bc04715617e3bb4d4796f67a4493290639ca