2 * Copyright (C) 2011 Canonical, Ltd.
5 * Florian Boucault <florian.boucault@canonical.com>
6 * Michal Hruby <michal.hruby@canonical.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25#include "categories.h"
27#include "variantutils.h"
32#include <QtGui/QDesktopServices>
35#include <UnityCore/Variant.h>
36#include <UnityCore/GLibWrapper.h>
41Scope::Scope(QObject *parent) : QObject(parent)
42 , m_formFactor("phone")
44 , m_searchInProgress(false)
46 m_categories.reset(new Categories(this));
48 connect(this, &Scope::isActiveChanged, this, &Scope::scopeIsActiveChanged);
51QString Scope::id() const
53 return QString::fromStdString(m_unityScope->id());
56QString Scope::name() const
58 return QString::fromStdString(m_unityScope->name());
61QString Scope::iconHint() const
63 return QString::fromStdString(m_unityScope->icon_hint());
66QString Scope::description() const
68 return QString::fromStdString(m_unityScope->description());
71QString Scope::searchHint() const
73 return QString::fromStdString(m_unityScope->search_hint());
76bool Scope::searchInProgress() const
78 return m_searchInProgress;
81bool Scope::visible() const
83 return m_unityScope->visible();
86QString Scope::shortcut() const
88 return QString::fromStdString(m_unityScope->shortcut());
91bool Scope::connected() const
93 return m_unityScope->connected();
96Categories* Scope::categories() const
98 return m_categories.get();
101QString Scope::searchQuery() const
103 return m_searchQuery;
106QString Scope::noResultsHint() const
108 return m_noResultsHint;
111QString Scope::formFactor() const
116bool Scope::isActive() const
121Filters* Scope::filters() const
123 return m_filters.get();
126void Scope::setSearchQuery(const QString& search_query)
128 /* Checking for m_searchQuery.isNull() which returns true only when the string
129 has never been set is necessary because when search_query is the empty
130 string ("") and m_searchQuery is the null string,
131 search_query != m_searchQuery is still true.
133 using namespace std::placeholders;
135 if (m_searchQuery.isNull() || search_query != m_searchQuery) {
136 m_searchQuery = search_query;
137 m_cancellable.Renew();
138 m_unityScope->Search(search_query.toStdString(), std::bind(&Scope::onSearchFinished, this, _1, _2, _3), m_cancellable);
139 Q_EMIT searchQueryChanged();
140 if (!m_searchInProgress) {
141 m_searchInProgress = true;
142 Q_EMIT searchInProgressChanged();
147void Scope::setNoResultsHint(const QString& hint) {
148 if (hint != m_noResultsHint) {
149 m_noResultsHint = hint;
150 Q_EMIT noResultsHintChanged();
154void Scope::setFormFactor(const QString& form_factor) {
155 if (form_factor != m_formFactor) {
156 m_formFactor = form_factor;
158 m_unityScope->form_factor = m_formFactor.toStdString();
159 synchronizeStates(); // will trigger a re-search
161 Q_EMIT formFactorChanged();
165void Scope::setActive(const bool active) {
166 if (active != m_isActive) {
168 Q_EMIT isActiveChanged(m_isActive);
172unity::dash::LocalResult Scope::createLocalResult(const QVariant &uri, const QVariant &icon_hint,
173 const QVariant &category, const QVariant &result_type,
174 const QVariant &mimetype, const QVariant &title,
175 const QVariant &comment, const QVariant &dnd_uri,
176 const QVariant &metadata)
178 unity::dash::LocalResult res;
179 res.uri = uri.toString().toStdString();
180 res.icon_hint = icon_hint.toString().toStdString();
181 res.category_index = category.toUInt();
182 res.result_type = result_type.toUInt();
183 res.mimetype = mimetype.toString().toStdString();
184 res.name = title.toString().toStdString();
185 res.comment = comment.toString().toStdString();
186 res.dnd_uri = dnd_uri.toString().toStdString();
187 res.hints = convertToHintsMap(metadata);
192// FIXME: Change to use row index.
193void Scope::activate(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
194 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
195 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
197 auto res = createLocalResult(uri, icon_hint, category, result_type, mimetype, title, comment, dnd_uri, metadata);
198 m_unityScope->Activate(res);
201// FIXME: Change to use row index.
202void Scope::preview(const QVariant &uri, const QVariant &icon_hint, const QVariant &category,
203 const QVariant &result_type, const QVariant &mimetype, const QVariant &title,
204 const QVariant &comment, const QVariant &dnd_uri, const QVariant &metadata)
206 QVariant real_metadata(metadata);
207 // handle overridden results, since QML doesn't support defining maps
208 if (metadata.type() == QVariant::String) {
209 QString metadata_uri(metadata.toString());
210 if (metadata_uri.startsWith(QLatin1String("subscope:"))) {
211 QString subscope_uri(metadata_uri.mid(9));
212 QStringList parts(subscope_uri.split(QChar('/')));
213 QVariantHash new_metadata;
214 for (int i = parts.size() - 1; i >= 0; i--) {
216 inner["scope-id"] = QVariant::fromValue(parts[i]);
217 inner["content"] = QVariant::fromValue(new_metadata);
218 new_metadata = inner;
220 real_metadata = QVariant::fromValue(new_metadata);
223 auto res = createLocalResult(uri, icon_hint, category, result_type, mimetype, title, comment, dnd_uri, real_metadata);
224 m_previewCancellable.Renew();
226 // canned queries don't have previews, must be activated
227 if (uri.toString().startsWith("x-unity-no-preview-scopes-query://")) {
228 m_unityScope->Activate(res);
230 m_unityScope->Preview(res, nullptr, m_previewCancellable);
234void Scope::cancelActivation()
236 m_previewCancellable.Cancel();
239void Scope::onActivated(unity::dash::LocalResult const& result, unity::dash::ScopeHandledType type, unity::glib::HintsMap const& hints)
241 // note: we will not get called on SHOW_PREVIEW, instead UnityCore will signal preview_ready.
244 case unity::dash::NOT_HANDLED:
245 fallbackActivate(QString::fromStdString(result.uri));
247 case unity::dash::SHOW_DASH:
250 case unity::dash::HIDE_DASH:
253 case unity::dash::GOTO_DASH_URI:
254 if (hints.find("goto-uri") != hints.end()) {
255 Q_EMIT gotoUri(QString::fromStdString(g_variant_get_string(hints.at("goto-uri"), nullptr)));
257 qWarning() << "Missing goto-uri hint for GOTO_DASH_URI activation reply";
260 case unity::dash::PERFORM_SEARCH:
261 if (hints.find("query") != hints.end()) {
262 // note: this will emit searchQueryChanged, and shell will call setSearchQuery back with a new query,
263 // but it will get ignored.
264 setSearchQuery(QString::fromStdString(g_variant_get_string(hints.at("query"), nullptr)));
268 qWarning() << "Unhandled activation response:" << type;
272void Scope::onPreviewReady(unity::dash::LocalResult const& /* result */, unity::dash::Preview::Ptr const& preview)
274 auto prv = Preview::newFromUnityPreview(preview);
275 // is this the best solution? QML may need to keep more than one preview instance around, so we can't own it.
276 // passing it by value is not possible.
277 QQmlEngine::setObjectOwnership(prv, QQmlEngine::JavaScriptOwnership);
278 Q_EMIT previewReady(prv);
281void Scope::fallbackActivate(const QString& uri)
283 /* Tries various methods to trigger a sensible action for the given 'uri'.
284 If it has no understanding of the given scheme it falls back on asking
288 if (url.scheme() == "file") {
289 /* Override the files place's default URI handler: we want the file
290 manager to handle opening folders, not the dash.
292 Ref: https://bugs.launchpad.net/upicek/+bug/689667
294 QDesktopServices::openUrl(url);
297 if (url.scheme() == "application") {
298 // get the full path to the desktop file
299 QString path(url.path().isEmpty() ? url.authority() : url.path());
300 if (path.startsWith("/")) {
301 Q_EMIT activateApplication(path);
303 // TODO: use the new desktop file finder/parser when it's ready
304 gchar* full_path = nullptr;
305 GKeyFile* key_file = g_key_file_new();
306 QString apps_path = "applications/" + path;
307 if (g_key_file_load_from_data_dirs(key_file, apps_path.toLocal8Bit().constData(), &full_path,
308 G_KEY_FILE_NONE, nullptr)) {
310 Q_EMIT activateApplication(path);
312 qWarning() << "Unable to activate " << path;
314 g_key_file_free(key_file);
320 qDebug() << "Trying to open" << uri;
323 QDesktopServices::openUrl(uri); //url?
326void Scope::setUnityScope(const unity::dash::Scope::Ptr& scope)
328 m_unityScope = scope;
330 m_categories->setUnityScope(m_unityScope);
331 m_filters.reset(new Filters(m_unityScope->filters, this));
333 m_unityScope->form_factor = m_formFactor.toStdString();
334 /* Property change signals */
335 m_unityScope->id.changed.connect(sigc::mem_fun(this, &Scope::idChanged));
336 m_unityScope->name.changed.connect(sigc::mem_fun(this, &Scope::nameChanged));
337 m_unityScope->icon_hint.changed.connect(sigc::mem_fun(this, &Scope::iconHintChanged));
338 m_unityScope->description.changed.connect(sigc::mem_fun(this, &Scope::descriptionChanged));
339 m_unityScope->search_hint.changed.connect(sigc::mem_fun(this, &Scope::searchHintChanged));
340 m_unityScope->visible.changed.connect(sigc::mem_fun(this, &Scope::visibleChanged));
341 m_unityScope->shortcut.changed.connect(sigc::mem_fun(this, &Scope::shortcutChanged));
342 m_unityScope->connected.changed.connect(sigc::mem_fun(this, &Scope::connectedChanged));
343 m_unityScope->results_dirty.changed.connect(sigc::mem_fun(this, &Scope::resultsDirtyToggled));
345 /* FIXME: signal should be forwarded instead of calling the handler directly */
346 m_unityScope->activated.connect(sigc::mem_fun(this, &Scope::onActivated));
348 m_unityScope->preview_ready.connect(sigc::mem_fun(this, &Scope::onPreviewReady));
350 /* Synchronize local states with m_unityScope right now and whenever
351 m_unityScope becomes connected */
352 /* FIXME: should emit change notification signals for all properties */
353 connect(this, SIGNAL(connectedChanged(bool)), SLOT(synchronizeStates()));
357unity::dash::Scope::Ptr Scope::unityScope() const
362void Scope::synchronizeStates()
364 using namespace std::placeholders;
367 /* Forward local states to m_unityScope */
368 if (!m_searchQuery.isNull()) {
369 m_cancellable.Renew();
370 m_unityScope->Search(m_searchQuery.toStdString(), std::bind(&Scope::onSearchFinished, this, _1, _2, _3), m_cancellable);
371 if (!m_searchInProgress) {
372 m_searchInProgress = true;
373 Q_EMIT searchInProgressChanged();
379void Scope::scopeIsActiveChanged()
381 if (!isActive() || !m_unityScope || !m_unityScope->results_dirty()) return;
387void Scope::resultsDirtyToggled(bool results_dirty)
389 if (!results_dirty || !isActive()) return;
395void Scope::onSearchFinished(std::string const& /* query */, unity::glib::HintsMap const& hints, unity::glib::Error const& err)
399 GError* error = const_cast<unity::glib::Error&>(err);
401 if (!err || !g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
402 if (m_searchInProgress) {
403 m_searchInProgress = false;
404 Q_EMIT searchInProgressChanged();
407 // no need to check the results hint, we're still searching
411 if (!m_unityScope->results()->count()) {
412 unity::glib::HintsMap::const_iterator it = hints.find("no-results-hint");
413 if (it != hints.end()) {
414 hint = QString::fromStdString(it->second.GetString());
416 hint = QString::fromUtf8(dgettext("unity", "Sorry, there is nothing that matches your search."));
420 setNoResultsHint(hint);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list