on 38f3ead help sign in
unity8/plugins/Unity/Indicators/rootactionstate.cpp
300 lines 8.4 kB history raw
1/*
2 * Copyright 2013 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authors:
17 * Nick Dedekind <nick.dedekind@canonical.com>
18 */
20#include "rootactionstate.h"
21#include "indicators.h"
23#include <unitymenumodel.h>
24#include <QVariant>
25#include <QIcon>
27extern "C" {
28#include <glib.h>
29#include <gio/gio.h>
32RootActionState::RootActionState(QObject *parent)
33 : ActionStateParser(parent),
34 m_menu(nullptr)
38RootActionState::~RootActionState()
42UnityMenuModel* RootActionState::menu() const
44 return m_menu;
47void RootActionState::setMenu(UnityMenuModel* menu)
49 if (m_menu != menu) {
50 if (m_menu) {
51 m_menu->disconnect(this);
52 }
53 m_menu = menu;
55 if (m_menu) {
56 connect(m_menu, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelRowsAdded(const QModelIndex&, int, int)));
57 connect(m_menu, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelRowsRemoved(const QModelIndex&, int, int)));
58 connect(m_menu, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)), SLOT(onModelDataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
60 connect(m_menu, SIGNAL(destroyed()), SLOT(reset()));
61 }
62 updateActionState();
63 Q_EMIT menuChanged();
64 }
67void RootActionState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
69 Q_UNUSED(parent);
70 if (start == 0 && end >= 0) {
71 updateActionState();
72 }
75void RootActionState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
77 Q_UNUSED(parent);
78 if (start == 0 && end >= 0) {
79 updateActionState();
80 }
83void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
85 Q_UNUSED(roles);
86 if (!topLeft.isValid() || !bottomRight.isValid()) {
87 return;
88 }
90 if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
91 updateActionState();
92 }
95void RootActionState::reset()
97 m_cachedState.clear();
98 m_menu = nullptr;
100 Q_EMIT menuChanged();
101 Q_EMIT updated();
104void RootActionState::updateActionState()
106 if (m_menu && m_menu->rowCount() > 0) {
107 ActionStateParser* oldParser = m_menu->actionStateParser();
108 m_menu->setActionStateParser(this);
110 m_cachedState = m_menu->get(0, "actionState").toMap();
112 m_menu->setActionStateParser(oldParser);
113 } else if (!m_menu) {
114 m_cachedState.clear();
115 }
116 // else if m_menu->rowCount() == 0, let's leave existing cache in place
117 // until the new menu comes in, to avoid flashing the UI empty for a moment
118 Q_EMIT updated();
121bool RootActionState::isValid() const
123 return !m_cachedState.empty();
126QString RootActionState::title() const
128 if (!isValid()) return QString();
130 return m_cachedState.value("title", QVariant::fromValue(QString())).toString();
133QString RootActionState::leftLabel() const
135 if (!isValid()) return QString();
137 return m_cachedState.value("pre-label", QVariant::fromValue(QString())).toString();
140QString RootActionState::rightLabel() const
142 if (!isValid()) return QString();
144 return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
147QStringList RootActionState::icons() const
149 if (!isValid()) return QStringList();
151 return m_cachedState.value("icons", QVariant::fromValue(QStringList())).toStringList();
154QString RootActionState::accessibleName() const
156 if (!isValid()) return QString();
158 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
161bool RootActionState::isVisible() const
163 if (!isValid()) return false;
165 return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
168static QString iconUri(GIcon *icon)
170 QString uri;
172 if (G_IS_THEMED_ICON (icon)) {
173 const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
175 QStringList iconNameList;
176 for (uint index = 0; iconNames[index] != nullptr; index++) {
177 iconNameList << iconNames[index];
178 }
180 if (!iconNameList.empty()) {
181 uri = QString("image://theme/%1").arg(iconNameList.join(","));
182 }
183 }
184 else if (G_IS_FILE_ICON (icon)) {
185 GFile *file;
187 file = g_file_icon_get_file (G_FILE_ICON (icon));
188 if (g_file_is_native (file)) {
189 gchar *fileuri;
191 fileuri = g_file_get_path (file);
192 uri = QString(fileuri);
194 g_free (fileuri);
195 }
196 }
197 else if (G_IS_BYTES_ICON (icon)) {
198 gsize size;
199 gconstpointer data;
200 gchar *base64;
202 data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size);
203 base64 = g_base64_encode ((const guchar *) data, size);
205 uri = QString("data://");
206 uri.append (base64);
208 g_free (base64);
209 }
211 return uri;
214QVariant RootActionState::toQVariant(GVariant* state) const
216 if (!state) {
217 return QVariant();
218 }
220 if (g_variant_is_of_type(state, G_VARIANT_TYPE_VARDICT)) {
221 GVariantIter iter;
222 GVariant *vvalue;
223 gchar *key;
224 QVariantMap qmap;
226 g_variant_iter_init (&iter, state);
227 while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
228 {
229 QString str = QString::fromUtf8(key);
230 if (str == "icon" && !qmap.contains("icons")) {
231 QStringList icons;
233 // FIXME - should be sending a url.
234 GIcon *gicon = g_icon_deserialize (vvalue);
235 if (gicon) {
236 icons << iconUri(gicon);
237 g_object_unref (gicon);
238 }
239 qmap.insert("icons", icons);
241 } else if (str == "icons") {
243 QStringList icons;
245 if (g_variant_is_of_type(vvalue, G_VARIANT_TYPE("av"))) {
246 GVariantIter iter;
247 GVariant *val = 0;
248 g_variant_iter_init (&iter, vvalue);
249 while (g_variant_iter_loop (&iter, "v", &val))
250 {
251 // FIXME - should be sending a url.
252 GIcon *gicon = g_icon_deserialize (val);
253 if (gicon) {
254 icons << iconUri(gicon);
255 g_object_unref (gicon);
256 }
257 }
258 }
259 // will overwrite icon.
260 qmap.insert("icons", icons);
262 } else {
263 qmap.insert(str, ActionStateParser::toQVariant(vvalue));
264 }
265 }
267 return QVariant::fromValue(qmap);
269 } else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) {
270 QVariantMap qmap;
272 char* label;
273 char* icon;
274 char* accessible_name;
275 gboolean visible;
276 GIcon *gicon;
278 g_variant_get(state, "(sssb)", &label,
279 &icon,
280 &accessible_name,
281 &visible);
283 qmap["label"] = label ? QString::fromUtf8(label) : "";
284 qmap["accessible-desc"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
285 qmap["visible"] = visible;
287 gicon = g_icon_new_for_string (icon, nullptr);
288 if (gicon) {
289 qmap["icons"] = QStringList() << iconUri(gicon);
290 g_object_unref (gicon);
291 }
293 if (label) g_free(label);
294 if (icon) g_free(icon);
295 if (accessible_name) g_free(accessible_name);
297 return QVariant::fromValue(qmap);
298 }
299 return ActionStateParser::toQVariant(state);