1 /***************************************************************************
2 * Copyright (C) 2009 by Lassi Väätämöinen *
3 * lassi.vaatamoinen@ixonos.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
19 ***************************************************************************/
22 #include <QtCore/qplugin.h>
23 #include <QVBoxLayout>
24 #include <QHBoxLayout>
27 #include <QPushButton>
33 #include <QNetworkReply>
34 #include <QDomDocument>
36 #include "SearchPlugin.h"
37 #include "DownloadManager.h"
41 const QString ENGINES_DIR = "engines";
42 const QString DESCRIPTION_FILENAME = "opensearch.xml";
43 const QString SEARCH_TERMS_STRING = "{searchTerms}";
44 const QString SEARCHPLUGIN_ID = "SearchPlugin";
46 SearchPlugin::SearchPlugin() :
47 comboBox_(NULL), searchLine_(NULL),
48 searchButton_(NULL), result_(NULL),
49 dlManager_(NULL), host_(NULL)
51 // TODO: Add back/forward/refresh -buttons to widget to allow some browsing functionality
55 void SearchPlugin::initialize(PluginHostInterface* host, Info info)
61 // Build up the plugin widget:
62 QWidget *pluginWidget = new QWidget;
63 QVBoxLayout *vbox = new QVBoxLayout;
64 QHBoxLayout *hbox = new QHBoxLayout;
65 comboBox_ = new QComboBox;
66 searchLine_ = new QLineEdit;
67 searchButton_ = new QPushButton("Search");
69 hbox->addWidget(searchLine_);
70 hbox->addWidget(searchButton_);
71 vbox->addWidget(comboBox_);
72 vbox->addLayout(hbox);
73 pluginWidget->setLayout(vbox);
75 connect(searchButton_, SIGNAL(clicked()), this, SLOT(on_searchButton_clicked()));
76 //connect(this, SIGNAL(searchResult(QWidget*)), this, SLOT(on_searchResult(QWidget*)));
77 qDebug() << info.directory;
78 QDir dir(info.directory);
79 if (dir.cd(ENGINES_DIR)) {
80 ParseSearchEngineDescriptions(dir);
83 host_->setGui(pluginWidget, qtrapids::PluginHostInterface::BASE_WIDGET, this);
87 QWidget* SearchPlugin::getGui()
92 QString SearchPlugin::identifier()
94 return SEARCHPLUGIN_ID;
98 void SearchPlugin::on_searchButton_clicked()
100 int i = comboBox_->currentIndex();
101 QString tmp = engineTemplates_.at(i);
103 i = tmp.indexOf(SEARCH_TERMS_STRING);
104 tmp.replace(i, SEARCH_TERMS_STRING.length(), searchLine_->text());
107 qDebug() << searchUrl;
108 result_ = new QWebView;
110 // Get underlying QWebPage and change link delegation, so we can meddle with links in on_linkClicked().
111 QWebPage *resultPage = result_->page();
112 resultPage->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks);
114 // Get the network access manager for examining HTTP replies.
115 QNetworkAccessManager *netwAccessManager_ = resultPage->networkAccessManager();
117 connect(resultPage, SIGNAL(linkClicked(const QUrl&)), this, SLOT(on_linkClicked(const QUrl&)));
118 connect(result_, SIGNAL(loadFinished(bool)), this, SLOT(on_loadFinished(bool)));
119 connect(netwAccessManager_, SIGNAL(finished(QNetworkReply*)), this, SLOT(on_networkReplyFinished(QNetworkReply*)));
121 result_->load(searchUrl);
123 on_searchResult((QWidget*)result_);
127 void SearchPlugin::on_loadFinished(bool ok)
129 #ifdef QTRAPIDS_DEBUG
131 qDebug() << "on_loadFinished(): success";
137 void SearchPlugin::on_networkReplyFinished(QNetworkReply* reply)
140 qDebug() << "on_networkReplyFinished()";
141 QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
143 QByteArray replyData;
144 QUrl url = reply->url();
146 qDebug() << "on_networkReplyFinished():" << url;
148 // If content type is torrent data, read reply data:
149 if (contentType == "application/x-bittorrent") {
151 // If HTTP-response has Content-Disposition: -header, then we must use that as filename.
152 if (reply->hasRawHeader("Content-Disposition")) { // NOTE this code block taken from kwebpage.cpp
153 const QString value = QLatin1String(reply->rawHeader("Content-Disposition"));
154 const int pos = value.indexOf(QLatin1String("filename="));
156 QString name = value.mid(pos + 9);
157 if (name.startsWith(QLatin1Char('"')) && name.endsWith(QLatin1Char('"')))
158 name = name.mid(1, name.size() - 2);
161 // No content-disposition header, use last part (the filename) of URL as filename:
163 QString path = url.path();
164 QFileInfo fInfo(path);
165 filename = fInfo.fileName();
168 // Destroy ongoing download, if any.
174 /// @todo Is this a bit of a hack now; we get the reply and check Content-Type,
175 /// The download is then started afterwards, so we get unecessarily one extra
176 /// HTTP-response here.
177 /// @todo Could this whole content-type checking be logically moved to DownloadManager?
178 // Start downloading Torrent file.
179 dlManager_ = new DownloadManager(url, "/tmp/" + filename);
180 connect(dlManager_, SIGNAL(finished(QString)), this, SLOT(on_downloadFinished(QString)));
187 void SearchPlugin::on_searchResult(QWidget* resultWidget)
189 #ifdef QTRAPIDS_DEBUG
190 qDebug() << "on_searchResult()";
193 host_->addPluginWidget(resultWidget, qtrapids::PluginHostInterface::TAB_PAGE);
198 /// @todo It may be that we don't actually need link delegation, because we check response Content-Type header
199 /// on_linkClicked() in that case unnecessary function
200 void SearchPlugin::on_linkClicked(const QUrl& url)
202 qDebug() << "on_linkClicked():" << url;
210 void SearchPlugin::on_downloadFinished(QString filepath)
212 #ifdef QTRAPIDS_DEBUG
213 qDebug() << "TORRENT DOWNLOADED: " << filepath;
218 host_->eventRequest(QVariant(filepath), qtrapids::PluginHostInterface::OPEN_FILE);
223 void SearchPlugin::ParseSearchEngineDescriptions(const QDir& dir)
225 foreach (QString dirName, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
227 QFile file(dir.path() + "/" + dirName + "/" + DESCRIPTION_FILENAME);
229 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
230 qWarning() << "Unable to open " << DESCRIPTION_FILENAME << " for reading in " << dir.path();
234 // Parse the XML file to DOM document.
235 QDomDocument document;
237 // Second parameter: nameSpaceProcessing = false
238 if (!document.setContent(&file, false)) {
239 qWarning() << "Unable to parse " << DESCRIPTION_FILENAME << " in " << dirName;
241 QDomNodeList urlElements = document.elementsByTagName("Url");
242 QDomNodeList shortNameElements = document.elementsByTagName("ShortName");
244 QDomNode n = urlElements.item(0);
246 QDomNamedNodeMap attribMap;
247 if (n.hasAttributes()) {
248 attribMap = n.attributes();
249 m = attribMap.namedItem("template");
250 engineTemplates_.push_back(m.nodeValue());
253 n = shortNameElements.item(0);
254 if (n.hasChildNodes()) {
256 comboBox_->addItem(m.nodeValue());
260 /// @todo save parsed xml elements <Shortname> and <Url template="<foo>"> to a model which is displayed in combobox.
263 } // namespace qtrapids
265 Q_EXPORT_PLUGIN2(searchplugin, qtrapids::SearchPlugin)