/* Copyright (C) 1997, 1998, 1999, 2000 Sven Radej (radej@kde.org) Copyright (C) 1997, 1998, 1999, 2000 Matthias Ettrich (ettrich@kde.org) Copyright (C) 1999, 2000 Daniel "Mosfet" Duley (mosfet@kde.org) Copyright (C) 2002 Siegfried Nijssen (snijssen@liacs.nl) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INCLUDE_MENUITEM_DEF #define INCLUDE_MENUITEM_DEF #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifndef Q_WS_QWS #include #include #include #endif #define dummy false // a parameter for change menu internal. class KMenuBar::KMenuBarPrivate { public: KMenuBarPrivate(): appName ( "kicker" ) { realPosition = Standard; appWantsToplevel = false; appWantsApplet = true; userWantsApplet = false; userWantsToplevel = false; oldAppWantsToplevel = false; // just to be sure oldAppWantsApplet = true; } enum KMenubarPosition { TopLevel, InApplet, Standard } realPosition; // where is the Menubar in reality? bool appWantsToplevel; // does the application force the menubar toplevel? bool appWantsApplet; // is the applet sufficient for the application if it wants it toplevel? bool oldAppWantsToplevel, oldAppWantsApplet; // used to store the situation when a "Full Screen" event is received. bool userWantsApplet; // does the user want the menubar in the applet if there is an applet? bool userWantsToplevel; // does the user want a toplevel menubar? int frameStyle; // what was the framestyle originally? (So, when displayed in the default position) int linewidth; // what was the width of the frame originally? BackgroundMode backgroundmode; // what was the backgroundmode of the menubar originally? QCString appName; // returns TRUE if an applet is found using DCOP. The exact path of the applet, // which can be used in subsequent calls, is stored in appletName. bool existsApplet (); // add a menubar to the applet. Height gives the height of the menubar. // This is for nice alignment. bool addToApplet ( WId id, WId topid ); // tell the applet that it should display the current menubar when there // is no currently active window. Typically, this is only used to initialize // the KDesktop menubar correctly. bool setNoWindowInApplet ( WId topid ); }; bool KMenuBar::KMenuBarPrivate::existsApplet () { DCOPClient *client = KApplication::dcopClient (); if ( !client->isAttached () ) client->attach (); QCString replyApp, replyObj; QByteArray nodata; bool returnStatus = false; // let's try the default server first to avoid the polling process. returnStatus = client->findObject(appName, QCString ( "MenubarApplet" ), QCString (), nodata,replyApp, replyObj, false); if ( !returnStatus && realPosition != InApplet ) { // unfortunately, a restarted applet is no longer in the "kicker" application path. // I don't know why, but this means we have to perform a brute-force search to recover // the path if available. // If the last position was in the applet, the old applet has disappeared. // We assume no applet-jumping. returnStatus = client->findObject( QCString ( "applet_proxy*" ), QCString ( "MenubarApplet" ), QCString (), nodata,replyApp, replyObj, false); if ( returnStatus ) appName = replyApp; } return returnStatus; // yeah! } /* communication between the menubar and the applet is restricted to registering */ bool KMenuBar::KMenuBarPrivate::addToApplet ( WId id, WId topid ) { QCString replyType; QByteArray replyData; QByteArray data; QDataStream arg(data, IO_WriteOnly); arg << id; arg << topid; return KApplication::dcopClient ()->call(appName, "MenubarApplet", "addMenubar(WId,WId)", data,replyType, replyData, true); } bool KMenuBar::KMenuBarPrivate::setNoWindowInApplet ( WId topid ) { QCString replyType; QByteArray replyData; QByteArray data; QDataStream arg(data, IO_WriteOnly); arg << topid; return KApplication::dcopClient ()->call(appName, "MenubarApplet", "setNoWindowMenubar(WId)", data,replyType, replyData, true); } KMenuBar::KMenuBar(QWidget *parent, const char *name) : QMenuBar(parent, name) { d = new KMenuBarPrivate; d->frameStyle = frameStyle (); d->linewidth = lineWidth (); d->backgroundmode = backgroundMode (); if ( kapp ) { // toolbarAppearanceChanged(int) is sent when changing macstyle connect( kapp, SIGNAL(toolbarAppearanceChanged(int)), this, SLOT(slotReadConfig())); connect( kapp, SIGNAL(kdisplayStyleChanged()), this, SLOT(slotChangeStyle())); } slotReadConfig(); } KMenuBar::~KMenuBar() { delete d; } void KMenuBar::setTopLevelMenu(bool top_level) { d->appWantsToplevel = top_level; if ( top_level == true && caption () == "KDE Desktop" ) d->appWantsApplet = true; else d->appWantsApplet = false; // this we assume for now setTopLevelMenuInternal(dummy); } #if QT_VERSION < 0x030100 namespace { class QWidgetHack : public QWidget { public: bool isFullScreen() { return isTopLevel() && topData()->fullscreen; } }; } #endif void KMenuBar::setTopLevelMenuInternal(bool top_level) { // first determine where the bar will go KMenuBarPrivate::KMenubarPosition togo; if ( ( ( d->userWantsApplet && d->appWantsApplet ) || ( d->appWantsToplevel && d->appWantsApplet ) ) && d->existsApplet () ) togo = KMenuBarPrivate::InApplet; else if ( d->userWantsToplevel || d->appWantsToplevel ) { if( parentWidget() #if QT_VERSION >= 0x030100 && parentWidget()->topLevelWidget()->isFullScreen()) { #else && static_cast(parentWidget()->topLevelWidget())->isFullScreen()) { #endif togo = KMenuBarPrivate::Standard; // we really want the menubar to be displayed toplevel, but as the parent is // fullscreen, there is no need to force this as the menubar is already toplevel! } else togo = KMenuBarPrivate::TopLevel; } else togo = KMenuBarPrivate::Standard; if ( togo == d->realPosition ) return; // we assume here that there are no 2 menubar applets // the menubar does not jump from one applet to the other // without being in another position first // so now there is work to be done if ( d->realPosition == KMenuBarPrivate::TopLevel ) KWin::setStrut( winId(), 0, 0, 0, 0 ); d->realPosition = togo; switch ( togo ) { case KMenuBarPrivate::InApplet: // reparenting is only done to keep a good layout in the parent window. removeEventFilter ( topLevelWidget () ); reparent( parentWidget(), WType_TopLevel | WType_Dialog | WStyle_NoBorder, QPoint(0,0), false ); KWin::setType ( winId(), NET::TopMenu ); installEventFilter( parentWidget()->topLevelWidget () ); QTimer::singleShot ( 0, this, SLOT(finishToApplet()) ); return; case KMenuBarPrivate::TopLevel: removeEventFilter( topLevelWidget() ); reparent( parentWidget(), WType_TopLevel | WType_Dialog | WStyle_NoBorder, QPoint(0,0), false ); #ifndef Q_WS_QWS //FIXME KWin::setType( winId(), NET::TopMenu ); #endif setFrameStyle ( MenuBarPanel ); installEventFilter( parentWidget()->topLevelWidget() ); break; default: if ( parentWidget() ) { reparent( parentWidget(), QPoint(0,0), false); installEventFilter( topLevelWidget() ); setFrameStyle( d->frameStyle ); setBackgroundMode ( d->backgroundmode ); setLineWidth ( d->linewidth ); } } // any visibility marker seems not reliable, just show the bar even if it was hidden // by the user/app. show (); } bool KMenuBar::isTopLevelMenu() const { return d->realPosition == KMenuBarPrivate::TopLevel || ( d->appWantsToplevel && d->appWantsApplet ); } void KMenuBar::show() { if ( d->realPosition != KMenuBarPrivate::Standard && isVisible () ) return; QMenuBar::show(); } void KMenuBar::slotReadConfig() { KConfig *config = KGlobal::config(); KConfigGroupSaver saver( config, "KDE" ); d->userWantsToplevel = config->readBoolEntry ( "macStyle", false ); d->userWantsApplet = config->readBoolEntry ( "menuApplet", true ); setTopLevelMenuInternal(dummy); } bool KMenuBar::eventFilter(QObject *obj, QEvent *ev) { if ( d->realPosition != KMenuBarPrivate::Standard ) { if ( ev->type() == QEvent::Resize ) return FALSE; if ( parentWidget () && obj == parentWidget ()->topLevelWidget () ) { if ( ev->type() == QEvent::Accel || ev->type() == QEvent::AccelAvailable ) if ( QApplication::sendEvent( topLevelWidget(), ev ) ) return TRUE; if ( ev->type() == QEvent::WindowActivate ) if ( d->realPosition != KMenuBarPrivate::InApplet ) raise (); if ( ev->type() == QEvent::ShowFullScreen ) { // will update the state properly d->oldAppWantsToplevel = d->appWantsToplevel; d->oldAppWantsApplet = d->appWantsApplet; d->appWantsToplevel = true; d->appWantsApplet = false; setTopLevelMenuInternal( dummy ); } } } else if ( topLevelWidget() && obj == topLevelWidget()) { if ( ev->type() == QEvent::ShowNormal ) { d->appWantsToplevel = d->oldAppWantsToplevel; d->appWantsApplet = d->oldAppWantsApplet; setTopLevelMenuInternal( dummy ); } } return QMenuBar::eventFilter( obj, ev ); } void KMenuBar::showEvent( QShowEvent *e ) { if ( d->realPosition != KMenuBarPrivate::Standard ) { #ifndef Q_WS_QWS //FIXME KConfigGroup xineramaConfig(KGlobal::config(),"Xinerama"); int screen = xineramaConfig.readNumEntry("MenubarScreen", QApplication::desktop()->screenNumber(QPoint(0,0)) ); QRect area = QApplication::desktop()->screenGeometry(screen); if ( d->realPosition == KMenuBarPrivate::InApplet ) { int height = heightForWidth( area.width() ); QMenuBar::setGeometry(area.left(), -2 * height, area.width(), height ); setLineWidth( 0 ); setFrameStyle ( Box ); setBackgroundMode( PaletteBackground ); // in most styles, the color of the menubar is incorrect. } else { QMenuBar::setGeometry(area.left(), area.top()-frameWidth()-2, area.width(), heightForWidth( area.width() ) ); KWin::setStrut( winId(), 0, 0, height()-frameWidth()-2, 0 ); } #endif } QMenuBar::showEvent(e); } void KMenuBar::finishToApplet () { // the menubar temporarily appears at a strange position. Remove it! d->addToApplet ( winId (), topLevelWidget ()->parentWidget ()->winId () ); // treat the KDesktop Menubar as a special case if ( caption () == "KDE Desktop" ) d->setNoWindowInApplet ( topLevelWidget ()->parentWidget ()->winId () ); show (); } void KMenuBar::setGeometry( int x, int y, int w, int h ) { if ( d->realPosition == KMenuBarPrivate::Standard ) QMenuBar::setGeometry(x,y,w,h); } void KMenuBar::virtual_hook( int, void* ) { /*BASE::virtual_hook( id, data );*/ } void KMenuBar::slotChangeStyle () { /* some styles display a frame around the menubar. This doesn't look nice when the menubar is in the applet. The KMenuBar therefore removes this frame. With every style change, this preference has to be reinforced. The PaletteBackground has to be set explicitely to override the preference of some styles. Especially, this has to be done at a show event to make sure everything is drawn properly. */ if ( d->realPosition == KMenuBarPrivate::InApplet ) { d->frameStyle = frameStyle (); d->linewidth = lineWidth (); d->backgroundmode = backgroundMode (); setLineWidth( 0 ); setFrameStyle ( Box ); setBackgroundMode( PaletteBackground ); } } #include "kmenubar.moc"