diff options
Diffstat (limited to 'tdeui/kmenubar.cpp')
-rw-r--r-- | tdeui/kmenubar.cpp | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/tdeui/kmenubar.cpp b/tdeui/kmenubar.cpp new file mode 100644 index 000000000..2014142e8 --- /dev/null +++ b/tdeui/kmenubar.cpp @@ -0,0 +1,571 @@ +/* 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 <tqevent.h> +#include <tqobjectlist.h> +#include <tqaccel.h> +#include <tqpainter.h> +#include <tqstyle.h> +#include <tqtimer.h> + +#include <kconfig.h> +#include <kglobalsettings.h> +#include <kmenubar.h> +#include <kapplication.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kmanagerselection.h> + +#ifdef Q_WS_X11 +#include <kwin.h> +#include <kwinmodule.h> +#include <qxembed.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/Xatom.h> + +#ifndef None +#define None 0L +#endif +#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 + TQTimer selection_timer; + TQSize 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(TQWidget *parent, const char *name) + : TQMenuBar(parent, name) +{ +#ifdef Q_WS_X11 + QXEmbed::initialize(); +#endif + d = new KMenuBarPrivate; + connect( &d->selection_timer, TQT_SIGNAL( timeout()), + this, TQT_SLOT( selectionTimeout())); + + connect( tqApp->desktop(), TQT_SIGNAL( resized( int )), TQT_SLOT( updateFallbackSize())); + + if ( kapp ) + // toolbarAppearanceChanged(int) is sent when changing macstyle + connect( kapp, TQT_SIGNAL(toolbarAppearanceChanged(int)), + this, TQT_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()->tqtopLevelWidget()->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, TQT_SIGNAL( newOwner( Window )), + this, TQT_SLOT( updateFallbackSize())); + connect( d->selection, TQT_SIGNAL( lostOwner()), + this, TQT_SLOT( updateFallbackSize())); +#endif + d->frameStyle = frameStyle(); + d->lineWidth = lineWidth(); + d->margin = margin(); + d->fallback_mode = false; + bool wasShown = !isHidden(); + reparent( parentWidget(), (WFlags)(WType_TopLevel | WStyle_Tool | WStyle_Customize | WStyle_NoBorder), TQPoint(0,0), false ); +#ifdef Q_WS_X11 + KWin::setType( winId(), NET::TopMenu ); + if( parentWidget()) + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->tqtopLevelWidget()->winId()); +#endif + TQMenuBar::setFrameStyle( NoFrame ); + TQMenuBar::setLineWidth( 0 ); + TQMenuBar::setMargin( 0 ); + updateFallbackSize(); + d->min_size = TQSize( 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(), TQPoint(0,0), !isHidden()); + } +} + +bool KMenuBar::isTopLevelMenu() const +{ + return d->topLevel; +} + +// KDE4 remove +void KMenuBar::show() +{ + TQMenuBar::show(); +} + +void KMenuBar::slotReadConfig() +{ + KConfig *config = KGlobal::config(); + KConfigGroupSaver saver( config, "KDE" ); + setTopLevelMenuInternal( config->readBoolEntry( "macStyle", false ) ); +} + +bool KMenuBar::eventFilter(TQObject *obj, TQEvent *ev) +{ + if ( d->topLevel ) + { + if ( parentWidget() && TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(parentWidget()->tqtopLevelWidget()) ) + { + if( ev->type() == TQEvent::Resize ) + return false; // ignore resizing of parent, TQMenuBar would try to adjust size + if ( ev->type() == TQEvent::Accel || ev->type() == TQEvent::AccelAvailable ) + { + if ( TQApplication::sendEvent( tqtopLevelWidget(), ev ) ) + return true; + } + if(ev->type() == TQEvent::ShowFullScreen ) + // will update the state properly + setTopLevelMenuInternal( d->topLevel ); + } + if( parentWidget() && TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(parentWidget()) && ev->type() == TQEvent::Reparent ) + { +#ifdef Q_WS_X11 + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->tqtopLevelWidget()->winId()); +#else + //TODO: WIN32? +#endif + setShown( parentWidget()->isTopLevel() || parentWidget()->isVisible()); + } + if( parentWidget() && !parentWidget()->isTopLevel() && TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(parentWidget())) + { // if the parent is not toplevel, KMenuBar needs to match its visibility status + if( ev->type() == TQEvent::Show ) + { +#ifdef Q_WS_X11 + XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->tqtopLevelWidget()->winId()); +#else + //TODO: WIN32? +#endif + show(); + } + if( ev->type() == TQEvent::Hide ) + hide(); + } + } + else + { + if( parentWidget() && TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(parentWidget()->tqtopLevelWidget())) + { + if( ev->type() == TQEvent::WindowStateChange + && !parentWidget()->tqtopLevelWidget()->isFullScreen() ) + setTopLevelMenuInternal( d->wasTopLevel ); + } + } + return TQMenuBar::eventFilter( obj, ev ); +} + +// KDE4 remove +void KMenuBar::showEvent( TQShowEvent *e ) +{ + TQMenuBar::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", + TQApplication::desktop()->screenNumber(TQPoint(0,0)) ); + TQRect area; + if (kapp->desktop()->numScreens() < 2) + area = kapp->desktop()->geometry(); + else + area = kapp->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( TQResizeEvent *e ) +{ + if( e->spontaneous() && d->topLevel && !d->fallback_mode ) + { + ++block_resize; // do not respond with configure request to ConfigureNotify event + TQMenuBar::resizeEvent(e); // to avoid possible infinite loop + --block_resize; + } + else + TQMenuBar::resizeEvent(e); +} + +void KMenuBar::setGeometry( const TQRect& 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() != TQRect( x, y, w, h )) + TQMenuBar::setGeometry( x, y, w, h ); +} + +void KMenuBar::resize( int w, int h ) +{ + if( block_resize > 0 ) + return; + checkSize( w, h ); + if( size() != TQSize( w, h )) + TQMenuBar::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; + TQSize s = tqsizeHint(); + 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 tqsizeHint() gives wrong size (insufficient width), which causes wrapping in the kicker applet +TQSize KMenuBar::tqsizeHint() const +{ + if( !d->topLevel || block_resize > 0 ) + return TQMenuBar::tqsizeHint(); + // Since TQMenuBar::tqsizeHint() 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 = TQMenuBar::tqsizeHint().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 TQSize( 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()) + { + // TQMenuBar 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 = TQSize( ev->xclient.data.l[ 1 ], ev->xclient.data.l[ 2 ] ); +// kdDebug() << "MINSIZE:" << d->min_size << endl; + updateMenuBarSize(); + return true; + } + return TQMenuBar::x11Event( ev ); +} +#endif + +void KMenuBar::updateMenuBarSize() + { + menuContentsChanged(); // trigger invalidating calculated size + resize( tqsizeHint()); // and resize to preferred size + } + +void KMenuBar::setFrameStyle( int style ) +{ + if( d->topLevel ) + d->frameStyle = style; + else + TQMenuBar::setFrameStyle( style ); +} + +void KMenuBar::setLineWidth( int width ) +{ + if( d->topLevel ) + d->lineWidth = width; + else + TQMenuBar::setLineWidth( width ); +} + +void KMenuBar::setMargin( int margin ) +{ + if( d->topLevel ) + d->margin = margin; + else + TQMenuBar::setMargin( margin ); +} + +void KMenuBar::closeEvent( TQCloseEvent* e ) +{ + if( d->topLevel ) + e->ignore(); // mainly for the fallback mode + else + TQMenuBar::closeEvent( e ); +} + +void KMenuBar::drawContents( TQPainter* 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 ) + { + TQMenuBar::drawContents(p); + } + else + { + bool up_enabled = isUpdatesEnabled(); + BackgroundMode bg_mode = backgroundMode(); + BackgroundOrigin bg_origin = backgroundOrigin(); + + tqsetUpdatesEnabled(false); + setBackgroundMode(X11ParentRelative); + setBackgroundOrigin(WindowOrigin); + + p->eraseRect( rect() ); + erase(); + + TQColorGroup g = tqcolorGroup(); + bool e; + + for ( int i=0; i<(int)count(); i++ ) + { + TQMenuItem *mi = findItem( idAt( i ) ); + + if ( !mi->text().isNull() || mi->pixmap() ) + { + TQRect r = tqitemRect(i); + if(r.isEmpty() || !mi->isVisible()) + continue; + + e = mi->isEnabledAndVisible(); + if ( e ) + g = isEnabled() ? ( isActiveWindow() ? tqpalette().active() : + tqpalette().inactive() ) : tqpalette().disabled(); + else + g = tqpalette().disabled(); + + bool item_active = ( actItem == i ); + + p->setClipRect(r); + + if( item_active ) + { + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled() && e) + flags |= TQStyle::Style_Enabled; + if ( item_active ) + flags |= TQStyle::Style_Active; + if ( item_active && actItemDown ) + flags |= TQStyle::Style_Down; + flags |= TQStyle::Style_HasFocus; + + tqstyle().tqdrawControl(TQStyle::CE_MenuBarItem, p, this, + r, g, flags, TQStyleOption(mi)); + } + else + { + tqstyle().drawItem(p, r, AlignCenter | AlignVCenter | ShowPrefix, + g, e, mi->pixmap(), mi->text()); + } + } + } + + setBackgroundOrigin(bg_origin); + setBackgroundMode(bg_mode); + tqsetUpdatesEnabled(up_enabled); + } +} + +void KMenuBar::virtual_hook( int, void* ) +{ /*BASE::virtual_hook( id, data );*/ } + +#include "kmenubar.moc" |