Unity 8
 All Classes Functions Properties
rootactionstate.cpp
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  connect(m_menu, SIGNAL(destroyed()), SLOT(reset()));
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::reset()
96 {
97  m_cachedState.clear();
98  m_menu = NULL;
99 
100  Q_EMIT menuChanged();
101  Q_EMIT updated();
102 }
103 
104 void RootActionState::updateActionState()
105 {
106  if (m_menu && m_menu->rowCount() > 0) {
107  ActionStateParser* oldParser = m_menu->actionStateParser();
108  m_menu->setActionStateParser(this);
109 
110  m_cachedState = m_menu->get(0, "actionState").toMap();
111 
112  m_menu->setActionStateParser(oldParser);
113  } else {
114  m_cachedState.clear();
115  }
116  Q_EMIT updated();
117 }
118 
119 bool RootActionState::isValid() const
120 {
121  return m_menu && m_menu->rowCount() > 0;
122 }
123 
124 QString RootActionState::title() const
125 {
126  if (!isValid()) return QString();
127 
128  return m_cachedState.value("title", QVariant::fromValue(QString())).toString();
129 }
130 
131 QString RootActionState::leftLabel() const
132 {
133  if (!isValid()) return QString();
134 
135  return m_cachedState.value("pre-label", QVariant::fromValue(QString())).toString();
136 }
137 
138 QString RootActionState::rightLabel() const
139 {
140  if (!isValid()) return QString();
141 
142  return m_cachedState.value("label", QVariant::fromValue(QString())).toString();
143 }
144 
145 QStringList RootActionState::icons() const
146 {
147  if (!isValid()) return QStringList();
148 
149  return m_cachedState.value("icons", QVariant::fromValue(QStringList())).toStringList();
150 }
151 
152 QString RootActionState::accessibleName() const
153 {
154  if (!isValid()) return QString();
155 
156  return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157 }
158 
159 bool RootActionState::isVisible() const
160 {
161  if (!isValid()) return false;
162 
163  return m_cachedState.value("visible", QVariant::fromValue(true)).toBool();
164 }
165 
166 static QString iconUri(GIcon *icon)
167 {
168  QString uri;
169 
170  if (G_IS_THEMED_ICON (icon)) {
171  const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon));
172 
173  QStringList iconNameList;
174  for (uint index = 0; iconNames[index] != NULL; index++) {
175  iconNameList << iconNames[index];
176  }
177 
178  if (!iconNameList.empty()) {
179  uri = QString("image://theme/%1").arg(iconNameList.join(","));
180  }
181  }
182  else if (G_IS_FILE_ICON (icon)) {
183  GFile *file;
184 
185  file = g_file_icon_get_file (G_FILE_ICON (icon));
186  if (g_file_is_native (file)) {
187  gchar *fileuri;
188 
189  fileuri = g_file_get_path (file);
190  uri = QString(fileuri);
191 
192  g_free (fileuri);
193  }
194  }
195  else if (G_IS_BYTES_ICON (icon)) {
196  gsize size;
197  gconstpointer data;
198  gchar *base64;
199 
200  data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size);
201  base64 = g_base64_encode ((const guchar *) data, size);
202 
203  uri = QString("data://");
204  uri.append (base64);
205 
206  g_free (base64);
207  }
208 
209  return uri;
210 }
211 
212 QVariant RootActionState::toQVariant(GVariant* state) const
213 {
214  if (!state) {
215  return QVariant();
216  }
217 
218  if (g_variant_is_of_type(state, G_VARIANT_TYPE_VARDICT)) {
219  GVariantIter iter;
220  GVariant *vvalue;
221  gchar *key;
222  QVariantMap qmap;
223 
224  g_variant_iter_init (&iter, state);
225  while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue))
226  {
227  QString str = QString::fromUtf8(key);
228  if (str == "icon" && !qmap.contains("icons")) {
229  QStringList icons;
230 
231  // FIXME - should be sending a url.
232  GIcon *gicon = g_icon_deserialize (vvalue);
233  if (gicon) {
234  icons << iconUri(gicon);
235  g_object_unref (gicon);
236  }
237  qmap.insert("icons", icons);
238 
239  } else if (str == "icons") {
240 
241  QStringList icons;
242 
243  if (g_variant_type_is_array(g_variant_get_type(vvalue))) {
244 
245 
246  for (int i = 0, iMax = g_variant_n_children(vvalue); i < iMax; i++) {
247  GVariant *child = g_variant_get_child_value(vvalue, i);
248 
249  // FIXME - should be sending a url.
250  GIcon *gicon = g_icon_deserialize (child);
251  if (gicon) {
252  icons << iconUri(gicon);
253  g_object_unref (gicon);
254  }
255  g_variant_unref(child);
256  }
257  }
258  // will overwrite icon.
259  qmap.insert("icons", icons);
260 
261  } else {
262  qmap.insert(str, ActionStateParser::toQVariant(vvalue));
263  }
264  }
265 
266  return QVariant::fromValue(qmap);
267 
268  } else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)"))) {
269  QVariantMap qmap;
270 
271  char* label;
272  char* icon;
273  char* accessible_name;
274  gboolean visible;
275  GIcon *gicon;
276 
277  g_variant_get(state, "(sssb)", &label,
278  &icon,
279  &accessible_name,
280  &visible);
281 
282  qmap["label"] = label ? QString::fromUtf8(label) : "";
283  qmap["accessible-desc"] = accessible_name ? QString::fromUtf8(accessible_name) : "";
284  qmap["visible"] = visible;
285 
286  gicon = g_icon_new_for_string (icon, NULL);
287  if (gicon) {
288  qmap["icons"] = QStringList() << iconUri(gicon);
289  g_object_unref (gicon);
290  }
291 
292  if (label) g_free(label);
293  if (icon) g_free(icon);
294  if (accessible_name) g_free(accessible_name);
295 
296  return QVariant::fromValue(qmap);
297  }
298  return ActionStateParser::toQVariant(state);
299 }