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