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