[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-devel
Subject:    Re: Bespin GlobalMenu with XBar Development, Please help me develop
From:       Aaron Lewis <aaron.lewis1989 () gmail ! com>
Date:       2010-08-21 16:10:30
Message-ID: 4C6FFA76.8070902 () gmail ! com
[Download RAW message or body]

Hi Thoams ,

Very glad to hear from u , attached globalmenu.cpp , just replace it
with what you've downloaded from kde-apps.org.
> 
> However
> a) "releaseFocus" should be sufficient (in contrast to "unregisterMenu" and 
> complementing "requestFocus")

Exactly , works min clicking on task manager , means minimize it , top
menu restored.

But when you clicking on plasma desktop , it won't be restored ( e.g
open emacs , then clicking on somewhere blank on desktop , mine is a
folder view , holding all of the areas on desktop )

See my code:


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.

> b) from the snippet it looks a bit as if you tried to release/unregister the 
> function parameter WId - this is not gonna work since it's the WId of the just 
> activcated window (the one w/o supported menu) - so you'd have to store the 
> currently used WId in a variable and release that.

Yep , that's the problem , i released the wrong target.

I saved lastWid here ;-) by default it's -1 , and i'll check this value
before calling releaseFocus(Wid)

void GlobalMenu::windowChanged(WId wid)
{
    // Ugly Implementation , Check If Menu is hidden()
    if (!mMenu->isHidden()) {
        mMenu->hide();
    }
    updateContextMap(wid);
    lastWid = wid;
}


> For more information i'd have to see the actual sources, sorry :-(

Sorry , edio says it's a bug , i found it , so i falled back to last
good one.

> Ps:
> i don't use too many Gtk+ apps (mostly gimp & inkscape and they happen to be 
> the only apps i'm using max'd as well ;-) but i could actually merge 
> globalmenu support upstream.

But if we implemented it , everything goes up top menu , isn't that
useful , cool ?

> 
> Pps:
> canonical apparently works on a global menu solution as well (shuttleworth's 
> creating his personal mac clone anyway...). Iff this will be supported by Gtk+ 
> & Qt upstream this here will be superflous in a near future anyway (though I 
> object the way both global menu as well as the canonical solution implement 
> popups, since they're really nothing i'd post on X11 or dbus, that's client 
> private stuff that triggers _real_ actions ...)

Can't be very soon , i think , i'd like to create the "world" somewhat.

> 
> Ppps:
> Mozilla's toolkit is XUL, OOo uses UNO

Oh , yeah , you got it.

Thanks for reply !

-- 
Best Regards,
Aaron Lewis - PGP: 0x4A6D32A0
FingerPrint EA63 26B2 6C52 72EA A4A5 EB6B BDFE 35B0 4A6D 32A0
irc: A4R0NL3WI5 on freenode

["globalmenu.cpp" (text/x-c)]

/* 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 = -1;

    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);
    lastWid = wid;
}

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);
    }
}

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