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