All Classes Namespaces Functions Variables Enumerations Properties Pages
checkupdatesdialog.cpp
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 <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 <QVersionNumber>
29 #include <QXmlStreamReader>
30 
31 #include "util.h"
32 
33 
34 CheckUpdatesDialog::CheckUpdatesDialog()
35 {
37  setWindowFlags(eFlags);
38  setMinimumSize(QSize(400, 150));
39 
40  QLabel* logoLabel = new QLabel;
41  logoLabel->setPixmap(QPixmap(":/icons/logo.png"));
42  logoLabel->setFixedSize(QSize(72, 72));
43 
44  mTitleLabel = new QLabel;
45  mTitleLabel->setText(tr("Checking for Updates...", "status description in the check-for-update dialog"));
46 
47  mDetailLabel = new QLabel;
48  mDetailLabel->setWordWrap(true);
49 
50  //If minimum and maximum both are set to 0, the bar shows a busy indicator instead of a percentage of steps.
51  mProgressBar = new QProgressBar;
52  mProgressBar->setMaximum(0);
53  mProgressBar->setMinimum(0);
54  mProgressBar->setValue(0);
55  mProgressBar->setTextVisible(false);
56 
57  mDownloadButton = new QPushButton(tr("Download"));
58  mCloseButton = new QPushButton(tr("Close"));
59 
60  QHBoxLayout* hButtonLayout = new QHBoxLayout;
61  hButtonLayout->addWidget(mDownloadButton);
62  hButtonLayout->addWidget(mCloseButton);
63 
64  QVBoxLayout* vLayout = new QVBoxLayout;
65  vLayout->addWidget(mTitleLabel);
66  vLayout->addWidget(mDetailLabel);
67  vLayout->addWidget(mProgressBar);
69  vLayout->addLayout(hButtonLayout);
70 
71  QHBoxLayout* mainLayout = new QHBoxLayout;
72  mainLayout->addWidget(logoLabel);
73  mainLayout->addLayout(vLayout);
74  setLayout(mainLayout);
75 
76  mDownloadButton->setDisabled(true);
77 
78  connect(mDownloadButton, &QPushButton::clicked, this, &CheckUpdatesDialog::gotoDownloadPage);
79  connect(mCloseButton, &QPushButton::clicked, this, &CheckUpdatesDialog::closeDialog);
80 }
81 
82 CheckUpdatesDialog::~CheckUpdatesDialog()
83 {
84  mNetworkManager->deleteLater();
85 }
86 
87 void CheckUpdatesDialog::startChecking()
88 {
89 #ifdef PENCIL2D_NIGHTLY_BUILD
90  nightlyBuildCheck();
91 #else
92  regularBuildCheck();
93 #endif
94 }
95 
96 void CheckUpdatesDialog::regularBuildCheck()
97 {
98  mNetworkManager = new QNetworkAccessManager(this);
99  QUrl url("https://github.com/pencil2d/pencil/releases.atom");
100 
101  QNetworkRequest req;
102  req.setUrl(url);
103  req.setRawHeader("User-Agent", "Mozilla Firefox");
104 
105  mNetworkManager->get(req);
106  connect(mNetworkManager, &QNetworkAccessManager::finished, this, &CheckUpdatesDialog::networkRequestFinished);
107 }
108 
109 void CheckUpdatesDialog::nightlyBuildCheck()
110 {
111  mTitleLabel->setText(tr("<b>You are using a Pencil2D nightly build</b>"));
112  mDetailLabel->setText(tr("Please go %1 here %2 to check new nightly builds.")
113  .arg("<a href=\"https://www.pencil2d.org/download/#nightlybuild\">", "</a>"));
114  mDetailLabel->setOpenExternalLinks(true);
115  mProgressBar->setRange(0, 1);
116  mProgressBar->setValue(1);
117  mDownloadButton->setEnabled(false);
118 }
119 
120 void CheckUpdatesDialog::networkErrorHappened()
121 {
122  mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
123  mDetailLabel->setText(tr("Please check your internet connection and try again later.", "error msg of check-for-update"));
124  mProgressBar->setRange(0, 1);
125  mProgressBar->setValue(1);
126  mDownloadButton->setEnabled(false);
127 }
128 
129 void CheckUpdatesDialog::networkResponseIsEmpty()
130 {
131  mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
132  mDetailLabel->setText(tr("Network response is empty", "error msg of check-for-update"));
133  mProgressBar->setRange(0, 1);
134  mProgressBar->setValue(1);
135  mDownloadButton->setEnabled(false);
136 }
137 
138 void CheckUpdatesDialog::invalidReleaseXml()
139 {
140  mTitleLabel->setText(tr("<b>An error occurred while checking for updates</b>", "error msg of check-for-update"));
141  mDetailLabel->setText(tr("Couldn't retrieve the version information", "error msg of check-for-update"));
142  mProgressBar->setRange(0, 1);
143  mProgressBar->setValue(1);
144  mDownloadButton->setEnabled(false);
145 }
146 
147 void CheckUpdatesDialog::networkRequestFinished(QNetworkReply* reply)
148 {
149  reply->deleteLater();
150 
151  QNetworkReply::NetworkError errorCode = reply->error();
152  if (errorCode != QNetworkReply::NoError)
153  {
154  // show error message
155  qDebug() << reply->errorString();
156  networkErrorHappened();
157  return;
158  }
159 
160  auto releasesAtom = QString::fromUtf8(reply->readAll()).trimmed();
161  if (releasesAtom.isEmpty())
162  {
163  networkResponseIsEmpty();
164  return;
165  }
166 
167  QString latestVersionString = getVersionNumberFromXml(releasesAtom);
168  if (latestVersionString == "0.0.1")
169  {
170  invalidReleaseXml();
171  return;
172  }
173 
174  bool isNewVersionAvailable = compareVersion(APP_VERSION, latestVersionString);
175  if (isNewVersionAvailable)
176  {
177  mTitleLabel->setText(tr("<b>A new version of Pencil2D is available!</b>"));
178  mDetailLabel->setText(tr("Pencil2D %1 is now available -- you have %2. Would you like to download it?")
179  .arg(latestVersionString, 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 
193 bool CheckUpdatesDialog::compareVersion(QString currentVersion, QString latestVersion)
194 {
195  return QVersionNumber::fromString(currentVersion) < QVersionNumber::fromString(latestVersion);
196 }
197 
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 
228 void CheckUpdatesDialog::gotoDownloadPage()
229 {
230  QString url = "https://pencil2d.org/download";
232 }
233 
234 void CheckUpdatesDialog::closeDialog()
235 {
237 }
QVersionNumber fromString(const QString &string, int *suffixIndex)
void setMaximum(int maximum)
void addSpacerItem(QSpacerItem *spacerItem)
void setUrl(const QUrl &url)
QString errorString() const const
void setPixmap(const QPixmap &)
virtual void done(int r)
QString & remove(int position, int n)
void setValue(int value)
QString tr(const char *sourceText, const char *disambiguation, int n)
void setMinimum(int minimum)
void setMinimumSize(const QSize &)
void setEnabled(bool)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QString fromUtf8(const char *str, int size)
void setLayout(QLayout *layout)
void clicked(bool checked)
QByteArray readAll()
void deleteLater()
void setText(const QString &)
void hide()
void setFixedSize(const QSize &s)
void setWindowFlags(Qt::WindowFlags type)
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
void finished(QNetworkReply *reply)
QNetworkReply::NetworkError error() const const
QNetworkReply * get(const QNetworkRequest &request)
void setDisabled(bool disable)
bool openUrl(const QUrl &url)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setTextVisible(bool visible)
void setRange(int minimum, int maximum)
typedef WindowFlags
void setWordWrap(bool on)
void addLayout(QLayout *layout, int stretch)