00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include "client.h"
00013
00014 #include <qapplication.h>
00015 #include <qpainter.h>
00016 #include <qdatetime.h>
00017 #include <kprocess.h>
00018 #include <unistd.h>
00019 #include <kstandarddirs.h>
00020 #include <qwhatsthis.h>
00021 #include <kwin.h>
00022 #include <kiconloader.h>
00023 #include <stdlib.h>
00024
00025 #include "bridge.h"
00026 #include "group.h"
00027 #include "workspace.h"
00028 #include "atoms.h"
00029 #include "notifications.h"
00030
00031 #include <X11/extensions/shape.h>
00032
00033
00034
00035
00036 extern Atom qt_wm_state;
00037 extern Time qt_x_time;
00038 extern Atom qt_window_role;
00039 extern Atom qt_sm_client_id;
00040
00041 namespace KWinInternal
00042 {
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00068 Client::Client( Workspace *ws )
00069 : QObject( NULL ),
00070 client( None ),
00071 wrapper( None ),
00072 frame( None ),
00073 decoration( NULL ),
00074 wspace( ws ),
00075 bridge( new Bridge( this )),
00076 move_faked_activity( false ),
00077 move_resize_grab_window( None ),
00078 transient_for( NULL ),
00079 transient_for_id( None ),
00080 original_transient_for_id( None ),
00081 in_group( NULL ),
00082 window_group( None ),
00083 in_layer( UnknownLayer ),
00084 ping_timer( NULL ),
00085 process_killer( NULL ),
00086 user_time( CurrentTime ),
00087 allowed_actions( 0 ),
00088 block_geometry( 0 ),
00089 shade_geometry_change( false ),
00090 border_left( 0 ),
00091 border_right( 0 ),
00092 border_top( 0 ),
00093 border_bottom( 0 )
00094
00095 {
00096 autoRaiseTimer = 0;
00097 shadeHoverTimer = 0;
00098
00099
00100 mapping_state = WithdrawnState;
00101 desk = 0;
00102
00103 mode = PositionCenter;
00104 buttonDown = FALSE;
00105 moveResizeMode = FALSE;
00106
00107 info = NULL;
00108
00109 shade_mode = ShadeNone;
00110 active = FALSE;
00111 keep_above = FALSE;
00112 keep_below = FALSE;
00113 is_shape = FALSE;
00114 motif_may_move = TRUE;
00115 motif_may_resize = TRUE;
00116 motif_may_close = TRUE;
00117 fullscreen_mode = FullScreenNone;
00118 skip_taskbar = FALSE;
00119 original_skip_taskbar = false;
00120 minimized = false;
00121 hidden = false;
00122 modal = false;
00123 noborder = false;
00124 user_noborder = false;
00125 not_obscured = false;
00126 urgency = false;
00127 ignore_focus_stealing = false;
00128
00129 Pdeletewindow = 0;
00130 Ptakefocus = 0;
00131 Pcontexthelp = 0;
00132 Pping = 0;
00133 input = FALSE;
00134 store_settings = FALSE;
00135 skip_pager = FALSE;
00136
00137
00138 max_mode = MaximizeRestore;
00139
00140 cmap = None;
00141
00142 frame_geometry = QRect( 0, 0, 100, 100 );
00143 client_size = QSize( 100, 100 );
00144
00145
00146 }
00147
00151 Client::~Client()
00152 {
00153 assert(!moveResizeMode);
00154 assert( client == None );
00155 assert( frame == None && wrapper == None );
00156 assert( decoration == NULL );
00157 assert( block_geometry == 0 );
00158 delete info;
00159 delete bridge;
00160 }
00161
00162
00163 void Client::deleteClient( Client* c, allowed_t )
00164 {
00165 delete c;
00166 }
00167
00171 void Client::releaseWindow( bool on_shutdown )
00172 {
00173 if (moveResizeMode)
00174 leaveMoveResize();
00175 setModal( false );
00176 hidden = true;
00177 if( !on_shutdown )
00178 workspace()->clientHidden( this );
00179 XUnmapWindow( qt_xdisplay(), frameId());
00180 destroyDecoration();
00181 cleanGrouping();
00182 if( !on_shutdown )
00183 {
00184 workspace()->removeClient( this, Allowed );
00185
00186
00187 info->setDesktop( 0 );
00188 desk = 0;
00189 info->setState( 0, info->state());
00190 }
00191 XDeleteProperty( qt_xdisplay(), client, atoms->kde_net_wm_user_creation_time);
00192
00193 XReparentWindow( qt_xdisplay(), client, workspace()->rootWin(), x(), y());
00194 XRemoveFromSaveSet( qt_xdisplay(), client );
00195 XSelectInput( qt_xdisplay(), client, NoEventMask );
00196 if( on_shutdown )
00197 {
00198 XMapWindow( qt_xdisplay(), client );
00199
00200 }
00201 else
00202 {
00203
00204
00205 XUnmapWindow( qt_xdisplay(), client );
00206 }
00207 setMappingState( WithdrawnState );
00208 client = None;
00209 XDestroyWindow( qt_xdisplay(), wrapper );
00210 wrapper = None;
00211 XDestroyWindow( qt_xdisplay(), frame );
00212 frame = None;
00213 deleteClient( this, Allowed );
00214 }
00215
00216
00217
00218 void Client::destroyClient()
00219 {
00220 if (moveResizeMode)
00221 leaveMoveResize();
00222 ++block_geometry;
00223 setModal( false );
00224 hidden = true;
00225 workspace()->clientHidden( this );
00226 destroyDecoration();
00227 cleanGrouping();
00228 workspace()->removeClient( this, Allowed );
00229 client = None;
00230 XDestroyWindow( qt_xdisplay(), wrapper );
00231 wrapper = None;
00232 XDestroyWindow( qt_xdisplay(), frame );
00233 frame = None;
00234 --block_geometry;
00235 deleteClient( this, Allowed );
00236 }
00237
00238 void Client::updateDecoration( bool check_workspace_pos, bool force )
00239 {
00240 if( !force && (( decoration == NULL && noBorder())
00241 || ( decoration != NULL && !noBorder())))
00242 return;
00243 bool do_show = false;
00244 ++block_geometry;
00245 if( force )
00246 destroyDecoration();
00247 if( !noBorder())
00248 {
00249 decoration = workspace()->createDecoration( bridge );
00250
00251 decoration->init();
00252 decoration->widget()->installEventFilter( this );
00253 XReparentWindow( qt_xdisplay(), decoration->widget()->winId(), frameId(), 0, 0 );
00254 decoration->widget()->lower();
00255 decoration->borders( border_left, border_right, border_top, border_bottom );
00256 int save_workarea_diff_x = workarea_diff_x;
00257 int save_workarea_diff_y = workarea_diff_y;
00258 move( calculateGravitation( false ));
00259 if( !isShade())
00260 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00261 else
00262 plainResize( sizeForClientSize( QSize( clientSize().width(), 0 ), SizemodeShaded ), ForceGeometrySet );
00263 workarea_diff_x = save_workarea_diff_x;
00264 workarea_diff_y = save_workarea_diff_y;
00265 do_show = true;
00266 }
00267 else
00268 destroyDecoration();
00269 if( check_workspace_pos )
00270 checkWorkspacePosition();
00271 --block_geometry;
00272 setGeometry( geometry(), ForceGeometrySet );
00273 if( do_show )
00274 decoration->widget()->show();
00275 updateFrameStrut();
00276 }
00277
00278 void Client::destroyDecoration()
00279 {
00280 if( decoration != NULL )
00281 {
00282 delete decoration;
00283 decoration = NULL;
00284 QPoint grav = calculateGravitation( true );
00285 border_left = border_right = border_top = border_bottom = 0;
00286 setMask( QRegion());
00287 int save_workarea_diff_x = workarea_diff_x;
00288 int save_workarea_diff_y = workarea_diff_y;
00289 if( !isShade())
00290 plainResize( clientSize(), ForceGeometrySet );
00291 else
00292 plainResize( QSize( clientSize().width(), 0 ), ForceGeometrySet );
00293 move( grav );
00294 workarea_diff_x = save_workarea_diff_x;
00295 workarea_diff_y = save_workarea_diff_y;
00296 }
00297 }
00298
00299 void Client::checkBorderSizes()
00300 {
00301 if( decoration == NULL )
00302 return;
00303 int new_left, new_right, new_top, new_bottom;
00304 decoration->borders( new_left, new_right, new_top, new_bottom );
00305 if( new_left == border_left && new_right == border_right
00306 && new_top == border_top && new_bottom == border_bottom )
00307 return;
00308 ++block_geometry;
00309 move( calculateGravitation( true ));
00310 border_left = new_left;
00311 border_right = new_right;
00312 border_top = new_top;
00313 border_bottom = new_bottom;
00314 move( calculateGravitation( false ));
00315 plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
00316 checkWorkspacePosition();
00317 --block_geometry;
00318 setGeometry( geometry(), ForceGeometrySet );
00319 }
00320
00321 void Client::detectNoBorder()
00322 {
00323 if( Shape::hasShape( window()) || Motif::noBorder( window()))
00324 {
00325 noborder = true;
00326 return;
00327 }
00328 switch( windowType())
00329 {
00330 case NET::Desktop :
00331 case NET::Dock :
00332 case NET::TopMenu :
00333 case NET::Override :
00334 case NET::Splash :
00335 noborder = true;
00336 break;
00337 case NET::Unknown :
00338 case NET::Normal :
00339 case NET::Toolbar :
00340 case NET::Menu :
00341 case NET::Dialog :
00342 case NET::Utility :
00343 noborder = false;
00344 break;
00345 default:
00346 assert( false );
00347 }
00348 }
00349
00350 void Client::updateFrameStrut()
00351 {
00352
00353 NETStrut strut;
00354 strut.left = border_left;
00355 strut.right = border_right;
00356 strut.top = border_top;
00357 strut.bottom = border_bottom;
00358 info->setKDEFrameStrut( strut );
00359 }
00360
00361
00362
00363
00364
00365
00366 void Client::resizeDecoration( const QSize& s )
00367 {
00368 if( decoration == NULL )
00369 return;
00370 QSize oldsize = decoration->widget()->size();
00371 decoration->resize( s );
00372 if( oldsize == s )
00373 {
00374 QResizeEvent e( s, oldsize );
00375 QApplication::sendEvent( decoration->widget(), &e );
00376 }
00377 }
00378
00379 bool Client::noBorder() const
00380 {
00381 return noborder || isFullScreen() || user_noborder;
00382 }
00383
00384 bool Client::userCanSetNoBorder() const
00385 {
00386 return !noborder && !isFullScreen() && !isShade();
00387 }
00388
00389 bool Client::isUserNoBorder() const
00390 {
00391 return user_noborder;
00392 }
00393
00394 void Client::setUserNoBorder( bool set )
00395 {
00396 if( !userCanSetNoBorder())
00397 return;
00398 if( user_noborder == set )
00399 return;
00400 user_noborder = set;
00401 updateDecoration( true, false );
00402 }
00403
00404 void Client::updateShape()
00405 {
00406 if ( shape() )
00407 XShapeCombineShape(qt_xdisplay(), frameId(), ShapeBounding,
00408 clientPos().x(), clientPos().y(),
00409 window(), ShapeBounding, ShapeSet);
00410 else
00411 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00412 None, ShapeSet);
00413
00414 if( shape() && !noBorder())
00415 {
00416 noborder = true;
00417 updateDecoration( true );
00418 }
00419 }
00420
00421 void Client::setMask( const QRegion& reg, int mode )
00422 {
00423 _mask = reg;
00424 if( reg.isNull())
00425 XShapeCombineMask( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00426 None, ShapeSet );
00427 else if( mode == X::Unsorted )
00428 XShapeCombineRegion( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00429 reg.handle(), ShapeSet );
00430 else
00431 {
00432 QMemArray< QRect > rects = reg.rects();
00433 XRectangle* xrects = new XRectangle[ rects.count() ];
00434 for( unsigned int i = 0;
00435 i < rects.count();
00436 ++i )
00437 {
00438 xrects[ i ].x = rects[ i ].x();
00439 xrects[ i ].y = rects[ i ].y();
00440 xrects[ i ].width = rects[ i ].width();
00441 xrects[ i ].height = rects[ i ].height();
00442 }
00443 XShapeCombineRectangles( qt_xdisplay(), frameId(), ShapeBounding, 0, 0,
00444 xrects, rects.count(), ShapeSet, mode );
00445 delete[] xrects;
00446 }
00447 }
00448
00449 QRegion Client::mask() const
00450 {
00451 if( _mask.isEmpty())
00452 return QRegion( 0, 0, width(), height());
00453 return _mask;
00454 }
00455
00456 void Client::hideClient( bool hide )
00457 {
00458 if( hidden == hide )
00459 return;
00460 hidden = hide;
00461 info->setState( hidden ? NET::Hidden : 0, NET::Hidden );
00462 if( hidden )
00463 {
00464 setMappingState( IconicState );
00465 rawHide();
00466 setSkipTaskbar( true, false );
00467 }
00468 else
00469 {
00470 setSkipTaskbar( original_skip_taskbar, false );
00471 if( isOnCurrentDesktop())
00472 {
00473 if( isShown( false ))
00474 setMappingState( NormalState );
00475 rawShow();
00476 }
00477 }
00478 }
00479
00480
00481
00482
00483 bool Client::isMinimizable() const
00484 {
00485 if( !wantsTabFocus()
00486 || ( isSpecialWindow() && !isOverride()))
00487 return false;
00488 if( isTransient())
00489 {
00490 ClientList mainclients = mainClients();
00491 for( ClientList::ConstIterator it = mainclients.begin();
00492 it != mainclients.end();
00493 ++it )
00494 if( (*it)->isShown( true ))
00495 return false;
00496 }
00497 return true;
00498 }
00499
00503 void Client::minimize()
00504 {
00505 if ( !isMinimizable() || isMinimized())
00506 return;
00507
00508 minimized = true;
00509
00510 Notify::raise( Notify::Minimize );
00511
00512
00513 if ( mainClients().isEmpty() && isOnCurrentDesktop())
00514 animateMinimizeOrUnminimize( true );
00515
00516 setMappingState( IconicState );
00517 info->setState( NET::Hidden, NET::Hidden );
00518 rawHide();
00519 updateAllowedActions();
00520 workspace()->updateMinimizedOfTransients( this );
00521 }
00522
00523 void Client::unminimize()
00524 {
00525 if( !isMinimized())
00526 return;
00527
00528 Notify::raise( Notify::UnMinimize );
00529 minimized = false;
00530 info->setState( 0, NET::Hidden );
00531 if( isOnCurrentDesktop())
00532 {
00533 if( mainClients().isEmpty())
00534 animateMinimizeOrUnminimize( FALSE );
00535 if( isShown( false ))
00536 setMappingState( NormalState );
00537 rawShow();
00538 }
00539 updateAllowedActions();
00540 workspace()->updateMinimizedOfTransients( this );
00541 }
00542
00543 extern bool blockAnimation;
00544
00545 void Client::animateMinimizeOrUnminimize( bool minimize )
00546 {
00547 if ( blockAnimation )
00548 return;
00549 if ( !options->animateMinimize )
00550 return;
00551
00552 if( decoration != NULL && decoration->animateMinimize( minimize ))
00553 return;
00554
00555
00556
00557
00558
00559 float lf,rf,tf,bf,step;
00560
00561 int speed = options->animateMinimizeSpeed;
00562 if ( speed > 10 )
00563 speed = 10;
00564 if ( speed < 0 )
00565 speed = 0;
00566
00567 step = 40. * (11 - speed );
00568
00569 NETRect r = info->iconGeometry();
00570 QRect icongeom( r.pos.x, r.pos.y, r.size.width, r.size.height );
00571 if ( !icongeom.isValid() )
00572 return;
00573
00574 QPixmap pm = animationPixmap( minimize ? width() : icongeom.width() );
00575
00576 QRect before, after;
00577 if ( minimize )
00578 {
00579 before = QRect( x(), y(), width(), pm.height() );
00580 after = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00581 }
00582 else
00583 {
00584 before = QRect( icongeom.x(), icongeom.y(), icongeom.width(), pm.height() );
00585 after = QRect( x(), y(), width(), pm.height() );
00586 }
00587
00588 lf = (after.left() - before.left())/step;
00589 rf = (after.right() - before.right())/step;
00590 tf = (after.top() - before.top())/step;
00591 bf = (after.bottom() - before.bottom())/step;
00592
00593 grabXServer();
00594
00595 QRect area = before;
00596 QRect area2;
00597 QPixmap pm2;
00598
00599 QTime t;
00600 t.start();
00601 float diff;
00602
00603 QPainter p ( workspace()->desktopWidget() );
00604 bool need_to_clear = FALSE;
00605 QPixmap pm3;
00606 do
00607 {
00608 if (area2 != area)
00609 {
00610 pm = animationPixmap( area.width() );
00611 pm2 = QPixmap::grabWindow( qt_xrootwin(), area.x(), area.y(), area.width(), area.height() );
00612 p.drawPixmap( area.x(), area.y(), pm );
00613 if ( need_to_clear )
00614 {
00615 p.drawPixmap( area2.x(), area2.y(), pm3 );
00616 need_to_clear = FALSE;
00617 }
00618 area2 = area;
00619 }
00620 XFlush(qt_xdisplay());
00621 XSync( qt_xdisplay(), FALSE );
00622 diff = t.elapsed();
00623 if (diff > step)
00624 diff = step;
00625 area.setLeft(before.left() + int(diff*lf));
00626 area.setRight(before.right() + int(diff*rf));
00627 area.setTop(before.top() + int(diff*tf));
00628 area.setBottom(before.bottom() + int(diff*bf));
00629 if (area2 != area )
00630 {
00631 if ( area2.intersects( area ) )
00632 p.drawPixmap( area2.x(), area2.y(), pm2 );
00633 else
00634 {
00635 pm3 = pm2;
00636 need_to_clear = TRUE;
00637 }
00638 }
00639 } while ( t.elapsed() < step);
00640 if (area2 == area || need_to_clear )
00641 p.drawPixmap( area2.x(), area2.y(), pm2 );
00642
00643 p.end();
00644 ungrabXServer();
00645 }
00646
00647
00651 QPixmap Client::animationPixmap( int w )
00652 {
00653 QFont font = options->font(isActive());
00654 QFontMetrics fm( font );
00655 QPixmap pm( w, fm.lineSpacing() );
00656 pm.fill( options->color(Options::ColorTitleBar, isActive() || isMinimized() ) );
00657 QPainter p( &pm );
00658 p.setPen(options->color(Options::ColorFont, isActive() || isMinimized() ));
00659 p.setFont(options->font(isActive()));
00660 p.drawText( pm.rect(), AlignLeft|AlignVCenter|SingleLine, caption() );
00661 return pm;
00662 }
00663
00664
00665 bool Client::isShadeable() const
00666 {
00667 return !isSpecialWindow() && !noBorder();
00668 }
00669
00670 void Client::setShade( ShadeMode mode )
00671 {
00672 if( !isShadeable())
00673 return;
00674 if( shade_mode == mode )
00675 return;
00676 bool was_shade = isShade();
00677 ShadeMode was_shade_mode = shade_mode;
00678 shade_mode = mode;
00679 if( was_shade == isShade())
00680 return;
00681
00682 if( shade_mode == ShadeNormal )
00683 {
00684 if ( isShown( true ) && isOnCurrentDesktop())
00685 Notify::raise( Notify::ShadeUp );
00686 }
00687 else if( shade_mode == ShadeNone )
00688 {
00689 if( isShown( true ) && isOnCurrentDesktop())
00690 Notify::raise( Notify::ShadeDown );
00691 }
00692
00693 assert( decoration != NULL );
00694 ++block_geometry;
00695
00696 decoration->borders( border_left, border_right, border_top, border_bottom );
00697
00698 int as = options->animateShade? 10 : 1;
00699
00700 if ( isShade())
00701 {
00702 int h = height();
00703 shade_geometry_change = true;
00704 QSize s( sizeForClientSize( QSize( clientSize().width(), 0), SizemodeShaded ) );
00705 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask );
00706 XUnmapWindow( qt_xdisplay(), wrapper );
00707 XUnmapWindow( qt_xdisplay(), client );
00708 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00709
00710
00711
00712 int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00713 do
00714 {
00715 h -= step;
00716 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00717 resizeDecoration( QSize( s.width(), h ));
00718 QApplication::syncX();
00719 } while ( h > s.height() + step );
00720
00721
00722 shade_geometry_change = false;
00723 plainResize( s );
00724 if( isActive())
00725 {
00726 if( was_shade_mode == ShadeHover )
00727 workspace()->activateNextClient( this );
00728 else
00729 workspace()->focusToNull();
00730 }
00731 }
00732 else
00733 {
00734 int h = height();
00735 shade_geometry_change = true;
00736 QSize s( sizeForClientSize( clientSize(), SizemodeShaded ));
00737
00738
00739 int step = QMAX( 4, QABS( h - s.height() ) / as )+1;
00740 do
00741 {
00742 h += step;
00743 XResizeWindow( qt_xdisplay(), frameId(), s.width(), h );
00744 resizeDecoration( QSize( s.width(), h ));
00745
00746
00747
00748 QApplication::syncX();
00749 } while ( h < s.height() - step );
00750
00751
00752 shade_geometry_change = false;
00753 plainResize( s );
00754 if( shade_mode == ShadeHover || shade_mode == ShadeActivated )
00755 setActive( TRUE );
00756 XMapWindow( qt_xdisplay(), wrapperId());
00757 XMapWindow( qt_xdisplay(), window());
00758 if ( isActive() )
00759 workspace()->requestFocus( this );
00760 }
00761 --block_geometry;
00762 setGeometry( geometry(), ForceGeometrySet );
00763 info->setState( isShade() ? NET::Shaded : 0, NET::Shaded );
00764 info->setState( isShown( false ) ? 0 : NET::Hidden, NET::Hidden );
00765 setMappingState( isShown( false ) && isOnCurrentDesktop() ? NormalState : IconicState );
00766 updateAllowedActions();
00767 workspace()->updateMinimizedOfTransients( this );
00768 decoration->shadeChange();
00769 }
00770
00771 void Client::shadeHover()
00772 {
00773 setShade( ShadeHover );
00774 delete shadeHoverTimer;
00775 shadeHoverTimer = 0;
00776 }
00777
00778 void Client::toggleShade()
00779 {
00780
00781 setShade( shade_mode == ShadeNone ? ShadeNormal : ShadeNone );
00782 }
00783
00784 void Client::virtualDesktopChange()
00785 {
00786 if( hidden || minimized )
00787 return;
00788
00789 if( isOnCurrentDesktop())
00790 {
00791 if( !isShade())
00792 setMappingState( NormalState );
00793 rawShow();
00794 }
00795 else
00796 {
00797 if( !isShade())
00798 setMappingState( IconicState );
00799 rawHide();
00800 }
00801 }
00802
00807 void Client::setMappingState(int s)
00808 {
00809 assert( client != None );
00810 if( mapping_state == s )
00811 return;
00812 bool was_unmanaged = ( mapping_state == WithdrawnState );
00813 mapping_state = s;
00814 if( mapping_state == WithdrawnState )
00815 {
00816 XDeleteProperty( qt_xdisplay(), window(), qt_wm_state );
00817 return;
00818 }
00819 assert( s == NormalState || s == IconicState );
00820
00821 unsigned long data[2];
00822 data[0] = (unsigned long) s;
00823 data[1] = (unsigned long) None;
00824 XChangeProperty(qt_xdisplay(), window(), qt_wm_state, qt_wm_state, 32,
00825 PropModeReplace, (unsigned char *)data, 2);
00826
00827 if( was_unmanaged )
00828 {
00829 assert( block_geometry == 1 );
00830 --block_geometry;
00831 setGeometry( frame_geometry, ForceGeometrySet );
00832 }
00833 }
00834
00839 void Client::rawShow()
00840 {
00841 if( decoration != NULL )
00842 decoration->widget()->show();
00843 XMapWindow( qt_xdisplay(), frame );
00844 if( !isShade())
00845 {
00846 XMapWindow( qt_xdisplay(), wrapper );
00847 XMapWindow( qt_xdisplay(), client );
00848 }
00849 }
00850
00856 void Client::rawHide()
00857 {
00858
00859
00860
00861
00862
00863
00864 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask );
00865 XUnmapWindow( qt_xdisplay(), frame );
00866 XUnmapWindow( qt_xdisplay(), wrapper );
00867 XUnmapWindow( qt_xdisplay(), client );
00868 XSelectInput( qt_xdisplay(), wrapper, ClientWinMask | SubstructureNotifyMask );
00869 if( decoration != NULL )
00870 decoration->widget()->hide();
00871 workspace()->clientHidden( this );
00872 }
00873
00874 static void sendClientMessage(Window w, Atom a, long x)
00875 {
00876 XEvent ev;
00877 long mask;
00878
00879 memset(&ev, 0, sizeof(ev));
00880 ev.xclient.type = ClientMessage;
00881 ev.xclient.window = w;
00882 ev.xclient.message_type = a;
00883 ev.xclient.format = 32;
00884 ev.xclient.data.l[0] = x;
00885 ev.xclient.data.l[1] = qt_x_time;
00886 mask = 0L;
00887 if (w == qt_xrootwin())
00888 mask = SubstructureRedirectMask;
00889 XSendEvent(qt_xdisplay(), w, False, mask, &ev);
00890 }
00891
00892
00893
00894
00895 bool Client::isCloseable() const
00896 {
00897 return motif_may_close && ( !isSpecialWindow() || isOverride());
00898 }
00899
00904 void Client::closeWindow()
00905 {
00906 if( !isCloseable())
00907 return;
00908
00909
00910
00911
00912 group()->updateUserTime();
00913 if ( Pdeletewindow )
00914 {
00915 Notify::raise( Notify::Close );
00916 sendClientMessage( window(), atoms->wm_protocols, atoms->wm_delete_window);
00917 pingWindow();
00918 }
00919 else
00920 {
00921
00922
00923 killWindow();
00924 }
00925 }
00926
00927
00931 void Client::killWindow()
00932 {
00933 kdDebug( 1212 ) << "Client::killWindow():" << caption() << endl;
00934
00935
00936 Notify::raise( Notify::Close );
00937
00938 if( isDialog())
00939 Notify::raise( Notify::TransDelete );
00940 if( isNormalWindow())
00941 Notify::raise( Notify::Delete );
00942 killProcess( false );
00943
00944 XKillClient(qt_xdisplay(), window() );
00945 destroyClient();
00946 }
00947
00948
00949
00950
00951 void Client::pingWindow()
00952 {
00953 if( !Pping )
00954 return;
00955 if( ping_timer != NULL )
00956 return;
00957 ping_timer = new QTimer( this );
00958 connect( ping_timer, SIGNAL( timeout()), SLOT( pingTimeout()));
00959 ping_timer->start( 5000, true );
00960 ping_timestamp = qt_x_time;
00961 workspace()->sendPingToWindow( window(), ping_timestamp );
00962 }
00963
00964 void Client::gotPing( Time timestamp )
00965 {
00966 if( timestamp != ping_timestamp )
00967 return;
00968 delete ping_timer;
00969 ping_timer = NULL;
00970 if( process_killer != NULL )
00971 {
00972 process_killer->kill();
00973 delete process_killer;
00974 process_killer = NULL;
00975 }
00976 }
00977
00978 void Client::pingTimeout()
00979 {
00980 kdDebug( 1212 ) << "Ping timeout:" << caption() << endl;
00981 delete ping_timer;
00982 ping_timer = NULL;
00983 killProcess( true, ping_timestamp );
00984 }
00985
00986 void Client::killProcess( bool ask, Time timestamp )
00987 {
00988 if( process_killer != NULL )
00989 return;
00990 Q_ASSERT( !ask || timestamp != CurrentTime );
00991 QCString machine = wmClientMachine();
00992 pid_t pid = info->pid();
00993 if( pid <= 0 || machine.isEmpty())
00994 return;
00995 kdDebug( 1212 ) << "Kill process:" << pid << "(" << machine << ")" << endl;
00996 if( !ask )
00997 {
00998 if( machine != "localhost" )
00999 {
01000 KProcess proc;
01001 proc << "xon" << machine << "kill" << pid;
01002 proc.start( KProcess::DontCare );
01003 }
01004 else
01005 ::kill( pid, SIGTERM );
01006 }
01007 else
01008 {
01009 process_killer = new KProcess( this );
01010 *process_killer << KStandardDirs::findExe( "kwin_killer_helper" )
01011 << "--pid" << QCString().setNum( pid ) << "--hostname" << machine
01012 << "--windowname" << caption().utf8()
01013 << "--applicationname" << resourceClass()
01014 << "--wid" << QCString().setNum( window())
01015 << "--timestamp" << QCString().setNum( timestamp );
01016 connect( process_killer, SIGNAL( processExited( KProcess* )),
01017 SLOT( processKillerExited()));
01018 if( !process_killer->start( KProcess::NotifyOnExit ))
01019 {
01020 delete process_killer;
01021 process_killer = NULL;
01022 return;
01023 }
01024 }
01025 }
01026
01027 void Client::processKillerExited()
01028 {
01029 kdDebug( 1212 ) << "Killer exited" << endl;
01030 delete process_killer;
01031 process_killer = NULL;
01032 }
01033
01034 void Client::setSkipTaskbar( bool b, bool from_outside )
01035 {
01036 if( from_outside )
01037 original_skip_taskbar = b;
01038 if ( b == skipTaskbar() )
01039 return;
01040 skip_taskbar = b;
01041 info->setState( b?NET::SkipTaskbar:0, NET::SkipTaskbar );
01042 }
01043
01044 void Client::setSkipPager( bool b )
01045 {
01046 if ( b == skipPager() )
01047 return;
01048 skip_pager = b;
01049 info->setState( b?NET::SkipPager:0, NET::SkipPager );
01050 }
01051
01052 void Client::setModal( bool m )
01053 {
01054 if( modal == m )
01055 return;
01056 modal = m;
01057 if( !modal )
01058 return;
01059
01060
01061 }
01062
01063 void Client::toggleOnAllDesktops()
01064 {
01065 setOnAllDesktops( !isOnAllDesktops());
01066 }
01067
01068 void Client::setDesktop( int desktop )
01069 {
01070 if( desktop != NET::OnAllDesktops )
01071 desktop = KMAX( 1, KMIN( workspace()->numberOfDesktops(), desktop ));
01072 if( desk == desktop )
01073 return;
01074 int was_desk = desk;
01075 desk = desktop;
01076 info->setDesktop( desktop );
01077 if(( was_desk == NET::OnAllDesktops ) != ( desktop == NET::OnAllDesktops ))
01078 {
01079 if ( isShown( true ))
01080 Notify::raise( isOnAllDesktops() ? Notify::OnAllDesktops : Notify::NotOnAllDesktops );
01081 workspace()->updateOnAllDesktopsOfTransients( this );
01082 }
01083 if( decoration != NULL )
01084 decoration->desktopChange();
01085 virtualDesktopChange();
01086 }
01087
01088 void Client::setOnAllDesktops( bool b )
01089 {
01090 if(( b && isOnAllDesktops())
01091 || ( !b && !isOnAllDesktops()))
01092 return;
01093 if( b )
01094 setDesktop( NET::OnAllDesktops );
01095 else
01096 setDesktop( workspace()->currentDesktop());
01097 }
01098
01099 bool Client::isOnCurrentDesktop() const
01100 {
01101 return isOnDesktop( workspace()->currentDesktop());
01102 }
01103
01108 void Client::takeFocus( bool force, allowed_t )
01109 {
01110 if ( !force && ( isTopMenu() || isDock() || isSplash()) )
01111 return;
01112
01113 #ifndef NDEBUG
01114 static Time previous_focus_timestamp;
01115 static Client* previous_client;
01116 if( previous_focus_timestamp == qt_x_time && previous_client != this )
01117 {
01118 kdWarning( 1212 ) << "Repeated use of the same X timestamp for focus" << endl;
01119 kdDebug( 1212 ) << kdBacktrace() << endl;
01120 }
01121 previous_focus_timestamp = qt_x_time;
01122 previous_client = this;
01123 #endif
01124 if ( input )
01125 {
01126 XSetInputFocus( qt_xdisplay(), window(), RevertToPointerRoot, qt_x_time );
01127 }
01128 if ( Ptakefocus )
01129 sendClientMessage(window(), atoms->wm_protocols, atoms->wm_take_focus);
01130 }
01131
01139 bool Client::providesContextHelp() const
01140 {
01141 return Pcontexthelp;
01142 }
01143
01144
01151 void Client::showContextHelp()
01152 {
01153 if ( Pcontexthelp )
01154 {
01155 sendClientMessage(window(), atoms->wm_protocols, atoms->net_wm_context_help);
01156 QWhatsThis::enterWhatsThisMode();
01157 }
01158 }
01159
01160
01165 KWIN_COMPARE_PREDICATE( FetchNameInternalPredicate, const Client*, (!cl->isSpecialWindow() || cl->isToolbar()) && cl != value && cl->caption() == value->caption());
01166
01167 void Client::fetchName()
01168 {
01169 QString s;
01170
01171 if ( info->name() && info->name()[ 0 ] != '\0' )
01172 s = QString::fromUtf8( info->name() );
01173 else
01174 s = KWin::readNameProperty( window(), XA_WM_NAME );
01175 if ( s != cap_normal )
01176 {
01177 bool reset_name = cap_normal.isEmpty();
01178 for( unsigned int i = 0;
01179 i < s.length();
01180 ++i )
01181 if( !s[ i ].isPrint())
01182 s[ i ] = ' ';
01183 cap_normal = s;
01184 bool was_suffix = ( !cap_suffix.isEmpty());
01185 cap_suffix = QString::null;
01186 if ( ( !isSpecialWindow() || isToolbar()) && workspace()->findClient( FetchNameInternalPredicate( this )))
01187 {
01188 int i = 2;
01189 do
01190 {
01191 cap_suffix = " <" + QString::number(i) + ">";
01192 i++;
01193 } while ( workspace()->findClient( FetchNameInternalPredicate( this )));
01194 info->setVisibleName( caption().utf8() );
01195 reset_name = false;
01196 }
01197 if(( was_suffix && cap_suffix.isEmpty()
01198 || reset_name ))
01199 {
01200 info->setVisibleName( "" );
01201 info->setVisibleIconName( "" );
01202 }
01203 else if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty())
01204 info->setVisibleIconName( ( cap_iconic + cap_suffix ).utf8() );
01205
01206 if( isManaged() && decoration != NULL )
01207 decoration->captionChange();
01208 }
01209 }
01210
01211 void Client::fetchIconicName()
01212 {
01213 QString s;
01214
01215 if ( info->iconName() && info->iconName()[ 0 ] != '\0' )
01216 s = QString::fromUtf8( info->iconName() );
01217 else
01218 s = KWin::readNameProperty( window(), XA_WM_ICON_NAME );
01219 if ( s != cap_iconic )
01220 {
01221 cap_iconic = s;
01222 if( !cap_suffix.isEmpty() && !cap_iconic.isEmpty())
01223 info->setVisibleIconName( ( s + cap_suffix ).utf8() );
01224 }
01225 }
01226
01229 QString Client::caption() const
01230 {
01231 return cap_normal + cap_suffix;
01232 }
01233
01234 void Client::getWMHints()
01235 {
01236 XWMHints *hints = XGetWMHints(qt_xdisplay(), window() );
01237 input = true;
01238 window_group = None;
01239 urgency = false;
01240 if ( hints )
01241 {
01242 if( hints->flags & InputHint )
01243 input = hints->input;
01244 if( hints->flags & WindowGroupHint )
01245 window_group = hints->window_group;
01246 urgency = ( hints->flags & UrgencyHint ) ? true : false;
01247 XFree( (char*)hints );
01248 }
01249 checkGroup();
01250 updateUrgency();
01251 updateAllowedActions();
01252 }
01253
01254 void Client::readIcons( Window win, QPixmap* icon, QPixmap* miniicon )
01255 {
01256
01257 if( icon != NULL )
01258 *icon = KWin::icon( win, 32, 32, TRUE, KWin::NETWM | KWin::WMHints );
01259 if( miniicon != NULL )
01260 if( icon == NULL || !icon->isNull())
01261 *miniicon = KWin::icon( win, 16, 16, TRUE, KWin::NETWM | KWin::WMHints );
01262 else
01263 *miniicon = QPixmap();
01264 }
01265
01266 void Client::getIcons()
01267 {
01268
01269 readIcons( window(), &icon_pix, &miniicon_pix );
01270 if( icon_pix.isNull())
01271 {
01272 icon_pix = group()->icon();
01273 miniicon_pix = group()->miniIcon();
01274 }
01275 if( icon_pix.isNull() && isTransient())
01276 {
01277 ClientList mainclients = mainClients();
01278 for( ClientList::ConstIterator it = mainclients.begin();
01279 it != mainclients.end() && icon_pix.isNull();
01280 ++it )
01281 {
01282 icon_pix = (*it)->icon();
01283 miniicon_pix = (*it)->miniIcon();
01284 }
01285 }
01286 if( icon_pix.isNull())
01287 {
01288 icon_pix = KWin::icon( window(), 32, 32, TRUE, KWin::ClassHint | KWin::XApp );
01289 miniicon_pix = KWin::icon( window(), 16, 16, TRUE, KWin::ClassHint | KWin::XApp );
01290 }
01291 if( isManaged() && decoration != NULL )
01292 decoration->iconChange();
01293 }
01294
01295 void Client::getWindowProtocols()
01296 {
01297 Atom *p;
01298 int i,n;
01299
01300 Pdeletewindow = 0;
01301 Ptakefocus = 0;
01302 Pcontexthelp = 0;
01303 Pping = 0;
01304
01305 if (XGetWMProtocols(qt_xdisplay(), window(), &p, &n))
01306 {
01307 for (i = 0; i < n; i++)
01308 if (p[i] == atoms->wm_delete_window)
01309 Pdeletewindow = 1;
01310 else if (p[i] == atoms->wm_take_focus)
01311 Ptakefocus = 1;
01312 else if (p[i] == atoms->net_wm_context_help)
01313 Pcontexthelp = 1;
01314 else if (p[i] == atoms->net_wm_ping)
01315 Pping = 1;
01316 if (n>0)
01317 XFree(p);
01318 }
01319 }
01320
01321 static int nullErrorHandler(Display *, XErrorEvent *)
01322 {
01323 return 0;
01324 }
01325
01329 QCString Client::staticWindowRole(WId w)
01330 {
01331 return getStringProperty(w, qt_window_role);
01332 }
01333
01337 QCString Client::staticSessionId(WId w)
01338 {
01339 return getStringProperty(w, qt_sm_client_id);
01340 }
01341
01345 QCString Client::staticWmCommand(WId w)
01346 {
01347 return getStringProperty(w, XA_WM_COMMAND, ' ');
01348 }
01349
01354 QCString Client::staticWmClientMachine(WId w)
01355 {
01356 QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE);
01357 if (result.isEmpty())
01358 {
01359 result = "localhost";
01360 }
01361 else
01362 {
01363
01364 char hostnamebuf[80];
01365 if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0)
01366 {
01367 hostnamebuf[sizeof(hostnamebuf)-1] = 0;
01368 if (result == hostnamebuf)
01369 result = "localhost";
01370 char *dot = strchr(hostnamebuf, '.');
01371 if (dot && !(*dot = 0) && result == hostnamebuf)
01372 result = "localhost";
01373 }
01374 }
01375 return result;
01376 }
01377
01381 Window Client::staticWmClientLeader(WId w)
01382 {
01383 Atom type;
01384 int format, status;
01385 unsigned long nitems = 0;
01386 unsigned long extra = 0;
01387 unsigned char *data = 0;
01388 Window result = w;
01389 XErrorHandler oldHandler = XSetErrorHandler(nullErrorHandler);
01390 status = XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
01391 FALSE, XA_WINDOW, &type, &format,
01392 &nitems, &extra, &data );
01393 XSetErrorHandler(oldHandler);
01394 if (status == Success )
01395 {
01396 if (data && nitems > 0)
01397 result = *((Window*) data);
01398 XFree(data);
01399 }
01400 return result;
01401 }
01402
01403
01404 void Client::getWmClientLeader()
01405 {
01406 wmClientLeaderWin = staticWmClientLeader(window());
01407 }
01408
01413 QCString Client::sessionId()
01414 {
01415 QCString result = staticSessionId(window());
01416 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01417 result = staticSessionId(wmClientLeaderWin);
01418 return result;
01419 }
01420
01425 QCString Client::wmCommand()
01426 {
01427 QCString result = staticWmCommand(window());
01428 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01429 result = staticWmCommand(wmClientLeaderWin);
01430 return result;
01431 }
01432
01437 QCString Client::wmClientMachine() const
01438 {
01439 QCString result = staticWmClientMachine(window());
01440 if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=window())
01441 result = staticWmClientMachine(wmClientLeaderWin);
01442 return result;
01443 }
01444
01449 Window Client::wmClientLeader() const
01450 {
01451 if (wmClientLeaderWin)
01452 return wmClientLeaderWin;
01453 return window();
01454 }
01455
01456 bool Client::wantsTabFocus() const
01457 {
01458 return ( isNormalWindow() || isDialog() || isOverride())
01459 && ( input || Ptakefocus ) && !skip_taskbar;
01460 }
01461
01462
01463 bool Client::wantsInput() const
01464 {
01465 return input;
01466 }
01467
01472 bool Client::isMovable() const
01473 {
01474 return motif_may_move && !isFullScreen() &&
01475 ( !isSpecialWindow() || isOverride() || isSplash() || isToolbar()) &&
01476 ( maximizeMode() != MaximizeFull || options->moveResizeMaximizedWindows() );
01477 }
01478
01479 bool Client::isDesktop() const
01480 {
01481 return windowType() == NET::Desktop;
01482 }
01483
01484 bool Client::isDock() const
01485 {
01486 return windowType() == NET::Dock;
01487 }
01488
01489 bool Client::isTopMenu() const
01490 {
01491 return windowType() == NET::TopMenu;
01492 }
01493
01494
01495 bool Client::isMenu() const
01496 {
01497 return windowType() == NET::Menu && !isTopMenu();
01498 }
01499
01500 bool Client::isToolbar() const
01501 {
01502 return windowType() == NET::Toolbar;
01503 }
01504
01505 bool Client::isOverride() const
01506 {
01507 return windowType() == NET::Override;
01508 }
01509
01510 bool Client::isSplash() const
01511 {
01512 return windowType() == NET::Splash;
01513 }
01514
01515 bool Client::isUtility() const
01516 {
01517 return windowType() == NET::Utility;
01518 }
01519
01520 bool Client::isDialog() const
01521 {
01522 return windowType() == NET::Dialog;
01523 }
01524
01525 bool Client::isNormalWindow() const
01526 {
01527 return windowType() == NET::Normal;
01528 }
01529
01530 bool Client::isSpecialWindow() const
01531 {
01532 return isDesktop() || isDock() || isSplash() || isTopMenu()
01533 || ( isOverride() && !isFullScreen())
01534 || isToolbar();
01535 }
01536
01537 NET::WindowType Client::windowType( bool strict, int supported_types ) const
01538 {
01539 NET::WindowType wt = info->windowType( supported_types );
01540
01541 if( !strict )
01542 {
01543 if( wt == NET::Menu )
01544 {
01545
01546
01547
01548 if( x() == 0 && y() < 0 && y() > -10 && height() < 100
01549 && abs( width() - workspace()->clientArea( FullArea, this ).width()) < 10 )
01550 wt = NET::TopMenu;
01551 }
01552 const char* const oo_prefix = "openoffice.org";
01553
01554 if( qstrncmp( resourceClass(), oo_prefix, strlen( oo_prefix )) == 0 && wt == NET::Dialog )
01555 wt = NET::Normal;
01556 if( wt == NET::Unknown )
01557 wt = isTransient() ? NET::Dialog : NET::Normal;
01558 }
01559 return wt;
01560 }
01561
01566 void Client::setCursor( Position m )
01567 {
01568 if ( !isResizable() || isShade() || noBorder())
01569 {
01570 setCursor( arrowCursor );
01571 return;
01572 }
01573 switch ( m )
01574 {
01575 case PositionTopLeft:
01576 case PositionBottomRight:
01577 setCursor( sizeFDiagCursor );
01578 break;
01579 case PositionBottomLeft:
01580 case PositionTopRight:
01581 setCursor( sizeBDiagCursor );
01582 break;
01583 case PositionTop:
01584 case PositionBottom:
01585 setCursor( sizeVerCursor );
01586 break;
01587 case PositionLeft:
01588 case PositionRight:
01589 setCursor( sizeHorCursor );
01590 break;
01591 default:
01592 if( buttonDown )
01593 setCursor( sizeAllCursor );
01594 else
01595 setCursor( arrowCursor );
01596 break;
01597 }
01598 }
01599
01600
01601 void Client::setCursor( const QCursor& c )
01602 {
01603 if( c.handle() == cursor.handle())
01604 return;
01605 cursor = c;
01606 if( decoration != NULL )
01607 decoration->widget()->setCursor( cursor );
01608 XDefineCursor( qt_xdisplay(), frameId(), cursor.handle());
01609 }
01610
01611 Client::Position Client::mousePosition( const QPoint& p ) const
01612 {
01613 if( decoration != NULL )
01614 return decoration->mousePosition( p );
01615 return PositionCenter;
01616 }
01617
01618 void Client::updateAllowedActions( bool force )
01619 {
01620 if( !isManaged() && !force )
01621 return;
01622 unsigned long old_allowed_actions = allowed_actions;
01623 allowed_actions = 0;
01624 if( isMovable())
01625 allowed_actions |= NET::ActionMove;
01626 if( isResizable())
01627 allowed_actions |= NET::ActionResize;
01628 if( isMinimizable())
01629 allowed_actions |= NET::ActionMinimize;
01630 if( isShadeable())
01631 allowed_actions |= NET::ActionShade;
01632
01633 if( isMaximizable())
01634 allowed_actions |= NET::ActionMax;
01635 if( userCanSetFullScreen())
01636 allowed_actions |= NET::ActionFullScreen;
01637 allowed_actions |= NET::ActionChangeDesktop;
01638 if( isCloseable())
01639 allowed_actions |= NET::ActionClose;
01640 if( old_allowed_actions == allowed_actions )
01641 return;
01642
01643 info->setAllowedActions( allowed_actions );
01644
01645 }
01646
01647 void Client::autoRaise()
01648 {
01649 workspace()->raiseClient( this );
01650 delete autoRaiseTimer;
01651 autoRaiseTimer = 0;
01652 }
01653
01654 void Client::cancelAutoRaise()
01655 {
01656 delete autoRaiseTimer;
01657 autoRaiseTimer = 0;
01658 }
01659
01660 #ifdef NDEBUG
01661 kndbgstream& operator<<( kndbgstream& stream, const Client* ) { return stream; }
01662 kndbgstream& operator<<( kndbgstream& stream, const ClientList& ) { return stream; }
01663 kndbgstream& operator<<( kndbgstream& stream, const ConstClientList& ) { return stream; }
01664 #else
01665 kdbgstream& operator<<( kdbgstream& stream, const Client* cl )
01666 {
01667 if( cl == NULL )
01668 return stream << "\'NULL_CLIENT\'";
01669 return stream << "\'ID:" << cl->window() << ";WMCLASS:" << cl->resourceClass() << ":" << cl->resourceName() << ";Caption:" << cl->caption() << "\'";
01670 }
01671 kdbgstream& operator<<( kdbgstream& stream, const ClientList& list )
01672 {
01673 stream << "LIST:(";
01674 bool first = true;
01675 for( ClientList::ConstIterator it = list.begin();
01676 it != list.end();
01677 ++it )
01678 {
01679 if( !first )
01680 stream << ":";
01681 first = false;
01682 stream << *it;
01683 }
01684 stream << ")";
01685 return stream;
01686 }
01687 kdbgstream& operator<<( kdbgstream& stream, const ConstClientList& list )
01688 {
01689 stream << "LIST:(";
01690 bool first = true;
01691 for( ConstClientList::ConstIterator it = list.begin();
01692 it != list.end();
01693 ++it )
01694 {
01695 if( !first )
01696 stream << ":";
01697 first = false;
01698 stream << *it;
01699 }
01700 stream << ")";
01701 return stream;
01702 }
01703 #endif
01704
01705 QPixmap * kwin_get_menu_pix_hack()
01706 {
01707 static QPixmap p;
01708 if ( p.isNull() )
01709 p = SmallIcon( "bx2" );
01710 return &p;
01711 }
01712
01713 }
01714
01715 #include "client.moc"