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