Lomiri
modelactionrootstate.cpp
1 /*
2  * Copyright 2013-2016 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 "modelactionrootstate.h"
21 #include "indicators.h"
22 
23 #include <ayatanamenumodel.h>
24 #include <QVariant>
25 #include <QIcon>
26 
27 extern "C" {
28 #include <glib.h>
29 #include <gio/gio.h>
30 }
31 
32 ModelActionRootState::ModelActionRootState(QObject *parent)
33  : RootStateObject(parent),
34  m_menu(nullptr)
35  , m_reentryGuard(false)
36 {
37 }
38 
39 ModelActionRootState::~ModelActionRootState()
40 {
41 }
42 
43 AyatanaMenuModel* ModelActionRootState::menu() const
44 {
45  return m_menu;
46 }
47 
48 void ModelActionRootState::setMenu(AyatanaMenuModel* menu)
49 {
50  if (m_menu != menu) {
51  bool wasValid = valid();
52 
53  if (m_menu) {
54  m_menu->disconnect(this);
55  }
56  m_menu = menu;
57 
58  if (m_menu) {
59  connect(m_menu, &AyatanaMenuModel::rowsInserted, this, &ModelActionRootState::onModelRowsAdded);
60  connect(m_menu, &AyatanaMenuModel::rowsRemoved, this, &ModelActionRootState::onModelRowsRemoved);
61  connect(m_menu, &AyatanaMenuModel::dataChanged, this, &ModelActionRootState::onModelDataChanged);
62 
63  connect(m_menu, &AyatanaMenuModel::destroyed, this, &ModelActionRootState::reset);
64  }
65  updateActionState();
66  updateOtherActions();
67  Q_EMIT menuChanged();
68 
69  if (wasValid != valid())
70  Q_EMIT validChanged();
71  }
72 }
73 
74 QString ModelActionRootState::secondaryAction() const
75 {
76  return m_secondaryAction;
77 }
78 
79 QString ModelActionRootState::scrollAction() const
80 {
81  return m_scrollAction;
82 }
83 
84 QString ModelActionRootState::submenuAction() const
85 {
86  return m_submenuAction;
87 }
88 
89 bool ModelActionRootState::valid() const
90 {
91  return !currentState().empty();
92 }
93 
94 void ModelActionRootState::onModelRowsAdded(const QModelIndex& parent, int start, int end)
95 {
96  Q_UNUSED(parent);
97  if (start == 0 && end >= 0) {
98  updateActionState();
99  updateOtherActions();
100  }
101 }
102 
103 void ModelActionRootState::onModelRowsRemoved(const QModelIndex& parent, int start, int end)
104 {
105  Q_UNUSED(parent);
106  if (start == 0 && end >= 0) {
107  updateActionState();
108  updateOtherActions();
109  }
110 }
111 
112 void ModelActionRootState::onModelDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector<int>& roles)
113 {
114  Q_UNUSED(roles);
115  if (!topLeft.isValid() || !bottomRight.isValid()) {
116  return;
117  }
118 
119  if (topLeft.row() <= 0 && bottomRight.row() >= 0) {
120  updateActionState();
121  updateOtherActions();
122  }
123 }
124 
125 void ModelActionRootState::reset()
126 {
127  m_menu = nullptr;
128 
129  Q_EMIT menuChanged();
130  setCurrentState(QVariantMap());
131 
132  updateOtherActions();
133 }
134 
135 void ModelActionRootState::updateActionState()
136 {
137  if (m_reentryGuard) return;
138  m_reentryGuard = true;
139 
140  if (m_menu && m_menu->rowCount() > 0) {
141  ActionStateParser* oldParser = m_menu->actionStateParser();
142  m_menu->setActionStateParser(&m_parser);
143 
144  QVariantMap state = m_menu->get(0, "actionState").toMap();
145 
146  m_menu->setActionStateParser(oldParser);
147 
148  setCurrentState(state);
149  } else if (!m_menu) {
150  setCurrentState(QVariantMap());
151  }
152  // else if m_menu->rowCount() == 0, let's leave existing cache in place
153  // until the new menu comes in, to avoid flashing the UI empty for a moment
154 
155  m_reentryGuard = false;
156 }
157 
158 void ModelActionRootState::updateOtherActions()
159 {
160  if (m_reentryGuard) return;
161  m_reentryGuard = true;
162 
163  if (m_menu && m_menu->rowCount() > 0) {
164  QVariantMap map;
165  map[QStringLiteral("submenu-action")] = QStringLiteral("string");
166  map[QStringLiteral("x-ayatana-scroll-action")] = QStringLiteral("string");
167  map[QStringLiteral("x-ayatana-secondary-action")] = QStringLiteral("string");
168  m_menu->loadExtendedAttributes(0, map);
169  QVariantMap extMap = m_menu->get(0, "ext").toMap();
170 
171  QString secondaryAction = extMap.value(QStringLiteral("xAyatanaSecondaryAction")).toString();
172  if (m_secondaryAction != secondaryAction) {
173  m_secondaryAction = secondaryAction;
174  Q_EMIT secondaryActionChanged();
175  }
176 
177  QString scrollAction = extMap.value(QStringLiteral("xAyatanaScrollAction")).toString();
178  if (m_scrollAction != scrollAction) {
179  m_scrollAction = scrollAction;
180  Q_EMIT scrollActionChanged();
181  }
182 
183  QString submenuAction = extMap.value(QStringLiteral("submenuAction")).toString();
184  if (m_submenuAction != submenuAction) {
185  m_submenuAction = submenuAction;
186  Q_EMIT submenuActionChanged();
187  }
188  } else {
189  if (!m_secondaryAction.isEmpty()) {
190  m_secondaryAction.clear();
191  Q_EMIT secondaryActionChanged();
192  }
193  if (!m_scrollAction.isEmpty()) {
194  m_scrollAction.clear();
195  Q_EMIT scrollActionChanged();
196  }
197  if (!m_submenuAction.isEmpty()) {
198  m_submenuAction.clear();
199  Q_EMIT submenuActionChanged();
200  }
201  }
202 
203  m_reentryGuard = false;
204 }