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