[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