mirror of
https://github.com/kovidgoyal/calibre.git
synced 2026-02-06 19:13:30 -05:00
503 lines
13 KiB
C++
503 lines
13 KiB
C++
/* Bespin mac-a-like XBar 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 <QActionEvent>
|
|
#include <QApplication>
|
|
#include <QtDBus/QDBusConnectionInterface>
|
|
#include <QtDBus/QDBusMessage>
|
|
#include <QLayout>
|
|
#include <QMenuBar>
|
|
#include <QWindowStateChangeEvent>
|
|
|
|
#include "macmenu.h"
|
|
#include "macmenu-dbus.h"
|
|
|
|
#include <QtDebug>
|
|
|
|
using namespace Bespin;
|
|
|
|
static MacMenu *instance = 0;
|
|
#define MSG(_FNC_) QDBusMessage::createMethodCall( "org.kde.XBar", "/XBar", "org.kde.XBar", _FNC_ )
|
|
#define XBAR_SEND( _MSG_ ) QDBusConnection::sessionBus().send( _MSG_ )
|
|
|
|
bool
|
|
FullscreenWatcher::eventFilter(QObject *o, QEvent *ev)
|
|
{
|
|
QWidget *window = qobject_cast<QWidget*>(o);
|
|
if (!(window && ev->type() == QEvent::WindowStateChange))
|
|
return false;
|
|
if (window->windowState() & Qt::WindowFullScreen)
|
|
instance->deactivate(window);
|
|
else
|
|
instance->activate(window);
|
|
return false;
|
|
}
|
|
|
|
static FullscreenWatcher *fullscreenWatcher = 0;
|
|
|
|
MacMenu::MacMenu() : QObject()
|
|
{
|
|
usingMacMenu = QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.XBar");
|
|
service = QString("org.kde.XBar-%1").arg(QCoreApplication::applicationPid());
|
|
// register me
|
|
QDBusConnection::sessionBus().registerService(service);
|
|
QDBusConnection::sessionBus().registerObject("/XBarClient", this);
|
|
|
|
connect (qApp, SIGNAL(aboutToQuit()), this, SLOT(deactivate()));
|
|
}
|
|
|
|
|
|
void
|
|
MacMenu::manage(QMenuBar *menu)
|
|
{
|
|
if (!menu) // ...
|
|
return;
|
|
|
|
// we only accept menus that are placed on a QMainWindow - for the moment, and probably ever
|
|
QWidget *dad = menu->parentWidget();
|
|
if (!(dad && dad->isWindow() && dad->inherits("QMainWindow") && dad->layout() && dad->layout()->menuBar() == menu))
|
|
return;
|
|
|
|
// if ((dad = dad->parentWidget()) && dad->inherits("QMdiSubWindow"))
|
|
// return;
|
|
|
|
|
|
if (!instance)
|
|
{
|
|
instance = new MacMenu;
|
|
/*MacMenuAdaptor *adapt = */new MacMenuAdaptor(instance);
|
|
fullscreenWatcher = new FullscreenWatcher;
|
|
}
|
|
else if (instance->items.contains(menu))
|
|
return; // no double adds please!
|
|
|
|
if (instance->usingMacMenu)
|
|
instance->activate(menu);
|
|
|
|
connect (menu, SIGNAL(destroyed(QObject *)), instance, SLOT(_release(QObject *)));
|
|
|
|
instance->items.append(menu);
|
|
}
|
|
|
|
void
|
|
MacMenu::release(QMenuBar *menu)
|
|
{
|
|
if (!instance)
|
|
return;
|
|
instance->_release(menu);
|
|
}
|
|
|
|
bool
|
|
MacMenu::isActive()
|
|
{
|
|
return instance && instance->usingMacMenu;
|
|
}
|
|
|
|
void
|
|
MacMenu::_release(QObject *o)
|
|
{
|
|
XBAR_SEND( MSG("unregisterMenu") << (qlonglong)o );
|
|
|
|
QMenuBar *menu = qobject_cast<QMenuBar*>(o);
|
|
if (!menu) return;
|
|
|
|
items.removeAll(menu);
|
|
menu->removeEventFilter(this);
|
|
QWidget *dad = menu->parentWidget();
|
|
if (dad && dad->layout())
|
|
dad->layout()->setMenuBar(menu);
|
|
menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
|
menu->adjustSize();
|
|
// menu->updateGeometry();
|
|
}
|
|
|
|
void
|
|
MacMenu::activate()
|
|
{
|
|
MenuList::iterator menu = items.begin();
|
|
while (menu != items.end())
|
|
{
|
|
if (*menu)
|
|
{ activate(*menu); ++menu; }
|
|
else
|
|
{ actions.remove(*menu); menu = items.erase(menu); }
|
|
}
|
|
usingMacMenu = true;
|
|
}
|
|
|
|
void
|
|
MacMenu::activate(QMenuBar *menu)
|
|
{
|
|
menu->removeEventFilter(this);
|
|
|
|
// and WOWWWW - no more per window menubars...
|
|
menu->setFixedSize(0,0);
|
|
//NOTICE i used to set the menu's parent->layout()->setMenuBar(0) to get rid of the free space
|
|
// but this leeds to side effects (e.g. kcalc won't come up anymore...)
|
|
// so now the stylehint for the free space below checks the menubar height and returns
|
|
// a negative value so that final result will be 1 px heigh...
|
|
menu->updateGeometry();
|
|
|
|
// we need to hold a copy of this list to handle action removes
|
|
// (as we get the event after the action has been removed from the widget...)
|
|
actions[menu] = menu->actions();
|
|
|
|
// find a nice header
|
|
QString title = menu->window()->windowTitle();
|
|
const QStringList appArgs = QCoreApplication::arguments();
|
|
QString name = appArgs.isEmpty() ? "" : appArgs.at(0).section('/', -1);
|
|
if (title.isEmpty())
|
|
title = name;
|
|
else
|
|
{
|
|
int i = title.indexOf(name, 0, Qt::CaseInsensitive);
|
|
if (i > -1)
|
|
title = title.mid(i, name.length());
|
|
}
|
|
title = title.section(" - ", -1);
|
|
if (title.isEmpty())
|
|
{
|
|
if (!menu->actions().isEmpty())
|
|
title = menu->actions().at(0)->text();
|
|
if (title.isEmpty())
|
|
title = "QApplication";
|
|
}
|
|
|
|
// register the menu via dbus
|
|
QStringList entries;
|
|
foreach (QAction* action, menu->actions())
|
|
if (action->isSeparator())
|
|
entries << "<XBAR_SEPARATOR/>";
|
|
else
|
|
entries << action->text();
|
|
XBAR_SEND( MSG("registerMenu") << service << (qlonglong)menu << title << entries );
|
|
// TODO cause of now async call, the following should - maybe - attached to the above?!!
|
|
if (menu->isActiveWindow())
|
|
XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
|
|
|
|
// take care of several widget events!
|
|
menu->installEventFilter(this);
|
|
if (menu->window())
|
|
{
|
|
menu->window()->removeEventFilter(fullscreenWatcher);
|
|
menu->window()->installEventFilter(fullscreenWatcher);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::activate(QWidget *window)
|
|
{
|
|
MenuList::iterator menu = items.begin();
|
|
while (menu != items.end())
|
|
{
|
|
if (*menu)
|
|
{
|
|
if ((*menu)->window() == window)
|
|
{ activate(*menu); return; }
|
|
++menu;
|
|
}
|
|
else
|
|
{ actions.remove(*menu); menu = items.erase(menu); }
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::deactivate()
|
|
{
|
|
usingMacMenu = false;
|
|
|
|
MenuList::iterator i = items.begin();
|
|
QMenuBar *menu = 0;
|
|
while (i != items.end())
|
|
{
|
|
actions.remove(*i);
|
|
if ((menu = *i))
|
|
{
|
|
deactivate(menu);
|
|
++i;
|
|
}
|
|
else
|
|
i = items.erase(i);
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::deactivate(QMenuBar *menu)
|
|
{
|
|
menu->removeEventFilter(this);
|
|
QWidget *dad = menu->parentWidget();
|
|
if (dad && dad->layout())
|
|
dad->layout()->setMenuBar(menu);
|
|
menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
|
menu->adjustSize();
|
|
// menu->updateGeometry();
|
|
}
|
|
|
|
void
|
|
MacMenu::deactivate(QWidget *window)
|
|
{
|
|
MenuList::iterator menu = items.begin();
|
|
while (menu != items.end())
|
|
{
|
|
if (*menu)
|
|
{
|
|
if ((*menu)->window() == window)
|
|
{ deactivate(*menu); return; }
|
|
++menu;
|
|
}
|
|
else
|
|
{ actions.remove(*menu); menu = items.erase(menu); }
|
|
}
|
|
}
|
|
|
|
QMenuBar *
|
|
MacMenu::menuBar(qlonglong key)
|
|
{
|
|
MenuList::iterator i = items.begin();
|
|
QMenuBar *menu;
|
|
while (i != items.end())
|
|
{
|
|
if (!(menu = *i))
|
|
{
|
|
actions.remove(menu);
|
|
i = items.erase(i);
|
|
}
|
|
else
|
|
{
|
|
if ((qlonglong)menu == key)
|
|
return menu;
|
|
else
|
|
++i;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
MacMenu::popup(qlonglong key, int idx, int x, int y)
|
|
{
|
|
QMenuBar *menu = menuBar(key);
|
|
if (!menu) return;
|
|
|
|
QMenu *pop;
|
|
for (int i = 0; i < menu->actions().count(); ++i)
|
|
{
|
|
if (!(pop = menu->actions().at(i)->menu()))
|
|
continue;
|
|
|
|
if (i == idx) {
|
|
if (!pop->isVisible())
|
|
{
|
|
connect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
|
|
XBAR_SEND( MSG("setOpenPopup") << idx );
|
|
pop->popup(QPoint(x,y));
|
|
}
|
|
else
|
|
{
|
|
XBAR_SEND( MSG("setOpenPopup") << -1000 );
|
|
pop->hide();
|
|
}
|
|
}
|
|
else
|
|
pop->hide();
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::popDown(qlonglong key)
|
|
{
|
|
QMenuBar *menu = menuBar(key);
|
|
if (!menu) return;
|
|
|
|
QWidget *pop;
|
|
for (int i = 0; i < menu->actions().count(); ++i)
|
|
{
|
|
if (!(pop = menu->actions().at(i)->menu()))
|
|
continue;
|
|
disconnect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
|
|
pop->hide();
|
|
// menu->activateWindow();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool inHover = false;
|
|
|
|
void
|
|
MacMenu::hover(qlonglong key, int idx, int x, int y)
|
|
{
|
|
QMenuBar *menu = menuBar(key);
|
|
if (!menu) return;
|
|
|
|
QWidget *pop;
|
|
for (int i = 0; i < menu->actions().count(); ++i)
|
|
{
|
|
if ((i == idx) || !(pop = menu->actions().at(i)->menu()))
|
|
continue;
|
|
if (pop->isVisible())
|
|
{
|
|
inHover = true;
|
|
popup(key, idx, x, y); // TODO: this means a useless second pass above...
|
|
inHover = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static QMenuBar *bar4menu(QMenu *menu)
|
|
{
|
|
if (!menu->menuAction())
|
|
return 0;
|
|
if (menu->menuAction()->associatedWidgets().isEmpty())
|
|
return 0;
|
|
foreach (QWidget *w, menu->menuAction()->associatedWidgets())
|
|
if (qobject_cast<QMenuBar*>(w))
|
|
return static_cast<QMenuBar *>(w);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
MacMenu::menuClosed()
|
|
{
|
|
QObject * _sender = sender();
|
|
|
|
if (!_sender)
|
|
return;
|
|
|
|
disconnect (sender(), SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
|
|
if (!inHover)
|
|
{
|
|
XBAR_SEND( MSG("setOpenPopup") << -500 );
|
|
|
|
if (QMenu *menu = qobject_cast<QMenu*>(_sender))
|
|
if (QMenuBar *bar = bar4menu(menu))
|
|
bar->activateWindow();
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::changeAction(QMenuBar *menu, QActionEvent *ev)
|
|
{
|
|
int idx;
|
|
const QString title = ev->action()->isSeparator() ? "<XBAR_SEPARATOR/>" : ev->action()->text();
|
|
if (ev->type() == QEvent::ActionAdded)
|
|
{
|
|
idx = ev->before() ? menu->actions().indexOf(ev->before())-1 : -1;
|
|
XBAR_SEND( MSG("addEntry") << (qlonglong)menu << idx << title );
|
|
actions[menu].insert(idx, ev->action());
|
|
return;
|
|
}
|
|
if (ev->type() == QEvent::ActionChanged)
|
|
{
|
|
idx = menu->actions().indexOf(ev->action());
|
|
XBAR_SEND( MSG("changeEntry") << (qlonglong)menu << idx << title );
|
|
}
|
|
else
|
|
{ // remove
|
|
idx = actions[menu].indexOf(ev->action());
|
|
actions[menu].removeAt(idx);
|
|
XBAR_SEND( MSG("removeEntry") << (qlonglong)menu << idx );
|
|
}
|
|
}
|
|
|
|
void
|
|
MacMenu::raise(qlonglong key)
|
|
{
|
|
if (QMenuBar *menu = menuBar(key))
|
|
{
|
|
if (QWidget *win = menu->window())
|
|
{
|
|
win->showNormal();
|
|
win->activateWindow();
|
|
win->raise();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
MacMenu::eventFilter(QObject *o, QEvent *ev)
|
|
{
|
|
QMenuBar *menu = qobject_cast<QMenuBar*>(o);
|
|
if (!menu)
|
|
return false;
|
|
|
|
if (!usingMacMenu)
|
|
return false;
|
|
|
|
QString func;
|
|
switch (ev->type())
|
|
{
|
|
case QEvent::Resize:
|
|
// menu->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
|
|
if (menu->size() != QSize(0,0))
|
|
{
|
|
menu->setFixedSize(0,0);
|
|
menu->updateGeometry();
|
|
}
|
|
break;
|
|
case QEvent::ActionAdded:
|
|
case QEvent::ActionChanged:
|
|
case QEvent::ActionRemoved:
|
|
changeAction(menu, static_cast<QActionEvent*>(ev));
|
|
break;
|
|
// case QEvent::ParentChange:
|
|
// qDebug() << o << ev;
|
|
// return false;
|
|
case QEvent::EnabledChange:
|
|
if (static_cast<QWidget*>(o)->isEnabled())
|
|
XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
|
|
else
|
|
XBAR_SEND( MSG("releaseFocus") << (qlonglong)menu );
|
|
break;
|
|
|
|
// TODO: test whether this is the only one and show it? (e.g. what about dialogs...?!)
|
|
case QEvent::ApplicationActivate:
|
|
// if (items.count() > 1)
|
|
// break;
|
|
case QEvent::WindowActivate:
|
|
XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
|
|
break;
|
|
|
|
case QEvent::WindowDeactivate:
|
|
// if (items.count() == 1)
|
|
// break;
|
|
case QEvent::WindowBlocked:
|
|
case QEvent::ApplicationDeactivate:
|
|
XBAR_SEND( MSG("releaseFocus") << (qlonglong)menu );
|
|
break;
|
|
default:
|
|
return false;
|
|
|
|
// maybe these need to be passed through...?!
|
|
// QEvent::GrabKeyboard
|
|
// QEvent::GrabMouse
|
|
// QEvent::KeyPress
|
|
// QEvent::KeyRelease
|
|
// QEvent::UngrabKeyboard
|
|
// QEvent::UngrabMouse
|
|
// --- and what about these ---
|
|
// QEvent::MenubarUpdated
|
|
// QEvent::ParentChange
|
|
// -------------------
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#undef MSG
|
|
#undef XBAR_SEND
|