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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:10:13 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003