tabbox.cpp
00001 /***************************************************************** 00002 KWin - the KDE window manager 00003 This file is part of the KDE project. 00004 00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> 00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> 00007 00008 You can Freely distribute this program under the GNU General Public 00009 License. See the file "COPYING" for the exact licensing terms. 00010 ******************************************************************/ 00011 00012 //#define QT_CLEAN_NAMESPACE 00013 #include "tabbox.h" 00014 #include "workspace.h" 00015 #include "client.h" 00016 #include <tqpainter.h> 00017 #include <tqlabel.h> 00018 #include <tqdrawutil.h> 00019 #include <tqstyle.h> 00020 #include <tdeglobal.h> 00021 #include <fixx11h.h> 00022 #include <tdeconfig.h> 00023 #include <tdelocale.h> 00024 #include <tqapplication.h> 00025 #include <tqdesktopwidget.h> 00026 #include <kstringhandler.h> 00027 #include <stdarg.h> 00028 #include <kdebug.h> 00029 #include <kglobalaccel.h> 00030 #include <kkeynative.h> 00031 #include <tdeglobalsettings.h> 00032 #include <kiconeffect.h> 00033 #include <X11/keysym.h> 00034 #include <X11/keysymdef.h> 00035 00036 // specify externals before namespace 00037 00038 namespace KWinInternal 00039 { 00040 00041 extern TQPixmap* twin_get_menu_pix_hack(); 00042 00043 TabBox::TabBox( Workspace *ws, const char *name ) 00044 : TQFrame( 0, name, TQt::WNoAutoErase ), current_client( NULL ), wspace(ws) 00045 { 00046 setFrameStyle(TQFrame::StyledPanel | TQFrame::Plain); 00047 setLineWidth(2); 00048 setMargin(2); 00049 00050 appsOnly = false; 00051 showMiniIcon = false; 00052 00053 no_tasks = i18n("*** No Windows ***"); 00054 m = DesktopMode; // init variables 00055 reconfigure(); 00056 reset(); 00057 connect(&delayedShowTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(show())); 00058 00059 XSetWindowAttributes attr; 00060 attr.override_redirect = 1; 00061 outline_left = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), 0, 0, 1, 1, 0, 00062 CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); 00063 outline_right = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), 0, 0, 1, 1, 0, 00064 CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); 00065 outline_top = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), 0, 0, 1, 1, 0, 00066 CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); 00067 outline_bottom = XCreateWindow( tqt_xdisplay(), tqt_xrootwin(), 0, 0, 1, 1, 0, 00068 CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); 00069 } 00070 00071 TabBox::~TabBox() 00072 { 00073 XDestroyWindow( tqt_xdisplay(), outline_left ); 00074 XDestroyWindow( tqt_xdisplay(), outline_right ); 00075 XDestroyWindow( tqt_xdisplay(), outline_top ); 00076 XDestroyWindow( tqt_xdisplay(), outline_bottom ); 00077 } 00078 00079 00085 void TabBox::setMode( Mode mode ) 00086 { 00087 m = mode; 00088 } 00089 00094 void TabBox::setAppsOnly( bool a ) 00095 { 00096 appsOnly = a; 00097 } 00098 00102 void TabBox::createClientList(ClientList &list, int desktop /*-1 = all*/, Client *c, bool chain) 00103 { 00104 ClientList::size_type idx = 0; 00105 TQString startClass; 00106 list.clear(); 00107 00108 Client* start = c; 00109 00110 if( start ) 00111 startClass = start->resourceClass(); 00112 00113 if ( chain ) 00114 c = workspace()->nextFocusChainClient(c); 00115 else 00116 c = workspace()->stackingOrder().first(); 00117 00118 Client* stop = c; 00119 00120 while ( c ) 00121 { 00122 Client* add = NULL; 00123 if ( ((desktop == -1) || c->isOnDesktop(desktop)) 00124 && c->wantsTabFocus() ) 00125 { // don't add windows that have modal dialogs 00126 Client* modal = c->findModal(); 00127 if( modal == NULL || modal == c ) 00128 add = c; 00129 else if( !list.contains( modal )) 00130 add = modal; 00131 else 00132 { 00133 // nothing 00134 } 00135 } 00136 if(appsOnly && (TQString::compare( startClass, c->resourceClass()) != 0)) 00137 { 00138 add = NULL; 00139 } 00140 00141 if( options->separateScreenFocus && options->xineramaEnabled ) 00142 { 00143 if( c->screen() != workspace()->activeScreen()) 00144 add = NULL; 00145 } 00146 00147 if( add != NULL ) 00148 { 00149 if ( start == add ) 00150 { 00151 list.remove( add ); 00152 list.prepend( add ); 00153 } 00154 else 00155 list += add; 00156 } 00157 00158 if ( chain ) 00159 c = workspace()->nextFocusChainClient( c ); 00160 else 00161 { 00162 if ( idx >= (workspace()->stackingOrder().size()-1) ) 00163 c = 0; 00164 else 00165 c = workspace()->stackingOrder()[++idx]; 00166 } 00167 00168 if ( c == stop ) 00169 break; 00170 } 00171 } 00172 00173 00178 void TabBox::reset() 00179 { 00180 int w, h, cw = 0, wmax = 0; 00181 00182 TQRect r = workspace()->screenGeometry( workspace()->activeScreen()); 00183 00184 // calculate height of 1 line 00185 // fontheight + 1 pixel above + 1 pixel below, or 32x32 icon + 2 pixel above + below 00186 lineHeight = TQMAX(fontMetrics().height() + 2, 32 + 4); 00187 00188 if ( mode() == WindowsMode ) 00189 { 00190 setCurrentClient( workspace()->activeClient()); 00191 00192 // get all clients to show 00193 createClientList(clients, options_traverse_all ? -1 : workspace()->currentDesktop(), current_client, true); 00194 00195 // calculate maximum caption width 00196 cw = fontMetrics().width(no_tasks)+20; 00197 for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 00198 { 00199 cw = fontMetrics().width( (*it)->caption() ); 00200 if ( cw > wmax ) wmax = cw; 00201 } 00202 00203 // calculate height for the popup 00204 if ( clients.count() == 0 ) // height for the "not tasks" text 00205 { 00206 TQFont f = font(); 00207 f.setBold( TRUE ); 00208 f.setPointSize( 14 ); 00209 00210 h = TQFontMetrics(f).height()*4; 00211 } 00212 else 00213 { 00214 showMiniIcon = false; 00215 h = clients.count() * lineHeight; 00216 00217 if ( h > (r.height()-(2*frameWidth())) ) // if too high, use mini icons 00218 { 00219 showMiniIcon = true; 00220 // fontheight + 1 pixel above + 1 pixel below, or 16x16 icon + 1 pixel above + below 00221 lineHeight = TQMAX(fontMetrics().height() + 2, 16 + 2); 00222 00223 h = clients.count() * lineHeight; 00224 00225 if ( h > (r.height()-(2*frameWidth())) ) // if still too high, remove some clients 00226 { 00227 // how many clients to remove 00228 int howMany = (h - (r.height()-(2*frameWidth())))/lineHeight; 00229 for (; howMany; howMany--) 00230 clients.remove(clients.last()); 00231 00232 h = clients.count() * lineHeight; 00233 } 00234 } 00235 } 00236 } 00237 else 00238 { // DesktopListMode 00239 showMiniIcon = false; 00240 desk = workspace()->currentDesktop(); 00241 00242 for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) 00243 { 00244 cw = fontMetrics().width( workspace()->desktopName(i) ); 00245 if ( cw > wmax ) wmax = cw; 00246 } 00247 00248 // calculate height for the popup (max. 16 desktops always fit in a 800x600 screen) 00249 h = workspace()->numberOfDesktops() * lineHeight; 00250 } 00251 00252 // height, width for the popup 00253 h += 2 * frameWidth(); 00254 w = 2*frameWidth() + 5*2 + ( showMiniIcon ? 16 : 32 ) + 8 + wmax; // 5*2=margins, ()=icon, 8=space between icon+text 00255 w = kClamp( w, r.width()/3 , r.width() * 4 / 5 ); 00256 00257 setGeometry( (r.width()-w)/2 + r.x(), 00258 (r.height()-h)/2+ r.y(), 00259 w, h ); 00260 } 00261 00262 00266 void TabBox::nextPrev( bool next) 00267 { 00268 if ( mode() == WindowsMode ) 00269 { 00270 Client* firstClient = NULL; 00271 Client* client = current_client; 00272 do 00273 { 00274 if ( next ) 00275 client = workspace()->nextFocusChainClient(client); 00276 else 00277 client = workspace()->previousFocusChainClient(client); 00278 if (!firstClient) 00279 { 00280 // When we see our first client for the second time, 00281 // it's time to stop. 00282 firstClient = client; 00283 } 00284 else if (client == firstClient) 00285 { 00286 // No candidates found. 00287 client = 0; 00288 break; 00289 } 00290 } while ( client && !clients.contains( client )); 00291 setCurrentClient( client ); 00292 } 00293 else if( mode() == DesktopMode ) 00294 { 00295 if ( next ) 00296 desk = workspace()->nextDesktopFocusChain( desk ); 00297 else 00298 desk = workspace()->previousDesktopFocusChain( desk ); 00299 } 00300 else 00301 { // DesktopListMode 00302 if ( next ) 00303 { 00304 desk++; 00305 if ( desk > workspace()->numberOfDesktops() ) 00306 desk = 1; 00307 } 00308 else 00309 { 00310 desk--; 00311 if ( desk < 1 ) 00312 desk = workspace()->numberOfDesktops(); 00313 } 00314 } 00315 00316 update(); 00317 } 00318 00319 00320 00325 Client* TabBox::currentClient() 00326 { 00327 if ( mode() != WindowsMode ) 00328 return 0; 00329 if (!workspace()->hasClient( current_client )) 00330 return 0; 00331 return current_client; 00332 } 00333 00334 void TabBox::setCurrentClient( Client* c ) 00335 { 00336 if( current_client != c ) 00337 { 00338 current_client = c; 00339 updateOutline(); 00340 } 00341 } 00342 00348 int TabBox::currentDesktop() 00349 { 00350 if ( mode() == DesktopListMode || mode() == DesktopMode ) 00351 return desk; 00352 else 00353 return -1; 00354 } 00355 00356 00360 void TabBox::showEvent( TQShowEvent* ) 00361 { 00362 updateOutline(); 00363 XRaiseWindow( tqt_xdisplay(), outline_left ); 00364 XRaiseWindow( tqt_xdisplay(), outline_right ); 00365 XRaiseWindow( tqt_xdisplay(), outline_top ); 00366 XRaiseWindow( tqt_xdisplay(), outline_bottom ); 00367 raise(); 00368 } 00369 00370 00374 void TabBox::hideEvent( TQHideEvent* ) 00375 { 00376 XUnmapWindow( tqt_xdisplay(), outline_left ); 00377 XUnmapWindow( tqt_xdisplay(), outline_right ); 00378 XUnmapWindow( tqt_xdisplay(), outline_top ); 00379 XUnmapWindow( tqt_xdisplay(), outline_bottom ); 00380 } 00381 00385 void TabBox::drawContents( TQPainter * ) 00386 { 00387 TQRect r(contentsRect()); 00388 TQPixmap pix(r.size()); // do double buffering to avoid flickers 00389 pix.fill(this, 0, 0); 00390 00391 TQPainter p; 00392 p.begin(&pix, this); 00393 00394 TQPixmap* menu_pix = twin_get_menu_pix_hack(); 00395 00396 int iconWidth = showMiniIcon ? 16 : 32; 00397 int x = 0; 00398 int y = 0; 00399 00400 if ( mode () == WindowsMode ) 00401 { 00402 if ( !currentClient() ) 00403 { 00404 TQFont f = font(); 00405 f.setBold( TRUE ); 00406 f.setPointSize( 14 ); 00407 00408 p.setFont(f); 00409 p.drawText( r, AlignCenter, no_tasks); 00410 } 00411 else 00412 { 00413 for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) 00414 { 00415 if ( workspace()->hasClient( *it ) ) // safety 00416 { 00417 // draw highlight background 00418 if ( (*it) == current_client ) 00419 p.fillRect(x, y, r.width(), lineHeight, colorGroup().highlight()); 00420 00421 // draw icon 00422 TQPixmap icon; 00423 if ( showMiniIcon ) 00424 { 00425 if ( !(*it)->miniIcon().isNull() ) 00426 icon = (*it)->miniIcon(); 00427 } 00428 else 00429 if ( !(*it)->icon().isNull() ) 00430 icon = (*it)->icon(); 00431 else if ( menu_pix ) 00432 icon = *menu_pix; 00433 00434 if( !icon.isNull()) 00435 { 00436 if( (*it)->isMinimized()) 00437 TDEIconEffect::semiTransparent( icon ); 00438 p.drawPixmap( x+5, y + (lineHeight - iconWidth)/2, icon ); 00439 } 00440 00441 // generate text to display 00442 TQString s; 00443 00444 if ( !(*it)->isOnDesktop(workspace()->currentDesktop()) ) 00445 s = workspace()->desktopName((*it)->desktop()) + ": "; 00446 00447 if ( (*it)->isMinimized() ) 00448 s += TQString("(") + (*it)->caption() + ")"; 00449 else 00450 s += (*it)->caption(); 00451 00452 s = KStringHandler::cPixelSqueeze(s, fontMetrics(), r.width() - 5 - iconWidth - 8); 00453 00454 // draw text 00455 if ( (*it) == current_client ) 00456 p.setPen(colorGroup().highlightedText()); 00457 else if( (*it)->isMinimized()) 00458 { 00459 TQColor c1 = colorGroup().text(); 00460 TQColor c2 = colorGroup().background(); 00461 // from kicker's TaskContainer::blendColors() 00462 int r1, g1, b1; 00463 int r2, g2, b2; 00464 00465 c1.rgb( &r1, &g1, &b1 ); 00466 c2.rgb( &r2, &g2, &b2 ); 00467 00468 r1 += (int) ( .5 * ( r2 - r1 ) ); 00469 g1 += (int) ( .5 * ( g2 - g1 ) ); 00470 b1 += (int) ( .5 * ( b2 - b1 ) ); 00471 00472 p.setPen(TQColor( r1, g1, b1 )); 00473 } 00474 else 00475 p.setPen(colorGroup().text()); 00476 00477 p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, 00478 Qt::AlignLeft | Qt::AlignVCenter | TQt::SingleLine, s); 00479 00480 y += lineHeight; 00481 } 00482 if ( y >= r.height() ) break; 00483 } 00484 } 00485 } 00486 else 00487 { // DesktopMode || DesktopListMode 00488 int iconHeight = iconWidth; 00489 00490 // get widest desktop name/number 00491 TQFont f(font()); 00492 f.setBold(true); 00493 f.setPixelSize(iconHeight - 4); // pixel, not point because I need to know the pixels 00494 TQFontMetrics fm(f); 00495 00496 int wmax = 0; 00497 for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) 00498 { 00499 wmax = TQMAX(wmax, fontMetrics().width(workspace()->desktopName(i))); 00500 00501 // calculate max width of desktop-number text 00502 TQString num = TQString::number(i); 00503 iconWidth = TQMAX(iconWidth - 4, fm.boundingRect(num).width()) + 4; 00504 } 00505 00506 // In DesktopMode, start at the current desktop 00507 // In DesktopListMode, start at desktop #1 00508 int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; 00509 for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) 00510 { 00511 // draw highlight background 00512 if ( iDesktop == desk ) // current desktop 00513 p.fillRect(x, y, r.width(), lineHeight, colorGroup().highlight()); 00514 00515 p.save(); 00516 00517 // draw "icon" (here: number of desktop) 00518 p.fillRect(x+5, y+2, iconWidth, iconHeight, colorGroup().base()); 00519 p.setPen(colorGroup().text()); 00520 p.drawRect(x+5, y+2, iconWidth, iconHeight); 00521 00522 // draw desktop-number 00523 p.setFont(f); 00524 TQString num = TQString::number(iDesktop); 00525 p.drawText(x+5, y+2, iconWidth, iconHeight, Qt::AlignCenter, num); 00526 00527 p.restore(); 00528 00529 // draw desktop name text 00530 if ( iDesktop == desk ) 00531 p.setPen(colorGroup().highlightedText()); 00532 else 00533 p.setPen(colorGroup().text()); 00534 00535 p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, 00536 Qt::AlignLeft | Qt::AlignVCenter | TQt::SingleLine, 00537 workspace()->desktopName(iDesktop)); 00538 00539 // show mini icons from that desktop aligned to each other 00540 int x1 = x + 5 + iconWidth + 8 + wmax + 5; 00541 00542 ClientList list; 00543 createClientList(list, iDesktop, 0, false); 00544 // clients are in reversed stacking order 00545 for (ClientList::ConstIterator it = list.fromLast(); it != list.end(); --it) 00546 { 00547 if ( !(*it)->miniIcon().isNull() ) 00548 { 00549 if ( x1+18 >= x+r.width() ) // only show full icons 00550 break; 00551 00552 p.drawPixmap( x1, y + (lineHeight - 16)/2, (*it)->miniIcon() ); 00553 x1 += 18; 00554 } 00555 } 00556 00557 // next desktop 00558 y += lineHeight; 00559 if ( y >= r.height() ) break; 00560 00561 if( mode() == DesktopMode ) 00562 iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); 00563 else 00564 iDesktop++; 00565 } 00566 } 00567 p.end(); 00568 bitBlt(this, r.x(), r.y(), &pix); 00569 } 00570 00571 void TabBox::updateOutline() 00572 { 00573 Client* c = currentClient(); 00574 if( !options->tabboxOutline || c == NULL || this->isHidden() || !c->isShown( true ) || !c->isOnCurrentDesktop()) 00575 { 00576 XUnmapWindow( tqt_xdisplay(), outline_left ); 00577 XUnmapWindow( tqt_xdisplay(), outline_right ); 00578 XUnmapWindow( tqt_xdisplay(), outline_top ); 00579 XUnmapWindow( tqt_xdisplay(), outline_bottom ); 00580 return; 00581 } 00582 // left/right parts are between top/bottom, they don't reach as far as the corners 00583 XMoveResizeWindow( tqt_xdisplay(), outline_left, c->x(), c->y() + 5, 5, c->height() - 10 ); 00584 XMoveResizeWindow( tqt_xdisplay(), outline_right, c->x() + c->width() - 5, c->y() + 5, 5, c->height() - 10 ); 00585 XMoveResizeWindow( tqt_xdisplay(), outline_top, c->x(), c->y(), c->width(), 5 ); 00586 XMoveResizeWindow( tqt_xdisplay(), outline_bottom, c->x(), c->y() + c->height() - 5, c->width(), 5 ); 00587 { 00588 TQPixmap pix( 5, c->height() - 10 ); 00589 TQPainter p( &pix ); 00590 p.setPen( white ); 00591 p.drawLine( 0, 0, 0, pix.height() - 1 ); 00592 p.drawLine( 4, 0, 4, pix.height() - 1 ); 00593 p.setPen( gray ); 00594 p.drawLine( 1, 0, 1, pix.height() - 1 ); 00595 p.drawLine( 3, 0, 3, pix.height() - 1 ); 00596 p.setPen( black ); 00597 p.drawLine( 2, 0, 2, pix.height() - 1 ); 00598 p.end(); 00599 XSetWindowBackgroundPixmap( tqt_xdisplay(), outline_left, pix.handle()); 00600 XSetWindowBackgroundPixmap( tqt_xdisplay(), outline_right, pix.handle()); 00601 } 00602 { 00603 TQPixmap pix( c->width(), 5 ); 00604 TQPainter p( &pix ); 00605 p.setPen( white ); 00606 p.drawLine( 0, 0, pix.width() - 1 - 0, 0 ); 00607 p.drawLine( 4, 4, pix.width() - 1 - 4, 4 ); 00608 p.drawLine( 0, 0, 0, 4 ); 00609 p.drawLine( pix.width() - 1 - 0, 0, pix.width() - 1 - 0, 4 ); 00610 p.setPen( gray ); 00611 p.drawLine( 1, 1, pix.width() - 1 - 1, 1 ); 00612 p.drawLine( 3, 3, pix.width() - 1 - 3, 3 ); 00613 p.drawLine( 1, 1, 1, 4 ); 00614 p.drawLine( 3, 3, 3, 4 ); 00615 p.drawLine( pix.width() - 1 - 1, 1, pix.width() - 1 - 1, 4 ); 00616 p.drawLine( pix.width() - 1 - 3, 3, pix.width() - 1 - 3, 4 ); 00617 p.setPen( black ); 00618 p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); 00619 p.drawLine( 2, 2, 2, 4 ); 00620 p.drawLine( pix.width() - 1 - 2, 2, pix.width() - 1 - 2, 4 ); 00621 p.end(); 00622 XSetWindowBackgroundPixmap( tqt_xdisplay(), outline_top, pix.handle()); 00623 } 00624 { 00625 TQPixmap pix( c->width(), 5 ); 00626 TQPainter p( &pix ); 00627 p.setPen( white ); 00628 p.drawLine( 4, 0, pix.width() - 1 - 4, 0 ); 00629 p.drawLine( 0, 4, pix.width() - 1 - 0, 4 ); 00630 p.drawLine( 0, 4, 0, 0 ); 00631 p.drawLine( pix.width() - 1 - 0, 4, pix.width() - 1 - 0, 0 ); 00632 p.setPen( gray ); 00633 p.drawLine( 3, 1, pix.width() - 1 - 3, 1 ); 00634 p.drawLine( 1, 3, pix.width() - 1 - 1, 3 ); 00635 p.drawLine( 3, 1, 3, 0 ); 00636 p.drawLine( 1, 3, 1, 0 ); 00637 p.drawLine( pix.width() - 1 - 3, 1, pix.width() - 1 - 3, 0 ); 00638 p.drawLine( pix.width() - 1 - 1, 3, pix.width() - 1 - 1, 0 ); 00639 p.setPen( black ); 00640 p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); 00641 p.drawLine( 2, 0, 2, 2 ); 00642 p.drawLine( pix.width() - 1 - 2, 0, pix.width() - 1 - 2, 2 ); 00643 p.end(); 00644 XSetWindowBackgroundPixmap( tqt_xdisplay(), outline_bottom, pix.handle()); 00645 } 00646 XClearWindow( tqt_xdisplay(), outline_left ); 00647 XClearWindow( tqt_xdisplay(), outline_right ); 00648 XClearWindow( tqt_xdisplay(), outline_top ); 00649 XClearWindow( tqt_xdisplay(), outline_bottom ); 00650 XMapWindow( tqt_xdisplay(), outline_left ); 00651 XMapWindow( tqt_xdisplay(), outline_right ); 00652 XMapWindow( tqt_xdisplay(), outline_top ); 00653 XMapWindow( tqt_xdisplay(), outline_bottom ); 00654 } 00655 00656 void TabBox::hide() 00657 { 00658 delayedShowTimer.stop(); 00659 TQWidget::hide(); 00660 TQApplication::syncX(); 00661 XEvent otherEvent; 00662 while (XCheckTypedEvent (tqt_xdisplay(), EnterNotify, &otherEvent ) ) 00663 ; 00664 appsOnly = false; 00665 } 00666 00667 00668 void TabBox::reconfigure() 00669 { 00670 TDEConfig * c(TDEGlobal::config()); 00671 c->setGroup("TabBox"); 00672 options_traverse_all = c->readBoolEntry("TraverseAll", false ); 00673 } 00674 00693 void TabBox::delayedShow() 00694 { 00695 TDEConfig * c(TDEGlobal::config()); 00696 c->setGroup("TabBox"); 00697 bool delay = c->readBoolEntry("ShowDelay", true); 00698 00699 if (!delay) 00700 { 00701 show(); 00702 return; 00703 } 00704 00705 int delayTime = c->readNumEntry("DelayTime", 90); 00706 delayedShowTimer.start(delayTime, true); 00707 } 00708 00709 00710 void TabBox::handleMouseEvent( XEvent* e ) 00711 { 00712 XAllowEvents( tqt_xdisplay(), AsyncPointer, GET_QT_X_TIME() ); 00713 if( e->type != ButtonPress ) 00714 return; 00715 TQPoint pos( e->xbutton.x_root, e->xbutton.y_root ); 00716 if( !geometry().contains( pos )) 00717 { 00718 workspace()->closeTabBox(); // click outside closes tab 00719 return; 00720 } 00721 pos.rx() -= x(); // pos is now inside tabbox 00722 pos.ry() -= y(); 00723 int num = (pos.y()-frameWidth()) / lineHeight; 00724 00725 if( mode() == WindowsMode ) 00726 { 00727 for( ClientList::ConstIterator it = clients.begin(); 00728 it != clients.end(); 00729 ++it) 00730 { 00731 if( workspace()->hasClient( *it ) && (num == 0) ) // safety 00732 { 00733 setCurrentClient( *it ); 00734 break; 00735 } 00736 num--; 00737 } 00738 } 00739 else 00740 { 00741 int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; 00742 for( int i = 1; 00743 i <= workspace()->numberOfDesktops(); 00744 ++i ) 00745 { 00746 if( num == 0 ) 00747 { 00748 desk = iDesktop; 00749 break; 00750 } 00751 num--; 00752 if( mode() == DesktopMode ) 00753 iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); 00754 else 00755 iDesktop++; 00756 } 00757 } 00758 update(); 00759 } 00760 00761 //******************************* 00762 // Workspace 00763 //******************************* 00764 00765 00770 static 00771 bool areKeySymXsDepressed( bool bAll, const uint keySyms[], int nKeySyms ) 00772 { 00773 char keymap[32]; 00774 00775 kdDebug(125) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms << endl; 00776 00777 XQueryKeymap( tqt_xdisplay(), keymap ); 00778 00779 for( int iKeySym = 0; iKeySym < nKeySyms; iKeySym++ ) 00780 { 00781 uint keySymX = keySyms[ iKeySym ]; 00782 uchar keyCodeX = XKeysymToKeycode( tqt_xdisplay(), keySymX ); 00783 int i = keyCodeX / 8; 00784 char mask = 1 << (keyCodeX - (i * 8)); 00785 00786 kdDebug(125) << iKeySym << ": keySymX=0x" << TQString::number( keySymX, 16 ) 00787 << " i=" << i << " mask=0x" << TQString::number( mask, 16 ) 00788 << " keymap[i]=0x" << TQString::number( keymap[i], 16 ) << endl; 00789 00790 // Abort if bad index value, 00791 if( i < 0 || i >= 32 ) 00792 return false; 00793 00794 // If ALL keys passed need to be depressed, 00795 if( bAll ) 00796 { 00797 if( (keymap[i] & mask) == 0 ) 00798 return false; 00799 } 00800 else 00801 { 00802 // If we are looking for ANY key press, and this key is depressed, 00803 if( keymap[i] & mask ) 00804 return true; 00805 } 00806 } 00807 00808 // If we were looking for ANY key press, then none was found, return false, 00809 // If we were looking for ALL key presses, then all were found, return true. 00810 return bAll; 00811 } 00812 00813 static bool areModKeysDepressed( const KKeySequence& seq ) 00814 { 00815 uint rgKeySyms[10]; 00816 int nKeySyms = 0; 00817 if( seq.isNull()) 00818 return false; 00819 int mod = seq.key(seq.count()-1).modFlags(); 00820 00821 if ( mod & KKey::SHIFT ) 00822 { 00823 rgKeySyms[nKeySyms++] = XK_Shift_L; 00824 rgKeySyms[nKeySyms++] = XK_Shift_R; 00825 } 00826 if ( mod & KKey::CTRL ) 00827 { 00828 rgKeySyms[nKeySyms++] = XK_Control_L; 00829 rgKeySyms[nKeySyms++] = XK_Control_R; 00830 } 00831 if( mod & KKey::ALT ) 00832 { 00833 rgKeySyms[nKeySyms++] = XK_Alt_L; 00834 rgKeySyms[nKeySyms++] = XK_Alt_R; 00835 } 00836 if( mod & KKey::WIN ) 00837 { 00838 // It would take some code to determine whether the Win key 00839 // is associated with Super or Meta, so check for both. 00840 // See bug #140023 for details. 00841 rgKeySyms[nKeySyms++] = XK_Super_L; 00842 rgKeySyms[nKeySyms++] = XK_Super_R; 00843 rgKeySyms[nKeySyms++] = XK_Meta_L; 00844 rgKeySyms[nKeySyms++] = XK_Meta_R; 00845 } 00846 00847 return areKeySymXsDepressed( false, rgKeySyms, nKeySyms ); 00848 } 00849 00850 static bool areModKeysDepressed( const TDEShortcut& cut ) 00851 { 00852 for( unsigned int i = 0; 00853 i < cut.count(); 00854 ++i ) 00855 { 00856 if( areModKeysDepressed( cut.seq( i ))) 00857 return true; 00858 } 00859 return false; 00860 } 00861 00862 void Workspace::slotWalkThroughWindows() 00863 { 00864 if ( root != tqt_xrootwin() ) 00865 return; 00866 if ( tab_grab || control_grab ) 00867 return; 00868 if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) 00869 { 00870 //XUngrabKeyboard(tqt_xdisplay(), GET_QT_X_TIME()); // need that because of accelerator raw mode 00871 // CDE style raise / lower 00872 CDEWalkThroughWindows( true ); 00873 } 00874 else 00875 { 00876 if ( areModKeysDepressed( cutWalkThroughWindows ) ) 00877 { 00878 if ( startKDEWalkThroughWindows() ) 00879 KDEWalkThroughWindows( true ); 00880 } 00881 else 00882 // if the shortcut has no modifiers, don't show the tabbox, 00883 // don't grab, but simply go to the next window 00884 KDEOneStepThroughWindows( true ); 00885 } 00886 } 00887 00888 void Workspace::slotWalkBackThroughWindows() 00889 { 00890 if ( root != tqt_xrootwin() ) 00891 return; 00892 if( tab_grab || control_grab ) 00893 return; 00894 if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) 00895 { 00896 // CDE style raise / lower 00897 CDEWalkThroughWindows( false ); 00898 } 00899 else 00900 { 00901 if ( areModKeysDepressed( cutWalkThroughWindowsReverse ) ) 00902 { 00903 if ( startKDEWalkThroughWindows() ) 00904 KDEWalkThroughWindows( false ); 00905 } 00906 else 00907 { 00908 KDEOneStepThroughWindows( false ); 00909 } 00910 } 00911 } 00912 00913 void Workspace::slotWalkThroughApps() 00914 { 00915 tab_box->setAppsOnly(true); 00916 slotWalkThroughWindows(); 00917 } 00918 00919 void Workspace::slotWalkBackThroughApps() 00920 { 00921 tab_box->setAppsOnly(true); 00922 slotWalkBackThroughWindows(); 00923 } 00924 00925 void Workspace::slotWalkThroughDesktops() 00926 { 00927 if ( root != tqt_xrootwin() ) 00928 return; 00929 if( tab_grab || control_grab ) 00930 return; 00931 if ( areModKeysDepressed( cutWalkThroughDesktops ) ) 00932 { 00933 if ( startWalkThroughDesktops() ) 00934 walkThroughDesktops( true ); 00935 } 00936 else 00937 { 00938 oneStepThroughDesktops( true ); 00939 } 00940 } 00941 00942 void Workspace::slotWalkBackThroughDesktops() 00943 { 00944 if ( root != tqt_xrootwin() ) 00945 return; 00946 if( tab_grab || control_grab ) 00947 return; 00948 if ( areModKeysDepressed( cutWalkThroughDesktopsReverse ) ) 00949 { 00950 if ( startWalkThroughDesktops() ) 00951 walkThroughDesktops( false ); 00952 } 00953 else 00954 { 00955 oneStepThroughDesktops( false ); 00956 } 00957 } 00958 00959 void Workspace::slotWalkThroughDesktopList() 00960 { 00961 if ( root != tqt_xrootwin() ) 00962 return; 00963 if( tab_grab || control_grab ) 00964 return; 00965 if ( areModKeysDepressed( cutWalkThroughDesktopList ) ) 00966 { 00967 if ( startWalkThroughDesktopList() ) 00968 walkThroughDesktops( true ); 00969 } 00970 else 00971 { 00972 oneStepThroughDesktopList( true ); 00973 } 00974 } 00975 00976 void Workspace::slotWalkBackThroughDesktopList() 00977 { 00978 if ( root != tqt_xrootwin() ) 00979 return; 00980 if( tab_grab || control_grab ) 00981 return; 00982 if ( areModKeysDepressed( cutWalkThroughDesktopListReverse ) ) 00983 { 00984 if ( startWalkThroughDesktopList() ) 00985 walkThroughDesktops( false ); 00986 } 00987 else 00988 { 00989 oneStepThroughDesktopList( false ); 00990 } 00991 } 00992 00993 bool Workspace::startKDEWalkThroughWindows() 00994 { 00995 if( !establishTabBoxGrab()) 00996 return false; 00997 tab_grab = TRUE; 00998 keys->suspend( true ); 00999 disable_shortcuts_keys->suspend( true ); 01000 client_keys->suspend( true ); 01001 tab_box->setMode( TabBox::WindowsMode ); 01002 tab_box->reset(); 01003 return TRUE; 01004 } 01005 01006 bool Workspace::startWalkThroughDesktops( int mode ) 01007 { 01008 if( !establishTabBoxGrab()) 01009 return false; 01010 control_grab = TRUE; 01011 keys->suspend( true ); 01012 disable_shortcuts_keys->suspend( true ); 01013 client_keys->suspend( true ); 01014 tab_box->setMode( (TabBox::Mode) mode ); 01015 tab_box->reset(); 01016 return TRUE; 01017 } 01018 01019 bool Workspace::startWalkThroughDesktops() 01020 { 01021 return startWalkThroughDesktops( TabBox::DesktopMode ); 01022 } 01023 01024 bool Workspace::startWalkThroughDesktopList() 01025 { 01026 return startWalkThroughDesktops( TabBox::DesktopListMode ); 01027 } 01028 01029 void Workspace::KDEWalkThroughWindows( bool forward ) 01030 { 01031 tab_box->nextPrev( forward ); 01032 tab_box->delayedShow(); 01033 } 01034 01035 void Workspace::walkThroughDesktops( bool forward ) 01036 { 01037 tab_box->nextPrev( forward ); 01038 tab_box->delayedShow(); 01039 } 01040 01041 void Workspace::CDEWalkThroughWindows( bool forward ) 01042 { 01043 Client* c = NULL; 01044 // this function find the first suitable client for unreasonable focus 01045 // policies - the topmost one, with some exceptions (can't be keepabove/below, 01046 // otherwise it gets stuck on them) 01047 Q_ASSERT( block_stacking_updates == 0 ); 01048 for( ClientList::ConstIterator it = stacking_order.fromLast(); 01049 it != stacking_order.end(); 01050 --it ) 01051 { 01052 if ( (*it)->isOnCurrentDesktop() && !(*it)->isSpecialWindow() 01053 && (*it)->isShown( false ) && (*it)->wantsTabFocus() 01054 && !(*it)->keepAbove() && !(*it)->keepBelow()) 01055 { 01056 c = *it; 01057 break; 01058 } 01059 } 01060 Client* nc = c; 01061 bool options_traverse_all; 01062 { 01063 TDEConfigGroupSaver saver( TDEGlobal::config(), "TabBox" ); 01064 options_traverse_all = TDEGlobal::config()->readBoolEntry("TraverseAll", false ); 01065 } 01066 01067 Client* firstClient = 0; 01068 do 01069 { 01070 nc = forward ? nextStaticClient(nc) : previousStaticClient(nc); 01071 if (!firstClient) 01072 { 01073 // When we see our first client for the second time, 01074 // it's time to stop. 01075 firstClient = nc; 01076 } 01077 else if (nc == firstClient) 01078 { 01079 // No candidates found. 01080 nc = 0; 01081 break; 01082 } 01083 } while (nc && nc != c && 01084 (( !options_traverse_all && !nc->isOnDesktop(currentDesktop())) || 01085 nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() ) ); 01086 if (nc) 01087 { 01088 if (c && c != nc) 01089 lowerClient( c ); 01090 if ( options->focusPolicyIsReasonable() ) 01091 { 01092 activateClient( nc ); 01093 if( nc->isShade() && options->shadeHover ) 01094 nc->setShade( ShadeActivated ); 01095 } 01096 else 01097 { 01098 if( !nc->isOnDesktop( currentDesktop())) 01099 setCurrentDesktop( nc->desktop()); 01100 raiseClient( nc ); 01101 } 01102 } 01103 } 01104 01105 void Workspace::KDEOneStepThroughWindows( bool forward ) 01106 { 01107 tab_box->setMode( TabBox::WindowsMode ); 01108 tab_box->reset(); 01109 tab_box->nextPrev( forward ); 01110 if( Client* c = tab_box->currentClient() ) 01111 { 01112 activateClient( c ); 01113 if( c->isShade() && options->shadeHover ) 01114 c->setShade( ShadeActivated ); 01115 } 01116 } 01117 01118 void Workspace::oneStepThroughDesktops( bool forward, int mode ) 01119 { 01120 tab_box->setMode( (TabBox::Mode) mode ); 01121 tab_box->reset(); 01122 tab_box->nextPrev( forward ); 01123 if ( tab_box->currentDesktop() != -1 ) 01124 setCurrentDesktop( tab_box->currentDesktop() ); 01125 } 01126 01127 void Workspace::oneStepThroughDesktops( bool forward ) 01128 { 01129 oneStepThroughDesktops( forward, TabBox::DesktopMode ); 01130 } 01131 01132 void Workspace::oneStepThroughDesktopList( bool forward ) 01133 { 01134 oneStepThroughDesktops( forward, TabBox::DesktopListMode ); 01135 } 01136 01140 void Workspace::tabBoxKeyPress( const KKeyNative& keyX ) 01141 { 01142 bool forward = false; 01143 bool backward = false; 01144 bool forwardapps = false; 01145 bool backwardapps = false; 01146 01147 if (tab_grab) 01148 { 01149 forward = cutWalkThroughWindows.contains( keyX ); 01150 backward = cutWalkThroughWindowsReverse.contains( keyX ); 01151 01152 forwardapps = cutWalkThroughApps.contains( keyX ); 01153 backwardapps = cutWalkThroughAppsReverse.contains( keyX ); 01154 01155 if ( (forward || backward) && (!tab_box->isAppsOnly()) ) 01156 { 01157 kdDebug(125) << "== " << cutWalkThroughWindows.toStringInternal() 01158 << " or " << cutWalkThroughWindowsReverse.toStringInternal() << endl; 01159 01160 KDEWalkThroughWindows( forward ); 01161 } 01162 01163 if ( (forwardapps || backwardapps) && (tab_box->isAppsOnly()) ) 01164 { 01165 kdDebug(125) << "== " << cutWalkThroughWindows.toStringInternal() 01166 << " or " << cutWalkThroughWindowsReverse.toStringInternal() << endl; 01167 KDEWalkThroughWindows( forwardapps ); 01168 } 01169 } 01170 01171 else if (control_grab) 01172 { 01173 forward = cutWalkThroughDesktops.contains( keyX ) || 01174 cutWalkThroughDesktopList.contains( keyX ); 01175 backward = cutWalkThroughDesktopsReverse.contains( keyX ) || 01176 cutWalkThroughDesktopListReverse.contains( keyX ); 01177 if (forward || backward) 01178 walkThroughDesktops(forward); 01179 } 01180 01181 if (control_grab || tab_grab) 01182 { 01183 uint keyQt = keyX.keyCodeQt(); 01184 if ( ((keyQt & 0xffff) == Qt::Key_Escape) 01185 && !(forward || backward) ) 01186 { // if Escape is part of the shortcut, don't cancel 01187 closeTabBox(); 01188 } 01189 } 01190 } 01191 01192 void Workspace::closeTabBox() 01193 { 01194 removeTabBoxGrab(); 01195 tab_box->hide(); 01196 keys->suspend( false ); 01197 disable_shortcuts_keys->suspend( false ); 01198 client_keys->suspend( false ); 01199 tab_grab = FALSE; 01200 control_grab = FALSE; 01201 } 01202 01206 void Workspace::tabBoxKeyRelease( const XKeyEvent& ev ) 01207 { 01208 unsigned int mk = ev.state & 01209 (KKeyNative::modX(KKey::SHIFT) | 01210 KKeyNative::modX(KKey::CTRL) | 01211 KKeyNative::modX(KKey::ALT) | 01212 KKeyNative::modX(KKey::WIN)); 01213 // ev.state is state before the key release, so just checking mk being 0 isn't enough 01214 // using XQueryPointer() also doesn't seem to work well, so the check that all 01215 // modifiers are released: only one modifier is active and the currently released 01216 // key is this modifier - if yes, release the grab 01217 int mod_index = -1; 01218 for( int i = ShiftMapIndex; 01219 i <= Mod5MapIndex; 01220 ++i ) 01221 if(( mk & ( 1 << i )) != 0 ) 01222 { 01223 if( mod_index >= 0 ) 01224 return; 01225 mod_index = i; 01226 } 01227 bool release = false; 01228 if( mod_index == -1 ) 01229 release = true; 01230 else 01231 { 01232 XModifierKeymap* xmk = XGetModifierMapping(tqt_xdisplay()); 01233 for (int i=0; i<xmk->max_keypermod; i++) 01234 if (xmk->modifiermap[xmk->max_keypermod * mod_index + i] 01235 == ev.keycode) 01236 release = true; 01237 XFreeModifiermap(xmk); 01238 } 01239 if( !release ) 01240 return; 01241 if (tab_grab) 01242 { 01243 removeTabBoxGrab(); 01244 tab_box->hide(); 01245 keys->suspend( false ); 01246 disable_shortcuts_keys->suspend( false ); 01247 client_keys->suspend( false ); 01248 tab_grab = false; 01249 if( Client* c = tab_box->currentClient()) 01250 { 01251 activateClient( c ); 01252 if( c->isShade() && options->shadeHover ) 01253 c->setShade( ShadeActivated ); 01254 } 01255 } 01256 if (control_grab) 01257 { 01258 removeTabBoxGrab(); 01259 tab_box->hide(); 01260 keys->suspend( false ); 01261 disable_shortcuts_keys->suspend( false ); 01262 client_keys->suspend( false ); 01263 control_grab = False; 01264 if ( tab_box->currentDesktop() != -1 ) 01265 { 01266 setCurrentDesktop( tab_box->currentDesktop() ); 01267 } 01268 } 01269 } 01270 01271 01272 int Workspace::nextDesktopFocusChain( int iDesktop ) const 01273 { 01274 int i = desktop_focus_chain.find( iDesktop ); 01275 if( i >= 0 && i+1 < (int)desktop_focus_chain.size() ) 01276 return desktop_focus_chain[i+1]; 01277 else if( desktop_focus_chain.size() > 0 ) 01278 return desktop_focus_chain[ 0 ]; 01279 else 01280 return 1; 01281 } 01282 01283 int Workspace::previousDesktopFocusChain( int iDesktop ) const 01284 { 01285 int i = desktop_focus_chain.find( iDesktop ); 01286 if( i-1 >= 0 ) 01287 return desktop_focus_chain[i-1]; 01288 else if( desktop_focus_chain.size() > 0 ) 01289 return desktop_focus_chain[desktop_focus_chain.size()-1]; 01290 else 01291 return numberOfDesktops(); 01292 } 01293 01298 Client* Workspace::nextFocusChainClient( Client* c ) const 01299 { 01300 if ( global_focus_chain.isEmpty() ) 01301 return 0; 01302 ClientList::ConstIterator it = global_focus_chain.find( c ); 01303 if ( it == global_focus_chain.end() ) 01304 return global_focus_chain.last(); 01305 if ( it == global_focus_chain.begin() ) 01306 return global_focus_chain.last(); 01307 --it; 01308 return *it; 01309 } 01310 01315 Client* Workspace::previousFocusChainClient( Client* c ) const 01316 { 01317 if ( global_focus_chain.isEmpty() ) 01318 return 0; 01319 ClientList::ConstIterator it = global_focus_chain.find( c ); 01320 if ( it == global_focus_chain.end() ) 01321 return global_focus_chain.first(); 01322 ++it; 01323 if ( it == global_focus_chain.end() ) 01324 return global_focus_chain.first(); 01325 return *it; 01326 } 01327 01332 Client* Workspace::nextStaticClient( Client* c ) const 01333 { 01334 if ( !c || clients.isEmpty() ) 01335 return 0; 01336 ClientList::ConstIterator it = clients.find( c ); 01337 if ( it == clients.end() ) 01338 return clients.first(); 01339 ++it; 01340 if ( it == clients.end() ) 01341 return clients.first(); 01342 return *it; 01343 } 01348 Client* Workspace::previousStaticClient( Client* c ) const 01349 { 01350 if ( !c || clients.isEmpty() ) 01351 return 0; 01352 ClientList::ConstIterator it = clients.find( c ); 01353 if ( it == clients.end() ) 01354 return clients.last(); 01355 if ( it == clients.begin() ) 01356 return clients.last(); 01357 --it; 01358 return *it; 01359 } 01360 01361 bool Workspace::establishTabBoxGrab() 01362 { 01363 if( XGrabKeyboard( tqt_xdisplay(), root, FALSE, 01364 GrabModeAsync, GrabModeAsync, GET_QT_X_TIME()) != GrabSuccess ) 01365 return false; 01366 // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent 01367 // using Alt+Tab while DND (#44972). However force passive grabs on all windows 01368 // in order to catch MouseRelease events and close the tabbox (#67416). 01369 // All clients already have passive grabs in their wrapper windows, so check only 01370 // the active client, which may not have it. 01371 assert( !forced_global_mouse_grab ); 01372 forced_global_mouse_grab = true; 01373 if( active_client != NULL ) 01374 active_client->updateMouseGrab(); 01375 return true; 01376 } 01377 01378 void Workspace::removeTabBoxGrab() 01379 { 01380 XUngrabKeyboard(tqt_xdisplay(), GET_QT_X_TIME()); 01381 assert( forced_global_mouse_grab ); 01382 forced_global_mouse_grab = false; 01383 if( active_client != NULL ) 01384 active_client->updateMouseGrab(); 01385 } 01386 01387 } // namespace 01388 01389 #include "tabbox.moc"