diff options
Diffstat (limited to 'twin/tabbox.cpp')
-rw-r--r-- | twin/tabbox.cpp | 1345 |
1 files changed, 1345 insertions, 0 deletions
diff --git a/twin/tabbox.cpp b/twin/tabbox.cpp new file mode 100644 index 000000000..4ca33a04f --- /dev/null +++ b/twin/tabbox.cpp @@ -0,0 +1,1345 @@ +/***************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> + +You can Freely distribute this program under the GNU General Public +License. See the file "COPYING" for the exact licensing terms. +******************************************************************/ + +//#define QT_CLEAN_NAMESPACE +#include "tabbox.h" +#include "workspace.h" +#include "client.h" +#include <tqpainter.h> +#include <tqlabel.h> +#include <tqdrawutil.h> +#include <tqstyle.h> +#include <kglobal.h> +#include <fixx11h.h> +#include <kconfig.h> +#include <klocale.h> +#include <tqapplication.h> +#include <tqdesktopwidget.h> +#include <kstringhandler.h> +#include <stdarg.h> +#include <kdebug.h> +#include <kglobalaccel.h> +#include <kkeynative.h> +#include <kglobalsettings.h> +#include <kiconeffect.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> + +// specify externals before namespace + +namespace KWinInternal +{ + +extern TQPixmap* twin_get_menu_pix_hack(); + +TabBox::TabBox( Workspace *ws, const char *name ) + : TQFrame( 0, name, TQt::WNoAutoErase ), current_client( NULL ), wspace(ws) + { + setFrameStyle(TQFrame::StyledPanel | TQFrame::Plain); + setLineWidth(2); + setMargin(2); + + showMiniIcon = false; + + no_tasks = i18n("*** No Windows ***"); + m = DesktopMode; // init variables + reconfigure(); + reset(); + connect(&delayedShowTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(show())); + + XSetWindowAttributes attr; + attr.override_redirect = 1; + outline_left = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_right = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_top = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + outline_bottom = XCreateWindow( qt_xdisplay(), qt_xrootwin(), 0, 0, 1, 1, 0, + CopyFromParent, CopyFromParent, CopyFromParent, CWOverrideRedirect, &attr ); + } + +TabBox::~TabBox() + { + XDestroyWindow( qt_xdisplay(), outline_left ); + XDestroyWindow( qt_xdisplay(), outline_right ); + XDestroyWindow( qt_xdisplay(), outline_top ); + XDestroyWindow( qt_xdisplay(), outline_bottom ); + } + + +/*! + Sets the current mode to \a mode, either DesktopListMode or WindowsMode + + \sa mode() + */ +void TabBox::setMode( Mode mode ) + { + m = mode; + } + + +/*! + Create list of clients on specified desktop, starting with client c +*/ +void TabBox::createClientList(ClientList &list, int desktop /*-1 = all*/, Client *c, bool chain) + { + ClientList::size_type idx = 0; + + list.clear(); + + Client* start = c; + + if ( chain ) + c = workspace()->nextFocusChainClient(c); + else + c = workspace()->stackingOrder().first(); + + Client* stop = c; + + while ( c ) + { + Client* add = NULL; + if ( ((desktop == -1) || c->isOnDesktop(desktop)) + && c->wantsTabFocus() ) + { // don't add windows that have modal dialogs + Client* modal = c->findModal(); + if( modal == NULL || modal == c ) + add = c; + else if( !list.contains( modal )) + add = modal; + else + { + // nothing + } + } + + if( options->separateScreenFocus && options->xineramaEnabled ) + { + if( c->screen() != workspace()->activeScreen()) + add = NULL; + } + + if( add != NULL ) + { + if ( start == add ) + { + list.remove( add ); + list.prepend( add ); + } + else + list += add; + } + + if ( chain ) + c = workspace()->nextFocusChainClient( c ); + else + { + if ( idx >= (workspace()->stackingOrder().size()-1) ) + c = 0; + else + c = workspace()->stackingOrder()[++idx]; + } + + if ( c == stop ) + break; + } + } + + +/*! + Resets the tab box to display the active client in WindowsMode, or the + current desktop in DesktopListMode + */ +void TabBox::reset() + { + int w, h, cw = 0, wmax = 0; + + TQRect r = workspace()->screenGeometry( workspace()->activeScreen()); + + // calculate height of 1 line + // fontheight + 1 pixel above + 1 pixel below, or 32x32 icon + 2 pixel above + below + lineHeight = QMAX(fontMetrics().height() + 2, 32 + 4); + + if ( mode() == WindowsMode ) + { + setCurrentClient( workspace()->activeClient()); + + // get all clients to show + createClientList(clients, options_traverse_all ? -1 : workspace()->currentDesktop(), current_client, true); + + // calculate maximum caption width + cw = fontMetrics().width(no_tasks)+20; + for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + cw = fontMetrics().width( (*it)->caption() ); + if ( cw > wmax ) wmax = cw; + } + + // calculate height for the popup + if ( clients.count() == 0 ) // height for the "not tasks" text + { + TQFont f = font(); + f.setBold( TRUE ); + f.setPointSize( 14 ); + + h = TQFontMetrics(f).height()*4; + } + else + { + showMiniIcon = false; + h = clients.count() * lineHeight; + + if ( h > (r.height()-(2*frameWidth())) ) // if too high, use mini icons + { + showMiniIcon = true; + // fontheight + 1 pixel above + 1 pixel below, or 16x16 icon + 1 pixel above + below + lineHeight = QMAX(fontMetrics().height() + 2, 16 + 2); + + h = clients.count() * lineHeight; + + if ( h > (r.height()-(2*frameWidth())) ) // if still too high, remove some clients + { + // how many clients to remove + int howMany = (h - (r.height()-(2*frameWidth())))/lineHeight; + for (; howMany; howMany--) + clients.remove(clients.last()); + + h = clients.count() * lineHeight; + } + } + } + } + else + { // DesktopListMode + showMiniIcon = false; + desk = workspace()->currentDesktop(); + + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + cw = fontMetrics().width( workspace()->desktopName(i) ); + if ( cw > wmax ) wmax = cw; + } + + // calculate height for the popup (max. 16 desktops always fit in a 800x600 screen) + h = workspace()->numberOfDesktops() * lineHeight; + } + + // height, width for the popup + h += 2 * frameWidth(); + w = 2*frameWidth() + 5*2 + ( showMiniIcon ? 16 : 32 ) + 8 + wmax; // 5*2=margins, ()=icon, 8=space between icon+text + w = kClamp( w, r.width()/3 , r.width() * 4 / 5 ); + + setGeometry( (r.width()-w)/2 + r.x(), + (r.height()-h)/2+ r.y(), + w, h ); + } + + +/*! + Shows the next or previous item, depending on \a next + */ +void TabBox::nextPrev( bool next) + { + if ( mode() == WindowsMode ) + { + Client* firstClient = NULL; + Client* client = current_client; + do + { + if ( next ) + client = workspace()->nextFocusChainClient(client); + else + client = workspace()->previousFocusChainClient(client); + if (!firstClient) + { + // When we see our first client for the second time, + // it's time to stop. + firstClient = client; + } + else if (client == firstClient) + { + // No candidates found. + client = 0; + break; + } + } while ( client && !clients.contains( client )); + setCurrentClient( client ); + } + else if( mode() == DesktopMode ) + { + if ( next ) + desk = workspace()->nextDesktopFocusChain( desk ); + else + desk = workspace()->previousDesktopFocusChain( desk ); + } + else + { // DesktopListMode + if ( next ) + { + desk++; + if ( desk > workspace()->numberOfDesktops() ) + desk = 1; + } + else + { + desk--; + if ( desk < 1 ) + desk = workspace()->numberOfDesktops(); + } + } + + update(); + } + + + +/*! + Returns the currently displayed client ( only works in WindowsMode ). + Returns 0 if no client is displayed. + */ +Client* TabBox::currentClient() + { + if ( mode() != WindowsMode ) + return 0; + if (!workspace()->hasClient( current_client )) + return 0; + return current_client; + } + +void TabBox::setCurrentClient( Client* c ) + { + if( current_client != c ) + { + current_client = c; + updateOutline(); + } + } + +/*! + Returns the currently displayed virtual desktop ( only works in + DesktopListMode ) + Returns -1 if no desktop is displayed. + */ +int TabBox::currentDesktop() + { + if ( mode() == DesktopListMode || mode() == DesktopMode ) + return desk; + else + return -1; + } + + +/*! + Reimplemented to raise the tab box as well + */ +void TabBox::showEvent( TQShowEvent* ) + { + updateOutline(); + XRaiseWindow( qt_xdisplay(), outline_left ); + XRaiseWindow( qt_xdisplay(), outline_right ); + XRaiseWindow( qt_xdisplay(), outline_top ); + XRaiseWindow( qt_xdisplay(), outline_bottom ); + raise(); + } + + +/*! + hide the icon box if necessary + */ +void TabBox::hideEvent( TQHideEvent* ) + { + XUnmapWindow( qt_xdisplay(), outline_left ); + XUnmapWindow( qt_xdisplay(), outline_right ); + XUnmapWindow( qt_xdisplay(), outline_top ); + XUnmapWindow( qt_xdisplay(), outline_bottom ); + } + +/*! + Paints the tab box + */ +void TabBox::drawContents( TQPainter * ) + { + TQRect r(contentsRect()); + TQPixmap pix(r.size()); // do double buffering to avoid flickers + pix.fill(this, 0, 0); + + TQPainter p; + p.tqbegin(&pix, this); + + TQPixmap* menu_pix = twin_get_menu_pix_hack(); + + int iconWidth = showMiniIcon ? 16 : 32; + int x = 0; + int y = 0; + + if ( mode () == WindowsMode ) + { + if ( !currentClient() ) + { + TQFont f = font(); + f.setBold( TRUE ); + f.setPointSize( 14 ); + + p.setFont(f); + p.drawText( r, AlignCenter, no_tasks); + } + else + { + for (ClientList::ConstIterator it = clients.begin(); it != clients.end(); ++it) + { + if ( workspace()->hasClient( *it ) ) // safety + { + // draw highlight background + if ( (*it) == current_client ) + p.fillRect(x, y, r.width(), lineHeight, tqcolorGroup().highlight()); + + // draw icon + TQPixmap icon; + if ( showMiniIcon ) + { + if ( !(*it)->miniIcon().isNull() ) + icon = (*it)->miniIcon(); + } + else + if ( !(*it)->icon().isNull() ) + icon = (*it)->icon(); + else if ( menu_pix ) + icon = *menu_pix; + + if( !icon.isNull()) + { + if( (*it)->isMinimized()) + KIconEffect::semiTransparent( icon ); + p.drawPixmap( x+5, y + (lineHeight - iconWidth)/2, icon ); + } + + // generate text to display + TQString s; + + if ( !(*it)->isOnDesktop(workspace()->currentDesktop()) ) + s = workspace()->desktopName((*it)->desktop()) + ": "; + + if ( (*it)->isMinimized() ) + s += TQString("(") + (*it)->caption() + ")"; + else + s += (*it)->caption(); + + s = KStringHandler::cPixelSqueeze(s, fontMetrics(), r.width() - 5 - iconWidth - 8); + + // draw text + if ( (*it) == current_client ) + p.setPen(tqcolorGroup().highlightedText()); + else if( (*it)->isMinimized()) + { + TQColor c1 = tqcolorGroup().text(); + TQColor c2 = tqcolorGroup().background(); + // from kicker's TaskContainer::blendColors() + int r1, g1, b1; + int r2, g2, b2; + + c1.rgb( &r1, &g1, &b1 ); + c2.rgb( &r2, &g2, &b2 ); + + r1 += (int) ( .5 * ( r2 - r1 ) ); + g1 += (int) ( .5 * ( g2 - g1 ) ); + b1 += (int) ( .5 * ( b2 - b1 ) ); + + p.setPen(TQColor( r1, g1, b1 )); + } + else + p.setPen(tqcolorGroup().text()); + + p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, + Qt::AlignLeft | Qt::AlignVCenter | TQt::SingleLine, s); + + y += lineHeight; + } + if ( y >= r.height() ) break; + } + } + } + else + { // DesktopMode || DesktopListMode + int iconHeight = iconWidth; + + // get widest desktop name/number + TQFont f(font()); + f.setBold(true); + f.setPixelSize(iconHeight - 4); // pixel, not point because I need to know the pixels + TQFontMetrics fm(f); + + int wmax = 0; + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + wmax = QMAX(wmax, fontMetrics().width(workspace()->desktopName(i))); + + // calculate max width of desktop-number text + TQString num = TQString::number(i); + iconWidth = QMAX(iconWidth - 4, fm.boundingRect(num).width()) + 4; + } + + // In DesktopMode, start at the current desktop + // In DesktopListMode, start at desktop #1 + int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; + for ( int i = 1; i <= workspace()->numberOfDesktops(); i++ ) + { + // draw highlight background + if ( iDesktop == desk ) // current desktop + p.fillRect(x, y, r.width(), lineHeight, tqcolorGroup().highlight()); + + p.save(); + + // draw "icon" (here: number of desktop) + p.fillRect(x+5, y+2, iconWidth, iconHeight, tqcolorGroup().base()); + p.setPen(tqcolorGroup().text()); + p.drawRect(x+5, y+2, iconWidth, iconHeight); + + // draw desktop-number + p.setFont(f); + TQString num = TQString::number(iDesktop); + p.drawText(x+5, y+2, iconWidth, iconHeight, Qt::AlignCenter, num); + + p.restore(); + + // draw desktop name text + if ( iDesktop == desk ) + p.setPen(tqcolorGroup().highlightedText()); + else + p.setPen(tqcolorGroup().text()); + + p.drawText(x+5 + iconWidth + 8, y, r.width() - 5 - iconWidth - 8, lineHeight, + Qt::AlignLeft | Qt::AlignVCenter | TQt::SingleLine, + workspace()->desktopName(iDesktop)); + + // show mini icons from that desktop aligned to each other + int x1 = x + 5 + iconWidth + 8 + wmax + 5; + + ClientList list; + createClientList(list, iDesktop, 0, false); + // clients are in reversed stacking order + for (ClientList::ConstIterator it = list.fromLast(); it != list.end(); --it) + { + if ( !(*it)->miniIcon().isNull() ) + { + if ( x1+18 >= x+r.width() ) // only show full icons + break; + + p.drawPixmap( x1, y + (lineHeight - 16)/2, (*it)->miniIcon() ); + x1 += 18; + } + } + + // next desktop + y += lineHeight; + if ( y >= r.height() ) break; + + if( mode() == DesktopMode ) + iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); + else + iDesktop++; + } + } + p.end(); + bitBlt(this, r.x(), r.y(), &pix); + } + +void TabBox::updateOutline() + { + Client* c = currentClient(); + if( !options->tabboxOutline || c == NULL || this->isHidden() || !c->isShown( true ) || !c->isOnCurrentDesktop()) + { + XUnmapWindow( qt_xdisplay(), outline_left ); + XUnmapWindow( qt_xdisplay(), outline_right ); + XUnmapWindow( qt_xdisplay(), outline_top ); + XUnmapWindow( qt_xdisplay(), outline_bottom ); + return; + } + // left/right parts are between top/bottom, they don't reach as far as the corners + XMoveResizeWindow( qt_xdisplay(), outline_left, c->x(), c->y() + 5, 5, c->height() - 10 ); + XMoveResizeWindow( qt_xdisplay(), outline_right, c->x() + c->width() - 5, c->y() + 5, 5, c->height() - 10 ); + XMoveResizeWindow( qt_xdisplay(), outline_top, c->x(), c->y(), c->width(), 5 ); + XMoveResizeWindow( qt_xdisplay(), outline_bottom, c->x(), c->y() + c->height() - 5, c->width(), 5 ); + { + TQPixmap pix( 5, c->height() - 10 ); + TQPainter p( &pix ); + p.setPen( white ); + p.drawLine( 0, 0, 0, pix.height() - 1 ); + p.drawLine( 4, 0, 4, pix.height() - 1 ); + p.setPen( gray ); + p.drawLine( 1, 0, 1, pix.height() - 1 ); + p.drawLine( 3, 0, 3, pix.height() - 1 ); + p.setPen( black ); + p.drawLine( 2, 0, 2, pix.height() - 1 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_left, pix.handle()); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_right, pix.handle()); + } + { + TQPixmap pix( c->width(), 5 ); + TQPainter p( &pix ); + p.setPen( white ); + p.drawLine( 0, 0, pix.width() - 1 - 0, 0 ); + p.drawLine( 4, 4, pix.width() - 1 - 4, 4 ); + p.drawLine( 0, 0, 0, 4 ); + p.drawLine( pix.width() - 1 - 0, 0, pix.width() - 1 - 0, 4 ); + p.setPen( gray ); + p.drawLine( 1, 1, pix.width() - 1 - 1, 1 ); + p.drawLine( 3, 3, pix.width() - 1 - 3, 3 ); + p.drawLine( 1, 1, 1, 4 ); + p.drawLine( 3, 3, 3, 4 ); + p.drawLine( pix.width() - 1 - 1, 1, pix.width() - 1 - 1, 4 ); + p.drawLine( pix.width() - 1 - 3, 3, pix.width() - 1 - 3, 4 ); + p.setPen( black ); + p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); + p.drawLine( 2, 2, 2, 4 ); + p.drawLine( pix.width() - 1 - 2, 2, pix.width() - 1 - 2, 4 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_top, pix.handle()); + } + { + TQPixmap pix( c->width(), 5 ); + TQPainter p( &pix ); + p.setPen( white ); + p.drawLine( 4, 0, pix.width() - 1 - 4, 0 ); + p.drawLine( 0, 4, pix.width() - 1 - 0, 4 ); + p.drawLine( 0, 4, 0, 0 ); + p.drawLine( pix.width() - 1 - 0, 4, pix.width() - 1 - 0, 0 ); + p.setPen( gray ); + p.drawLine( 3, 1, pix.width() - 1 - 3, 1 ); + p.drawLine( 1, 3, pix.width() - 1 - 1, 3 ); + p.drawLine( 3, 1, 3, 0 ); + p.drawLine( 1, 3, 1, 0 ); + p.drawLine( pix.width() - 1 - 3, 1, pix.width() - 1 - 3, 0 ); + p.drawLine( pix.width() - 1 - 1, 3, pix.width() - 1 - 1, 0 ); + p.setPen( black ); + p.drawLine( 2, 2, pix.width() - 1 - 2, 2 ); + p.drawLine( 2, 0, 2, 2 ); + p.drawLine( pix.width() - 1 - 2, 0, pix.width() - 1 - 2, 2 ); + p.end(); + XSetWindowBackgroundPixmap( qt_xdisplay(), outline_bottom, pix.handle()); + } + XClearWindow( qt_xdisplay(), outline_left ); + XClearWindow( qt_xdisplay(), outline_right ); + XClearWindow( qt_xdisplay(), outline_top ); + XClearWindow( qt_xdisplay(), outline_bottom ); + XMapWindow( qt_xdisplay(), outline_left ); + XMapWindow( qt_xdisplay(), outline_right ); + XMapWindow( qt_xdisplay(), outline_top ); + XMapWindow( qt_xdisplay(), outline_bottom ); + } + +void TabBox::hide() + { + delayedShowTimer.stop(); + TQWidget::hide(); + TQApplication::syncX(); + XEvent otherEvent; + while (XCheckTypedEvent (qt_xdisplay(), EnterNotify, &otherEvent ) ) + ; + } + + +void TabBox::reconfigure() + { + KConfig * c(KGlobal::config()); + c->setGroup("TabBox"); + options_traverse_all = c->readBoolEntry("TraverseAll", false ); + } + +/*! + Rikkus: please document! (Matthias) + + Ok, here's the docs :) + + You call delayedShow() instead of show() directly. + + If the 'ShowDelay' setting is false, show() is simply called. + + Otherwise, we start a timer for the delay given in the settings and only + do a show() when it times out. + + This means that you can alt-tab between windows and you don't see the + tab box immediately. Not only does this make alt-tabbing faster, it gives + less 'flicker' to the eyes. You don't need to see the tab box if you're + just quickly switching between 2 or 3 windows. It seems to work quite + nicely. + */ +void TabBox::delayedShow() + { + KConfig * c(KGlobal::config()); + c->setGroup("TabBox"); + bool delay = c->readBoolEntry("ShowDelay", true); + + if (!delay) + { + show(); + return; + } + + int delayTime = c->readNumEntry("DelayTime", 90); + delayedShowTimer.start(delayTime, true); + } + + +void TabBox::handleMouseEvent( XEvent* e ) + { + XAllowEvents( qt_xdisplay(), AsyncPointer, GET_QT_X_TIME() ); + if( e->type != ButtonPress ) + return; + TQPoint pos( e->xbutton.x_root, e->xbutton.y_root ); + if( !tqgeometry().contains( pos )) + { + workspace()->closeTabBox(); // click outside closes tab + return; + } + pos.rx() -= x(); // pos is now inside tabbox + pos.ry() -= y(); + int num = (pos.y()-frameWidth()) / lineHeight; + + if( mode() == WindowsMode ) + { + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it) + { + if( workspace()->hasClient( *it ) && (num == 0) ) // safety + { + setCurrentClient( *it ); + break; + } + num--; + } + } + else + { + int iDesktop = (mode() == DesktopMode) ? workspace()->currentDesktop() : 1; + for( int i = 1; + i <= workspace()->numberOfDesktops(); + ++i ) + { + if( num == 0 ) + { + desk = iDesktop; + break; + } + num--; + if( mode() == DesktopMode ) + iDesktop = workspace()->nextDesktopFocusChain( iDesktop ); + else + iDesktop++; + } + } + update(); + } + +//******************************* +// Workspace +//******************************* + + +/*! + Handles alt-tab / control-tab + */ + +static +bool areKeySymXsDepressed( bool bAll, const uint keySyms[], int nKeySyms ) + { + char keymap[32]; + + kdDebug(125) << "areKeySymXsDepressed: " << (bAll ? "all of " : "any of ") << nKeySyms << endl; + + XQueryKeymap( qt_xdisplay(), keymap ); + + for( int iKeySym = 0; iKeySym < nKeySyms; iKeySym++ ) + { + uint keySymX = keySyms[ iKeySym ]; + uchar keyCodeX = XKeysymToKeycode( qt_xdisplay(), keySymX ); + int i = keyCodeX / 8; + char mask = 1 << (keyCodeX - (i * 8)); + + kdDebug(125) << iKeySym << ": keySymX=0x" << TQString::number( keySymX, 16 ) + << " i=" << i << " mask=0x" << TQString::number( mask, 16 ) + << " keymap[i]=0x" << TQString::number( keymap[i], 16 ) << endl; + + // Abort if bad index value, + if( i < 0 || i >= 32 ) + return false; + + // If ALL keys passed need to be depressed, + if( bAll ) + { + if( (keymap[i] & mask) == 0 ) + return false; + } + else + { + // If we are looking for ANY key press, and this key is depressed, + if( keymap[i] & mask ) + return true; + } + } + + // If we were looking for ANY key press, then none was found, return false, + // If we were looking for ALL key presses, then all were found, return true. + return bAll; + } + +static bool areModKeysDepressed( const KKeySequence& seq ) + { + uint rgKeySyms[10]; + int nKeySyms = 0; + if( seq.isNull()) + return false; + int mod = seq.key(seq.count()-1).modFlags(); + + if ( mod & KKey::SHIFT ) + { + rgKeySyms[nKeySyms++] = XK_Shift_L; + rgKeySyms[nKeySyms++] = XK_Shift_R; + } + if ( mod & KKey::CTRL ) + { + rgKeySyms[nKeySyms++] = XK_Control_L; + rgKeySyms[nKeySyms++] = XK_Control_R; + } + if( mod & KKey::ALT ) + { + rgKeySyms[nKeySyms++] = XK_Alt_L; + rgKeySyms[nKeySyms++] = XK_Alt_R; + } + if( mod & KKey::WIN ) + { + // It would take some code to determine whether the Win key + // is associated with Super or Meta, so check for both. + // See bug #140023 for details. + rgKeySyms[nKeySyms++] = XK_Super_L; + rgKeySyms[nKeySyms++] = XK_Super_R; + rgKeySyms[nKeySyms++] = XK_Meta_L; + rgKeySyms[nKeySyms++] = XK_Meta_R; + } + + return areKeySymXsDepressed( false, rgKeySyms, nKeySyms ); + } + +static bool areModKeysDepressed( const KShortcut& cut ) + { + for( unsigned int i = 0; + i < cut.count(); + ++i ) + { + if( areModKeysDepressed( cut.seq( i ))) + return true; + } + return false; + } + +void Workspace::slotWalkThroughWindows() + { + if ( root != qt_xrootwin() ) + return; + if ( tab_grab || control_grab ) + return; + if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) + { + //XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); // need that because of accelerator raw mode + // CDE style raise / lower + CDEWalkThroughWindows( true ); + } + else + { + if ( areModKeysDepressed( cutWalkThroughWindows ) ) + { + if ( startKDEWalkThroughWindows() ) + KDEWalkThroughWindows( true ); + } + else + // if the shortcut has no modifiers, don't show the tabbox, + // don't grab, but simply go to the next window + KDEOneStepThroughWindows( true ); + } + } + +void Workspace::slotWalkBackThroughWindows() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( options->altTabStyle == Options::CDE || !options->focusPolicyIsReasonable()) + { + // CDE style raise / lower + CDEWalkThroughWindows( false ); + } + else + { + if ( areModKeysDepressed( cutWalkThroughWindowsReverse ) ) + { + if ( startKDEWalkThroughWindows() ) + KDEWalkThroughWindows( false ); + } + else + { + KDEOneStepThroughWindows( false ); + } + } + } + +void Workspace::slotWalkThroughDesktops() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktops ) ) + { + if ( startWalkThroughDesktops() ) + walkThroughDesktops( true ); + } + else + { + oneStepThroughDesktops( true ); + } + } + +void Workspace::slotWalkBackThroughDesktops() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopsReverse ) ) + { + if ( startWalkThroughDesktops() ) + walkThroughDesktops( false ); + } + else + { + oneStepThroughDesktops( false ); + } + } + +void Workspace::slotWalkThroughDesktopList() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopList ) ) + { + if ( startWalkThroughDesktopList() ) + walkThroughDesktops( true ); + } + else + { + oneStepThroughDesktopList( true ); + } + } + +void Workspace::slotWalkBackThroughDesktopList() + { + if ( root != qt_xrootwin() ) + return; + if( tab_grab || control_grab ) + return; + if ( areModKeysDepressed( cutWalkThroughDesktopListReverse ) ) + { + if ( startWalkThroughDesktopList() ) + walkThroughDesktops( false ); + } + else + { + oneStepThroughDesktopList( false ); + } + } + +bool Workspace::startKDEWalkThroughWindows() + { + if( !establishTabBoxGrab()) + return false; + tab_grab = TRUE; + keys->suspend( true ); + disable_shortcuts_keys->suspend( true ); + client_keys->suspend( true ); + tab_box->setMode( TabBox::WindowsMode ); + tab_box->reset(); + return TRUE; + } + +bool Workspace::startWalkThroughDesktops( int mode ) + { + if( !establishTabBoxGrab()) + return false; + control_grab = TRUE; + keys->suspend( true ); + disable_shortcuts_keys->suspend( true ); + client_keys->suspend( true ); + tab_box->setMode( (TabBox::Mode) mode ); + tab_box->reset(); + return TRUE; + } + +bool Workspace::startWalkThroughDesktops() + { + return startWalkThroughDesktops( TabBox::DesktopMode ); + } + +bool Workspace::startWalkThroughDesktopList() + { + return startWalkThroughDesktops( TabBox::DesktopListMode ); + } + +void Workspace::KDEWalkThroughWindows( bool forward ) + { + tab_box->nextPrev( forward ); + tab_box->delayedShow(); + } + +void Workspace::walkThroughDesktops( bool forward ) + { + tab_box->nextPrev( forward ); + tab_box->delayedShow(); + } + +void Workspace::CDEWalkThroughWindows( bool forward ) + { + Client* c = NULL; +// this function find the first suitable client for unreasonable focus +// policies - the topmost one, with some exceptions (can't be keepabove/below, +// otherwise it gets stuck on them) + Q_ASSERT( block_stacking_updates == 0 ); + for( ClientList::ConstIterator it = stacking_order.fromLast(); + it != stacking_order.end(); + --it ) + { + if ( (*it)->isOnCurrentDesktop() && !(*it)->isSpecialWindow() + && (*it)->isShown( false ) && (*it)->wantsTabFocus() + && !(*it)->keepAbove() && !(*it)->keepBelow()) + { + c = *it; + break; + } + } + Client* nc = c; + bool options_traverse_all; + { + KConfigGroupSaver saver( KGlobal::config(), "TabBox" ); + options_traverse_all = KGlobal::config()->readBoolEntry("TraverseAll", false ); + } + + Client* firstClient = 0; + do + { + nc = forward ? nextStaticClient(nc) : previousStaticClient(nc); + if (!firstClient) + { + // When we see our first client for the second time, + // it's time to stop. + firstClient = nc; + } + else if (nc == firstClient) + { + // No candidates found. + nc = 0; + break; + } + } while (nc && nc != c && + (( !options_traverse_all && !nc->isOnDesktop(currentDesktop())) || + nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() ) ); + if (nc) + { + if (c && c != nc) + lowerClient( c ); + if ( options->focusPolicyIsReasonable() ) + { + activateClient( nc ); + if( nc->isShade() && options->shadeHover ) + nc->setShade( ShadeActivated ); + } + else + { + if( !nc->isOnDesktop( currentDesktop())) + setCurrentDesktop( nc->desktop()); + raiseClient( nc ); + } + } + } + +void Workspace::KDEOneStepThroughWindows( bool forward ) + { + tab_box->setMode( TabBox::WindowsMode ); + tab_box->reset(); + tab_box->nextPrev( forward ); + if( Client* c = tab_box->currentClient() ) + { + activateClient( c ); + if( c->isShade() && options->shadeHover ) + c->setShade( ShadeActivated ); + } + } + +void Workspace::oneStepThroughDesktops( bool forward, int mode ) + { + tab_box->setMode( (TabBox::Mode) mode ); + tab_box->reset(); + tab_box->nextPrev( forward ); + if ( tab_box->currentDesktop() != -1 ) + setCurrentDesktop( tab_box->currentDesktop() ); + } + +void Workspace::oneStepThroughDesktops( bool forward ) + { + oneStepThroughDesktops( forward, TabBox::DesktopMode ); + } + +void Workspace::oneStepThroughDesktopList( bool forward ) + { + oneStepThroughDesktops( forward, TabBox::DesktopListMode ); + } + +/*! + Handles holding alt-tab / control-tab + */ +void Workspace::tabBoxKeyPress( const KKeyNative& keyX ) + { + bool forward = false; + bool backward = false; + + if (tab_grab) + { + forward = cutWalkThroughWindows.contains( keyX ); + backward = cutWalkThroughWindowsReverse.contains( keyX ); + if (forward || backward) + { + kdDebug(125) << "== " << cutWalkThroughWindows.toStringInternal() + << " or " << cutWalkThroughWindowsReverse.toStringInternal() << endl; + KDEWalkThroughWindows( forward ); + } + } + else if (control_grab) + { + forward = cutWalkThroughDesktops.contains( keyX ) || + cutWalkThroughDesktopList.contains( keyX ); + backward = cutWalkThroughDesktopsReverse.contains( keyX ) || + cutWalkThroughDesktopListReverse.contains( keyX ); + if (forward || backward) + walkThroughDesktops(forward); + } + + if (control_grab || tab_grab) + { + uint keyQt = keyX.keyCodeQt(); + if ( ((keyQt & 0xffff) == Qt::Key_Escape) + && !(forward || backward) ) + { // if Escape is part of the shortcut, don't cancel + closeTabBox(); + } + } + } + +void Workspace::closeTabBox() + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + tab_grab = FALSE; + control_grab = FALSE; + } + +/*! + Handles alt-tab / control-tab releasing + */ +void Workspace::tabBoxKeyRelease( const XKeyEvent& ev ) + { + unsigned int mk = ev.state & + (KKeyNative::modX(KKey::SHIFT) | + KKeyNative::modX(KKey::CTRL) | + KKeyNative::modX(KKey::ALT) | + KKeyNative::modX(KKey::WIN)); + // ev.state is state before the key release, so just checking mk being 0 isn't enough + // using XQueryPointer() also doesn't seem to work well, so the check that all + // modifiers are released: only one modifier is active and the currently released + // key is this modifier - if yes, release the grab + int mod_index = -1; + for( int i = ShiftMapIndex; + i <= Mod5MapIndex; + ++i ) + if(( mk & ( 1 << i )) != 0 ) + { + if( mod_index >= 0 ) + return; + mod_index = i; + } + bool release = false; + if( mod_index == -1 ) + release = true; + else + { + XModifierKeymap* xmk = XGetModifierMapping(qt_xdisplay()); + for (int i=0; i<xmk->max_keypermod; i++) + if (xmk->modifiermap[xmk->max_keypermod * mod_index + i] + == ev.keycode) + release = true; + XFreeModifiermap(xmk); + } + if( !release ) + return; + if (tab_grab) + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + tab_grab = false; + if( Client* c = tab_box->currentClient()) + { + activateClient( c ); + if( c->isShade() && options->shadeHover ) + c->setShade( ShadeActivated ); + } + } + if (control_grab) + { + removeTabBoxGrab(); + tab_box->hide(); + keys->suspend( false ); + disable_shortcuts_keys->suspend( false ); + client_keys->suspend( false ); + control_grab = False; + if ( tab_box->currentDesktop() != -1 ) + { + setCurrentDesktop( tab_box->currentDesktop() ); + } + } + } + + +int Workspace::nextDesktopFocusChain( int iDesktop ) const + { + int i = desktop_focus_chain.find( iDesktop ); + if( i >= 0 && i+1 < (int)desktop_focus_chain.size() ) + return desktop_focus_chain[i+1]; + else if( desktop_focus_chain.size() > 0 ) + return desktop_focus_chain[ 0 ]; + else + return 1; + } + +int Workspace::previousDesktopFocusChain( int iDesktop ) const + { + int i = desktop_focus_chain.find( iDesktop ); + if( i-1 >= 0 ) + return desktop_focus_chain[i-1]; + else if( desktop_focus_chain.size() > 0 ) + return desktop_focus_chain[desktop_focus_chain.size()-1]; + else + return numberOfDesktops(); + } + +/*! + auxiliary functions to travers all clients according the focus + order. Useful for kwms Alt-tab feature. +*/ +Client* Workspace::nextFocusChainClient( Client* c ) const + { + if ( global_focus_chain.isEmpty() ) + return 0; + ClientList::ConstIterator it = global_focus_chain.find( c ); + if ( it == global_focus_chain.end() ) + return global_focus_chain.last(); + if ( it == global_focus_chain.begin() ) + return global_focus_chain.last(); + --it; + return *it; + } + +/*! + auxiliary functions to travers all clients according the focus + order. Useful for kwms Alt-tab feature. +*/ +Client* Workspace::previousFocusChainClient( Client* c ) const + { + if ( global_focus_chain.isEmpty() ) + return 0; + ClientList::ConstIterator it = global_focus_chain.find( c ); + if ( it == global_focus_chain.end() ) + return global_focus_chain.first(); + ++it; + if ( it == global_focus_chain.end() ) + return global_focus_chain.first(); + return *it; + } + +/*! + auxiliary functions to travers all clients according the static + order. Useful for the CDE-style Alt-tab feature. +*/ +Client* Workspace::nextStaticClient( Client* c ) const + { + if ( !c || clients.isEmpty() ) + return 0; + ClientList::ConstIterator it = clients.find( c ); + if ( it == clients.end() ) + return clients.first(); + ++it; + if ( it == clients.end() ) + return clients.first(); + return *it; + } +/*! + auxiliary functions to travers all clients according the static + order. Useful for the CDE-style Alt-tab feature. +*/ +Client* Workspace::previousStaticClient( Client* c ) const + { + if ( !c || clients.isEmpty() ) + return 0; + ClientList::ConstIterator it = clients.find( c ); + if ( it == clients.end() ) + return clients.last(); + if ( it == clients.begin() ) + return clients.last(); + --it; + return *it; + } + +bool Workspace::establishTabBoxGrab() + { + if( XGrabKeyboard( qt_xdisplay(), root, FALSE, + GrabModeAsync, GrabModeAsync, GET_QT_X_TIME()) != GrabSuccess ) + return false; + // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent + // using Alt+Tab while DND (#44972). However force passive grabs on all windows + // in order to catch MouseRelease events and close the tabbox (#67416). + // All clients already have passive grabs in their wrapper windows, so check only + // the active client, which may not have it. + assert( !forced_global_mouse_grab ); + forced_global_mouse_grab = true; + if( active_client != NULL ) + active_client->updateMouseGrab(); + return true; + } + +void Workspace::removeTabBoxGrab() + { + XUngrabKeyboard(qt_xdisplay(), GET_QT_X_TIME()); + assert( forced_global_mouse_grab ); + forced_global_mouse_grab = false; + if( active_client != NULL ) + active_client->updateMouseGrab(); + } + +} // namespace + +#include "tabbox.moc" |