1
//////////////////////////////////////////////////////////////////////////////
5
// Copyright (c) 2009 Hugo Pereira Da Costa <hugo.pereira@free.fr>
6
// Copyright (c) 2006, 2007 Casper Boemann <cbr@boemann.dk>
7
// Copyright (c) 2006, 2007 Riccardo Iaconelli <riccardo@kde.org>
9
// Permission is hereby granted, free of charge, to any person obtaining a copy
10
// of this software and associated documentation files (the "Software"), to
11
// deal in the Software without restriction, including without limitation the
12
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13
// sell copies of the Software, and to permit persons to whom the Software is
14
// furnished to do so, subject to the following conditions:
16
// The above copyright notice and this permission notice shall be included in
17
// all copies or substantial portions of the Software.
19
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26
//////////////////////////////////////////////////////////////////////////////
28
#include "oxygenclient.h"
29
#include "oxygenclient.moc"
31
#include "oxygenbutton.h"
32
#include "oxygensizegrip.h"
38
#include <KColorUtils>
41
#include <QtGui/QApplication>
42
#include <QtGui/QLabel>
43
#include <QtGui/QPainter>
44
#include <QtGui/QBitmap>
45
#include <QtGui/QX11Info>
46
#include <QtCore/QObjectList>
49
#include <X11/Xatom.h>
54
//___________________________________________
55
Client::Client(KDecorationBridge *b, Factory *f):
56
KCommonDecorationUnstable(b, f),
59
_glowAnimation( new Animation( 200, this ) ),
60
_titleAnimationData( new TitleAnimationData( this ) ),
62
_initialized( false ),
63
_forceActive( false ),
64
_mouseButton( Qt::NoButton ),
70
//___________________________________________
74
// delete sizegrip if any
75
if( hasSizeGrip() ) deleteSizeGrip();
79
//___________________________________________
80
QString Client::visibleName() const
81
{ return i18n("Oxygen"); }
83
//___________________________________________
87
KCommonDecoration::init();
89
widget()->setAttribute(Qt::WA_NoSystemBackground );
90
widget()->setAutoFillBackground( false );
91
widget()->setAcceptDrops( true );
93
// setup glow animation
94
_glowAnimation->setStartValue( glowBias() );
95
_glowAnimation->setEndValue( 1.0 );
96
_glowAnimation->setTargetObject( this );
97
_glowAnimation->setPropertyName( "glowIntensity" );
98
_glowAnimation->setEasingCurve( QEasingCurve::InOutQuad );
99
connect( _glowAnimation, SIGNAL( finished( void ) ), this, SLOT( clearForceActive( void ) ) );
102
// title animation data
103
_titleAnimationData->initialize();
104
connect( _titleAnimationData, SIGNAL( pixmapsChanged() ), SLOT( updateTitleRect() ) );
107
connect( _itemData.animation().data(), SIGNAL( finished() ), this, SLOT( clearTargetItem() ) );
109
// in case of preview, one wants to make the label used
110
// for the central widget transparent. This allows one to have
111
// the correct background (with gradient) rendered
112
// Remark: this is minor (and safe) a hack.
113
// This should be moved upstream (into kwin/lib/kdecoration)
117
QList<QLabel*> children( widget()->findChildren<QLabel*>() );
118
foreach( QLabel* widget, children )
119
{ widget->setAutoFillBackground( false ); }
125
// first reset is needed to store Oxygen configuration
130
//___________________________________________
131
void Client::reset( unsigned long changed )
133
KCommonDecorationUnstable::reset( changed );
135
// update window mask when compositing is changed
136
if( !_initialized ) return;
137
if( changed & SettingCompositing )
143
_configuration = _factory->configuration( *this );
145
// animations duration
146
_glowAnimation->setDuration( configuration().animationsDuration() );
147
_titleAnimationData->setDuration( configuration().animationsDuration() );
148
_itemData.animation().data()->setDuration( configuration().animationsDuration() );
149
_itemData.setAnimationsEnabled( useAnimations() );
151
// reset title transitions
152
_titleAnimationData->reset();
154
// should also update animations for buttons
157
// also reset tab buttons
158
for( int index = 0; index < _itemData.count(); index++ )
160
ClientGroupItemData& item( _itemData[index] );
161
if( item._closeButton ) { item._closeButton.data()->reset(0); }
164
// reset tab geometry
165
_itemData.setDirty( true );
168
if( configuration().drawSizeGrip() )
171
if( !hasSizeGrip() ) createSizeGrip();
173
} else if( hasSizeGrip() ) deleteSizeGrip();
175
// needs to remove shadow property on window since shadows are handled by the decoration
180
//___________________________________________
181
bool Client::decorationBehaviour(DecorationBehaviour behaviour) const
193
return KCommonDecoration::decorationBehaviour(behaviour);
197
//_________________________________________________________
198
KCommonDecorationButton *Client::createButton(::ButtonType type)
204
return new Button(*this, i18n("Menu"), ButtonMenu);
207
return new Button(*this, i18n("Help"), ButtonHelp);
210
return new Button(*this, i18n("Minimize"), ButtonMin);
213
return new Button(*this, i18n("Maximize"), ButtonMax);
216
return new Button(*this, i18n("Close"), ButtonClose);
219
return new Button(*this, i18n("Keep Above Others"), ButtonAbove);
222
return new Button(*this, i18n("Keep Below Others"), ButtonBelow);
224
case OnAllDesktopsButton:
225
return new Button(*this, i18n("On All Desktops"), ButtonSticky);
228
return new Button(*this, i18n("Shade Button"), ButtonShade);
238
//_________________________________________________________
239
QRegion Client::calcMask( void ) const
242
if( isMaximized() ) { return widget()->rect(); }
243
const QRect frame( widget()->rect().adjusted(
244
layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop ),
245
-layoutMetric( LM_OuterPaddingRight ), -layoutMetric( LM_OuterPaddingBottom ) ) );
248
if( configuration().frameBorder() == Configuration::BorderNone && !isShade() )
251
if( hideTitleBar() ) mask = QRegion();
252
else if( compositingActive() ) mask = QRegion();
253
else mask = helper().roundedMask( frame, 1, 1, 1, 0 );
257
if( compositingActive() ) mask = QRegion();
258
else mask = helper().roundedMask( frame );
266
//___________________________________________
267
int Client::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const
270
const bool maximized( isMaximized() );
271
const bool narrowSpacing( configuration().useNarrowButtonSpacing() );
272
const int frameBorder( configuration().frameBorder() );
273
const int buttonSize( hideTitleBar() ? 0 : configuration().buttonSize() );
279
case LM_BorderBottom:
282
if( respectWindowState && maximized )
287
} else if( lm == LM_BorderBottom && frameBorder >= Configuration::BorderNoSide ) {
289
// for tiny border, the convention is to have a larger bottom area in order to
290
// make resizing easier
291
border = qMax(frameBorder, 4);
293
} else if( frameBorder < Configuration::BorderTiny ) {
297
} else if( !compositingActive() && frameBorder == Configuration::BorderTiny ) {
299
border = qMax( frameBorder, 3 );
303
border = frameBorder;
310
case LM_TitleEdgeTop:
313
if( frameBorder == Configuration::BorderNone && hideTitleBar() )
318
} else if( !( respectWindowState && maximized )) {
328
case LM_TitleEdgeBottom:
333
case LM_TitleEdgeLeft:
334
case LM_TitleEdgeRight:
337
if( !(respectWindowState && maximized) )
344
case LM_TitleBorderLeft:
345
case LM_TitleBorderRight:
349
// if title outline is to be drawn, one adds the space needed to
350
// separate title and tab border. namely the same value
351
if( configuration().drawTitleOutline() ) border += border;
357
case LM_ButtonHeight:
363
case LM_ButtonSpacing:
364
return narrowSpacing ? 1:3;
366
case LM_ButtonMarginTop:
369
// outer margin for shadow/glow
370
case LM_OuterPaddingLeft:
371
case LM_OuterPaddingRight:
372
case LM_OuterPaddingTop:
373
case LM_OuterPaddingBottom:
374
if( maximized ) return 0;
375
else return shadowCache().shadowSize();
378
return KCommonDecoration::layoutMetric(lm, respectWindowState, btn);
383
//_________________________________________________________
384
QRect Client::defaultTitleRect( bool active ) const
387
QRect titleRect( this->titleRect().adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 ) );
389
// when drawing title outline, shrink the rect so that it matches the actual caption size
390
if( active && configuration().drawTitleOutline() && isActive() )
394
if( configuration().centerTitleOnFullWidth() )
396
titleRect.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) );
397
titleRect.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) );
400
const QRect textRect( titleBoundingRect( options()->font( true, false), titleRect, caption() ) );
401
titleRect.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) );
402
titleRect.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) );
406
// buttons are properly accounted for in titleBoundingRect method
407
titleRect.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) );
408
titleRect.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) );
416
//_________________________________________________________
417
QRect Client::titleBoundingRect( const QFont& font, QRect rect, const QString& caption ) const
420
// get title bounding rect
421
QRect boundingRect( QFontMetrics( font ).boundingRect( rect, configuration().titleAlignment() | Qt::AlignVCenter, caption ) );
423
// adjust to make sure bounding rect
424
// 1/ has same vertical alignment as original titleRect
425
// 2/ does not exceeds available horizontal space
426
boundingRect.setTop( rect.top() );
427
boundingRect.setBottom( rect.bottom() );
429
// check bounding rect against input rect
430
boundRectTo( boundingRect, rect );
432
if( configuration().centerTitleOnFullWidth() )
436
check bounding rect against max available space, for buttons
437
this is not needed if centerTitleOnFullWidth flag is set to false,
438
because it was already done before calling titleBoundingRect
440
boundRectTo( boundingRect, titleRect() );
448
//_________________________________________________________
449
void Client::boundRectTo( QRect& rect, const QRect& bound ) const
452
if( bound.left() > rect.left() )
454
rect.moveLeft( bound.left() );
455
if( bound.right() < rect.right() )
456
{ rect.setRight( bound.right() ); }
458
} else if( bound.right() < rect.right() ) {
460
rect.moveRight( bound.right() );
461
if( bound.left() > rect.left() )
462
{ rect.setLeft( bound.left() ); }
469
//_________________________________________________________
470
void Client::clearTargetItem( void )
473
if( _itemData.animationType() == AnimationLeave )
474
{ _itemData.setDirty( true ); }
478
//_________________________________________________________
479
void Client::updateItemBoundingRects( bool alsoUpdate )
482
// make sure items are not animated
483
_itemData.animate( AnimationNone );
485
// maximum available space
486
const QRect titleRect( this->titleRect() );
489
const int items( clientGroupItems().count() );
491
// make sure item data have the correct number of items
492
while( _itemData.count() < items ) _itemData.push_back( ClientGroupItemData() );
493
while( _itemData.count() > items )
495
if( _itemData.back()._closeButton ) delete _itemData.back()._closeButton.data();
496
_itemData.pop_back();
499
assert( !_itemData.isEmpty() );
502
if( _itemData.count() == 1 )
506
if( _itemData.front()._closeButton )
507
{ delete _itemData.front()._closeButton.data(); }
510
_itemData.front()._activeRect = titleRect.adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
514
int left( titleRect.left() );
515
const int width( titleRect.width()/items );
516
for( int index = 0; index < _itemData.count(); index++ )
519
ClientGroupItemData& item(_itemData[index]);
521
// make sure button exists
522
if( !item._closeButton )
524
item._closeButton = ClientGroupItemData::ButtonPointer( new Button( *this, "Close this tab", ButtonItemClose ) );
525
item._closeButton.data()->show();
526
item._closeButton.data()->installEventFilter( this );
530
QRect local( QPoint( left, titleRect.top() ), QSize( width, titleRect.height() ) );
531
local.adjust( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
532
item._activeRect = local;
539
if( _itemData.count() == 1 )
542
_itemData.front().reset( defaultTitleRect() );
546
for( int index = 0; index < _itemData.count(); index++ )
547
{ _itemData[index].reset( _itemData[index]._activeRect ); }
552
_itemData.updateButtonActivity( visibleClientGroupItem() );
554
// reset buttons location
555
_itemData.updateButtons( alsoUpdate );
556
_itemData.setDirty( false );
562
//_________________________________________________________
563
QColor Client::titlebarTextColor(const QPalette &palette) const
565
if( glowIsAnimated() ) return KColorUtils::mix(
566
titlebarTextColor( palette, false ),
567
titlebarTextColor( palette, true ),
569
else return titlebarTextColor( palette, isActive() );
572
//_________________________________________________________
573
void Client::renderWindowBackground( QPainter* painter, const QRect& rect, const QWidget* widget, const QPalette& palette ) const
578
configuration().blendColor() == Configuration::NoBlending ||
579
( configuration().blendColor() == Configuration::BlendFromStyle &&
580
!helper().hasBackgroundGradient( windowId() )
584
painter->fillRect( rect, palette.color( QPalette::Window ) );
588
int offset = layoutMetric( LM_OuterPaddingTop );
590
// radial gradient positionning
591
int height = 64 - Configuration::ButtonDefault;
592
if( !hideTitleBar() ) height += configuration().buttonSize();
599
const QWidget* window( isPreview() ? this->widget() : widget->window() );
600
helper().renderWindowBackground(painter, rect, widget, window, palette, offset, height );
605
if( helper().hasBackgroundPixmap( windowId() ) )
607
int offset = layoutMetric( LM_OuterPaddingTop );
609
// radial gradient positionning
610
int height = 64 - Configuration::ButtonDefault;
611
if( !hideTitleBar() ) height += configuration().buttonSize();
612
if( isMaximized() ) offset -= 3;
615
QPoint backgroundPixmapOffset( layoutMetric( LM_OuterPaddingLeft ) + layoutMetric( LM_BorderLeft ), 0 );
616
helper().setBackgroundPixmapOffset( backgroundPixmapOffset );
618
const QWidget* window( isPreview() ? this->widget() : widget->window() );
619
helper().renderBackgroundPixmap(painter, rect, widget, window, offset, height );
625
//_________________________________________________________
626
void Client::renderWindowBorder( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QPalette& palette ) const
629
// get coordinates relative to the client area
630
// this is annoying. One could use mapTo if this was taking const QWidget* and not
631
// const QWidget* as argument.
632
const QWidget* window = (isPreview()) ? this->widget() : widget->window();
633
const QWidget* w = widget;
634
QPoint position( 0, 0 );
635
while ( w != window && !w->isWindow() && w != w->parentWidget() )
637
position += w->geometry().topLeft();
638
w = w->parentWidget();
642
if( clipRect.isValid() )
645
painter->setClipRegion(clipRect,Qt::IntersectClip);
648
QRect r = (isPreview()) ? this->widget()->rect():window->rect();
650
const qreal shadowSize( shadowCache().shadowSize() );
651
r.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize );
655
QColor color( palette.window().color() );
658
const int titleHeight( layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleEdgeBottom ) + layoutMetric( LM_TitleHeight ) );
660
// make titlebar background darker for tabbed, non-outline window
661
if( ( clientGroupItems().count() >= 2 || _itemData.isAnimated() ) && !(configuration().drawTitleOutline() && isActive() ) )
664
const QPoint topLeft( r.topLeft()-position );
665
const QRect rect( topLeft, QSize( r.width(), titleHeight ) );
667
QLinearGradient lg( rect.topLeft(), rect.bottomLeft() );
668
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) );
669
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) );
670
painter->setBrush( lg );
671
painter->setPen( Qt::NoPen );
672
painter->drawRect( rect );
678
const int shadowSize = 7;
679
const int height = shadowSize-3;
681
const QPoint topLeft( r.topLeft()+QPoint(0,titleHeight-height)-position );
682
QRect rect( topLeft, QSize( r.width(), height ) );
684
// adjustements to cope with shadow size and outline border.
685
rect.adjust( -shadowSize, 0, shadowSize-1, 0 );
686
if( configuration().frameBorder() > Configuration::BorderTiny && configuration().drawTitleOutline() && isActive() && !isMaximized() )
687
{ rect.adjust( HFRAMESIZE-1, 0, -HFRAMESIZE+1, 0 ); }
689
helper().slab( color, 0, shadowSize )->render( rect, painter, TileSet::Top );
693
if( configuration().drawTitleOutline() && isActive() )
696
// save mask and frame to where
697
// grey window background is to be rendered
702
const int leftOffset = qMin( layoutMetric( LM_BorderLeft ), int(HFRAMESIZE) );
703
const int rightOffset = qMin( layoutMetric( LM_BorderRight ), int(HFRAMESIZE) );
704
if( configuration().frameBorder() > Configuration::BorderNone )
707
const int height = qMax( 0, layoutMetric( LM_BorderBottom ) - HFRAMESIZE );
708
const int width = r.width() - leftOffset - rightOffset - 1;
710
const QRect rect( r.bottomLeft()-position + QPoint( leftOffset, -layoutMetric( LM_BorderBottom ) ), QSize( width, height ) );
711
if( height > 0 ) { mask += rect; frame |= rect; }
713
const QColor shadow( helper().calcDarkColor( color ) );
714
painter->setPen( shadow );
715
painter->drawLine( rect.bottomLeft()+QPoint(0,1), rect.bottomRight()+QPoint(0,1) );
720
const int topOffset = titleHeight;
721
const int bottomOffset = qMin( layoutMetric( LM_BorderBottom ), int(HFRAMESIZE) );
722
const int height = r.height() - topOffset - bottomOffset - 1;
724
if( configuration().frameBorder() >= Configuration::BorderTiny )
727
const QColor shadow( helper().calcLightColor( color ) );
728
painter->setPen( shadow );
731
int width = qMax( 0, layoutMetric( LM_BorderLeft ) - HFRAMESIZE );
732
QRect rect( r.topLeft()-position + QPoint( layoutMetric( LM_BorderLeft ) - width, topOffset ), QSize( width, height ) );
733
if( width > 0 ) { mask += rect; frame |= rect; }
735
painter->drawLine( rect.topLeft()-QPoint(1,0), rect.bottomLeft()-QPoint(1, 0) );
738
width = qMax( 0, layoutMetric( LM_BorderRight ) - HFRAMESIZE );
739
rect = QRect(r.topRight()-position + QPoint( -layoutMetric( LM_BorderRight ), topOffset ), QSize( width, height ));
740
if( width > 0 ) { mask += rect; frame |= rect; }
742
painter->drawLine( rect.topRight()+QPoint(1,0), rect.bottomRight()+QPoint(1, 0) );
745
// in preview mode also adds center square
748
const QRect rect( r.topLeft()-position + QPoint( layoutMetric( LM_BorderLeft ), topOffset ), QSize(r.width()-layoutMetric( LM_BorderLeft )-layoutMetric( LM_BorderRight ),height) );
749
mask += rect; frame |= rect;
753
if( !mask.isEmpty() )
755
painter->setClipRegion( mask, Qt::IntersectClip);
756
renderWindowBackground(painter, frame, widget, palette );
762
if( clipRect.isValid() )
763
{ painter->restore(); }
767
//_________________________________________________________
768
void Client::renderSeparator( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QColor& color ) const
771
const QWidget* window = (isPreview()) ? this->widget() : widget->window();
773
// get coordinates relative to the client area
774
// this is annoying. One could use mapTo if this was taking const QWidget* and not
775
// const QWidget* as argument.
776
QPoint position( 0, 0 );
778
const QWidget* w = widget;
779
while ( w != window && !w->isWindow() && w != w->parentWidget() ) {
780
position += w->geometry().topLeft();
781
w = w->parentWidget();
786
if (clipRect.isValid())
789
painter->setClipRegion(clipRect,Qt::IntersectClip);
792
QRect r = (isPreview()) ? this->widget()->rect():window->rect();
793
qreal shadowSize( shadowCache().shadowSize() );
794
r.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize );
797
const int titleHeight = layoutMetric(LM_TitleHeight);
798
const int titleTop = layoutMetric(LM_TitleEdgeTop) + r.top();
800
QColor local( color );
801
if( glowIsAnimated() ) local = helper().alphaColor( color, glowIntensity() );
802
helper().drawSeparator( painter, QRect(r.top(), titleTop+titleHeight-1.5, r.width(), 2).translated( -position ), local, Qt::Horizontal);
804
if (clipRect.isValid()) { painter->restore(); }
808
//_________________________________________________________
809
void Client::renderTitleOutline( QPainter* painter, const QRect& rect, const QPalette& palette ) const
812
// center (for active windows only)
815
QRect adjustedRect( rect.adjusted( 1, 1, -1, 1 ) );
817
// prepare painter mask
818
QRegion mask( adjustedRect.adjusted( 1, 0, -1, 0 ) );
819
mask += adjustedRect.adjusted( 0, 1, 0, 0 );
820
painter->setClipRegion( mask, Qt::IntersectClip );
822
// draw window background
823
renderWindowBackground(painter, adjustedRect, widget(), palette );
828
const int shadowSize( 7 );
829
const int offset( -3 );
830
const int voffset( 5-shadowSize );
831
const QRect adjustedRect( rect.adjusted(offset, voffset, -offset, shadowSize) );
832
helper().slab( palette.color( widget()->backgroundRole() ), 0, shadowSize )->render( adjustedRect, painter, TileSet::Tiles(TileSet::Top|TileSet::Left|TileSet::Right) );
836
//_________________________________________________________
837
void Client::renderTitleText( QPainter* painter, const QRect& rect, const QColor& color, const QColor& contrast ) const
840
if( !_titleAnimationData->isValid() )
843
_titleAnimationData->reset(
845
renderTitleText( rect, caption(), color ),
846
renderTitleText( rect, caption(), contrast ) );
849
if( _titleAnimationData->isDirty() )
853
_titleAnimationData->setDirty( false );
855
// finish current animation if running
856
if( _titleAnimationData->isAnimated() )
857
{ _titleAnimationData->finishAnimation(); }
859
if( !_titleAnimationData->isLocked() )
863
_titleAnimationData->setPixmaps(
865
renderTitleText( rect, caption(), color ),
866
renderTitleText( rect, caption(), contrast ) );
868
_titleAnimationData->startAnimation();
869
renderTitleText( painter, rect, color, contrast );
871
} else if( !caption().isEmpty() ) {
873
renderTitleText( painter, rect, caption(), color, contrast );
877
// lock animations (this must be done whether or not
878
// animation was actually started, in order to extend locking
879
// every time title get changed too rapidly
880
_titleAnimationData->lockAnimations();
882
} else if( _titleAnimationData->isAnimated() ) {
884
if( isMaximized() ) painter->translate( 0, 2 );
885
if( !_titleAnimationData->contrastPixmap().isNull() )
887
painter->translate( 0, 1 );
888
painter->drawPixmap( rect.topLeft(), _titleAnimationData->contrastPixmap() );
889
painter->translate( 0, -1 );
892
painter->drawPixmap( rect.topLeft(), _titleAnimationData->pixmap() );
894
if( isMaximized() ) painter->translate( 0, -2 );
896
} else if( !caption().isEmpty() ) {
898
renderTitleText( painter, rect, caption(), color, contrast );
904
//_______________________________________________________________________
905
void Client::renderTitleText( QPainter* painter, const QRect& rect, const QString& caption, const QColor& color, const QColor& contrast, bool elide ) const
908
const Qt::Alignment alignment( configuration().titleAlignment() | Qt::AlignVCenter );
909
const QString local( elide ? QFontMetrics( painter->font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption );
911
// translate title down in case of maximized window
912
if( isMaximized() ) painter->translate( 0, 2 );
914
if( contrast.isValid() )
916
painter->setPen( contrast );
917
painter->translate( 0, 1 );
918
painter->drawText( rect, alignment, local );
919
painter->translate( 0, -1 );
922
painter->setPen( color );
923
painter->drawText( rect, alignment, local );
926
if( isMaximized() ) painter->translate( 0, -2 );
930
//_______________________________________________________________________
931
QPixmap Client::renderTitleText( const QRect& rect, const QString& caption, const QColor& color, bool elide ) const
934
QPixmap out( rect.size() );
935
out.fill( Qt::transparent );
936
if( caption.isEmpty() || !color.isValid() ) return out;
938
QPainter painter( &out );
939
painter.setFont( options()->font(isActive(), false) );
940
const Qt::Alignment alignment( configuration().titleAlignment() | Qt::AlignVCenter );
941
const QString local( elide ? QFontMetrics( painter.font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption );
943
painter.setPen( color );
944
painter.drawText( out.rect(), alignment, local );
950
//_______________________________________________________________________
951
void Client::renderItem( QPainter* painter, int index, const QPalette& palette )
954
const ClientGroupItemData& item( _itemData[index] );
956
// see if tag is active
957
const int itemCount( _itemData.count() );
959
// check item bounding rect
960
if( !item._boundingRect.isValid() ) return;
962
// create rect in which text is to be drawn
963
QRect textRect( item._boundingRect.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
965
// add extra space needed for title outline
966
if( itemCount > 1 || _itemData.isAnimated() )
967
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
969
// add extra space for the button
970
if( itemCount > 1 && item._closeButton && item._closeButton.data()->isVisible() )
971
{ textRect.adjust( 0, 0, - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 ); }
973
// check if current item is active
974
const bool active( index == visibleClientGroupItem() );
976
// get current item caption and update text rect
977
const QList< ClientGroupItem >& items( clientGroupItems() );
978
const QString caption( itemCount == 1 ? this->caption() : items[index].title() );
980
if( !configuration().centerTitleOnFullWidth() )
981
{ boundRectTo( textRect, titleRect() ); }
984
textRect = titleBoundingRect( painter->font(), textRect, caption );
990
// no title outline if the window caption is empty
991
if( !caption.trimmed().isEmpty() )
993
if( _itemData.isAnimated() ) {
995
renderTitleOutline( painter, item._boundingRect, palette );
997
} else if( (isActive()||glowIsAnimated()) && configuration().drawTitleOutline() ) {
999
// adjusts boundingRect accordingly
1000
QRect boundingRect( item._boundingRect );
1001
boundingRect.setLeft( textRect.left() - layoutMetric( LM_TitleBorderLeft ) );
1002
boundingRect.setRight( textRect.right() + layoutMetric( LM_TitleBorderRight ) );
1004
// render bounding rect around it with extra margins
1005
renderTitleOutline( painter, boundingRect, palette );
1011
} else if( active ) {
1013
// in multiple tabs render title outline in all cases
1014
renderTitleOutline( painter, item._boundingRect, palette );
1019
if( active || itemCount == 1 )
1022
// for active tab, current caption is "merged" with old caption, if any
1025
titlebarTextColor( palette ),
1026
titlebarContrastColor( palette ) );
1031
QColor background( backgroundPalette( widget(), palette ).color( widget()->window()->backgroundRole() ) );
1033
// add extra shade (as used in renderWindowBorder
1034
if( !( isActive() && configuration().drawTitleOutline() ) )
1035
{ background = KColorUtils::mix( background, Qt::black, 0.10 ); }
1037
// otherwise current caption is rendered directly
1039
painter, textRect, caption,
1040
titlebarTextColor( backgroundPalette( widget(), palette ), false ),
1041
titlebarContrastColor( background ) );
1045
// render separators between inactive tabs
1046
if( !( active || itemCount == 1 ) && item._closeButton && item._closeButton.data()->isVisible() )
1050
// draw Left separator
1051
const QColor color( backgroundPalette( widget(), palette ).window().color() );
1052
const bool isFirstItem( index == 0 || (index == 1 && !_itemData[0]._boundingRect.isValid() ) );
1053
if( !active && ( ( isFirstItem && buttonsLeftWidth() > 0 ) || _itemData.isTarget( index ) ) )
1056
const QRect local( item._boundingRect.topLeft()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) );
1057
helper().drawSeparator( painter, local, color, Qt::Vertical);
1061
// draw right separator
1063
( index == itemCount-1 && buttonsRightWidth() > 0 ) ||
1064
( index+1 < itemCount && ( _itemData.isTarget( index+1 ) ||
1065
!( index+1 == visibleClientGroupItem() && _itemData[index+1]._boundingRect.isValid() ) ) ) )
1068
const QRect local( item._boundingRect.topRight()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) );
1069
helper().drawSeparator( painter, local, color, Qt::Vertical);
1077
//_______________________________________________________________________
1078
void Client::renderTargetRect( QPainter* p, const QPalette& palette )
1080
if( _itemData.targetRect().isNull() || _itemData.isAnimationRunning() ) return;
1082
const QColor color = palette.color(QPalette::Highlight);
1083
p->setPen(KColorUtils::mix(color, palette.color(QPalette::Active, QPalette::WindowText)));
1084
p->setBrush( helper().alphaColor( color, 0.5 ) );
1085
p->drawRect( QRectF(_itemData.targetRect()).adjusted( 4.5, 2.5, -4.5, -2.5 ) );
1089
//_______________________________________________________________________
1090
void Client::renderCorners( QPainter* painter, const QRect& frame, const QPalette& palette ) const
1093
const QColor color( backgroundColor( widget(), palette ) );
1095
QLinearGradient lg = QLinearGradient(0, -0.5, 0, qreal( frame.height() )+0.5);
1096
lg.setColorAt(0.0, helper().calcLightColor( helper().backgroundTopColor(color) ));
1097
lg.setColorAt(0.51, helper().backgroundBottomColor(color) );
1098
lg.setColorAt(1.0, helper().backgroundBottomColor(color) );
1100
painter->setPen( QPen( lg, 1 ) );
1101
painter->setBrush( Qt::NoBrush );
1102
painter->drawRoundedRect( QRectF( frame ).adjusted( 0.5, 0.5, -0.5, -0.5 ), 3.5, 3.5 );
1106
//_______________________________________________________________________
1107
void Client::renderFloatFrame( QPainter* painter, const QRect& frame, const QPalette& palette ) const
1110
// shadow and resize handles
1111
if( !isMaximized() )
1114
if( configuration().frameBorder() >= Configuration::BorderTiny )
1117
helper().drawFloatFrame(
1118
painter, frame, backgroundColor( widget(), palette ),
1119
!compositingActive(), isActive() && configuration().useOxygenShadows(),
1120
KDecoration::options()->color(ColorTitleBar)
1125
// for small borders, use a frame that matches the titlebar only
1126
const QRect local( frame.topLeft(), QSize( frame.width(), layoutMetric(LM_TitleHeight) + layoutMetric(LM_TitleEdgeTop) ) );
1127
helper().drawFloatFrame(
1128
painter, local, backgroundColor( widget(), palette ),
1129
false, isActive() && configuration().useOxygenShadows(),
1130
KDecoration::options()->color(ColorTitleBar)
1134
} else if( isShade() ) {
1136
// for shaded maximized windows adjust frame and draw the bottom part of it
1137
helper().drawFloatFrame(
1138
painter, frame, backgroundColor( widget(), palette ),
1139
!( compositingActive() || configuration().frameBorder() == Configuration::BorderNone ), isActive(),
1140
KDecoration::options()->color(ColorTitleBar),
1148
//____________________________________________________________________________
1149
void Client::renderDots( QPainter* painter, const QRect& frame, const QColor& color ) const
1152
if( configuration().frameBorder() >= Configuration::BorderTiny )
1157
frame.getRect(&x, &y, &w, &h);
1159
if( isResizable() && !isShade() && !isMaximized() )
1162
// Draw right side 3-dots resize handles
1163
const int cenY = (h / 2 + y) ;
1164
const int posX = (w + x - 3);
1166
helper().renderDot( painter, QPoint(posX, cenY - 3), color);
1167
helper().renderDot( painter, QPoint(posX, cenY), color);
1168
helper().renderDot( painter, QPoint(posX, cenY + 3), color);
1172
// Draw bottom-right cornet 3-dots resize handles
1173
if( isResizable() && !isShade() && !configuration().drawSizeGrip() )
1177
painter->translate(x + w-9, y + h-9);
1178
helper().renderDot( painter, QPoint(2, 6), color);
1179
helper().renderDot( painter, QPoint(5, 5), color);
1180
helper().renderDot( painter, QPoint(6, 2), color);
1188
//_________________________________________________________
1189
void Client::activeChange( void )
1192
KCommonDecorationUnstable::activeChange();
1193
_itemData.setDirty( true );
1196
if( animateActiveChange() )
1198
_glowAnimation->setDirection( isActive() ? Animation::Forward : Animation::Backward );
1199
if(!glowIsAnimated()) { _glowAnimation->start(); }
1202
// update size grip so that it gets the right color
1203
// also make sure it is remaped to from z stack,
1205
if( hasSizeGrip() && !(isShade() || isMaximized() ))
1207
sizeGrip().activeChange();
1208
sizeGrip().update();
1213
//_________________________________________________________
1214
void Client::maximizeChange( void )
1216
if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) );
1217
KCommonDecorationUnstable::maximizeChange();
1220
//_________________________________________________________
1221
void Client::shadeChange( void )
1223
if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) );
1224
KCommonDecorationUnstable::shadeChange();
1227
//_________________________________________________________
1228
void Client::captionChange( void )
1231
KCommonDecorationUnstable::captionChange();
1232
_itemData.setDirty( true );
1233
if( animateTitleChange() )
1234
{ _titleAnimationData->setDirty( true ); }
1238
//_________________________________________________________
1239
QPalette Client::backgroundPalette( const QWidget* widget, QPalette palette ) const
1242
if( configuration().drawTitleOutline() )
1244
if( glowIsAnimated() && !isForcedActive() )
1247
const QColor inactiveColor( backgroundColor( widget, palette, false ) );
1248
const QColor activeColor( backgroundColor( widget, palette, true ) );
1249
const QColor mixed( KColorUtils::mix( inactiveColor, activeColor, glowIntensity() ) );
1250
palette.setColor( QPalette::Window, mixed );
1251
palette.setColor( QPalette::Button, mixed );
1253
} else if( isActive() || isForcedActive() ) {
1255
const QColor color = options()->color( KDecorationDefines::ColorTitleBar, true );
1256
palette.setColor( QPalette::Window, color );
1257
palette.setColor( QPalette::Button, color );
1267
//_________________________________________________________
1268
QColor Client::backgroundColor( const QWidget*, QPalette palette, bool active ) const
1271
return ( configuration().drawTitleOutline() && active ) ?
1272
options()->color( KDecorationDefines::ColorTitleBar, true ):
1273
palette.color( QPalette::Window );
1277
//_________________________________________________________
1278
QString Client::defaultButtonsLeft() const
1279
{ return KCommonDecoration::defaultButtonsLeft(); }
1281
//_________________________________________________________
1282
QString Client::defaultButtonsRight() const
1285
//________________________________________________________________
1286
void Client::updateWindowShape()
1289
if(isMaximized()) clearMask();
1290
else setMask( calcMask() );
1294
//______________________________________________________________________________
1295
bool Client::eventFilter( QObject* object, QEvent* event )
1298
// all dedicated event filtering is here to handle multiple tabs.
1299
// if tabs are disabled, do nothing
1300
if( !configuration().tabsEnabled() )
1301
{ return KCommonDecorationUnstable::eventFilter( object, event ); }
1304
switch( event->type() )
1308
if( widget() == object )
1309
{ _itemData.setDirty( true ); }
1312
case QEvent::MouseButtonPress:
1313
if( widget() == object )
1314
{ state = mousePressEvent( static_cast< QMouseEvent* >( event ) ); }
1317
case QEvent::MouseButtonRelease:
1318
if( widget() == object ) state = mouseReleaseEvent( static_cast< QMouseEvent* >( event ) );
1319
else if( Button *btn = qobject_cast< Button* >( object ) )
1321
QMouseEvent* mouseEvent( static_cast< QMouseEvent* >( event ) );
1322
if( mouseEvent->button() == Qt::LeftButton && btn->rect().contains( mouseEvent->pos() ) )
1323
{ state = closeItem( btn ); }
1328
case QEvent::MouseMove:
1329
state = mouseMoveEvent( static_cast< QMouseEvent* >( event ) );
1332
case QEvent::DragEnter:
1333
if( widget() == object )
1334
{ state = dragEnterEvent( static_cast< QDragEnterEvent* >( event ) ); }
1337
case QEvent::DragMove:
1338
if( widget() == object )
1339
{ state = dragMoveEvent( static_cast< QDragMoveEvent* >( event ) ); }
1342
case QEvent::DragLeave:
1343
if( widget() == object )
1344
{ state = dragLeaveEvent( static_cast< QDragLeaveEvent* >( event ) ); }
1348
if( widget() == object )
1349
{ state = dropEvent( static_cast< QDropEvent* >( event ) ); }
1355
return state || KCommonDecorationUnstable::eventFilter( object, event );
1359
//_________________________________________________________
1360
void Client::resizeEvent( QResizeEvent* event )
1362
_itemData.setDirty( true );
1363
KCommonDecorationUnstable::resizeEvent( event );
1366
//_________________________________________________________
1367
void Client::paintEvent( QPaintEvent* event )
1371
if(!( _initialized && _factory->initialized() ) ) return;
1374
QPalette palette = widget()->palette();
1375
palette.setCurrentColorGroup( (isActive() ) ? QPalette::Active : QPalette::Inactive );
1378
QPainter painter(widget());
1379
painter.setRenderHint(QPainter::Antialiasing);
1380
painter.setClipRegion( event->region() );
1383
QRect frame = widget()->rect();
1386
QColor color = palette.window().color();
1389
if( compositingActive() && shadowCache().shadowSize() > 0 && !isMaximized() )
1392
TileSet *tileSet( 0 );
1393
const ShadowCache::Key key( this->key() );
1394
if( configuration().useOxygenShadows() && glowIsAnimated() && !isForcedActive() )
1397
tileSet = shadowCache().tileSet( key, glowIntensity() );
1401
tileSet = shadowCache().tileSet( key );
1405
tileSet->render( frame, &painter, TileSet::Ring);
1411
layoutMetric(LM_OuterPaddingLeft),
1412
layoutMetric(LM_OuterPaddingTop),
1413
-layoutMetric(LM_OuterPaddingRight),
1414
-layoutMetric(LM_OuterPaddingBottom) );
1417
if( compositingActive() || isPreview() )
1420
if( isMaximized() ) {
1422
painter.setClipRect( frame, Qt::IntersectClip );
1428
const int right = 1;
1432
// disable bottom corners when border frame is too small and window is not shaded
1433
if( configuration().frameBorder() == Configuration::BorderNone && !isShade() ) bottom = 0;
1434
QRegion mask( helper().roundedMask( frame, left, right, top, bottom ) );
1436
renderCorners( &painter, frame, palette );
1437
painter.setClipRegion( mask, Qt::IntersectClip );
1443
// make sure ItemData and clientGroupItems are synchronized
1445
this needs to be done before calling RenderWindowBorder
1446
since some painting in there depend on the clientGroups state
1448
if( _itemData.isDirty() || _itemData.count() != clientGroupItems().count() )
1449
{ updateItemBoundingRects( false ); }
1451
// window background
1452
renderWindowBackground( &painter, frame, widget(), backgroundPalette( widget(), palette ) );
1454
// window border (for title outline)
1455
if( hasTitleOutline() ) renderWindowBorder( &painter, frame, widget(), palette );
1458
if( compositingActive() )
1460
painter.setClipping(false);
1461
frame.adjust(-1,-1, 1, 1);
1465
renderFloatFrame( &painter, frame, palette );
1468
renderDots( &painter, frame, backgroundColor( widget(), palette ) );
1470
if( !hideTitleBar() )
1473
// title bounding rect
1474
painter.setFont( options()->font(isActive(), false) );
1476
// draw ClientGroupItems
1477
const int itemCount( _itemData.count() );
1478
for( int i = 0; i < itemCount; i++ ) renderItem( &painter, i, palette );
1481
renderTargetRect( &painter, widget()->palette() );
1484
if( itemCount == 1 && !_itemData.isAnimated() && drawSeparator() )
1485
{ renderSeparator(&painter, frame, widget(), color ); }
1491
//_____________________________________________________________
1492
bool Client::mousePressEvent( QMouseEvent* event )
1495
const QPoint point = event->pos();
1496
if( itemClicked( point ) < 0 ) return false;
1499
_mouseButton = event->button();
1500
bool accepted( false );
1501
if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
1506
} else if( buttonToWindowOperation( _mouseButton ) == OperationsOp ) {
1508
QPoint point = event->pos();
1509
int itemClicked( this->itemClicked( point ) );
1510
displayClientMenu( itemClicked, widget()->mapToGlobal( event->pos() ) );
1511
_mouseButton = Qt::NoButton;
1512
accepted = true; // displayClientMenu can possibly destroy the deco...
1518
//_____________________________________________________________
1519
bool Client::mouseReleaseEvent( QMouseEvent* event )
1522
bool accepted( false );
1523
if( _mouseButton == event->button() &&
1524
buttonToWindowOperation( _mouseButton ) != OperationsOp )
1527
const QPoint point = event->pos();
1529
const int visibleItem = visibleClientGroupItem();
1530
const int itemClicked( this->itemClicked( point ) );
1531
if( itemClicked >= 0 && visibleItem != itemClicked )
1533
setVisibleClientGroupItem( itemClicked );
1534
setForceActive( true );
1540
_mouseButton = Qt::NoButton;
1545
//_____________________________________________________________
1546
bool Client::mouseMoveEvent( QMouseEvent* event )
1549
// check button and distance to drag point
1550
if( hideTitleBar() || _mouseButton == Qt::NoButton || ( event->pos() - _dragPoint ).manhattanLength() <= QApplication::startDragDistance() )
1553
bool accepted( false );
1554
if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
1557
const QPoint point = event->pos();
1558
const int itemClicked( this->itemClicked( point ) );
1559
if( itemClicked < 0 ) return false;
1561
_titleAnimationData->reset();
1563
QDrag *drag = new QDrag( widget() );
1564
QMimeData *groupData = new QMimeData();
1565
groupData->setData( clientGroupItemDragMimeType(), QString().setNum( itemId( itemClicked )).toAscii() );
1566
drag->setMimeData( groupData );
1567
_sourceItem = this->itemClicked( _dragPoint );
1570
QRect geometry( _itemData[itemClicked]._boundingRect );
1572
// remove space used for buttons
1573
if( _itemData.count() > 1 )
1576
geometry.adjust( 0, 0, - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 );
1578
} else if( !( isActive() && configuration().drawTitleOutline() ) ) {
1582
buttonsLeftWidth() + layoutMetric( LM_TitleEdgeLeft ) , 0,
1583
-( buttonsRightWidth() + layoutMetric( LM_TitleEdgeRight )), 0 );
1587
drag->setPixmap( itemDragPixmap( itemClicked, geometry ) );
1589
// note: the pixmap is moved just above the pointer on purpose
1590
// because overlapping pixmap and pointer slows down the pixmap a lot.
1591
QPoint hotSpot( QPoint( event->pos().x() - geometry.left(), -1 ) );
1593
// make sure the horizontal hotspot position is not too far away (more than 1px)
1595
if( hotSpot.x() < -1 ) hotSpot.setX(-1);
1596
if( hotSpot.x() > geometry.width() ) hotSpot.setX( geometry.width() );
1598
drag->setHotSpot( hotSpot );
1600
_dragStartTimer.start( 50, this );
1601
drag->exec( Qt::MoveAction );
1603
// detach tab from window
1604
if( drag->target() == 0 && _itemData.count() > 1 )
1606
_itemData.setDirty( true );
1607
removeFromClientGroup( _sourceItem,
1608
widget()->frameGeometry().adjusted(
1609
layoutMetric( LM_OuterPaddingLeft ),
1610
layoutMetric( LM_OuterPaddingTop ),
1611
-layoutMetric( LM_OuterPaddingRight ),
1612
-layoutMetric( LM_OuterPaddingBottom )
1613
).translated( QCursor::pos() - event->pos() +
1614
QPoint( layoutMetric( LM_OuterPaddingLeft ), layoutMetric( LM_OuterPaddingTop )))
1619
_mouseButton = Qt::NoButton;
1628
//_____________________________________________________________
1629
bool Client::dragEnterEvent( QDragEnterEvent* event )
1632
// check if drag enter is allowed
1633
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) || hideTitleBar() ) return false;
1636
event->acceptProposedAction();
1637
if( event->source() != widget() )
1640
const QPoint position( event->pos() );
1641
_itemData.animate( AnimationEnter, itemClicked( position, true ) );
1643
} else if( _itemData.count() > 1 ) {
1645
const QPoint position( event->pos() );
1646
const int itemClicked( this->itemClicked( position, false ) );
1647
_itemData.animate( AnimationEnter|AnimationSameTarget, itemClicked );
1655
//_____________________________________________________________
1656
bool Client::dragLeaveEvent( QDragLeaveEvent* )
1659
if( _itemData.animationType() & AnimationSameTarget )
1662
if( _dragStartTimer.isActive() ) _dragStartTimer.stop();
1663
_itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem );
1665
} else if( _itemData.isAnimated() ) {
1667
_itemData.animate( AnimationLeave );
1676
//_____________________________________________________________
1677
bool Client::dragMoveEvent( QDragMoveEvent* event )
1681
if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) ) return false;
1682
if( event->source() != widget() )
1685
const QPoint position( event->pos() );
1686
_itemData.animate( AnimationMove, itemClicked( position, true ) );
1688
} else if( _itemData.count() > 1 ) {
1690
if( _dragStartTimer.isActive() ) _dragStartTimer.stop();
1692
const QPoint position( event->pos() );
1693
const int itemClicked( this->itemClicked( position, false ) );
1694
_itemData.animate( AnimationMove|AnimationSameTarget, itemClicked );
1702
//_____________________________________________________________
1703
bool Client::dropEvent( QDropEvent* event )
1706
const QPoint point = event->pos();
1707
_itemData.animate( AnimationNone );
1709
const QMimeData *groupData = event->mimeData();
1710
if( !groupData->hasFormat( clientGroupItemDragMimeType() ) ) return false;
1712
if( widget() == event->source() )
1715
const int from = this->itemClicked( _dragPoint );
1716
int itemClicked( this->itemClicked( point, false ) );
1718
if( itemClicked > from )
1721
if( itemClicked >= clientGroupItems().count() )
1722
{ itemClicked = -1; }
1725
_itemData.setDirty( true );
1726
moveItemInClientGroup( from, itemClicked );
1731
setForceActive( true );
1732
const int itemClicked( this->itemClicked( point, true ) );
1733
const long source = QString( groupData->data( clientGroupItemDragMimeType() ) ).toLong();
1734
_itemData.setDirty( true );
1735
moveItemToClientGroup( source, itemClicked );
1739
_titleAnimationData->reset();
1744
//_____________________________________________________________
1745
void Client::timerEvent( QTimerEvent* event )
1748
if( event->timerId() != _dragStartTimer.timerId() )
1749
{ return KCommonDecorationUnstable::timerEvent( event ); }
1751
_dragStartTimer.stop();
1753
// do nothing if there is only one tab
1754
if( _itemData.count() > 1 )
1756
_itemData.animate( AnimationMove|AnimationSameTarget, _sourceItem );
1757
_itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem );
1762
//_____________________________________________________________
1763
bool Client::closeItem( const Button* button )
1766
for( int i=0; i < _itemData.count(); i++ )
1768
if( button == _itemData[i]._closeButton.data() )
1770
_itemData.setDirty( true );
1771
closeClientGroupItem( i );
1779
//________________________________________________________________
1780
QPixmap Client::itemDragPixmap( int index, const QRect& geometry )
1782
const bool itemValid( index >= 0 && index < clientGroupItems().count() );
1784
QPixmap pixmap( geometry.size() );
1785
QPainter painter( &pixmap );
1786
painter.setRenderHints(QPainter::SmoothPixmapTransform|QPainter::Antialiasing);
1788
painter.translate( -geometry.topLeft() );
1790
// render window background
1791
renderWindowBackground( &painter, geometry, widget(), widget()->palette() );
1793
// darken background if item is inactive
1794
const bool itemActive = !( itemValid && index != visibleClientGroupItem() );
1798
QLinearGradient lg( geometry.topLeft(), geometry.bottomLeft() );
1799
lg.setColorAt( 0, helper().alphaColor( Qt::black, 0.05 ) );
1800
lg.setColorAt( 1, helper().alphaColor( Qt::black, 0.10 ) );
1801
painter.setBrush( lg );
1802
painter.setPen( Qt::NoPen );
1803
painter.drawRect( geometry );
1807
// render title text
1808
painter.setFont( options()->font(isActive(), false) );
1809
QRect textRect( geometry.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
1812
{ textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
1814
const QString caption( itemValid ? clientGroupItems()[index].title() : this->caption() );
1816
&painter, textRect, caption,
1817
titlebarTextColor( widget()->palette(), isActive() && itemActive ),
1818
titlebarContrastColor( widget()->palette() ) );
1821
helper().drawFloatFrame(
1822
&painter, geometry, widget()->palette().window().color(),
1824
KDecoration::options()->color(ColorTitleBar)
1829
// create pixmap mask
1830
QBitmap bitmap( geometry.size() );
1833
QPainter painter( &bitmap );
1835
path.addRegion( helper().roundedMask( geometry.translated( -geometry.topLeft() ) ) );
1836
painter.fillPath( path, Qt::color1 );
1839
pixmap.setMask( bitmap );
1844
//_________________________________________________________________
1845
void Client::createSizeGrip( void )
1848
assert( !hasSizeGrip() );
1849
if( ( isResizable() && windowId() != 0 ) || isPreview() )
1851
_sizeGrip = new SizeGrip( this );
1852
sizeGrip().setVisible( !( isMaximized() || isShade() ) );
1857
//_________________________________________________________________
1858
void Client::deleteSizeGrip( void )
1860
assert( hasSizeGrip() );
1861
_sizeGrip->deleteLater();
1865
//_________________________________________________________________
1866
void Client::removeShadowHint( void )
1869
// do nothing if no window id
1870
if( !windowId() ) return;
1874
{ _shadowAtom = XInternAtom( QX11Info::display(), "_KDE_NET_WM_SHADOW", False); }
1876
XDeleteProperty(QX11Info::display(), windowId(), _shadowAtom);