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