/* 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 #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 TDESelectionWatcher* 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( tqt_xdisplay())); char nm2[] = "_KDE_TOPMENU_MINSIZE"; char* names[ 2 ] = { nm, nm2 }; Atom atoms[ 2 ]; XInternAtoms( tqt_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()->topLevelWidget()->isFullScreen()) top_level = false; if ( isTopLevelMenu() == top_level ) return; d->topLevel = top_level; if ( isTopLevelMenu() ) { #ifdef Q_WS_X11 d->selection = new TDESelectionWatcher( KMenuBarPrivate::makeSelectionAtom(), DefaultScreen( tqt_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( tqt_xdisplay(), winId(), parentWidget()->topLevelWidget()->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( TQWIDGETSIZE_MAX, TQWIDGETSIZE_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() { TDEConfig *config = TDEGlobal::config(); TDEConfigGroupSaver 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()->topLevelWidget()) ) { 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( topLevelWidget(), 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( tqt_xdisplay(), winId(), parentWidget()->topLevelWidget()->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( tqt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId()); #else //TODO: WIN32? #endif show(); } if( ev->type() == TQEvent::Hide ) hide(); } } else { if( parentWidget() && TQT_BASE_OBJECT(obj) == TQT_BASE_OBJECT(parentWidget()->topLevelWidget())) { if( ev->type() == TQEvent::WindowStateChange && !parentWidget()->topLevelWidget()->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( TQWIDGETSIZE_MAX, TQWIDGETSIZE_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 TDEConfigGroup xineramaConfig(TDEGlobal::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 = 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 TQSize KMenuBar::sizeHint() const { if( !d->topLevel || block_resize > 0 ) return TQMenuBar::sizeHint(); // Since TQMenuBar::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 = TQMenuBar::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 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( sizeHint()); // 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(); setUpdatesEnabled(false); setBackgroundMode(X11ParentRelative); setBackgroundOrigin(WindowOrigin); p->eraseRect( rect() ); erase(); TQColorGroup g = colorGroup(); bool e; for ( int i=0; i<(int)count(); i++ ) { TQMenuItem *mi = findItem( idAt( i ) ); if ( !mi->text().isNull() || mi->pixmap() ) { TQRect 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 ) { 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; style().drawControl(TQStyle::CE_MenuBarItem, p, this, r, g, flags, TQStyleOption(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 "tdemenubar.moc"