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 <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 
33 CheckUpdatesDialog::CheckUpdatesDialog()
34 {
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);
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 
81 CheckUpdatesDialog::~CheckUpdatesDialog()
82 {
83  mNetworkManager->deleteLater();
84 }
85 
86 void CheckUpdatesDialog::startChecking()
87 {
88 #ifdef NIGHTLY
89  nightlyBuildCheck();
90 #else
91  regularBuildCheck();
92 #endif
93 }
94 
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 
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 
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 
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 
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 
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 
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)