@@ -0,0 +1,249 @@
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+ */
19+
20+#include "rootactionstate.h"
21+#include "indicators.h"
22+
23+#include <unitymenumodel.h>
24+#include <QVariant>
25+#include <QIcon>
26+
27+extern "C" {
28+#include <glib.h>
29+#include <gio/gio.h>
30+}
31+
32+RootActionState::RootActionState(QObject *parent)
33+ : ActionStateParser(parent),
34+ m_menu(NULL)
35+{
36+}
37+
38+RootActionState::~RootActionState()
39+{
40+}
41+
42+UnityMenuModel* RootActionState::menu() const
43+{
44+ return m_menu;
45+}
46+
47+void RootActionState::setMenu(UnityMenuModel* menu)
48+{
49+ if (m_menu != menu) {
50+ if (m_menu) {
51+ m_menu->disconnect(this);
52+ }
53+ m_menu = menu;
54+
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>&)));
59+
60+ m_menu->setActionStateParser(this);
61+ }
62+ updateActionState();
63+ Q_EMIT menuChanged();
64+ }
65+}
66+
67+void RootActionState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
68+{
69+ Q_UNUSED(parent);
70+ if (start == 0 && end >= 0) {
71+ updateActionState();
72+ }
73+}
74+
75+void RootActionState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
76+{
77+ Q_UNUSED(parent);
78+ if (start == 0 && end >= 0) {
79+ updateActionState();
80+ }
81+}
82+
83+void RootActionState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
84+{
85+ Q_UNUSED(roles);
86+ if (!topLeft.isValid() || !bottomRight.isValid()) {
87+ return;
88+ }
89+
90+ if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
91+ updateActionState();
92+ }
93+}
94+
95+void RootActionState::updateActionState()
96+{
97+ if (m_menu && m_menu->rowCount() > 0) {
98+ m_cachedState = m_menu->get(0, "actionState").toMap();
99+ } else {
100+ m_cachedState.clear();
101+ }
102+ Q_EMIT updated();
103+}
104+
105+
106+bool RootActionState::isValid() const
107+{
108+ return m_menu && m_menu->rowCount() > 0;
109+}
110+
111+QString RootActionState::label() const
112+{
113+ if (!isValid()) return QString();
114+
115+ return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
116+}
117+
118+QString RootActionState::icon() const
119+{
120+ if (!isValid()) return QString();
121+
122+ return m_cachedState.value("icon", QVariant::fromValue(QString())).toString();
123+}
124+
125+QString RootActionState::accessibleName() const
126+{
127+ if (!isValid()) return QString();
128+
129+ return m_cachedState.value("accessible-name", QVariant::fromValue(QString())).toString();
130+}
131+
132+bool RootActionState::isVisible() const
133+{
134+ if (!isValid()) return false;
135+
136+ return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
137+}
138+
139+static QString iconUri(GIcon *icon)
140+{
141+ QString uri;
142+
143+ if (G_IS_THEMED_ICON (icon)) {
144+
145+ const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
146+ guint index = 0;
147+ while(iconNames[index] != NULL) {
148+ if (QIcon::hasThemeIcon(iconNames[index])) {
149+ uri = QString("image://theme/%1").arg(iconNames[index]);
150+ break;
151+ }
152+ index++;
153+ }
154+ }
155+ else if (G_IS_FILE_ICON (icon)) {
156+ GFile *file;
157+
158+ file = g_file_icon_get_file (G_FILE_ICON (icon));
159+ if (g_file_is_native (file)) {
160+ gchar *fileuri;
161+
162+ fileuri = g_file_get_path (file);
163+ uri = QString(fileuri);
164+
165+ g_free (fileuri);
166+ }
167+ }
168+ else if (G_IS_BYTES_ICON (icon)) {
169+ gsize size;
170+ gconstpointer data;
171+ gchar *base64;
172+
173+ data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size);
174+ base64 = g_base64_encode ((const guchar *) data, size);
175+
176+ uri = QString("data://");
177+ uri.append (base64);
178+
179+ g_free (base64);
180+ }
181+
182+ return uri;
183+}
184+
185+QVariant RootActionState::toQVariant(GVariant* state) const
186+{
187+ if (!state) {
188+ return QVariant();
189+ }
190+
191+ if (g_variant_is_of_type(state, G_VARIANT_TYPE_VARDICT)) {
192+ GVariantIter iter;
193+ GVariant *vvalue;
194+ gchar *key;
195+ QVariantMap qmap;
196+
197+ g_variant_iter_init (&iter, state);
198+ while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
199+ {
200+ QString str = QString::fromUtf8(key);
201+ if (str == "icon") {
202+ // FIXME - should be sending a url.
203+ GIcon *icon = g_icon_deserialize (vvalue);
204+ if (icon) {
205+ qmap.insert(str, iconUri(icon));
206+ g_object_unref (icon);
207+ }
208+ else {
209+ qmap.insert(str, QVariant(""));
210+ }
211+ } else {
212+ qmap.insert(str, ActionStateParser::toQVariant(vvalue));
213+ }
214+ }
215+
216+ return QVariant::fromValue(qmap);
217+
218+ } else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) {
219+ QVariantMap qmap;
220+
221+ char* label;
222+ char* icon;
223+ char* accessible_name;
224+ gboolean visible;
225+ GIcon *gicon;
226+
227+ g_variant_get(state, "(sssb)", &label,
228+ &icon,
229+ &accessible_name,
230+ &visible);
231+
232+ qmap["label"] = label ? QString::fromUtf8(label) : "";
233+ qmap["accessible-name"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
234+ qmap["visible"] = visible;
235+
236+ gicon = g_icon_new_for_string (icon, NULL);
237+ if (gicon) {
238+ qmap["icon"] = iconUri(gicon);
239+ g_object_unref (gicon);
240+ }
241+
242+ if (label) g_free(label);
243+ if (icon) g_free(icon);
244+ if (accessible_name) g_free(accessible_name);
245+
246+ return QVariant::fromValue(qmap);
247+ }
248+ return ActionStateParser::toQVariant(state);
249+}