kdeui Library API Documentation

kmenubar.cpp

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