[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-devel
Subject: Re: Bespin GlobalMenu with XBar Development,
From: Thomas =?iso-8859-15?q?L=FCbking?= <thomas.luebking () web ! de>
Date: 2010-08-21 18:47:17
Message-ID: 201008212047.17905.thomas.luebking () web ! de
[Download RAW message or body]
Am Saturday 21 August 2010 schrieb Aaron Lewis:
> Hi Thoams ,
Hi Aaorn ... ;-P
> void GlobalMenu::activeWindowChanged(WId wid)
> {
> if (!mMenu->isHidden()) {
> mMenu->hide();
> }
>
> kDebug() << QString::number(wid,16);
> QString ctxXML = mContextMap[wid];
> if (!ctxXML.isEmpty()) {
> bindGlobalShortcut(ctxXML);
> mXBarIfce->requestFocus(wid);
> } else {
> qDebug() << "Released . ";
> if ( lastWid )
> mXBarIfce->releaseFocus(lastWid);
> }
> }
>
> You compile and run it , qDebug() << "Released" always prints , when
> clicking on blank areas or minimized , but clicking on blank areas just
> don't work .. couldn't figure out.
checked whether lastWid is still correct?
Afaics it's unconditionally set in windowChanged() what can happen quite
often* to every kind of window (and likely even if you just deactivate a
window. Also there's a good chance that you receive a windowChange on
de/activation as well (since this in not the property variant), so you should
rather set
lastWid in activeWindowChanged() and _only_ if !ctxXML.isEmpty()
(assuming this means there's a globalmenu property ont he window - see
attachment. Also WId is iirc unsigned, so "-1" is no good idea, rather use "0"
here)
Thomas
*there's probably space for optimization by connecting the signal that only
fires on property changes and check whether it's actuall _your_ property
before doing anything.
["globalmenu.cpp" (text/x-c++src)]
/* Bespin mac-a-like GlobalMenu KDE4
Copyright (C) 2007 Thomas Luebking <thomas.luebking@web.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <Plasma/Containment>
#include <Plasma/Theme>
#include <KDebug>
#include <KWindowSystem>
#include <KAction>
#include <KSelectAction>
#include <KShortcut>
#include <QApplication>
#include <QAbstractEventDispatcher>
#include <QCursor>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDesktopWidget>
#include <QDomDocument>
#include <QDomElement>
#include <QDomNode>
#include <QGraphicsLinearLayout>
#include <QGraphicsScene>
#include <QGraphicsSceneWheelEvent>
#include <QGraphicsView>
#include <QMessageBox>
#include <QPaintEvent>
#include <QPainter>
#include <QRectF>
#include <QSettings>
#include <QStyle>
#include <QStyleOption>
#include <QTextCodec>
#include <QTimer>
#include <QX11Info>
#include <netwm.h>
#include "globalmenu.h"
#include "xbarclientadaptor.h"
#define MSG(_FNC_) QDBusMessage::createMethodCall( "org.kde.XBar", "/XBar", \
"org.kde.XBar", _FNC_ ) #define XBAR_SEND( _MSG_ ) \
QDBusConnection::sessionBus().send( _MSG_ )
static GlobalMenu *self = 0;
static const QString SERVICE_NAME = "org.kde.XBar-gtk";
static QString windowName(WId wid)
{
NETRootInfo rootInfo(QX11Info::display(), -1);
NETWinInfo winInfo(QX11Info::display(), wid, rootInfo.rootWindow(), -1);
QString name = QString::fromUtf8(winInfo.visibleIconName());
if (name.isEmpty()) {
name = QString::fromUtf8(winInfo.visibleName());
}
if (name.isEmpty()) {
name = QString::fromUtf8(winInfo.name());
}
if (name.isEmpty()) {
name = "unknown";
}
return name;
}
static QStringList topMenuList(const QString &source)
{
QDomDocument doc;
QDomElement docElem;
QDomNode rootNode;
QDomNodeList topNodes;
QString error;
QStringList result;
doc.setContent(source,false,&error);
rootNode = doc.firstChild();
for (QDomElement elem = rootNode.firstChildElement(); !elem.isNull(); elem = \
elem.nextSiblingElement()) { if (elem.attribute("visible") != "0") {
QString result_tmp = elem.attribute("label").replace("_","&");
result << result_tmp;
}
}
return result;
}
GlobalMenu::GlobalMenu(QWidget *parent)
: QWidget(parent),
mMenu(0),
mMapper(new QSignalMapper(this))
{
Q_ASSERT(self == 0);
self = this;
lastWid = 0;
QAbstractEventDispatcher::instance()->setEventFilter(x11EventFilter);
mCtxAtom = XInternAtom(QX11Info::display(),"_NET_GLOBALMENU_MENU_CONTEXT",FALSE);
mEvtAtom = XInternAtom(QX11Info::display(),"_NET_GLOBALMENU_MENU_EVENT",FALSE);
QDBusConnection conn = QDBusConnection::connectToBus(QDBusConnection::SessionBus, \
"globalmenu");
mXBarIfce = new OrgKdeXBarInterface("org.kde.XBar","/XBar",conn);
mMenu = new KMenu(parent);
connect(mMapper,SIGNAL(mapped(const QString &)),this,SLOT(triggered(const QString \
&)));
conn.registerService(SERVICE_NAME);
new XBarClientAdaptor(this);
conn.registerObject("/XBarClient", this);
connect(KWindowSystem::self(),SIGNAL(activeWindowChanged(WId)),
this,SLOT(activeWindowChanged(WId)));
connect(KWindowSystem::self(),SIGNAL(windowChanged(WId)),
this,SLOT(windowChanged(WId)));
connect(KWindowSystem::self(),SIGNAL(windowAdded(WId)),
this,SLOT(windowAdded(WId)));
connect(KWindowSystem::self(),SIGNAL(windowRemoved(WId)),
this,SLOT(windowRemoved(WId)));
}
GlobalMenu::~GlobalMenu()
{
Q_ASSERT(self);
self = 0;
}
void GlobalMenu::activate()
{
// qDebug() << "Activate Now !";
kDebug();
}
void GlobalMenu::deactivate()
{
// qDebug() << "Deactivate Now !";
kDebug();
}
void GlobalMenu::hover(qlonglong key, int idx, int x, int y)
{
if (mMenu->isVisible()) {
popup(key,idx,x,y);
}
}
void GlobalMenu::popDown(qlonglong key)
{
Q_UNUSED(key);
mMenu->hide();
}
void GlobalMenu::popup(qlonglong key, int idx, int x, int y)
{
QDomDocument doc;
QDomElement rootElem;
idx = idx+1;
if (doc.setContent(mContextMap[key],false)) {
for (QDomElement elem = doc.firstChildElement().firstChildElement(); \
!elem.isNull(); elem = elem.nextSiblingElement()) { if (elem.attribute("visible") != \
"0") { idx--;
}
if (idx == 0) {
rootElem = elem;
break;
}
}
//mMenu = new KMenu(rootElem.attribute("label"),0);
mMenu->clear();
mMenu->setWindowFlags(Qt::X11BypassWindowManagerHint);
buildChildMenu(mMenu,rootElem.firstChildElement("menu"),"/"+rootElem.attribute("id"));
mMenu->exec(QPoint(x,y));
}
}
void GlobalMenu::raise(qlonglong key)
{
Q_UNUSED(key);
kDebug();
}
void GlobalMenu::windowAdded(WId wid)
{
kDebug() << QString::number(wid,16);
XSelectInput(QX11Info::display(),wid,PropertyChangeMask);
qApp->syncX();
}
void GlobalMenu::windowRemoved(WId wid)
{
kDebug() << QString::number(wid,16);
// Restore to "plasma" top menu when window closed.
XBAR_SEND( MSG("unregisterMenu") << (qlonglong)wid );
}
void GlobalMenu::windowChanged(WId wid)
{
// Ugly Implementation , Check If Menu is hidden()
if (!mMenu->isHidden()) {
mMenu->hide();
}
updateContextMap(wid);
}
void GlobalMenu::activeWindowChanged(WId wid)
{
if (!mMenu->isHidden()) {
mMenu->hide();
}
// check whether we have another presently active client
if ( lastWid && lastWid != wid )
mXBarIfce->releaseFocus(lastWid); // and release it
// invalidate active client
lastWid = 0;
QString ctxXML = mContextMap[wid];
if (!ctxXML.isEmpty()) {
lastWid = wid; // update active client to this one
bindGlobalShortcut(ctxXML);
mXBarIfce->requestFocus(wid);
}
}
void GlobalMenu::updateContextMap(WId wid)
{
int list_count;
char **list_return;
QString ctxXML;
Display *display = QX11Info::display();
Status status;
XTextProperty tp;
status = XGetTextProperty(display, wid, &tp, mCtxAtom);
if (status) {
status = XTextPropertyToStringList(&tp,&list_return,&list_count);
if (status) {
if (list_count) {
// for ( int i = 0 ; i < list_count ; i ++ ) {
// qDebug() << "Register , I is " << i;
ctxXML = QString::fromUtf8(list_return[0]);
// if (ctxXML != mContextMap[wid]) {
// qDebug() << "Step 4";
kDebug() << ctxXML;
mContextMap.insert(wid,ctxXML);
mXBarIfce->registerMenu(SERVICE_NAME,wid,
windowName(wid),
topMenuList(ctxXML));
if (KWindowSystem::activeWindow() == wid) {
mXBarIfce->requestFocus(wid);
}
// break;
// }
// }
} else {
ctxXML.clear();
}
XFreeStringList(list_return);
}
} else {
ctxXML.clear();
}
}
void GlobalMenu::buildChildMenu(KMenu *topMenu,const QDomElement &elem, const QString \
&prefix) {
if (elem.isNull()) {
return;
}
KAction *action;
//KSelectAction *selectAction = 0;
for (QDomElement itemElem = elem.firstChildElement("item"); !itemElem.isNull();
itemElem = itemElem.nextSiblingElement("item")) {
if (itemElem.attribute("visible") == "0" ) {
continue;
} else if (itemElem.attribute("type") == "s") {
topMenu->addSeparator();
} else if (!itemElem.firstChildElement("menu").isNull() ) {
QString menu_tmp = itemElem.attribute("label").replace("_","&");
KMenu *childMenu = new KMenu(menu_tmp,topMenu);
buildChildMenu(childMenu,itemElem.firstChildElement("menu"),
\
QString("%1/%2").arg(prefix).arg(itemElem.attribute("id")));
topMenu->addMenu(childMenu);
continue;
} else if ( !itemElem.attribute("label").isEmpty() ) {
QString menu_tmp = itemElem.attribute("label").replace("_","&");
action = new KAction(menu_tmp,0);
mMapper->setMapping(action,QString("%1/%2").arg(prefix).arg(itemElem.attribute("id")));
connect(action,SIGNAL(triggered()),mMapper,SLOT(map()));
if (itemElem.attribute("sensible") == "0") {
action->setEnabled(false);
}
topMenu->addAction(action);
}
}
}
void GlobalMenu::triggered(const QString &id)
{
kDebug() << id;
mMenu->hide();
WId wid = KWindowSystem::activeWindow();
char *data = id.toUtf8().append("\0").data();
QString ctxXML;
Display *display = QX11Info::display();
XTextProperty tp;
XStringListToTextProperty(&data,1,&tp);
XSetTextProperty(display, wid, &tp, mEvtAtom);
}
bool GlobalMenu::processXEvent(XEvent *ev)
{
if (ev->type == PropertyNotify) {
if (ev->xproperty.atom == mCtxAtom) {
updateContextMap(ev->xproperty.window);
}
if (ev->xproperty.atom == mEvtAtom) {
kDebug() << "Evt:" << QString::number(ev->xproperty.window) << \
XGetAtomName(QX11Info::display(),ev->xproperty.atom); }
}
return false;
}
bool GlobalMenu::x11EventFilter(void *message)
{
return self->processXEvent(static_cast<XEvent *>(message));
}
void GlobalMenu::bindGlobalShortcut(const QString &source)
{
QDomDocument doc;
QDomElement docElem;
QDomElement rootElem;
QDomNodeList topNodes;
QString error;
QStringList result;
doc.setContent(source,false,&error);
rootElem = doc.firstChildElement();
for (QDomElement elem = rootElem.firstChildElement(); !elem.isNull(); elem = \
elem.nextSiblingElement()) { if (elem.attribute("visible") != "0") {
KShortcut shortcut(elem.attribute("accel"));
QString label = elem.attribute("label").replace("_","&");
KAction *action = new KAction(label,0);
mMapper->setMapping(action,QString("/%1").arg(elem.attribute("id")));
connect(action,SIGNAL(triggered()),mMapper,SLOT(map()));
action->setGlobalShortcut(shortcut);
result << label;
}
}
return;
}
#include "globalmenu.moc"
>> Visit http://mail.kde.org/mailman/listinfo/kde-devel#unsub to unsubscribe <<
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic