7#include "dbusmenuexporter.h"
10#include <QActionGroup>
18#include <QWidgetAction>
21#include "dbusmenu_p.h"
22#include "dbusmenuexporterdbus_p.h"
23#include "dbusmenuexporterprivate_p.h"
24#include "dbusmenushortcut_p.h"
25#include "dbusmenutypes_p.h"
29static const char *KMENU_TITLE =
"kmenu_title";
36int DBusMenuExporterPrivate::idForAction(
QAction *action)
const
38 DMRETURN_VALUE_IF_FAIL(action, -1);
39 return m_idForAction.value(action, -2);
42void DBusMenuExporterPrivate::addMenu(
QMenu *menu,
int parentId)
49 new DBusMenu(menu, q, parentId);
50 const auto actions = menu->
actions();
51 for (
QAction *action : actions) {
52 addAction(action, parentId);
56QVariantMap DBusMenuExporterPrivate::propertiesForAction(
QAction *action)
const
58 DMRETURN_VALUE_IF_FAIL(action, QVariantMap());
62 return propertiesForKMenuTitleAction(action);
64 return propertiesForSeparatorAction(action);
66 return propertiesForStandardAction(action);
70QVariantMap DBusMenuExporterPrivate::propertiesForKMenuTitleAction(
QAction *action_)
const
74 map.insert(QStringLiteral(
"enabled"),
false);
75 map.insert(QStringLiteral(
"x-kde-title"),
true);
77 const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction *>(action_);
78 DMRETURN_VALUE_IF_FAIL(widgetAction, map);
80 DMRETURN_VALUE_IF_FAIL(button, map);
82 DMRETURN_VALUE_IF_FAIL(action, map);
85 insertIconProperty(&map, action);
87 map.insert(QStringLiteral(
"visible"),
false);
92QVariantMap DBusMenuExporterPrivate::propertiesForSeparatorAction(
QAction *action)
const
95 map.insert(QStringLiteral(
"type"), QStringLiteral(
"separator"));
97 map.insert(QStringLiteral(
"visible"),
false);
102QVariantMap DBusMenuExporterPrivate::propertiesForStandardAction(
QAction *action)
const
107 map.insert(QStringLiteral(
"enabled"),
false);
110 map.insert(QStringLiteral(
"visible"),
false);
112 if (action->
menu()) {
113 map.insert(QStringLiteral(
"children-display"), QStringLiteral(
"submenu"));
117 map.insert(QStringLiteral(
"toggle-type"), exclusive ? QStringLiteral(
"radio") : QStringLiteral(
"checkmark"));
118 map.insert(QStringLiteral(
"toggle-state"), action->
isChecked() ? 1 : 0);
120 insertIconProperty(&map, action);
123 DBusMenuShortcut
shortcut = DBusMenuShortcut::fromKeySequence(keySequence);
129QMenu *DBusMenuExporterPrivate::menuForId(
int id)
const
134 QAction *action = m_actionForId.value(
id);
138 return action ? action->
menu() :
nullptr;
141void DBusMenuExporterPrivate::fillLayoutItem(DBusMenuLayoutItem *item,
QMenu *menu,
int id,
int depth,
const QStringList &propertyNames)
144 item->properties = m_dbusObject->getProperties(
id, propertyNames);
146 if (depth != 0 && menu) {
147 const auto actions = menu->
actions();
148 for (
QAction *action : actions) {
149 int actionId = m_idForAction.value(action, -1);
150 if (actionId == -1) {
151 DMWARNING <<
"No id for action";
155 DBusMenuLayoutItem child;
156 fillLayoutItem(&child, action->
menu(), actionId, depth - 1, propertyNames);
157 item->children << child;
162void DBusMenuExporterPrivate::updateAction(
QAction *action)
164 int id = idForAction(action);
165 if (m_itemUpdatedIds.contains(
id)) {
168 m_itemUpdatedIds << id;
169 m_itemUpdatedTimer->start();
172void DBusMenuExporterPrivate::addAction(
QAction *action,
int parentId)
174 int id = m_idForAction.value(action, -1);
176 DMWARNING <<
"Already tracking action" << action->
text() <<
"under id" << id;
179 QVariantMap
map = propertiesForAction(action);
182 m_actionForId.insert(
id, action);
183 m_idForAction.insert(action,
id);
184 m_actionProperties.insert(action, map);
185 if (action->
menu()) {
186 addMenu(action->
menu(),
id);
189 emitLayoutUpdated(parentId);
197void DBusMenuExporterPrivate::removeActionInternal(
QObject *
object)
200 m_actionProperties.remove(action);
201 int id = m_idForAction.take(action);
202 m_actionForId.remove(
id);
205void DBusMenuExporterPrivate::removeAction(
QAction *action,
int parentId)
207 removeActionInternal(action);
210 emitLayoutUpdated(parentId);
213void DBusMenuExporterPrivate::emitLayoutUpdated(
int id)
215 if (m_layoutUpdatedIds.contains(
id)) {
218 m_layoutUpdatedIds << id;
219 m_layoutUpdatedTimer->start();
222void DBusMenuExporterPrivate::insertIconProperty(QVariantMap *map,
QAction *action)
const
225 const QString iconName = q->iconNameForAction(action);
227 map->insert(QStringLiteral(
"icon-name"), iconName);
236 map->insert(QStringLiteral(
"icon-data"), buffer.
data());
240static void collapseSeparator(
QAction *action)
265void DBusMenuExporterPrivate::collapseSeparators(
QMenu *menu)
276 for (; it !=
begin; --it) {
277 if ((*it)->isSeparator()) {
278 collapseSeparator(*it);
288 for (; it !=
end; ++it) {
289 if ((*it)->isSeparator()) {
290 collapseSeparator(*it);
297 bool previousWasSeparator =
false;
298 for (; it !=
end; ++it) {
301 if (previousWasSeparator) {
302 collapseSeparator(action);
304 previousWasSeparator =
true;
307 previousWasSeparator =
false;
319 , d(new DBusMenuExporterPrivate)
322 d->m_objectPath = objectPath;
323 d->m_rootMenu = menu;
326 d->m_emittedLayoutUpdatedOnce =
false;
327 d->m_itemUpdatedTimer =
new QTimer(
this);
328 d->m_layoutUpdatedTimer =
new QTimer(
this);
329 d->m_dbusObject =
new DBusMenuExporterDBus(
this);
331 d->addMenu(d->m_rootMenu, 0);
333 d->m_itemUpdatedTimer->setInterval(0);
334 d->m_itemUpdatedTimer->setSingleShot(
true);
335 connect(d->m_itemUpdatedTimer, SIGNAL(timeout()), SLOT(doUpdateActions()));
337 d->m_layoutUpdatedTimer->setInterval(0);
338 d->m_layoutUpdatedTimer->setSingleShot(
true);
339 connect(d->m_layoutUpdatedTimer, SIGNAL(timeout()), SLOT(doEmitLayoutUpdated()));
345DBusMenuExporter::~DBusMenuExporter()
350void DBusMenuExporter::doUpdateActions()
352 if (d->m_itemUpdatedIds.isEmpty()) {
355 DBusMenuItemList updatedList;
356 DBusMenuItemKeysList removedList;
358 for (
int id : d->m_itemUpdatedIds) {
359 QAction *action = d->m_actionForId.value(
id);
365 QVariantMap &oldProperties = d->m_actionProperties[action];
366 QVariantMap newProperties = d->propertiesForAction(action);
367 QVariantMap updatedProperties;
371 QVariantMap::ConstIterator newEnd = newProperties.
constEnd();
373 QVariantMap::ConstIterator oldIt = oldProperties.constBegin(), oldEnd = oldProperties.constEnd();
374 for (; oldIt != oldEnd; ++oldIt) {
376 QVariantMap::ConstIterator newIt = newProperties.constFind(key);
377 if (newIt != newEnd) {
378 if (newIt.value() != oldIt.value()) {
379 updatedProperties.insert(key, newIt.value());
382 removedProperties << key;
387 QVariantMap::ConstIterator newIt = newProperties.constBegin();
388 for (; newIt != newEnd; ++newIt) {
390 oldIt = oldProperties.constFind(key);
391 if (oldIt == oldEnd) {
392 updatedProperties.insert(key, newIt.value());
397 oldProperties = newProperties;
400 d->addMenu(menu,
id);
403 if (!updatedProperties.isEmpty()) {
406 item.properties = updatedProperties;
409 if (!removedProperties.
isEmpty()) {
410 DBusMenuItemKeys itemKeys;
412 itemKeys.properties = removedProperties;
413 removedList << itemKeys;
416 d->m_itemUpdatedIds.clear();
417 if (!d->m_emittedLayoutUpdatedOnce) {
425 if (!updatedList.isEmpty() || !removedList.isEmpty()) {
426 d->m_dbusObject->ItemsPropertiesUpdated(updatedList, removedList);
430void DBusMenuExporter::doEmitLayoutUpdated()
433 for (
int id : d->m_layoutUpdatedIds) {
434 QMenu *menu = d->menuForId(
id);
436 d->collapseSeparators(menu);
441 if (d->m_emittedLayoutUpdatedOnce) {
442 for (
int id : std::as_const(d->m_layoutUpdatedIds)) {
443 d->m_dbusObject->LayoutUpdated(d->m_revision,
id);
448 d->m_dbusObject->LayoutUpdated(d->m_revision, 0);
449 d->m_emittedLayoutUpdatedOnce =
true;
451 d->m_layoutUpdatedIds.clear();
456 DMRETURN_VALUE_IF_FAIL(action,
QString());
467 int id = d->idForAction(action);
468 DMRETURN_IF_FAIL(
id >= 0);
470 d->m_dbusObject->ItemActivationRequested(
id, timeStamp);
473void DBusMenuExporter::slotActionDestroyed(
QObject *
object)
475 d->removeActionInternal(
object);
480 d->m_dbusObject->setStatus(
status);
485 return d->m_dbusObject->status();
488#include "moc_dbusmenuexporter.cpp"
Q_SCRIPTABLE CaptureState status()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
const QList< QKeySequence > & shortcut(StandardShortcut id)
QActionGroup * actionGroup() const const
bool isCheckable() const const
bool isChecked() const const
bool isEnabled() const const
bool isSeparator() const const
bool isVisible() const const
bool isExclusive() const const
const QByteArray & data() const const
QDateTime currentDateTime()
qint64 toMSecsSinceEpoch() const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QPixmap pixmap(QWindow *window, const QSize &size, Mode mode, State state) const const
bool isNull() const const
QString name() const const
const_iterator constEnd() const const
bool isEmpty() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
T findChild(const QString &name, Qt::FindChildOptions options) const const
bool save(QIODevice *device, const char *format, int quality) const const
QString fromLatin1(QByteArrayView str)
bool isEmpty() const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
void keySequence(QWidget *widget, const QKeySequence &keySequence)
QVariant fromValue(T &&value)