From ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kdeui/kmenubar.cpp | 563 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 kdeui/kmenubar.cpp (limited to 'kdeui/kmenubar.cpp') diff --git a/kdeui/kmenubar.cpp b/kdeui/kmenubar.cpp new file mode 100644 index 000000000..fcadd8648 --- /dev/null +++ b/kdeui/kmenubar.cpp @@ -0,0 +1,563 @@ +/* This file is part of the KDE libraries + 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) + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + */ + + +#ifndef INCLUDE_MENUITEM_DEF +#define INCLUDE_MENUITEM_DEF +#endif + +#include "config.h" +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_WS_X11 +#include +#include +#include + +#include +#include +#include +#endif + +/* + + Toplevel menubar (not for the fallback size handling done by itself): + - should not alter position or set strut + - every toplevel must have at most one matching topmenu + - embedder won't allow shrinking below a certain size + - must have WM_TRANSIENT_FOR pointing the its mainwindow + - the exception is desktop's menubar, which can be transient for root window + because of using root window as the desktop window + - Fitts' Law + +*/ + +class KMenuBar::KMenuBarPrivate +{ +public: + KMenuBarPrivate() + : forcedTopLevel( false ), + topLevel( false ), + wasTopLevel( false ), +#ifdef Q_WS_X11 + selection( NULL ), +#endif + min_size( 0, 0 ) + { + } + ~KMenuBarPrivate() + { +#ifdef Q_WS_X11 + delete selection; +#endif + } + bool forcedTopLevel; + bool topLevel; + bool wasTopLevel; // when TLW is fullscreen, remember state + int frameStyle; // only valid in toplevel mode + int lineWidth; // dtto + int margin; // dtto + bool fallback_mode; // dtto +#ifdef Q_WS_X11 + KSelectionWatcher* selection; +#endif + QTimer selection_timer; + QSize min_size; + static Atom makeSelectionAtom(); +}; + +#ifdef Q_WS_X11 +static Atom selection_atom = None; +static Atom msg_type_atom = None; + +static +void initAtoms() +{ + char nm[ 100 ]; + sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay())); + char nm2[] = "_KDE_TOPMENU_MINSIZE"; + char* names[ 2 ] = { nm, nm2 }; + Atom atoms[ 2 ]; + XInternAtoms( qt_xdisplay(), names, 2, False, atoms ); + selection_atom = atoms[ 0 ]; + msg_type_atom = atoms[ 1 ]; +} +#endif + +Atom KMenuBar::KMenuBarPrivate::makeSelectionAtom() +{ +#ifdef Q_WS_X11 + if( selection_atom == None ) + initAtoms(); + return selection_atom; +#else + return 0; +#endif +} + +KMenuBar::KMenuBar(QWidget *parent, const char *name) + : QMenuBar(parent, name) +{ +#ifdef Q_WS_X11 + QXEmbed::initialize(); +#endif + d = new KMenuBarPrivate; + connect( &d->selection_timer, SIGNAL( timeout()), + this, SLOT( selectionTimeout())); + + connect( qApp->desktop(), SIGNAL( resized( int )), SLOT( updateFallbackSize())); + + if ( kapp ) + // toolbarAppearanceChanged(int) is sent when changing macstyle + connect( kapp, SIGNAL(toolbarAppearanceChanged(int)), + this, SLOT(slotReadConfig())); + + slotReadConfig(); +} + +KMenuBar::~KMenuBar() +{ + delete d; +} + +void KMenuBar::setTopLevelMenu(bool top_level) +{ + d->forcedTopLevel = top_level; + setTopLevelMenuInternal( top_level ); +} + +void KMenuBar::setTopLevelMenuInternal(bool top_level) +{ + if (d->forcedTopLevel) + top_level = true; + + d->wasTopLevel = top_level; + if( parentWidget() + && parentWidget()->topLevelWidget()->isFullScreen()) + top_level = false; + + if ( isTopLevelMenu() == top_level ) + return; + d->topLevel = top_level; + if ( isTopLevelMenu() ) + { +#ifdef Q_WS_X11 + d->selection = new KSelectionWatcher( KMenuBarPrivate::makeSelectionAtom(), + DefaultScreen( qt_xdisplay())); + connect( d->selection, SIGNAL( newOwner( Window )), + this, SLOT( updateFallbackSize())); + connect( d->selection, SIGNAL( lostOwner()), + this, SLOT( updateFallbackSize())); +#endif + d->frameStyle = frameStyle(); + d->lineWidth = lineWidth(); + d->margin = margin(); + d->fallback_mode = false; + bool wasShown = !isHidden(); + reparent( parentWidget(), WType_TopLevel | WStyle_Tool | WStyle_Customize | WStyle_NoBorder, QPoint(0,0), false ); +#ifdef Q_WS_X11 + KWin::setType( winId(), NET::TopMenu ); + if( parentWidget()) + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId()); +#endif + QMenuBar::setFrameStyle( NoFrame ); + QMenuBar::setLineWidth( 0 ); + QMenuBar::setMargin( 0 ); + updateFallbackSize(); + d->min_size = QSize( 0, 0 ); + if( parentWidget() && !parentWidget()->isTopLevel()) + setShown( parentWidget()->isVisible()); + else if ( wasShown ) + show(); + } else + { +#ifdef Q_WS_X11 + delete d->selection; + d->selection = NULL; +#endif + setBackgroundMode( PaletteButton ); + setFrameStyle( d->frameStyle ); + setLineWidth( d->lineWidth ); + setMargin( d->margin ); + setMinimumSize( 0, 0 ); + setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); + updateMenuBarSize(); + if ( parentWidget() ) + reparent( parentWidget(), QPoint(0,0), !isHidden()); + } +} + +bool KMenuBar::isTopLevelMenu() const +{ + return d->topLevel; +} + +// KDE4 remove +void KMenuBar::show() +{ + QMenuBar::show(); +} + +void KMenuBar::slotReadConfig() +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver saver( config, "KDE" ); + setTopLevelMenuInternal( config->readBoolEntry( "macStyle", false ) ); +} + +bool KMenuBar::eventFilter(QObject *obj, QEvent *ev) +{ + if ( d->topLevel ) + { + if ( parentWidget() && obj == parentWidget()->topLevelWidget() ) + { + if( ev->type() == QEvent::Resize ) + return false; // ignore resizing of parent, QMenuBar would try to adjust size + if ( ev->type() == QEvent::Accel || ev->type() == QEvent::AccelAvailable ) + { + if ( QApplication::sendEvent( topLevelWidget(), ev ) ) + return true; + } + if(ev->type() == QEvent::ShowFullScreen ) + // will update the state properly + setTopLevelMenuInternal( d->topLevel ); + } + if( parentWidget() && obj == parentWidget() && ev->type() == QEvent::Reparent ) + { +#ifdef Q_WS_X11 + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId()); +#else + //TODO: WIN32? +#endif + setShown( parentWidget()->isTopLevel() || parentWidget()->isVisible()); + } + if( parentWidget() && !parentWidget()->isTopLevel() && obj == parentWidget()) + { // if the parent is not toplevel, KMenuBar needs to match its visibility status + if( ev->type() == QEvent::Show ) + { +#ifdef Q_WS_X11 + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId()); +#else + //TODO: WIN32? +#endif + show(); + } + if( ev->type() == QEvent::Hide ) + hide(); + } + } + else + { + if( parentWidget() && obj == parentWidget()->topLevelWidget()) + { + if( ev->type() == QEvent::WindowStateChange + && !parentWidget()->topLevelWidget()->isFullScreen() ) + setTopLevelMenuInternal( d->wasTopLevel ); + } + } + return QMenuBar::eventFilter( obj, ev ); +} + +// KDE4 remove +void KMenuBar::showEvent( QShowEvent *e ) +{ + QMenuBar::showEvent(e); +} + +void KMenuBar::updateFallbackSize() +{ + if( !d->topLevel ) + return; +#ifdef Q_WS_X11 + if( d->selection->owner() != None ) +#endif + { // somebody is managing us, don't mess anything, undo changes + // done in fallback mode if needed + d->selection_timer.stop(); + if( d->fallback_mode ) + { + d->fallback_mode = false; + KWin::setStrut( winId(), 0, 0, 0, 0 ); + setMinimumSize( 0, 0 ); + setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); + updateMenuBarSize(); + } + return; + } + if( d->selection_timer.isActive()) + return; + d->selection_timer.start( 100, true ); +} + +void KMenuBar::selectionTimeout() +{ // nobody is managing us, handle resizing + if ( d->topLevel ) + { + d->fallback_mode = true; // KMenuBar is handling its position itself + KConfigGroup xineramaConfig(KGlobal::config(),"Xinerama"); + int screen = xineramaConfig.readNumEntry("MenubarScreen", + QApplication::desktop()->screenNumber(QPoint(0,0)) ); + QRect area = QApplication::desktop()->screenGeometry(screen); + int margin = 0; + move(area.left() - margin, area.top() - margin); + setFixedSize(area.width() + 2* margin , heightForWidth( area.width() + 2 * margin ) ); +#ifdef Q_WS_X11 + int strut_height = height() - margin; + if( strut_height < 0 ) + strut_height = 0; + KWin::setStrut( winId(), 0, 0, strut_height, 0 ); +#endif + } +} + +int KMenuBar::block_resize = 0; + +void KMenuBar::resizeEvent( QResizeEvent *e ) +{ + if( e->spontaneous() && d->topLevel && !d->fallback_mode ) + { + ++block_resize; // do not respond with configure request to ConfigureNotify event + QMenuBar::resizeEvent(e); // to avoid possible infinite loop + --block_resize; + } + else + QMenuBar::resizeEvent(e); +} + +void KMenuBar::setGeometry( const QRect& r ) +{ + setGeometry( r.x(), r.y(), r.width(), r.height() ); +} + +void KMenuBar::setGeometry( int x, int y, int w, int h ) +{ + if( block_resize > 0 ) + { + move( x, y ); + return; + } + checkSize( w, h ); + if( geometry() != QRect( x, y, w, h )) + QMenuBar::setGeometry( x, y, w, h ); +} + +void KMenuBar::resize( int w, int h ) +{ + if( block_resize > 0 ) + return; + checkSize( w, h ); + if( size() != QSize( w, h )) + QMenuBar::resize( w, h ); +// kdDebug() << "RS:" << w << ":" << h << ":" << width() << ":" << height() << ":" << minimumWidth() << ":" << minimumHeight() << endl; +} + +void KMenuBar::checkSize( int& w, int& h ) +{ + if( !d->topLevel || d->fallback_mode ) + return; + QSize s = sizeHint(); + w = s.width(); + h = s.height(); + // This is not done as setMinimumSize(), because that would set the minimum + // size in WM_NORMAL_HINTS, and KWin would not allow changing to smaller size + // anymore + w = KMAX( w, d->min_size.width()); + h = KMAX( h, d->min_size.height()); +} + +// QMenuBar's sizeHint() gives wrong size (insufficient width), which causes wrapping in the kicker applet +QSize KMenuBar::sizeHint() const +{ + if( !d->topLevel || block_resize > 0 ) + return QMenuBar::sizeHint(); + // Since QMenuBar::sizeHint() may indirectly call resize(), + // avoid infinite recursion. + ++block_resize; + // find the minimum useful height, and enlarge the width until the menu fits in that height (one row) + int h = heightForWidth( 1000000 ); + int w = QMenuBar::sizeHint().width(); + // optimization - don't call heightForWidth() too many times + while( heightForWidth( w + 12 ) > h ) + w += 12; + while( heightForWidth( w + 4 ) > h ) + w += 4; + while( heightForWidth( w ) > h ) + ++w; + --block_resize; + return QSize( w, h ); +} + +#ifdef Q_WS_X11 +bool KMenuBar::x11Event( XEvent* ev ) +{ + if( ev->type == ClientMessage && ev->xclient.message_type == msg_type_atom + && ev->xclient.window == winId()) + { + // QMenuBar is trying really hard to keep the size it deems right. + // Forcing minimum size and blocking resizing to match parent size + // in checkResizingToParent() seem to be the only way to make + // KMenuBar keep the size it wants + d->min_size = QSize( ev->xclient.data.l[ 1 ], ev->xclient.data.l[ 2 ] ); +// kdDebug() << "MINSIZE:" << d->min_size << endl; + updateMenuBarSize(); + return true; + } + return QMenuBar::x11Event( ev ); +} +#endif + +void KMenuBar::updateMenuBarSize() + { + menuContentsChanged(); // trigger invalidating calculated size + resize( sizeHint()); // and resize to preferred size + } + +void KMenuBar::setFrameStyle( int style ) +{ + if( d->topLevel ) + d->frameStyle = style; + else + QMenuBar::setFrameStyle( style ); +} + +void KMenuBar::setLineWidth( int width ) +{ + if( d->topLevel ) + d->lineWidth = width; + else + QMenuBar::setLineWidth( width ); +} + +void KMenuBar::setMargin( int margin ) +{ + if( d->topLevel ) + d->margin = margin; + else + QMenuBar::setMargin( margin ); +} + +void KMenuBar::closeEvent( QCloseEvent* e ) +{ + if( d->topLevel ) + e->ignore(); // mainly for the fallback mode + else + QMenuBar::closeEvent( e ); +} + +void KMenuBar::drawContents( QPainter* p ) +{ + // Closes the BR77113 + // We need to overload this method to paint only the menu items + // This way when the KMenuBar is embedded in the menu applet it + // integrates correctly. + // + // Background mode and origin are set so late because of styles + // using the polish() method to modify these settings. + // + // Of course this hack can safely be removed when real transparency + // will be available + + if( !d->topLevel ) + { + QMenuBar::drawContents(p); + } + else + { + bool up_enabled = isUpdatesEnabled(); + BackgroundMode bg_mode = backgroundMode(); + BackgroundOrigin bg_origin = backgroundOrigin(); + + setUpdatesEnabled(false); + setBackgroundMode(X11ParentRelative); + setBackgroundOrigin(WindowOrigin); + + p->eraseRect( rect() ); + erase(); + + QColorGroup g = colorGroup(); + bool e; + + for ( int i=0; i<(int)count(); i++ ) + { + QMenuItem *mi = findItem( idAt( i ) ); + + if ( !mi->text().isNull() || mi->pixmap() ) + { + QRect r = itemRect(i); + if(r.isEmpty() || !mi->isVisible()) + continue; + + e = mi->isEnabledAndVisible(); + if ( e ) + g = isEnabled() ? ( isActiveWindow() ? palette().active() : + palette().inactive() ) : palette().disabled(); + else + g = palette().disabled(); + + bool item_active = ( actItem == i ); + + p->setClipRect(r); + + if( item_active ) + { + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled() && e) + flags |= QStyle::Style_Enabled; + if ( item_active ) + flags |= QStyle::Style_Active; + if ( item_active && actItemDown ) + flags |= QStyle::Style_Down; + flags |= QStyle::Style_HasFocus; + + style().drawControl(QStyle::CE_MenuBarItem, p, this, + r, g, flags, QStyleOption(mi)); + } + else + { + style().drawItem(p, r, AlignCenter | AlignVCenter | ShowPrefix, + g, e, mi->pixmap(), mi->text()); + } + } + } + + setBackgroundOrigin(bg_origin); + setBackgroundMode(bg_mode); + setUpdatesEnabled(up_enabled); + } +} + +void KMenuBar::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "kmenubar.moc" -- cgit v1.2.1