1 #include <QDomDocument>
2 #include <QEventLoop>
3 #include <QFile>
4 #include <QNetworkAccessManager>
5 #include <QNetworkReply>
6 #include <QNetworkRequest>
7 #include <QThread>
8
9 #include <ThreadWeaver/Exception>
10 #include <ThreadWeaver/ThreadWeaver>
11
12 #include "MainWidget.h"
13 #include "ViewController.h"
14
ViewController(MainWidget * mainwidget)15 ViewController::ViewController(MainWidget *mainwidget)
16 : QObject() // no parent
17 , m_apiPostUrl(QStringLiteral("http://fickedinger.tumblr.com/api/read?id=94635924143"))
18 //@@snippet_begin(hellointernet-sequence)
19 {
20 connect(this, SIGNAL(setImage(QImage)), mainwidget, SLOT(setImage(QImage)));
21 connect(this, SIGNAL(setCaption(QString)), mainwidget, SLOT(setCaption(QString)));
22 connect(this, SIGNAL(setStatus(QString)), mainwidget, SLOT(setStatus(QString)));
23
24 using namespace ThreadWeaver;
25 auto s = new Sequence;
26 *s << make_job([=]() {
27 loadPlaceholderFromResource();
28 }) << make_job([=]() {
29 loadPostFromTumblr();
30 }) << make_job([=]() {
31 loadImageFromTumblr();
32 });
33 stream() << s;
34 }
35 //@@snippet_end
36
~ViewController()37 ViewController::~ViewController()
38 {
39 ThreadWeaver::Queue::instance()->finish();
40 }
41
42 //@@snippet_begin(hellointernet-loadresource)
loadPlaceholderFromResource()43 void ViewController::loadPlaceholderFromResource()
44 {
45 QThread::msleep(500);
46 showResourceImage("IMG_20140813_004131.png");
47 Q_EMIT setStatus(tr("Downloading post..."));
48 }
49 //@@snippet_end
50
51 //@@snippet_begin(hellointernet-loadpost)
loadPostFromTumblr()52 void ViewController::loadPostFromTumblr()
53 {
54 const QUrl url(m_apiPostUrl);
55
56 auto const data = download(url);
57 Q_EMIT setStatus(tr("Post downloaded..."));
58
59 QDomDocument doc;
60 if (!doc.setContent(data)) {
61 error(tr("Post format not recognized!"));
62 }
63
64 auto textOfFirst = [&doc](const char *name) {
65 auto const s = QString::fromLatin1(name);
66 auto elements = doc.elementsByTagName(s);
67 if (elements.isEmpty()) {
68 return QString();
69 }
70 return elements.at(0).toElement().text();
71 };
72
73 auto const caption = textOfFirst("photo-caption");
74 if (caption.isEmpty()) {
75 error(tr("Post does not contain a caption!"));
76 }
77 Q_EMIT setCaption(caption);
78 auto const imageUrl = textOfFirst("photo-url");
79 if (imageUrl.isEmpty()) {
80 error(tr("Post does not contain an image!"));
81 }
82
83 m_fullPostUrl = attributeTextFor(doc, "post", "url-with-slug");
84 if (m_fullPostUrl.isEmpty()) {
85 error(tr("Response does not contain URL with slug!"));
86 }
87 m_imageUrl = QUrl(imageUrl);
88 showResourceImage("IMG_20140813_004131-colors-cubed.png");
89 Q_EMIT setStatus(tr("Downloading image..."));
90 QThread::msleep(500);
91 }
92 //@@snippet_end
93
loadImageFromTumblr()94 void ViewController::loadImageFromTumblr()
95 {
96 auto const data = download(m_imageUrl);
97 Q_EMIT setStatus(tr("Image downloaded..."));
98 const QImage image = QImage::fromData(data);
99 if (!image.isNull()) {
100 Q_EMIT setImage(image);
101 Q_EMIT setStatus(tr("Download complete (see %1).").arg(m_fullPostUrl));
102 } else {
103 error(tr("Image format error!"));
104 }
105 }
106
download(const QUrl & url)107 QByteArray ViewController::download(const QUrl &url)
108 {
109 QNetworkAccessManager manager;
110 QEventLoop loop;
111 QObject::connect(&manager, SIGNAL(finished(QNetworkReply *)), &loop, SLOT(quit()));
112 auto reply = manager.get(QNetworkRequest(url));
113 loop.exec();
114 if (reply->error() == QNetworkReply::NoError) {
115 const QByteArray data = reply->readAll();
116 return data;
117 } else {
118 error(tr("Unable to download data for \"%1\"!").arg(url.toString()));
119 return QByteArray();
120 }
121 }
122
123 //@@snippet_begin(hellointernet-error)
error(const QString & message)124 void ViewController::error(const QString &message)
125 {
126 showResourceImage("IMG_20140813_004131-colors-cubed.png");
127 Q_EMIT setCaption(tr("Error"));
128 Q_EMIT setStatus(tr("%1").arg(message));
129 throw ThreadWeaver::JobFailed(message);
130 }
131 //@@snippet_end
132
showResourceImage(const char * file)133 void ViewController::showResourceImage(const char *file)
134 {
135 const QString path(QStringLiteral("://resources/%1").arg(QString::fromLatin1(file)));
136 Q_ASSERT(QFile::exists(path));
137 const QImage i(path);
138 Q_ASSERT(!i.isNull());
139 Q_EMIT setImage(i);
140 }
141
attributeTextFor(const QDomDocument & doc,const char * tag,const char * attribute)142 QString ViewController::attributeTextFor(const QDomDocument &doc, const char *tag, const char *attribute)
143 {
144 auto const tagString = QString::fromLatin1(tag);
145 auto const attributeString = QString::fromLatin1(attribute);
146 auto elements = doc.elementsByTagName(tagString);
147 if (elements.isEmpty()) {
148 return QString();
149 }
150 const QString content = elements.at(0).toElement().attribute(attributeString);
151 return content;
152 }
153