kdeui Library API Documentation

kmenubar.cpp

00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1997, 1998, 1999, 2000  Sven Radej (radej@kde.org)
00003     Copyright (C) 1997, 1998, 1999, 2000 Matthias Ettrich (ettrich@kde.org)
00004     Copyright (C) 1999, 2000 Daniel "Mosfet" Duley (mosfet@kde.org)
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019     Boston, MA 02111-1307, USA.
00020     */
00021 
00022 
00023 #ifndef INCLUDE_MENUITEM_DEF
00024 #define INCLUDE_MENUITEM_DEF
00025 #endif
00026 
00027 #include "config.h"
00028 #include <qevent.h>
00029 #include <qobjectlist.h>
00030 #include <qaccel.h>
00031 #include <qpainter.h>
00032 #include <qstyle.h>
00033 #include <qtimer.h>
00034 
00035 #include <kconfig.h>
00036 #include <kglobalsettings.h>
00037 #include <kmenubar.h>
00038 #include <kapplication.h>
00039 #include <kglobal.h>
00040 #include <kdebug.h>
00041 #include <kmanagerselection.h>
00042 
00043 #ifdef Q_WS_X11
00044 #include <kwin.h> 
00045 #include <kwinmodule.h> 
00046 #include <qxembed.h> 
00047 
00048 #include <X11/Xlib.h> 
00049 #include <X11/Xutil.h> 
00050 #include <X11/Xatom.h> 
00051 #endif
00052 
00053 /*
00054 
00055  Toplevel menubar (not for the fallback size handling done by itself):
00056  - should not alter position or set strut
00057  - every toplevel must have at most one matching topmenu
00058  - embedder won't allow shrinking below a certain size
00059  - must have WM_TRANSIENT_FOR pointing the its mainwindow
00060      - the exception is desktop's menubar, which can be transient for root window
00061        because of using root window as the desktop window
00062  - Fitts' Law
00063 
00064 */
00065 
00066 class KMenuBar::KMenuBarPrivate
00067 {
00068 public:
00069     KMenuBarPrivate()
00070     :   forcedTopLevel( false ),
00071         topLevel( false ),
00072         wasTopLevel( false ),
00073 #ifdef Q_WS_X11
00074         selection( NULL ),
00075 #endif
00076             min_size( 0, 0 )
00077     {
00078     }
00079     ~KMenuBarPrivate()
00080         {
00081 #ifdef Q_WS_X11
00082         delete selection;
00083 #endif
00084         }
00085     bool forcedTopLevel;
00086     bool topLevel;
00087     bool wasTopLevel; // when TLW is fullscreen, remember state
00088     int frameStyle; // only valid in toplevel mode
00089     int lineWidth;  // dtto
00090     int margin;     // dtto
00091     bool fallback_mode; // dtto
00092 #ifdef Q_WS_X11
00093     KSelectionWatcher* selection;
00094 #endif
00095     QTimer selection_timer;
00096     QSize min_size;
00097     static Atom makeSelectionAtom();
00098 };
00099 
00100 #ifdef Q_WS_X11
00101 static Atom selection_atom = None;
00102 static Atom msg_type_atom = None;
00103 
00104 static
00105 void initAtoms()
00106 {
00107     char nm[ 100 ];
00108     sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( qt_xdisplay()));
00109     char nm2[] = "_KDE_TOPMENU_MINSIZE";
00110     char* names[ 2 ] = { nm, nm2 };
00111     Atom atoms[ 2 ];
00112     XInternAtoms( qt_xdisplay(), names, 2, False, atoms );
00113     selection_atom = atoms[ 0 ];
00114     msg_type_atom = atoms[ 1 ];
00115 }
00116 #endif
00117 
00118 Atom KMenuBar::KMenuBarPrivate::makeSelectionAtom()
00119 {
00120 #ifdef Q_WS_X11
00121     if( selection_atom == None )
00122     initAtoms();
00123     return selection_atom;
00124 #else
00125     return 0;
00126 #endif
00127 }
00128 
00129 KMenuBar::KMenuBar(QWidget *parent, const char *name)
00130   : QMenuBar(parent, name)
00131 {
00132 #ifdef Q_WS_X11
00133     QXEmbed::initialize();
00134 #endif
00135     d = new KMenuBarPrivate;
00136     connect( &d->selection_timer, SIGNAL( timeout()),
00137         this, SLOT( selectionTimeout()));
00138 
00139 #if (QT_VERSION-0 >= 0x030200) // XRANDR support
00140     connect( qApp->desktop(), SIGNAL( resized( int )), SLOT( updateFallbackSize()));
00141 #endif
00142 
00143     if ( kapp )
00144         // toolbarAppearanceChanged(int) is sent when changing macstyle
00145         connect( kapp, SIGNAL(toolbarAppearanceChanged(int)),
00146             this, SLOT(slotReadConfig()));
00147 
00148     slotReadConfig();
00149 }
00150 
00151 KMenuBar::~KMenuBar()
00152 {
00153   delete d;
00154 }
00155 
00156 void KMenuBar::setTopLevelMenu(bool top_level)
00157 {
00158   d->forcedTopLevel = top_level;
00159   setTopLevelMenuInternal( top_level );
00160 }
00161 
00162 void KMenuBar::setTopLevelMenuInternal(bool top_level)
00163 {
00164   if (d->forcedTopLevel)
00165     top_level = true;
00166 
00167   d->wasTopLevel = top_level;
00168   if( parentWidget()
00169       && parentWidget()->topLevelWidget()->isFullScreen())
00170     top_level = false;
00171 
00172   if ( isTopLevelMenu() == top_level )
00173     return;
00174   d->topLevel = top_level;
00175   if ( isTopLevelMenu() )
00176   {
00177 #ifdef Q_WS_X11
00178       d->selection = new KSelectionWatcher( KMenuBarPrivate::makeSelectionAtom(),
00179           DefaultScreen( qt_xdisplay()));
00180       connect( d->selection, SIGNAL( newOwner( Window )),
00181           this, SLOT( updateFallbackSize()));
00182       connect( d->selection, SIGNAL( lostOwner()),
00183           this, SLOT( updateFallbackSize()));
00184 #endif
00185       d->frameStyle = frameStyle();
00186       d->lineWidth = lineWidth();
00187       d->margin = margin();
00188       d->fallback_mode = false;
00189       bool wasShown = !isHidden();
00190       reparent( parentWidget(), WType_TopLevel | WStyle_Tool | WStyle_Customize | WStyle_NoBorder, QPoint(0,0), false );
00191 #ifdef Q_WS_X11
00192       KWin::setType( winId(), NET::TopMenu );
00193       if( parentWidget())
00194           XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId());
00195 #endif
00196       QMenuBar::setFrameStyle( NoFrame );
00197       QMenuBar::setLineWidth( 0 );
00198       QMenuBar::setMargin( 0 );
00199       updateFallbackSize();
00200       d->min_size = QSize( 0, 0 );
00201       if( parentWidget() && !parentWidget()->isTopLevel())
00202           setShown( parentWidget()->isVisible());
00203       else if ( wasShown )
00204           show();
00205   } else
00206   {
00207 #ifdef Q_WS_X11
00208       delete d->selection;
00209       d->selection = NULL;
00210 #endif
00211       setBackgroundMode( PaletteButton );
00212       setFrameStyle( d->frameStyle );
00213       setLineWidth( d->lineWidth );
00214       setMargin( d->margin );
00215       setMinimumSize( 0, 0 );
00216       setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
00217       updateMenuBarSize();
00218       if ( parentWidget() )
00219           reparent( parentWidget(), QPoint(0,0), !isHidden());
00220   }
00221 }
00222 
00223 bool KMenuBar::isTopLevelMenu() const
00224 {
00225   return d->topLevel;
00226 }
00227 
00228 // KDE4 remove
00229 void KMenuBar::show()
00230 {
00231     QMenuBar::show();
00232 }
00233 
00234 void KMenuBar::slotReadConfig()
00235 {
00236   KConfig *config = KGlobal::config();
00237   KConfigGroupSaver saver( config, "KDE" );
00238   setTopLevelMenuInternal( config->readBoolEntry( "macStyle", false ) );
00239 }
00240 
00241 bool KMenuBar::eventFilter(QObject *obj, QEvent *ev)
00242 {
00243     if ( d->topLevel )
00244     {
00245     if ( parentWidget() && obj == parentWidget()->topLevelWidget()  )
00246         {
00247         if( ev->type() == QEvent::Resize )
00248         return false; // ignore resizing of parent, QMenuBar would try to adjust size
00249         if ( ev->type() == QEvent::Accel || ev->type() == QEvent::AccelAvailable )
00250             {
00251         if ( QApplication::sendEvent( topLevelWidget(), ev ) )
00252             return true;
00253         }
00254             if(ev->type() == QEvent::ShowFullScreen )
00255                 // will update the state properly
00256                 setTopLevelMenuInternal( d->topLevel );
00257         }
00258         if( parentWidget() && obj == parentWidget() && ev->type() == QEvent::Reparent )
00259             {
00260 #ifdef Q_WS_X11
00261             XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId());
00262 #else
00263             //TODO: WIN32?
00264 #endif
00265             setShown( parentWidget()->isTopLevel() || parentWidget()->isVisible());
00266             }
00267         if( parentWidget() && !parentWidget()->isTopLevel() && obj == parentWidget())
00268         { // if the parent is not toplevel, KMenuBar needs to match its visibility status
00269             if( ev->type() == QEvent::Show )
00270                 {
00271 #ifdef Q_WS_X11
00272                 XSetTransientForHint( qt_xdisplay(), winId(), parentWidget()->topLevelWidget()->winId());
00273 #else
00274                 //TODO: WIN32?
00275 #endif
00276                 show();
00277                 }
00278             if( ev->type() == QEvent::Hide )
00279                 hide();
00280     }
00281     }
00282     else
00283     {
00284         if( parentWidget() && obj == parentWidget()->topLevelWidget())
00285         {
00286 #if QT_VERSION >= 0x030300
00287             if( ev->type() == QEvent::WindowStateChange
00288 #else
00289             if( ( ev->type() == QEvent::ShowNormal || ev->type() == QEvent::ShowMaximized )
00290 #endif
00291                 && !parentWidget()->topLevelWidget()->isFullScreen() )
00292                 setTopLevelMenuInternal( d->wasTopLevel );
00293         }
00294     }
00295     return QMenuBar::eventFilter( obj, ev );
00296 }
00297 
00298 // KDE4 remove
00299 void KMenuBar::showEvent( QShowEvent *e )
00300 {
00301     QMenuBar::showEvent(e);
00302 }
00303 
00304 void KMenuBar::updateFallbackSize()
00305 {
00306     if( !d->topLevel )
00307     return;
00308 #ifdef Q_WS_X11
00309     if( d->selection->owner() != None )
00310 #endif
00311     { // somebody is managing us, don't mess anything, undo changes
00312       // done in fallback mode if needed
00313         d->selection_timer.stop();
00314         if( d->fallback_mode )
00315         {
00316             d->fallback_mode = false;
00317 //            KWin::setStrut( winId(), 0, 0, 0, 0 ); KWin will set strut as it will see fit
00318             setMinimumSize( 0, 0 );
00319             setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
00320             updateMenuBarSize();
00321         }
00322     return;
00323     }
00324     if( d->selection_timer.isActive())
00325     return;
00326     d->selection_timer.start( 100, true );
00327 }
00328 
00329 void KMenuBar::selectionTimeout()
00330 { // nobody is managing us, handle resizing
00331     if ( d->topLevel )
00332     {
00333         d->fallback_mode = true; // KMenuBar is handling its position itself
00334         KConfigGroup xineramaConfig(KGlobal::config(),"Xinerama");
00335         int screen = xineramaConfig.readNumEntry("MenubarScreen",
00336             QApplication::desktop()->screenNumber(QPoint(0,0)) );
00337         QRect area = QApplication::desktop()->screenGeometry(screen);
00338 #if QT_VERSION < 0x030200
00339         int margin = frameWidth() + 2;
00340 #else  // hopefully I'll manage to persuade TT on Fitts' Law for QMenuBar for Qt-3.2
00341         int margin = 0;
00342 #endif
00343     move(area.left() - margin, area.top() - margin); 
00344         setFixedSize(area.width() + 2* margin , heightForWidth( area.width() + 2 * margin ) );
00345 #ifdef Q_WS_X11
00346         int strut_height = height() - margin;
00347         if( strut_height < 0 )
00348             strut_height = 0;
00349         KWin::setStrut( winId(), 0, 0, strut_height, 0 );
00350 #endif
00351     }
00352 }
00353 
00354 int KMenuBar::block_resize = 0;
00355 
00356 void KMenuBar::resizeEvent( QResizeEvent *e )
00357 {
00358     if( e->spontaneous() && d->topLevel && !d->fallback_mode )
00359         {
00360         ++block_resize; // do not respond with configure request to ConfigureNotify event
00361         QMenuBar::resizeEvent(e); // to avoid possible infinite loop
00362         --block_resize;
00363         }
00364     else
00365         QMenuBar::resizeEvent(e);
00366 }
00367 
00368 void KMenuBar::setGeometry( const QRect& r )
00369 {
00370     setGeometry( r.x(), r.y(), r.width(), r.height() );
00371 }
00372 
00373 void KMenuBar::setGeometry( int x, int y, int w, int h )
00374 {
00375     if( block_resize > 0 )
00376     {
00377     move( x, y );
00378     return;
00379     }
00380     checkSize( w, h );
00381     if( geometry() != QRect( x, y, w, h ))
00382         QMenuBar::setGeometry( x, y, w, h );
00383 }
00384 
00385 void KMenuBar::resize( int w, int h )
00386 {
00387     if( block_resize > 0 )
00388     return;
00389     checkSize( w, h );
00390     if( size() != QSize( w, h ))
00391         QMenuBar::resize( w, h );
00392 //    kdDebug() << "RS:" << w << ":" << h << ":" << width() << ":" << height() << ":" << minimumWidth() << ":" << minimumHeight() << endl;
00393 }
00394 
00395 void KMenuBar::checkSize( int& w, int& h )
00396 {
00397     if( !d->topLevel || d->fallback_mode )
00398     return;
00399     QSize s = sizeHint();
00400     w = s.width();
00401     h = s.height();
00402     // This is not done as setMinimumSize(), because that would set the minimum
00403     // size in WM_NORMAL_HINTS, and KWin would not allow changing to smaller size
00404     // anymore
00405     w = KMAX( w, d->min_size.width());
00406     h = KMAX( h, d->min_size.height());
00407 }
00408 
00409 // QMenuBar's sizeHint() gives wrong size (insufficient width), which causes wrapping in the kicker applet
00410 QSize KMenuBar::sizeHint() const
00411 {
00412     if( !d->topLevel || block_resize > 0 )
00413         return QMenuBar::sizeHint();
00414     // Since QMenuBar::sizeHint() may indirectly call resize(),
00415     // avoid infinite recursion.
00416     ++block_resize;
00417     // find the minimum useful height, and enlarge the width until the menu fits in that height (one row)
00418     int h = heightForWidth( 1000000 );
00419     int w = QMenuBar::sizeHint().width();
00420     // optimization - don't call heightForWidth() too many times
00421     while( heightForWidth( w + 12 ) > h )
00422         w += 12;
00423     while( heightForWidth( w + 4 ) > h )
00424         w += 4;
00425     while( heightForWidth( w ) > h )
00426         ++w;
00427     --block_resize;
00428     return QSize( w, h );
00429 }
00430 
00431 #ifdef Q_WS_X11
00432 bool KMenuBar::x11Event( XEvent* ev )
00433 {
00434     if( ev->type == ClientMessage && ev->xclient.message_type == msg_type_atom
00435         && ev->xclient.window == winId())
00436     {
00437         // QMenuBar is trying really hard to keep the size it deems right.
00438         // Forcing minimum size and blocking resizing to match parent size
00439         // in checkResizingToParent() seem to be the only way to make
00440         // KMenuBar keep the size it wants
00441     d->min_size = QSize( ev->xclient.data.l[ 1 ], ev->xclient.data.l[ 2 ] );
00442 //        kdDebug() << "MINSIZE:" << d->min_size << endl;
00443         updateMenuBarSize();
00444     return true;
00445     }
00446     return QMenuBar::x11Event( ev );
00447 }
00448 #endif
00449 
00450 void KMenuBar::updateMenuBarSize()
00451     {
00452     menuContentsChanged(); // trigger invalidating calculated size
00453     resize( sizeHint());   // and resize to preferred size
00454     }
00455 
00456 void KMenuBar::setFrameStyle( int style )
00457 {
00458     if( d->topLevel )
00459     d->frameStyle = style;
00460     else
00461     QMenuBar::setFrameStyle( style );
00462 }
00463 
00464 void KMenuBar::setLineWidth( int width )
00465 {
00466     if( d->topLevel )
00467     d->lineWidth = width;
00468     else
00469     QMenuBar::setLineWidth( width );
00470 }
00471 
00472 void KMenuBar::setMargin( int margin )
00473 {
00474     if( d->topLevel )
00475     d->margin = margin;
00476     else
00477     QMenuBar::setMargin( margin );
00478 }
00479 
00480 void KMenuBar::closeEvent( QCloseEvent* e )
00481 {
00482     if( d->topLevel )
00483         e->ignore(); // mainly for the fallback mode 
00484     else
00485         QMenuBar::closeEvent( e );
00486 }
00487 
00488 void KMenuBar::drawContents( QPainter* p )
00489 {
00490     // Closes the BR77113
00491     // We need to overload this method to paint only the menu items
00492     // This way when the KMenuBar is embedded in the menu applet it
00493     // integrates correctly.
00494     //
00495     // Background mode and origin are set so late because of styles
00496     // using the polish() method to modify these settings.
00497     //
00498     // Of course this hack can safely be removed when real transparency
00499     // will be available
00500 
00501     if( !d->topLevel )
00502     {
00503         QMenuBar::drawContents(p);
00504     }
00505     else
00506     {
00507         bool up_enabled = isUpdatesEnabled();
00508         BackgroundMode bg_mode = backgroundMode();
00509         BackgroundOrigin bg_origin = backgroundOrigin();
00510         
00511         setUpdatesEnabled(false);
00512         setBackgroundMode(X11ParentRelative);
00513         setBackgroundOrigin(WindowOrigin);
00514 
00515     p->eraseRect( rect() );
00516     erase();
00517         
00518         QColorGroup g = colorGroup();
00519         bool e;
00520 
00521         for ( int i=0; i<(int)count(); i++ )
00522         {
00523             QMenuItem *mi = findItem( idAt( i ) );
00524 
00525             if ( !mi->text().isNull() || mi->pixmap() )
00526             {
00527                 QRect r = itemRect(i);
00528                 if(r.isEmpty() || !mi->isVisible())
00529                     continue;
00530 
00531                 e = mi->isEnabledAndVisible();
00532                 if ( e )
00533                     g = isEnabled() ? ( isActiveWindow() ? palette().active() :
00534                                         palette().inactive() ) : palette().disabled();
00535                 else
00536                     g = palette().disabled();
00537 
00538                 bool item_active = ( actItem ==  i );
00539 
00540                 p->setClipRect(r);
00541 
00542                 if( item_active )
00543                 {
00544                     QStyle::SFlags flags = QStyle::Style_Default;
00545                     if (isEnabled() && e)
00546                         flags |= QStyle::Style_Enabled;
00547                     if ( item_active )
00548                         flags |= QStyle::Style_Active;
00549                     if ( item_active && actItemDown )
00550                         flags |= QStyle::Style_Down;
00551                     flags |= QStyle::Style_HasFocus;
00552 
00553                     style().drawControl(QStyle::CE_MenuBarItem, p, this,
00554                                         r, g, flags, QStyleOption(mi));
00555                 }
00556                 else
00557                 {
00558                     style().drawItem(p, r, AlignCenter | AlignVCenter | ShowPrefix,
00559                                      g, e, mi->pixmap(), mi->text());
00560                 }
00561             }
00562         }
00563 
00564         setBackgroundOrigin(bg_origin);
00565         setBackgroundMode(bg_mode);
00566         setUpdatesEnabled(up_enabled);
00567     }
00568 }
00569 
00570 void KMenuBar::virtual_hook( int, void* )
00571 { /*BASE::virtual_hook( id, data );*/ }
00572 
00573 #include "kmenubar.moc"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Aug 2 12:04:25 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003