KDEUI
kmenubar.cpp
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 00023 #include "kmenubar.h" 00024 00025 #include <config.h> 00026 00027 #include <stdio.h> 00028 00029 #include <QtCore/QObject> 00030 #include <QtCore/QTimer> 00031 #include <QtGui/QActionEvent> 00032 #include <QtGui/QDesktopWidget> 00033 #include <QtGui/QMenuItem> 00034 #include <QtGui/QPainter> 00035 #include <QtGui/QStyle> 00036 #include <QtGui/QStyleOptionMenuItem> 00037 00038 #include <kconfig.h> 00039 #include <kglobalsettings.h> 00040 #include <kapplication.h> 00041 #include <kglobal.h> 00042 #include <kdebug.h> 00043 #include <kmanagerselection.h> 00044 #include <kconfiggroup.h> 00045 #include <kwindowsystem.h> 00046 00047 #ifdef Q_WS_X11 00048 #include <qx11info_x11.h> 00049 00050 #include <X11/Xlib.h> 00051 #include <X11/Xutil.h> 00052 #include <X11/Xatom.h> 00053 #endif 00054 00055 /* 00056 00057 Toplevel menubar (not for the fallback size handling done by itself): 00058 - should not alter position or set strut 00059 - every toplevel must have at most one matching topmenu 00060 - embedder won't allow shrinking below a certain size 00061 - must have WM_TRANSIENT_FOR pointing the its mainwindow 00062 - the exception is desktop's menubar, which can be transient for root window 00063 because of using root window as the desktop window 00064 - Fitts' Law 00065 00066 */ 00067 00068 static int block_resize = 0; 00069 00070 class KMenuBar::KMenuBarPrivate 00071 { 00072 public: 00073 KMenuBarPrivate() 00074 : forcedTopLevel( false ), 00075 topLevel( false ), 00076 wasTopLevel( false ), 00077 #ifdef Q_WS_X11 00078 selection( NULL ), 00079 #endif 00080 min_size( 0, 0 ) 00081 { 00082 } 00083 ~KMenuBarPrivate() 00084 { 00085 #ifdef Q_WS_X11 00086 delete selection; 00087 #endif 00088 } 00089 int frameStyle; // only valid in toplevel mode 00090 int lineWidth; // dtto 00091 int margin; // dtto 00092 bool fallback_mode : 1; // dtto 00093 00094 bool forcedTopLevel : 1; 00095 bool topLevel : 1; 00096 bool wasTopLevel : 1; // when TLW is fullscreen, remember state 00097 00098 #ifdef Q_WS_X11 00099 KSelectionWatcher* selection; 00100 #endif 00101 QTimer selection_timer; 00102 QSize min_size; 00103 static Atom makeSelectionAtom(); 00104 }; 00105 00106 #ifdef Q_WS_X11 00107 static Atom selection_atom = None; 00108 static Atom msg_type_atom = None; 00109 00110 static 00111 void initAtoms() 00112 { 00113 char nm[ 100 ]; 00114 sprintf( nm, "_KDE_TOPMENU_OWNER_S%d", DefaultScreen( QX11Info::display())); 00115 char nm2[] = "_KDE_TOPMENU_MINSIZE"; 00116 char* names[ 2 ] = { nm, nm2 }; 00117 Atom atoms[ 2 ]; 00118 XInternAtoms( QX11Info::display(), names, 2, False, atoms ); 00119 selection_atom = atoms[ 0 ]; 00120 msg_type_atom = atoms[ 1 ]; 00121 } 00122 #endif 00123 00124 Atom KMenuBar::KMenuBarPrivate::makeSelectionAtom() 00125 { 00126 #ifdef Q_WS_X11 00127 if( selection_atom == None ) 00128 initAtoms(); 00129 return selection_atom; 00130 #else 00131 return 0; 00132 #endif 00133 } 00134 00135 KMenuBar::KMenuBar(QWidget *parent) 00136 : QMenuBar(parent), d(new KMenuBarPrivate) 00137 { 00138 connect( &d->selection_timer, SIGNAL( timeout()), 00139 this, SLOT( selectionTimeout())); 00140 00141 connect( qApp->desktop(), SIGNAL( resized( int )), SLOT( updateFallbackSize())); 00142 00143 // toolbarAppearanceChanged(int) is sent when changing macstyle 00144 connect( KGlobalSettings::self(), SIGNAL(toolbarAppearanceChanged(int)), 00145 this, SLOT(slotReadConfig())); 00146 00147 slotReadConfig(); 00148 } 00149 00150 KMenuBar::~KMenuBar() 00151 { 00152 delete d; 00153 } 00154 00155 void KMenuBar::setTopLevelMenu(bool top_level) 00156 { 00157 d->forcedTopLevel = top_level; 00158 setTopLevelMenuInternal( top_level ); 00159 } 00160 00161 void KMenuBar::setTopLevelMenuInternal(bool top_level) 00162 { 00163 if (d->forcedTopLevel) 00164 top_level = true; 00165 00166 d->wasTopLevel = top_level; 00167 if( parentWidget() 00168 && parentWidget()->topLevelWidget()->isFullScreen()) 00169 top_level = false; 00170 00171 if ( isTopLevelMenu() == top_level ) 00172 return; 00173 d->topLevel = top_level; 00174 if ( isTopLevelMenu() ) 00175 { 00176 #ifdef Q_WS_X11 00177 d->selection = new KSelectionWatcher( KMenuBarPrivate::makeSelectionAtom(), 00178 DefaultScreen( QX11Info::display())); 00179 connect( d->selection, SIGNAL( newOwner( Window )), 00180 this, SLOT( updateFallbackSize())); 00181 connect( d->selection, SIGNAL( lostOwner()), 00182 this, SLOT( updateFallbackSize())); 00183 #endif 00184 d->frameStyle = 0; //frameStyle(); 00185 d->lineWidth = 0; //lineWidth(); 00186 d->margin = 0; //margin(); 00187 d->fallback_mode = false; 00188 bool wasShown = !isHidden(); 00189 setParent(parentWidget(), Qt::Window | Qt::Tool | Qt::FramelessWindowHint); 00190 setGeometry(0,0,width(),height()); 00191 #ifdef Q_WS_X11 00192 KWindowSystem::setType( winId(), NET::TopMenu ); 00193 #endif 00194 if( parentWidget()) 00195 KWindowSystem::setMainWindow( this, parentWidget()->topLevelWidget()->winId()); 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 setVisible( 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 setAttribute(Qt::WA_NoSystemBackground, false); 00212 setBackgroundRole(QPalette::Button); 00213 setFrameStyle( d->frameStyle ); 00214 setLineWidth( d->lineWidth ); 00215 setMargin( d->margin ); 00216 setMinimumSize( 0, 0 ); 00217 setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00218 updateMenuBarSize(); 00219 if ( parentWidget() ) 00220 setParent( parentWidget() ); 00221 } 00222 } 00223 00224 bool KMenuBar::isTopLevelMenu() const 00225 { 00226 return d->topLevel; 00227 } 00228 00229 00230 void KMenuBar::slotReadConfig() 00231 { 00232 KConfigGroup cg( KGlobal::config(), "KDE" ); 00233 setTopLevelMenuInternal( cg.readEntry( "macStyle", false ) ); 00234 } 00235 00236 bool KMenuBar::eventFilter(QObject *obj, QEvent *ev) 00237 { 00238 if ( d->topLevel ) 00239 { 00240 if ( parentWidget() && obj == parentWidget()->topLevelWidget() ) 00241 { 00242 if( ev->type() == QEvent::Resize ) 00243 return false; // ignore resizing of parent, QMenuBar would try to adjust size 00244 #ifdef QT3_SUPPORT 00245 if ( ev->type() == QEvent::Accel || ev->type() == QEvent::AccelAvailable ) 00246 { 00247 if ( QApplication::sendEvent( topLevelWidget(), ev ) ) 00248 return true; 00249 } 00250 #endif 00251 /* FIXME QEvent::ShowFullScreen is no more 00252 if(ev->type() == QEvent::ShowFullScreen ) 00253 // will update the state properly 00254 setTopLevelMenuInternal( d->topLevel ); 00255 */ 00256 } 00257 if( parentWidget() && obj == parentWidget() && ev->type() == QEvent::ParentChange ) 00258 { 00259 KWindowSystem::setMainWindow( this, parentWidget()->topLevelWidget()->winId()); 00260 setVisible( parentWidget()->isTopLevel() || parentWidget()->isVisible()); 00261 } 00262 if( parentWidget() && !parentWidget()->isTopLevel() && obj == parentWidget()) 00263 { // if the parent is not toplevel, KMenuBar needs to match its visibility status 00264 if( ev->type() == QEvent::Show ) 00265 { 00266 KWindowSystem::setMainWindow( this, parentWidget()->topLevelWidget()->winId()); 00267 show(); 00268 } 00269 if( ev->type() == QEvent::Hide ) 00270 hide(); 00271 } 00272 } 00273 else 00274 { 00275 if( parentWidget() && obj == parentWidget()->topLevelWidget()) 00276 { 00277 if( ev->type() == QEvent::WindowStateChange 00278 && !parentWidget()->topLevelWidget()->isFullScreen() ) 00279 setTopLevelMenuInternal( d->wasTopLevel ); 00280 } 00281 } 00282 return QMenuBar::eventFilter( obj, ev ); 00283 } 00284 00285 00286 void KMenuBar::updateFallbackSize() 00287 { 00288 if( !d->topLevel ) 00289 return; 00290 #ifdef Q_WS_X11 00291 if( d->selection->owner() != None ) 00292 #endif 00293 { // somebody is managing us, don't mess anything, undo changes 00294 // done in fallback mode if needed 00295 d->selection_timer.stop(); 00296 if( d->fallback_mode ) 00297 { 00298 d->fallback_mode = false; 00299 KWindowSystem::setStrut( winId(), 0, 0, 0, 0 ); 00300 setMinimumSize( 0, 0 ); 00301 setMaximumSize( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX ); 00302 updateMenuBarSize(); 00303 } 00304 return; 00305 } 00306 if( d->selection_timer.isActive()) 00307 return; 00308 d->selection_timer.setInterval(100); 00309 d->selection_timer.setSingleShot(true); 00310 d->selection_timer.start(); 00311 } 00312 00313 void KMenuBar::selectionTimeout() 00314 { // nobody is managing us, handle resizing 00315 if ( d->topLevel ) 00316 { 00317 d->fallback_mode = true; // KMenuBar is handling its position itself 00318 KConfigGroup xineramaConfig(KGlobal::config(),"Xinerama"); 00319 int screen = xineramaConfig.readEntry("MenubarScreen", 00320 QApplication::desktop()->screenNumber(QPoint(0,0)) ); 00321 QRect area = QApplication::desktop()->screenGeometry(screen); 00322 int margin = 0; 00323 move(area.left() - margin, area.top() - margin); 00324 setFixedSize(area.width() + 2* margin , heightForWidth( area.width() + 2 * margin ) ); 00325 #ifdef Q_WS_X11 00326 int strut_height = height() - margin; 00327 if( strut_height < 0 ) 00328 strut_height = 0; 00329 KWindowSystem::setStrut( winId(), 0, 0, strut_height, 0 ); 00330 #endif 00331 } 00332 } 00333 00334 void KMenuBar::resizeEvent( QResizeEvent *e ) 00335 { 00336 if( e->spontaneous() && d->topLevel && !d->fallback_mode ) 00337 { 00338 ++block_resize; // do not respond with configure request to ConfigureNotify event 00339 QMenuBar::resizeEvent(e); // to avoid possible infinite loop 00340 --block_resize; 00341 } 00342 else 00343 QMenuBar::resizeEvent(e); 00344 } 00345 00346 void KMenuBar::setGeometry( const QRect& r ) 00347 { 00348 setGeometry( r.x(), r.y(), r.width(), r.height() ); 00349 } 00350 00351 void KMenuBar::setGeometry( int x, int y, int w, int h ) 00352 { 00353 if( block_resize > 0 ) 00354 { 00355 move( x, y ); 00356 return; 00357 } 00358 checkSize( w, h ); 00359 if( geometry() != QRect( x, y, w, h )) 00360 QMenuBar::setGeometry( x, y, w, h ); 00361 } 00362 00363 void KMenuBar::resize( int w, int h ) 00364 { 00365 if( block_resize > 0 ) 00366 return; 00367 checkSize( w, h ); 00368 if( size() != QSize( w, h )) 00369 QMenuBar::resize( w, h ); 00370 // kDebug() << "RS:" << w << ":" << h << ":" << width() << ":" << height() << ":" << minimumWidth() << ":" << minimumHeight(); 00371 } 00372 00373 void KMenuBar::resize( const QSize& s ) 00374 { 00375 QMenuBar::resize( s ); 00376 } 00377 00378 void KMenuBar::checkSize( int& w, int& h ) 00379 { 00380 if( !d->topLevel || d->fallback_mode ) 00381 return; 00382 QSize s = sizeHint(); 00383 w = s.width(); 00384 h = s.height(); 00385 // This is not done as setMinimumSize(), because that would set the minimum 00386 // size in WM_NORMAL_HINTS, and KWin would not allow changing to smaller size 00387 // anymore 00388 w = qMax( w, d->min_size.width()); 00389 h = qMax( h, d->min_size.height()); 00390 } 00391 00392 // QMenuBar's sizeHint() gives wrong size (insufficient width), which causes wrapping in the kicker applet 00393 QSize KMenuBar::sizeHint() const 00394 { 00395 if( !d->topLevel || block_resize > 0 ) 00396 return QMenuBar::sizeHint(); 00397 // Since QMenuBar::sizeHint() may indirectly call resize(), 00398 // avoid infinite recursion. 00399 ++block_resize; 00400 // find the minimum useful height, and enlarge the width until the menu fits in that height (one row) 00401 int h = heightForWidth( 1000000 ); 00402 int w = QMenuBar::sizeHint().width(); 00403 // optimization - don't call heightForWidth() too many times 00404 while( heightForWidth( w + 12 ) > h ) 00405 w += 12; 00406 while( heightForWidth( w + 4 ) > h ) 00407 w += 4; 00408 while( heightForWidth( w ) > h ) 00409 ++w; 00410 --block_resize; 00411 return QSize( w, h ); 00412 } 00413 00414 #ifdef Q_WS_X11 00415 bool KMenuBar::x11Event( XEvent* ev ) 00416 { 00417 if( ev->type == ClientMessage && ev->xclient.message_type == msg_type_atom 00418 && ev->xclient.window == winId()) 00419 { 00420 // QMenuBar is trying really hard to keep the size it deems right. 00421 // Forcing minimum size and blocking resizing to match parent size 00422 // in checkResizingToParent() seem to be the only way to make 00423 // KMenuBar keep the size it wants 00424 d->min_size = QSize( ev->xclient.data.l[ 1 ], ev->xclient.data.l[ 2 ] ); 00425 // kDebug() << "MINSIZE:" << d->min_size; 00426 updateMenuBarSize(); 00427 return true; 00428 } 00429 return QMenuBar::x11Event( ev ); 00430 } 00431 #endif 00432 00433 void KMenuBar::updateMenuBarSize() 00434 { 00435 //menuContentsChanged(); // trigger invalidating calculated size 00436 resize( sizeHint()); // and resize to preferred size 00437 } 00438 00439 void KMenuBar::setFrameStyle( int style ) 00440 { 00441 if( d->topLevel ) 00442 d->frameStyle = style; 00443 // else 00444 // QMenuBar::setFrameStyle( style ); 00445 } 00446 00447 void KMenuBar::setLineWidth( int width ) 00448 { 00449 if( d->topLevel ) 00450 d->lineWidth = width; 00451 // else 00452 // QMenuBar::setLineWidth( width ); 00453 } 00454 00455 void KMenuBar::setMargin( int margin ) 00456 { 00457 if( d->topLevel ) 00458 d->margin = margin; 00459 // else 00460 // QMenuBar::setMargin( margin ); 00461 } 00462 00463 void KMenuBar::closeEvent( QCloseEvent* e ) 00464 { 00465 if( d->topLevel ) 00466 e->ignore(); // mainly for the fallback mode 00467 else 00468 QMenuBar::closeEvent( e ); 00469 } 00470 00471 void KMenuBar::paintEvent( QPaintEvent* pe ) 00472 { 00473 // Closes the BR77113 00474 // We need to overload this method to paint only the menu items 00475 // This way when the KMenuBar is embedded in the menu applet it 00476 // integrates correctly. 00477 // 00478 // Background mode and origin are set so late because of styles 00479 // using the polish() method to modify these settings. 00480 // 00481 // Of course this hack can safely be removed when real transparency 00482 // will be available 00483 00484 // if( !d->topLevel ) 00485 { 00486 QMenuBar::paintEvent(pe); 00487 } 00488 #if 0 00489 else 00490 { 00491 QPainter p(this); 00492 bool up_enabled = isUpdatesEnabled(); 00493 Qt::BackgroundMode bg_mode = backgroundMode(); 00494 BackgroundOrigin bg_origin = backgroundOrigin(); 00495 00496 setUpdatesEnabled(false); 00497 setBackgroundMode(Qt::X11ParentRelative); 00498 setBackgroundOrigin(WindowOrigin); 00499 00500 p.eraseRect( rect() ); 00501 erase(); 00502 00503 QColorGroup g = colorGroup(); 00504 bool e; 00505 00506 for ( int i=0; i<(int)count(); i++ ) 00507 { 00508 QMenuItem *mi = findItem( idAt( i ) ); 00509 00510 if ( !mi->text().isEmpty() || !mi->icon().isNull() ) 00511 { 00512 QRect r = itemRect(i); 00513 if(r.isEmpty() || !mi->isVisible()) 00514 continue; 00515 00516 e = mi->isEnabled() && mi->isVisible(); 00517 if ( e ) 00518 g = isEnabled() ? ( isActiveWindow() ? palette().active() : 00519 palette().inactive() ) : palette().disabled(); 00520 else 00521 g = palette().disabled(); 00522 00523 bool item_active = ( activeAction() == mi ); 00524 00525 p.setClipRect(r); 00526 00527 if( item_active ) 00528 { 00529 QStyleOptionMenuItem miOpt; 00530 miOpt.init(this); 00531 miOpt.rect = r; 00532 miOpt.text = mi->text(); 00533 miOpt.icon = mi->icon(); 00534 miOpt.palette = g; 00535 00536 QStyle::State flags = QStyle::State_None; 00537 if (isEnabled() && e) 00538 flags |= QStyle::State_Enabled; 00539 if ( item_active ) 00540 flags |= QStyle::State_Active; 00541 if ( item_active && actItemDown ) 00542 flags |= QStyle::State_Down; 00543 flags |= QStyle::State_HasFocus; 00544 00545 mi->state = flags; 00546 00547 00548 style()->drawControl(QStyle::CE_MenuBarItem, &miOpt, &p, this); 00549 } 00550 else 00551 { 00552 style()->drawItem(p, r, Qt::AlignCenter | Qt::AlignVCenter | Qt::TextShowMnemonic, 00553 g, e, mi->pixmap(), mi->text()); 00554 } 00555 } 00556 } 00557 00558 setBackgroundOrigin(bg_origin); 00559 setBackgroundMode(bg_mode); 00560 setUpdatesEnabled(up_enabled); 00561 } 00562 #endif 00563 } 00564 00565 #include "kmenubar.moc"
KDE 4.6 API Reference