2
* This file is part of unity-2d
4
* Copyright 2011 Canonical Ltd.
7
* - Aurélien Gâteau <aurelien.gateau@canonical.com>
9
* This program is free software; you can redistribute it and/or modify
10
* it under the terms of the GNU General Public License as published by
11
* the Free Software Foundation; version 3.
13
* This program is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include "indicatorsmanager.h"
26
#include <indicatorentrywidget.h>
29
#include <QApplication>
36
using namespace unity::indicator;
38
IndicatorsManager::IndicatorsManager(QObject* parent)
40
, m_indicators(new DBusIndicators)
41
, m_geometrySyncTimer(new QTimer(this))
42
, m_mouseTrackerTimer(new QTimer(this))
44
m_geometrySyncTimer->setInterval(0);
45
m_geometrySyncTimer->setSingleShot(true);
46
connect(m_geometrySyncTimer, SIGNAL(timeout()), SLOT(syncGeometries()));
48
// m_mouseTrackerTimer is inspired from
49
// plugins/unityshell/src/PanelView.cpp in OnEntryActivated()
51
// Rationale copied from Unity source code:
53
// Track menus being scrubbed at 60Hz (about every 16 millisec)
54
// It might sound ugly, but it's far nicer (and more responsive) than the
55
// code it replaces which used to capture motion events in another process
56
// (unity-panel-service) and send them to us over dbus.
57
// NOTE: The reason why we have to use a timer instead of tracking motion
58
// events is because the motion events will never be delivered to this
59
// process. All the motion events will go to unity-panel-service while
60
// scrubbing because the active panel menu has (needs) the pointer grab.
62
m_mouseTrackerTimer->setInterval(16);
63
m_mouseTrackerTimer->setSingleShot(false);
64
connect(m_mouseTrackerTimer, SIGNAL(timeout()), SLOT(checkMousePosition()));
66
m_indicators->on_entry_show_menu.connect(
67
sigc::mem_fun(this, &IndicatorsManager::onEntryShowMenu)
70
m_indicators->on_entry_activate_request.connect(
71
sigc::mem_fun(this, &IndicatorsManager::onEntryActivateRequest)
74
m_indicators->on_entry_activated.connect(
75
sigc::mem_fun(this, &IndicatorsManager::onEntryActivated)
78
m_indicators->on_synced.connect(
79
sigc::mem_fun(this, &IndicatorsManager::onSynced)
83
unity::indicator::DBusIndicators::Ptr IndicatorsManager::indicators() const
88
void IndicatorsManager::onEntryShowMenu(const std::string& /*entryId*/, int posX, int posY, int /*timestamp*/, int /*button*/)
90
// Copied from plugins/unityshell/src/PanelView.cpp, in OnEntryShowMenu()
91
// Without this code, menus cannot be shown from mousePressEvent() (but can
92
// be shown from mouseReleaseEvent())
95
On button down, X automatically gives Qt a passive grab on the mouse this
96
means that, if the panel service tries to grab the pointer to show the menu
97
(gtk does this automatically), it fails and the menu can't show.
98
We connect to the on_entry_show_menu signal, which is emitted before
99
DBusIndicators does anything else, and just break the grab.
101
Display* display = QX11Info::display();
102
XUngrabPointer(display, CurrentTime);
105
XButtonEvent event = {
120
qApp->x11ProcessEvent(reinterpret_cast<XEvent*>(&event));
123
void IndicatorsManager::checkMousePosition()
125
// Called by m_mouseTrackerTimer to implement mouse scrubbing
126
// (Assuming item A menu is opened, move mouse over item B => item B menu opens)
127
// Also, delivers motion events to Qt, which will generate correct
128
// enter/leave events for IndicatorEntry widgets.
129
QPoint pos = QCursor::pos();
130
QWidget* widget = QApplication::widgetAt(pos);
131
Display* display = QX11Info::display();
133
QPoint relPos = widget != 0 ? widget->mapFromGlobal(pos) : pos;
134
XMotionEvent event = {
139
widget != 0 ? widget->effectiveWinId() : 0,
140
widget != 0 ? RootWindow(display, widget->x11Info().screen()) : 0,
144
relPos.x(), relPos.y(),
149
qApp->x11ProcessEvent(reinterpret_cast<XEvent*>(&event));
151
IndicatorEntryWidget* entryWidget = qobject_cast<IndicatorEntryWidget*>(widget);
155
entryWidget->showMenu(Qt::NoButton);
158
void IndicatorsManager::onEntryActivateRequest(const std::string& entryId)
160
if (entryId.empty()) {
163
IndicatorEntryWidget* widget = 0;
164
Q_FOREACH(widget, m_widgetList) {
165
if (widget->entry()->id() == entryId) {
170
UQ_WARNING << "Could not find a widget for IndicatorEntry with id" << QString::fromStdString(entryId);
173
widget->showMenu(Qt::NoButton);
176
void IndicatorsManager::onEntryActivated(const std::string& entryId)
178
if (entryId.empty()) {
179
m_mouseTrackerTimer->stop();
181
m_mouseTrackerTimer->start();
185
void IndicatorsManager::onSynced()
187
QMetaObject::invokeMethod(m_geometrySyncTimer, "start", Qt::QueuedConnection);
190
void IndicatorsManager::addIndicatorEntryWidget(IndicatorEntryWidget* widget)
192
m_widgetList.append(widget);
193
widget->installEventFilter(this);
196
bool IndicatorsManager::eventFilter(QObject*, QEvent* event)
198
switch (event->type()) {
203
m_geometrySyncTimer->start();
211
void IndicatorsManager::syncGeometries()
213
EntryLocationMap locations;
214
Q_FOREACH(IndicatorEntryWidget* widget, m_widgetList) {
215
if (!widget->isVisible()) {
218
Entry::Ptr entry = widget->entry();
219
if (entry->IsUnused()) {
222
QPoint topLeft = widget->mapToGlobal(QPoint(0, 0));
223
nux::Rect rect(topLeft.x(), topLeft.y(), widget->width(), widget->height());
224
locations[widget->entry()->id()] = rect;
226
m_indicators->SyncGeometries("Panel", locations);
229
#include "indicatorsmanager.moc"