~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to kwin/clients/oxygen/oxygenclient.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//////////////////////////////////////////////////////////////////////////////
 
2
// oxygenclient.cpp
 
3
// -------------------
 
4
//
 
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>
 
8
//
 
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:
 
15
//
 
16
// The above copyright notice and this permission notice shall be included in
 
17
// all copies or substantial portions of the Software.
 
18
//
 
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
 
25
// IN THE SOFTWARE.
 
26
//////////////////////////////////////////////////////////////////////////////
 
27
 
 
28
#include "oxygenclient.h"
 
29
#include "oxygenclient.moc"
 
30
 
 
31
#include "oxygenbutton.h"
 
32
#include "oxygensizegrip.h"
 
33
 
 
34
#include <cassert>
 
35
#include <cmath>
 
36
 
 
37
#include <KLocale>
 
38
#include <KColorUtils>
 
39
#include <KDebug>
 
40
 
 
41
#include <QtGui/QApplication>
 
42
#include <QtGui/QLabel>
 
43
#include <QtGui/QPainter>
 
44
#include <QtGui/QBitmap>
 
45
#include <QtGui/QX11Info>
 
46
#include <QtCore/QObjectList>
 
47
 
 
48
#include <X11/Xlib.h>
 
49
#include <X11/Xatom.h>
 
50
 
 
51
namespace Oxygen
 
52
{
 
53
 
 
54
    //___________________________________________
 
55
    Client::Client(KDecorationBridge *b, Factory *f):
 
56
        KCommonDecorationUnstable(b, f),
 
57
        _factory( f ),
 
58
        _sizeGrip( 0 ),
 
59
        _glowAnimation( new Animation( 200, this ) ),
 
60
        _titleAnimationData( new TitleAnimationData( this ) ),
 
61
        _glowIntensity(0),
 
62
        _initialized( false ),
 
63
        _forceActive( false ),
 
64
        _mouseButton( Qt::NoButton ),
 
65
        _itemData( this ),
 
66
        _sourceItem( -1 ),
 
67
        _shadowAtom( 0 )
 
68
    {}
 
69
 
 
70
    //___________________________________________
 
71
    Client::~Client()
 
72
    {
 
73
 
 
74
        // delete sizegrip if any
 
75
        if( hasSizeGrip() ) deleteSizeGrip();
 
76
 
 
77
    }
 
78
 
 
79
    //___________________________________________
 
80
    QString Client::visibleName() const
 
81
    { return i18n("Oxygen"); }
 
82
 
 
83
    //___________________________________________
 
84
    void Client::init()
 
85
    {
 
86
 
 
87
        KCommonDecoration::init();
 
88
 
 
89
        widget()->setAttribute(Qt::WA_NoSystemBackground );
 
90
        widget()->setAutoFillBackground( false );
 
91
        widget()->setAcceptDrops( true );
 
92
 
 
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 ) ) );
 
100
 
 
101
 
 
102
        // title animation data
 
103
        _titleAnimationData->initialize();
 
104
        connect( _titleAnimationData, SIGNAL( pixmapsChanged() ), SLOT( updateTitleRect() ) );
 
105
 
 
106
        // lists
 
107
        connect( _itemData.animation().data(), SIGNAL( finished() ), this, SLOT( clearTargetItem() ) );
 
108
 
 
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)
 
114
        if( isPreview() )
 
115
        {
 
116
 
 
117
            QList<QLabel*> children( widget()->findChildren<QLabel*>() );
 
118
            foreach( QLabel* widget, children )
 
119
            { widget->setAutoFillBackground( false ); }
 
120
 
 
121
        }
 
122
 
 
123
        _initialized = true;
 
124
 
 
125
        // first reset is needed to store Oxygen configuration
 
126
        reset(0);
 
127
 
 
128
    }
 
129
 
 
130
    //___________________________________________
 
131
    void Client::reset( unsigned long changed )
 
132
    {
 
133
        KCommonDecorationUnstable::reset( changed );
 
134
 
 
135
        // update window mask when compositing is changed
 
136
        if( !_initialized ) return;
 
137
        if( changed & SettingCompositing )
 
138
        {
 
139
            updateWindowShape();
 
140
            widget()->update();
 
141
        }
 
142
 
 
143
        _configuration = _factory->configuration( *this );
 
144
 
 
145
        // animations duration
 
146
        _glowAnimation->setDuration( configuration().animationsDuration() );
 
147
        _titleAnimationData->setDuration( configuration().animationsDuration() );
 
148
        _itemData.animation().data()->setDuration( configuration().animationsDuration() );
 
149
        _itemData.setAnimationsEnabled( useAnimations() );
 
150
 
 
151
        // reset title transitions
 
152
        _titleAnimationData->reset();
 
153
 
 
154
        // should also update animations for buttons
 
155
        resetButtons();
 
156
 
 
157
        // also reset tab buttons
 
158
        for( int index = 0; index < _itemData.count(); index++ )
 
159
        {
 
160
            ClientGroupItemData& item( _itemData[index] );
 
161
            if( item._closeButton ) { item._closeButton.data()->reset(0); }
 
162
        }
 
163
 
 
164
        // reset tab geometry
 
165
        _itemData.setDirty( true );
 
166
 
 
167
        // handle size grip
 
168
        if( configuration().drawSizeGrip() )
 
169
        {
 
170
 
 
171
            if( !hasSizeGrip() ) createSizeGrip();
 
172
 
 
173
        } else if( hasSizeGrip() ) deleteSizeGrip();
 
174
 
 
175
        // needs to remove shadow property on window since shadows are handled by the decoration
 
176
        removeShadowHint();
 
177
 
 
178
    }
 
179
 
 
180
    //___________________________________________
 
181
    bool Client::decorationBehaviour(DecorationBehaviour behaviour) const
 
182
    {
 
183
        switch (behaviour)
 
184
        {
 
185
 
 
186
            case DB_MenuClose:
 
187
            return true;
 
188
 
 
189
            case DB_WindowMask:
 
190
            return false;
 
191
 
 
192
            default:
 
193
            return KCommonDecoration::decorationBehaviour(behaviour);
 
194
        }
 
195
    }
 
196
 
 
197
    //_________________________________________________________
 
198
    KCommonDecorationButton *Client::createButton(::ButtonType type)
 
199
    {
 
200
 
 
201
        switch (type) {
 
202
 
 
203
            case MenuButton:
 
204
            return new Button(*this, i18n("Menu"), ButtonMenu);
 
205
 
 
206
            case HelpButton:
 
207
            return new Button(*this, i18n("Help"), ButtonHelp);
 
208
 
 
209
            case MinButton:
 
210
            return new Button(*this, i18n("Minimize"), ButtonMin);
 
211
 
 
212
            case MaxButton:
 
213
            return new Button(*this, i18n("Maximize"), ButtonMax);
 
214
 
 
215
            case CloseButton:
 
216
            return new Button(*this, i18n("Close"), ButtonClose);
 
217
 
 
218
            case AboveButton:
 
219
            return new Button(*this, i18n("Keep Above Others"), ButtonAbove);
 
220
 
 
221
            case BelowButton:
 
222
            return new Button(*this, i18n("Keep Below Others"), ButtonBelow);
 
223
 
 
224
            case OnAllDesktopsButton:
 
225
            return new Button(*this, i18n("On All Desktops"), ButtonSticky);
 
226
 
 
227
            case ShadeButton:
 
228
            return new Button(*this, i18n("Shade Button"), ButtonShade);
 
229
 
 
230
            default: break;
 
231
 
 
232
        }
 
233
 
 
234
        return NULL;
 
235
 
 
236
    }
 
237
 
 
238
    //_________________________________________________________
 
239
    QRegion Client::calcMask( void ) const
 
240
    {
 
241
 
 
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 ) ) );
 
246
 
 
247
        QRegion mask;
 
248
        if( configuration().frameBorder() == Configuration::BorderNone && !isShade() )
 
249
        {
 
250
 
 
251
            if( hideTitleBar() ) mask = QRegion();
 
252
            else if( compositingActive() ) mask = QRegion();
 
253
            else mask = helper().roundedMask( frame, 1, 1, 1, 0 );
 
254
 
 
255
        } else {
 
256
 
 
257
            if( compositingActive() ) mask = QRegion();
 
258
            else mask = helper().roundedMask( frame );
 
259
 
 
260
        }
 
261
 
 
262
        return mask;
 
263
 
 
264
    }
 
265
 
 
266
    //___________________________________________
 
267
    int Client::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const
 
268
    {
 
269
 
 
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() );
 
274
 
 
275
        switch (lm)
 
276
        {
 
277
            case LM_BorderLeft:
 
278
            case LM_BorderRight:
 
279
            case LM_BorderBottom:
 
280
            {
 
281
                int border( 0 );
 
282
                if( respectWindowState && maximized )
 
283
                {
 
284
 
 
285
                    border = 0;
 
286
 
 
287
                } else if( lm == LM_BorderBottom && frameBorder >= Configuration::BorderNoSide ) {
 
288
 
 
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);
 
292
 
 
293
                } else if( frameBorder < Configuration::BorderTiny ) {
 
294
 
 
295
                    border = 0;
 
296
 
 
297
                } else if( !compositingActive() && frameBorder == Configuration::BorderTiny ) {
 
298
 
 
299
                    border = qMax( frameBorder, 3 );
 
300
 
 
301
                } else {
 
302
 
 
303
                    border = frameBorder;
 
304
 
 
305
                }
 
306
 
 
307
                return border;
 
308
            }
 
309
 
 
310
            case LM_TitleEdgeTop:
 
311
            {
 
312
                int border = 0;
 
313
                if( frameBorder == Configuration::BorderNone && hideTitleBar() )
 
314
                {
 
315
 
 
316
                    border = 0;
 
317
 
 
318
                } else if( !( respectWindowState && maximized )) {
 
319
 
 
320
                    border = TFRAMESIZE;
 
321
 
 
322
                }
 
323
 
 
324
                return border;
 
325
 
 
326
            }
 
327
 
 
328
            case LM_TitleEdgeBottom:
 
329
            {
 
330
                return 0;
 
331
            }
 
332
 
 
333
            case LM_TitleEdgeLeft:
 
334
            case LM_TitleEdgeRight:
 
335
            {
 
336
                int border = 0;
 
337
                if( !(respectWindowState && maximized) )
 
338
                { border = 4; }
 
339
 
 
340
                return border;
 
341
 
 
342
            }
 
343
 
 
344
            case LM_TitleBorderLeft:
 
345
            case LM_TitleBorderRight:
 
346
            {
 
347
                int border = 5;
 
348
 
 
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;
 
352
 
 
353
                return border;
 
354
            }
 
355
 
 
356
            case LM_ButtonWidth:
 
357
            case LM_ButtonHeight:
 
358
            case LM_TitleHeight:
 
359
            {
 
360
                return buttonSize;
 
361
            }
 
362
 
 
363
            case LM_ButtonSpacing:
 
364
            return narrowSpacing ? 1:3;
 
365
 
 
366
            case LM_ButtonMarginTop:
 
367
            return 0;
 
368
 
 
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();
 
376
 
 
377
            default:
 
378
            return KCommonDecoration::layoutMetric(lm, respectWindowState, btn);
 
379
        }
 
380
 
 
381
    }
 
382
 
 
383
    //_________________________________________________________
 
384
    QRect Client::defaultTitleRect( bool active ) const
 
385
    {
 
386
 
 
387
        QRect titleRect( this->titleRect().adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 ) );
 
388
 
 
389
        // when drawing title outline, shrink the rect so that it matches the actual caption size
 
390
        if( active && configuration().drawTitleOutline() && isActive() )
 
391
        {
 
392
 
 
393
 
 
394
            if( configuration().centerTitleOnFullWidth() )
 
395
            {
 
396
                titleRect.setLeft( widget()->rect().left() + layoutMetric( LM_OuterPaddingLeft ) );
 
397
                titleRect.setRight( widget()->rect().right() - layoutMetric( LM_OuterPaddingRight ) );
 
398
            }
 
399
 
 
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 ) );
 
403
 
 
404
        } else {
 
405
 
 
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 ) );
 
409
 
 
410
        }
 
411
 
 
412
        return titleRect;
 
413
 
 
414
    }
 
415
 
 
416
    //_________________________________________________________
 
417
    QRect Client::titleBoundingRect( const QFont& font, QRect rect, const QString& caption ) const
 
418
    {
 
419
 
 
420
        // get title bounding rect
 
421
        QRect boundingRect( QFontMetrics( font ).boundingRect( rect, configuration().titleAlignment() | Qt::AlignVCenter, caption ) );
 
422
 
 
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() );
 
428
 
 
429
        // check bounding rect against input rect
 
430
        boundRectTo( boundingRect, rect );
 
431
 
 
432
        if( configuration().centerTitleOnFullWidth() )
 
433
        {
 
434
 
 
435
            /*
 
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
 
439
            */
 
440
            boundRectTo( boundingRect, titleRect() );
 
441
 
 
442
        }
 
443
 
 
444
        return boundingRect;
 
445
 
 
446
    }
 
447
 
 
448
    //_________________________________________________________
 
449
    void Client::boundRectTo( QRect& rect, const QRect& bound ) const
 
450
    {
 
451
 
 
452
        if( bound.left() > rect.left() )
 
453
        {
 
454
            rect.moveLeft( bound.left() );
 
455
            if( bound.right() < rect.right() )
 
456
            { rect.setRight( bound.right() ); }
 
457
 
 
458
        } else if( bound.right() < rect.right() ) {
 
459
 
 
460
            rect.moveRight( bound.right() );
 
461
            if( bound.left() > rect.left() )
 
462
            { rect.setLeft( bound.left() ); }
 
463
 
 
464
        }
 
465
 
 
466
        return;
 
467
    }
 
468
 
 
469
    //_________________________________________________________
 
470
    void Client::clearTargetItem( void )
 
471
    {
 
472
 
 
473
        if( _itemData.animationType() == AnimationLeave )
 
474
        { _itemData.setDirty( true ); }
 
475
 
 
476
    }
 
477
 
 
478
    //_________________________________________________________
 
479
    void Client::updateItemBoundingRects( bool alsoUpdate )
 
480
    {
 
481
 
 
482
        // make sure items are not animated
 
483
        _itemData.animate( AnimationNone );
 
484
 
 
485
        // maximum available space
 
486
        const QRect titleRect( this->titleRect() );
 
487
 
 
488
        // get tabs
 
489
        const int items( clientGroupItems().count() );
 
490
 
 
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 )
 
494
        {
 
495
            if( _itemData.back()._closeButton ) delete _itemData.back()._closeButton.data();
 
496
            _itemData.pop_back();
 
497
        }
 
498
 
 
499
        assert( !_itemData.isEmpty() );
 
500
 
 
501
        // create buttons
 
502
        if( _itemData.count() == 1 )
 
503
        {
 
504
 
 
505
            // remove button
 
506
            if( _itemData.front()._closeButton )
 
507
            { delete _itemData.front()._closeButton.data(); }
 
508
 
 
509
            // set active rect
 
510
            _itemData.front()._activeRect = titleRect.adjusted( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
 
511
 
 
512
        } else {
 
513
 
 
514
            int left( titleRect.left() );
 
515
            const int width( titleRect.width()/items );
 
516
            for( int index = 0; index < _itemData.count(); index++ )
 
517
            {
 
518
 
 
519
                ClientGroupItemData& item(_itemData[index]);
 
520
 
 
521
                // make sure button exists
 
522
                if( !item._closeButton )
 
523
                {
 
524
                    item._closeButton = ClientGroupItemData::ButtonPointer( new Button( *this, "Close this tab", ButtonItemClose ) );
 
525
                    item._closeButton.data()->show();
 
526
                    item._closeButton.data()->installEventFilter( this );
 
527
                }
 
528
 
 
529
                // set active rect
 
530
                QRect local(  QPoint( left, titleRect.top() ), QSize( width, titleRect.height() ) );
 
531
                local.adjust( 0, -layoutMetric( LM_TitleEdgeTop ), 0, 0 );
 
532
                item._activeRect = local;
 
533
                left += width;
 
534
 
 
535
            }
 
536
 
 
537
        }
 
538
 
 
539
        if( _itemData.count() == 1 )
 
540
        {
 
541
 
 
542
            _itemData.front().reset( defaultTitleRect() );
 
543
 
 
544
        } else {
 
545
 
 
546
            for( int index = 0; index < _itemData.count(); index++ )
 
547
            { _itemData[index].reset( _itemData[index]._activeRect ); }
 
548
 
 
549
        }
 
550
 
 
551
        // button activity
 
552
        _itemData.updateButtonActivity( visibleClientGroupItem() );
 
553
 
 
554
        // reset buttons location
 
555
        _itemData.updateButtons( alsoUpdate );
 
556
        _itemData.setDirty( false );
 
557
 
 
558
        return;
 
559
 
 
560
    }
 
561
 
 
562
    //_________________________________________________________
 
563
    QColor Client::titlebarTextColor(const QPalette &palette) const
 
564
    {
 
565
        if( glowIsAnimated() ) return KColorUtils::mix(
 
566
            titlebarTextColor( palette, false ),
 
567
            titlebarTextColor( palette, true ),
 
568
            glowIntensity() );
 
569
        else return titlebarTextColor( palette, isActive() );
 
570
    }
 
571
 
 
572
    //_________________________________________________________
 
573
    void Client::renderWindowBackground( QPainter* painter, const QRect& rect, const QWidget* widget, const QPalette& palette ) const
 
574
    {
 
575
 
 
576
        // window background
 
577
        if(
 
578
            configuration().blendColor() == Configuration::NoBlending ||
 
579
            ( configuration().blendColor() == Configuration::BlendFromStyle &&
 
580
            !helper().hasBackgroundGradient( windowId() )
 
581
            ) )
 
582
        {
 
583
 
 
584
            painter->fillRect( rect, palette.color( QPalette::Window ) );
 
585
 
 
586
        } else {
 
587
 
 
588
            int offset = layoutMetric( LM_OuterPaddingTop );
 
589
 
 
590
            // radial gradient positionning
 
591
            int height = 64 - Configuration::ButtonDefault;
 
592
            if( !hideTitleBar() ) height += configuration().buttonSize();
 
593
            if( isMaximized() )
 
594
            {
 
595
                offset -= 3;
 
596
                height -= 3;
 
597
            }
 
598
 
 
599
            const QWidget* window( isPreview() ? this->widget() : widget->window() );
 
600
            helper().renderWindowBackground(painter, rect, widget, window, palette, offset, height );
 
601
 
 
602
        }
 
603
 
 
604
        // background pixmap
 
605
        if( helper().hasBackgroundPixmap( windowId() ) )
 
606
        {
 
607
            int offset = layoutMetric( LM_OuterPaddingTop );
 
608
 
 
609
            // radial gradient positionning
 
610
            int height = 64 - Configuration::ButtonDefault;
 
611
            if( !hideTitleBar() ) height += configuration().buttonSize();
 
612
            if( isMaximized() ) offset -= 3;
 
613
 
 
614
            // background pixmap
 
615
            QPoint backgroundPixmapOffset( layoutMetric( LM_OuterPaddingLeft ) + layoutMetric( LM_BorderLeft ), 0 );
 
616
            helper().setBackgroundPixmapOffset( backgroundPixmapOffset );
 
617
 
 
618
            const QWidget* window( isPreview() ? this->widget() : widget->window() );
 
619
            helper().renderBackgroundPixmap(painter, rect, widget, window, offset, height );
 
620
 
 
621
        }
 
622
 
 
623
    }
 
624
 
 
625
    //_________________________________________________________
 
626
    void Client::renderWindowBorder( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QPalette& palette ) const
 
627
    {
 
628
 
 
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() )
 
636
        {
 
637
            position += w->geometry().topLeft();
 
638
            w = w->parentWidget();
 
639
        }
 
640
 
 
641
        // save painter
 
642
        if( clipRect.isValid() )
 
643
        {
 
644
            painter->save();
 
645
            painter->setClipRegion(clipRect,Qt::IntersectClip);
 
646
        }
 
647
 
 
648
        QRect r = (isPreview()) ? this->widget()->rect():window->rect();
 
649
 
 
650
        const qreal shadowSize( shadowCache().shadowSize() );
 
651
        r.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize );
 
652
        r.adjust(0,0, 1, 1);
 
653
 
 
654
        // base color
 
655
        QColor color( palette.window().color() );
 
656
 
 
657
        // title height
 
658
        const int titleHeight( layoutMetric( LM_TitleEdgeTop ) + layoutMetric( LM_TitleEdgeBottom ) + layoutMetric( LM_TitleHeight ) );
 
659
 
 
660
        // make titlebar background darker for tabbed, non-outline window
 
661
        if( ( clientGroupItems().count() >= 2 || _itemData.isAnimated() ) && !(configuration().drawTitleOutline() && isActive() ) )
 
662
        {
 
663
 
 
664
            const QPoint topLeft( r.topLeft()-position );
 
665
            const QRect rect( topLeft, QSize( r.width(), titleHeight ) );
 
666
 
 
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 );
 
673
 
 
674
        }
 
675
 
 
676
        // horizontal line
 
677
        {
 
678
            const int shadowSize = 7;
 
679
            const int height = shadowSize-3;
 
680
 
 
681
            const QPoint topLeft( r.topLeft()+QPoint(0,titleHeight-height)-position );
 
682
            QRect rect( topLeft, QSize( r.width(), height ) );
 
683
 
 
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 ); }
 
688
 
 
689
            helper().slab( color, 0, shadowSize )->render( rect, painter, TileSet::Top );
 
690
 
 
691
        }
 
692
 
 
693
        if( configuration().drawTitleOutline() && isActive() )
 
694
        {
 
695
 
 
696
            // save mask and frame to where
 
697
            // grey window background is to be rendered
 
698
            QRegion mask;
 
699
            QRect frame;
 
700
 
 
701
            // bottom line
 
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 )
 
705
            {
 
706
 
 
707
                const int height = qMax( 0, layoutMetric( LM_BorderBottom ) - HFRAMESIZE );
 
708
                const int width = r.width() - leftOffset - rightOffset - 1;
 
709
 
 
710
                const QRect rect( r.bottomLeft()-position + QPoint( leftOffset, -layoutMetric( LM_BorderBottom ) ), QSize( width, height ) );
 
711
                if( height > 0 ) { mask += rect; frame |= rect; }
 
712
 
 
713
                const QColor shadow( helper().calcDarkColor( color ) );
 
714
                painter->setPen( shadow );
 
715
                painter->drawLine( rect.bottomLeft()+QPoint(0,1), rect.bottomRight()+QPoint(0,1) );
 
716
 
 
717
            }
 
718
 
 
719
            // left and right
 
720
            const int topOffset = titleHeight;
 
721
            const int bottomOffset = qMin( layoutMetric( LM_BorderBottom ), int(HFRAMESIZE) );
 
722
            const int height = r.height() - topOffset - bottomOffset - 1;
 
723
 
 
724
            if( configuration().frameBorder() >= Configuration::BorderTiny )
 
725
            {
 
726
 
 
727
                const QColor shadow( helper().calcLightColor( color ) );
 
728
                painter->setPen( shadow );
 
729
 
 
730
                // left
 
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; }
 
734
 
 
735
                painter->drawLine( rect.topLeft()-QPoint(1,0), rect.bottomLeft()-QPoint(1, 0) );
 
736
 
 
737
                // right
 
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; }
 
741
 
 
742
                painter->drawLine( rect.topRight()+QPoint(1,0), rect.bottomRight()+QPoint(1, 0) );
 
743
            }
 
744
 
 
745
            // in preview mode also adds center square
 
746
            if( isPreview() )
 
747
            {
 
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;
 
750
            }
 
751
 
 
752
            // paint
 
753
            if( !mask.isEmpty() )
 
754
            {
 
755
                painter->setClipRegion( mask, Qt::IntersectClip);
 
756
                renderWindowBackground(painter, frame, widget, palette );
 
757
            }
 
758
 
 
759
        }
 
760
 
 
761
        // restore painter
 
762
        if( clipRect.isValid() )
 
763
        { painter->restore(); }
 
764
 
 
765
    }
 
766
 
 
767
    //_________________________________________________________
 
768
    void Client::renderSeparator( QPainter* painter, const QRect& clipRect, const QWidget* widget, const QColor& color ) const
 
769
    {
 
770
 
 
771
        const QWidget* window = (isPreview()) ? this->widget() : widget->window();
 
772
 
 
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 );
 
777
        {
 
778
            const QWidget* w = widget;
 
779
            while (  w != window && !w->isWindow() && w != w->parentWidget() ) {
 
780
                position += w->geometry().topLeft();
 
781
                w = w->parentWidget();
 
782
            }
 
783
        }
 
784
 
 
785
        // setup painter
 
786
        if (clipRect.isValid())
 
787
        {
 
788
            painter->save();
 
789
            painter->setClipRegion(clipRect,Qt::IntersectClip);
 
790
        }
 
791
 
 
792
        QRect r = (isPreview()) ? this->widget()->rect():window->rect();
 
793
        qreal shadowSize( shadowCache().shadowSize() );
 
794
        r.adjust( shadowSize, shadowSize, -shadowSize, -shadowSize );
 
795
 
 
796
        // dimensions
 
797
        const int titleHeight = layoutMetric(LM_TitleHeight);
 
798
        const int titleTop = layoutMetric(LM_TitleEdgeTop) + r.top();
 
799
 
 
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);
 
803
 
 
804
        if (clipRect.isValid()) { painter->restore(); }
 
805
 
 
806
    }
 
807
 
 
808
    //_________________________________________________________
 
809
    void Client::renderTitleOutline(  QPainter* painter, const QRect& rect, const QPalette& palette ) const
 
810
    {
 
811
 
 
812
        // center (for active windows only)
 
813
        {
 
814
            painter->save();
 
815
            QRect adjustedRect( rect.adjusted( 1, 1, -1, 1 ) );
 
816
 
 
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 );
 
821
 
 
822
            // draw window background
 
823
            renderWindowBackground(painter, adjustedRect, widget(), palette );
 
824
            painter->restore();
 
825
        }
 
826
 
 
827
        // shadow
 
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) );
 
833
 
 
834
    }
 
835
 
 
836
    //_________________________________________________________
 
837
    void Client::renderTitleText( QPainter* painter, const QRect& rect, const QColor& color, const QColor& contrast ) const
 
838
    {
 
839
 
 
840
        if( !_titleAnimationData->isValid() )
 
841
        {
 
842
            // contrast pixmap
 
843
            _titleAnimationData->reset(
 
844
                rect,
 
845
                renderTitleText( rect, caption(), color ),
 
846
                renderTitleText( rect, caption(), contrast ) );
 
847
        }
 
848
 
 
849
        if( _titleAnimationData->isDirty() )
 
850
        {
 
851
 
 
852
            // clear dirty flags
 
853
            _titleAnimationData->setDirty( false );
 
854
 
 
855
            // finish current animation if running
 
856
            if( _titleAnimationData->isAnimated() )
 
857
            { _titleAnimationData->finishAnimation(); }
 
858
 
 
859
            if( !_titleAnimationData->isLocked() )
 
860
            {
 
861
 
 
862
                // set pixmaps
 
863
                _titleAnimationData->setPixmaps(
 
864
                    rect,
 
865
                    renderTitleText( rect, caption(), color ),
 
866
                    renderTitleText( rect, caption(), contrast ) );
 
867
 
 
868
                _titleAnimationData->startAnimation();
 
869
                renderTitleText( painter, rect, color, contrast );
 
870
 
 
871
            } else if( !caption().isEmpty() ) {
 
872
 
 
873
                renderTitleText( painter, rect, caption(), color, contrast );
 
874
 
 
875
            }
 
876
 
 
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();
 
881
 
 
882
        } else if( _titleAnimationData->isAnimated() ) {
 
883
 
 
884
            if( isMaximized() ) painter->translate( 0, 2 );
 
885
            if( !_titleAnimationData->contrastPixmap().isNull() )
 
886
            {
 
887
                painter->translate( 0, 1 );
 
888
                painter->drawPixmap( rect.topLeft(), _titleAnimationData->contrastPixmap() );
 
889
                painter->translate( 0, -1 );
 
890
            }
 
891
 
 
892
            painter->drawPixmap( rect.topLeft(), _titleAnimationData->pixmap() );
 
893
 
 
894
            if( isMaximized() ) painter->translate( 0, -2 );
 
895
 
 
896
        } else if( !caption().isEmpty() ) {
 
897
 
 
898
            renderTitleText( painter, rect, caption(), color, contrast );
 
899
 
 
900
        }
 
901
 
 
902
    }
 
903
 
 
904
    //_______________________________________________________________________
 
905
    void Client::renderTitleText( QPainter* painter, const QRect& rect, const QString& caption, const QColor& color, const QColor& contrast, bool elide ) const
 
906
    {
 
907
 
 
908
        const Qt::Alignment alignment( configuration().titleAlignment() | Qt::AlignVCenter );
 
909
        const QString local( elide ? QFontMetrics( painter->font() ).elidedText( caption, Qt::ElideRight, rect.width() ):caption );
 
910
 
 
911
        // translate title down in case of maximized window
 
912
        if( isMaximized() ) painter->translate( 0, 2 );
 
913
 
 
914
        if( contrast.isValid() )
 
915
        {
 
916
            painter->setPen( contrast );
 
917
            painter->translate( 0, 1 );
 
918
            painter->drawText( rect, alignment, local );
 
919
            painter->translate( 0, -1 );
 
920
        }
 
921
 
 
922
        painter->setPen( color );
 
923
        painter->drawText( rect, alignment, local );
 
924
 
 
925
        // translate back
 
926
        if( isMaximized() ) painter->translate( 0, -2 );
 
927
 
 
928
    }
 
929
 
 
930
    //_______________________________________________________________________
 
931
    QPixmap Client::renderTitleText( const QRect& rect, const QString& caption, const QColor& color, bool elide ) const
 
932
    {
 
933
 
 
934
        QPixmap out( rect.size() );
 
935
        out.fill( Qt::transparent );
 
936
        if( caption.isEmpty() || !color.isValid() ) return out;
 
937
 
 
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 );
 
942
 
 
943
        painter.setPen( color );
 
944
        painter.drawText( out.rect(), alignment, local );
 
945
        painter.end();
 
946
        return out;
 
947
 
 
948
    }
 
949
 
 
950
    //_______________________________________________________________________
 
951
    void Client::renderItem( QPainter* painter, int index, const QPalette& palette )
 
952
    {
 
953
 
 
954
        const ClientGroupItemData& item( _itemData[index] );
 
955
 
 
956
        // see if tag is active
 
957
        const int itemCount( _itemData.count() );
 
958
 
 
959
        // check item bounding rect
 
960
        if( !item._boundingRect.isValid() ) return;
 
961
 
 
962
        // create rect in which text is to be drawn
 
963
        QRect textRect( item._boundingRect.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
 
964
 
 
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 ); }
 
968
 
 
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 ); }
 
972
 
 
973
        // check if current item is active
 
974
        const bool active( index == visibleClientGroupItem() );
 
975
 
 
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() );
 
979
 
 
980
        if( !configuration().centerTitleOnFullWidth() )
 
981
        { boundRectTo( textRect, titleRect() ); }
 
982
 
 
983
        // adjust textRect
 
984
        textRect = titleBoundingRect( painter->font(), textRect, caption );
 
985
 
 
986
        // title outline
 
987
        if( itemCount == 1 )
 
988
        {
 
989
 
 
990
            // no title outline if the window caption is empty
 
991
            if( !caption.trimmed().isEmpty() )
 
992
            {
 
993
                if( _itemData.isAnimated() ) {
 
994
 
 
995
                    renderTitleOutline( painter, item._boundingRect, palette );
 
996
 
 
997
                } else if( (isActive()||glowIsAnimated()) && configuration().drawTitleOutline() ) {
 
998
 
 
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 ) );
 
1003
 
 
1004
                    // render bounding rect around it with extra margins
 
1005
                    renderTitleOutline( painter, boundingRect, palette );
 
1006
 
 
1007
                }
 
1008
 
 
1009
            }
 
1010
 
 
1011
        } else if( active ) {
 
1012
 
 
1013
            // in multiple tabs render title outline in all cases
 
1014
            renderTitleOutline( painter, item._boundingRect, palette );
 
1015
 
 
1016
        }
 
1017
 
 
1018
        // render text
 
1019
        if( active || itemCount == 1 )
 
1020
        {
 
1021
 
 
1022
            // for active tab, current caption is "merged" with old caption, if any
 
1023
            renderTitleText(
 
1024
                painter, textRect,
 
1025
                titlebarTextColor( palette ),
 
1026
                titlebarContrastColor( palette ) );
 
1027
 
 
1028
 
 
1029
        } else {
 
1030
 
 
1031
            QColor background( backgroundPalette( widget(), palette ).color( widget()->window()->backgroundRole() ) );
 
1032
 
 
1033
            // add extra shade (as used in renderWindowBorder
 
1034
            if( !( isActive() && configuration().drawTitleOutline() ) )
 
1035
            { background = KColorUtils::mix( background, Qt::black, 0.10 ); }
 
1036
 
 
1037
            // otherwise current caption is rendered directly
 
1038
            renderTitleText(
 
1039
                painter, textRect, caption,
 
1040
                titlebarTextColor( backgroundPalette( widget(), palette ), false ),
 
1041
                titlebarContrastColor( background ) );
 
1042
 
 
1043
        }
 
1044
 
 
1045
        // render separators between inactive tabs
 
1046
        if( !( active || itemCount == 1 ) && item._closeButton && item._closeButton.data()->isVisible() )
 
1047
        {
 
1048
 
 
1049
            // separators
 
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 ) ) )
 
1054
            {
 
1055
 
 
1056
                const QRect local( item._boundingRect.topLeft()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) );
 
1057
                helper().drawSeparator( painter, local, color, Qt::Vertical);
 
1058
 
 
1059
            }
 
1060
 
 
1061
            // draw right separator
 
1062
            if(
 
1063
                ( index == itemCount-1 && buttonsRightWidth() > 0 ) ||
 
1064
                ( index+1 < itemCount && ( _itemData.isTarget( index+1 ) ||
 
1065
                !( index+1 == visibleClientGroupItem() && _itemData[index+1]._boundingRect.isValid() ) ) ) )
 
1066
            {
 
1067
 
 
1068
                const QRect local( item._boundingRect.topRight()+QPoint(0,2), QSize( 2, item._boundingRect.height()-3 ) );
 
1069
                helper().drawSeparator( painter, local, color, Qt::Vertical);
 
1070
 
 
1071
            }
 
1072
 
 
1073
        }
 
1074
 
 
1075
    }
 
1076
 
 
1077
    //_______________________________________________________________________
 
1078
    void Client::renderTargetRect( QPainter* p, const QPalette& palette )
 
1079
    {
 
1080
        if( _itemData.targetRect().isNull() || _itemData.isAnimationRunning() ) return;
 
1081
 
 
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 ) );
 
1086
 
 
1087
    }
 
1088
 
 
1089
    //_______________________________________________________________________
 
1090
    void Client::renderCorners( QPainter* painter, const QRect& frame, const QPalette& palette ) const
 
1091
    {
 
1092
 
 
1093
        const QColor color( backgroundColor( widget(), palette ) );
 
1094
 
 
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) );
 
1099
 
 
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 );
 
1103
 
 
1104
    }
 
1105
 
 
1106
    //_______________________________________________________________________
 
1107
    void Client::renderFloatFrame( QPainter* painter, const QRect& frame, const QPalette& palette ) const
 
1108
    {
 
1109
 
 
1110
        // shadow and resize handles
 
1111
        if( !isMaximized() )
 
1112
        {
 
1113
 
 
1114
            if( configuration().frameBorder() >= Configuration::BorderTiny )
 
1115
            {
 
1116
 
 
1117
                helper().drawFloatFrame(
 
1118
                    painter, frame, backgroundColor( widget(), palette ),
 
1119
                    !compositingActive(), isActive() && configuration().useOxygenShadows(),
 
1120
                    KDecoration::options()->color(ColorTitleBar)
 
1121
                    );
 
1122
 
 
1123
            } else {
 
1124
 
 
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)
 
1131
                    );
 
1132
            }
 
1133
 
 
1134
        } else if( isShade() ) {
 
1135
 
 
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),
 
1141
                TileSet::Bottom
 
1142
                );
 
1143
 
 
1144
        }
 
1145
 
 
1146
    }
 
1147
 
 
1148
    //____________________________________________________________________________
 
1149
    void Client::renderDots( QPainter* painter, const QRect& frame, const QColor& color ) const
 
1150
    {
 
1151
 
 
1152
        if( configuration().frameBorder() >= Configuration::BorderTiny )
 
1153
        {
 
1154
 
 
1155
            // dimensions
 
1156
            int x,y,w,h;
 
1157
            frame.getRect(&x, &y, &w, &h);
 
1158
 
 
1159
            if( isResizable() && !isShade() && !isMaximized() )
 
1160
            {
 
1161
 
 
1162
                // Draw right side 3-dots resize handles
 
1163
                const int cenY = (h / 2 + y) ;
 
1164
                const int posX = (w + x - 3);
 
1165
 
 
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);
 
1169
 
 
1170
            }
 
1171
 
 
1172
            // Draw bottom-right cornet 3-dots resize handles
 
1173
            if( isResizable() && !isShade() && !configuration().drawSizeGrip() )
 
1174
            {
 
1175
 
 
1176
                painter->save();
 
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);
 
1181
                painter->restore();
 
1182
            }
 
1183
 
 
1184
        }
 
1185
 
 
1186
    }
 
1187
 
 
1188
    //_________________________________________________________
 
1189
    void Client::activeChange( void )
 
1190
    {
 
1191
 
 
1192
        KCommonDecorationUnstable::activeChange();
 
1193
        _itemData.setDirty( true );
 
1194
 
 
1195
        // reset animation
 
1196
        if( animateActiveChange() )
 
1197
        {
 
1198
            _glowAnimation->setDirection( isActive() ? Animation::Forward : Animation::Backward );
 
1199
            if(!glowIsAnimated()) { _glowAnimation->start(); }
 
1200
        }
 
1201
 
 
1202
        // update size grip so that it gets the right color
 
1203
        // also make sure it is remaped to from z stack,
 
1204
        // unless hidden
 
1205
        if( hasSizeGrip() && !(isShade() || isMaximized() ))
 
1206
        {
 
1207
            sizeGrip().activeChange();
 
1208
            sizeGrip().update();
 
1209
        }
 
1210
 
 
1211
    }
 
1212
 
 
1213
    //_________________________________________________________
 
1214
    void Client::maximizeChange( void  )
 
1215
    {
 
1216
        if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) );
 
1217
        KCommonDecorationUnstable::maximizeChange();
 
1218
    }
 
1219
 
 
1220
    //_________________________________________________________
 
1221
    void Client::shadeChange( void  )
 
1222
    {
 
1223
        if( hasSizeGrip() ) sizeGrip().setVisible( !( isShade() || isMaximized() ) );
 
1224
        KCommonDecorationUnstable::shadeChange();
 
1225
    }
 
1226
 
 
1227
    //_________________________________________________________
 
1228
    void Client::captionChange( void  )
 
1229
    {
 
1230
 
 
1231
        KCommonDecorationUnstable::captionChange();
 
1232
        _itemData.setDirty( true );
 
1233
        if( animateTitleChange() )
 
1234
        { _titleAnimationData->setDirty( true ); }
 
1235
 
 
1236
    }
 
1237
 
 
1238
    //_________________________________________________________
 
1239
    QPalette Client::backgroundPalette( const QWidget* widget, QPalette palette ) const
 
1240
    {
 
1241
 
 
1242
        if( configuration().drawTitleOutline() )
 
1243
        {
 
1244
            if( glowIsAnimated() && !isForcedActive() )
 
1245
            {
 
1246
 
 
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 );
 
1252
 
 
1253
            } else if( isActive() || isForcedActive() ) {
 
1254
 
 
1255
                const QColor color =  options()->color( KDecorationDefines::ColorTitleBar, true );
 
1256
                palette.setColor( QPalette::Window, color );
 
1257
                palette.setColor( QPalette::Button, color );
 
1258
 
 
1259
            }
 
1260
 
 
1261
        }
 
1262
 
 
1263
        return palette;
 
1264
 
 
1265
    }
 
1266
 
 
1267
    //_________________________________________________________
 
1268
    QColor Client::backgroundColor( const QWidget*, QPalette palette, bool active ) const
 
1269
    {
 
1270
 
 
1271
        return ( configuration().drawTitleOutline() && active ) ?
 
1272
            options()->color( KDecorationDefines::ColorTitleBar, true ):
 
1273
            palette.color( QPalette::Window );
 
1274
 
 
1275
    }
 
1276
 
 
1277
    //_________________________________________________________
 
1278
    QString Client::defaultButtonsLeft() const
 
1279
    { return KCommonDecoration::defaultButtonsLeft(); }
 
1280
 
 
1281
    //_________________________________________________________
 
1282
    QString Client::defaultButtonsRight() const
 
1283
    { return "HIAX"; }
 
1284
 
 
1285
    //________________________________________________________________
 
1286
    void Client::updateWindowShape()
 
1287
    {
 
1288
 
 
1289
        if(isMaximized()) clearMask();
 
1290
        else setMask( calcMask() );
 
1291
 
 
1292
    }
 
1293
 
 
1294
    //______________________________________________________________________________
 
1295
    bool Client::eventFilter( QObject* object, QEvent* event )
 
1296
    {
 
1297
 
 
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 ); }
 
1302
 
 
1303
        bool state = false;
 
1304
        switch( event->type() )
 
1305
        {
 
1306
 
 
1307
            case QEvent::Show:
 
1308
            if( widget() == object )
 
1309
            { _itemData.setDirty( true ); }
 
1310
            break;
 
1311
 
 
1312
            case QEvent::MouseButtonPress:
 
1313
            if( widget() == object )
 
1314
            { state = mousePressEvent( static_cast< QMouseEvent* >( event ) ); }
 
1315
            break;
 
1316
 
 
1317
            case QEvent::MouseButtonRelease:
 
1318
            if( widget() == object ) state = mouseReleaseEvent( static_cast< QMouseEvent* >( event ) );
 
1319
            else if( Button *btn = qobject_cast< Button* >( object ) )
 
1320
            {
 
1321
                QMouseEvent* mouseEvent( static_cast< QMouseEvent* >( event ) );
 
1322
                if( mouseEvent->button() == Qt::LeftButton && btn->rect().contains( mouseEvent->pos() ) )
 
1323
                { state = closeItem( btn ); }
 
1324
            }
 
1325
 
 
1326
            break;
 
1327
 
 
1328
            case QEvent::MouseMove:
 
1329
            state = mouseMoveEvent( static_cast< QMouseEvent* >( event ) );
 
1330
            break;
 
1331
 
 
1332
            case QEvent::DragEnter:
 
1333
            if(  widget() == object )
 
1334
            { state = dragEnterEvent( static_cast< QDragEnterEvent* >( event ) ); }
 
1335
            break;
 
1336
 
 
1337
            case QEvent::DragMove:
 
1338
            if( widget() == object )
 
1339
            { state = dragMoveEvent( static_cast< QDragMoveEvent* >( event ) ); }
 
1340
            break;
 
1341
 
 
1342
            case QEvent::DragLeave:
 
1343
            if( widget() == object )
 
1344
            { state = dragLeaveEvent( static_cast< QDragLeaveEvent* >( event ) ); }
 
1345
            break;
 
1346
 
 
1347
            case QEvent::Drop:
 
1348
            if( widget() == object )
 
1349
            { state = dropEvent( static_cast< QDropEvent* >( event ) ); }
 
1350
            break;
 
1351
 
 
1352
            default: break;
 
1353
 
 
1354
        }
 
1355
        return state || KCommonDecorationUnstable::eventFilter( object, event );
 
1356
 
 
1357
    }
 
1358
 
 
1359
    //_________________________________________________________
 
1360
    void Client::resizeEvent( QResizeEvent* event )
 
1361
    {
 
1362
        _itemData.setDirty( true );
 
1363
        KCommonDecorationUnstable::resizeEvent( event );
 
1364
    }
 
1365
 
 
1366
    //_________________________________________________________
 
1367
    void Client::paintEvent( QPaintEvent* event )
 
1368
    {
 
1369
 
 
1370
        // factory
 
1371
        if(!( _initialized && _factory->initialized() ) ) return;
 
1372
 
 
1373
        // palette
 
1374
        QPalette palette = widget()->palette();
 
1375
        palette.setCurrentColorGroup( (isActive() ) ? QPalette::Active : QPalette::Inactive );
 
1376
 
 
1377
        // painter
 
1378
        QPainter painter(widget());
 
1379
        painter.setRenderHint(QPainter::Antialiasing);
 
1380
        painter.setClipRegion( event->region() );
 
1381
 
 
1382
        // define frame
 
1383
        QRect frame = widget()->rect();
 
1384
 
 
1385
        // base color
 
1386
        QColor color = palette.window().color();
 
1387
 
 
1388
        // draw shadows
 
1389
        if( compositingActive() && shadowCache().shadowSize() > 0 && !isMaximized() )
 
1390
        {
 
1391
 
 
1392
            TileSet *tileSet( 0 );
 
1393
            const ShadowCache::Key key( this->key() );
 
1394
            if( configuration().useOxygenShadows() && glowIsAnimated() && !isForcedActive() )
 
1395
            {
 
1396
 
 
1397
                tileSet = shadowCache().tileSet( key, glowIntensity() );
 
1398
 
 
1399
            } else {
 
1400
 
 
1401
                tileSet = shadowCache().tileSet( key );
 
1402
 
 
1403
            }
 
1404
 
 
1405
            tileSet->render( frame, &painter, TileSet::Ring);
 
1406
 
 
1407
        }
 
1408
 
 
1409
        // adjust frame
 
1410
        frame.adjust(
 
1411
            layoutMetric(LM_OuterPaddingLeft),
 
1412
            layoutMetric(LM_OuterPaddingTop),
 
1413
            -layoutMetric(LM_OuterPaddingRight),
 
1414
            -layoutMetric(LM_OuterPaddingBottom) );
 
1415
 
 
1416
        //  adjust mask
 
1417
        if( compositingActive() || isPreview() )
 
1418
        {
 
1419
 
 
1420
            if( isMaximized() ) {
 
1421
 
 
1422
                painter.setClipRect( frame, Qt::IntersectClip );
 
1423
 
 
1424
            } else {
 
1425
 
 
1426
                // multipliers
 
1427
                const int left = 1;
 
1428
                const int right = 1;
 
1429
                const int top = 1;
 
1430
                int bottom = 1;
 
1431
 
 
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 ) );
 
1435
 
 
1436
                renderCorners( &painter, frame, palette );
 
1437
                painter.setClipRegion( mask, Qt::IntersectClip );
 
1438
 
 
1439
            }
 
1440
 
 
1441
        }
 
1442
 
 
1443
        // make sure ItemData and clientGroupItems are synchronized
 
1444
        /*
 
1445
        this needs to be done before calling RenderWindowBorder
 
1446
        since some painting in there depend on the clientGroups state
 
1447
        */
 
1448
        if(  _itemData.isDirty() || _itemData.count() != clientGroupItems().count() )
 
1449
        { updateItemBoundingRects( false ); }
 
1450
 
 
1451
        // window background
 
1452
        renderWindowBackground( &painter, frame, widget(), backgroundPalette( widget(), palette ) );
 
1453
 
 
1454
        // window border (for title outline)
 
1455
        if( hasTitleOutline() ) renderWindowBorder( &painter, frame, widget(), palette );
 
1456
 
 
1457
        // clipping
 
1458
        if( compositingActive() )
 
1459
        {
 
1460
            painter.setClipping(false);
 
1461
            frame.adjust(-1,-1, 1, 1);
 
1462
        }
 
1463
 
 
1464
        // float frame
 
1465
        renderFloatFrame( &painter, frame, palette );
 
1466
 
 
1467
        // resize handles
 
1468
        renderDots( &painter, frame, backgroundColor( widget(), palette ) );
 
1469
 
 
1470
        if( !hideTitleBar() )
 
1471
        {
 
1472
 
 
1473
            // title bounding rect
 
1474
            painter.setFont( options()->font(isActive(), false) );
 
1475
 
 
1476
            // draw ClientGroupItems
 
1477
            const int itemCount( _itemData.count() );
 
1478
            for( int i = 0; i < itemCount; i++ ) renderItem( &painter, i, palette );
 
1479
 
 
1480
            // draw target rect
 
1481
            renderTargetRect( &painter, widget()->palette() );
 
1482
 
 
1483
            // separator
 
1484
            if( itemCount == 1 && !_itemData.isAnimated() && drawSeparator() )
 
1485
            { renderSeparator(&painter, frame, widget(), color ); }
 
1486
 
 
1487
        }
 
1488
 
 
1489
    }
 
1490
 
 
1491
    //_____________________________________________________________
 
1492
    bool Client::mousePressEvent( QMouseEvent* event )
 
1493
    {
 
1494
 
 
1495
        const QPoint point = event->pos();
 
1496
        if( itemClicked( point ) < 0 ) return false;
 
1497
        _dragPoint = point;
 
1498
 
 
1499
        _mouseButton = event->button();
 
1500
        bool accepted( false );
 
1501
        if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
 
1502
        {
 
1503
 
 
1504
            accepted = true;
 
1505
 
 
1506
        } else if( buttonToWindowOperation( _mouseButton ) == OperationsOp ) {
 
1507
 
 
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...
 
1513
 
 
1514
        }
 
1515
        return accepted;
 
1516
    }
 
1517
 
 
1518
    //_____________________________________________________________
 
1519
    bool Client::mouseReleaseEvent( QMouseEvent* event )
 
1520
    {
 
1521
 
 
1522
        bool accepted( false );
 
1523
        if( _mouseButton == event->button() &&
 
1524
            buttonToWindowOperation( _mouseButton ) != OperationsOp )
 
1525
        {
 
1526
 
 
1527
            const QPoint point = event->pos();
 
1528
 
 
1529
            const int visibleItem = visibleClientGroupItem();
 
1530
            const int itemClicked( this->itemClicked( point ) );
 
1531
            if( itemClicked >= 0 && visibleItem != itemClicked )
 
1532
            {
 
1533
                setVisibleClientGroupItem( itemClicked );
 
1534
                setForceActive( true );
 
1535
                accepted = true;
 
1536
            }
 
1537
 
 
1538
        }
 
1539
 
 
1540
        _mouseButton = Qt::NoButton;
 
1541
        return accepted;
 
1542
 
 
1543
    }
 
1544
 
 
1545
    //_____________________________________________________________
 
1546
    bool Client::mouseMoveEvent( QMouseEvent* event )
 
1547
    {
 
1548
 
 
1549
        // check button and distance to drag point
 
1550
        if( hideTitleBar() || _mouseButton == Qt::NoButton  || ( event->pos() - _dragPoint ).manhattanLength() <= QApplication::startDragDistance() )
 
1551
        { return false; }
 
1552
 
 
1553
        bool accepted( false );
 
1554
        if( buttonToWindowOperation( _mouseButton ) == ClientGroupDragOp )
 
1555
        {
 
1556
 
 
1557
            const QPoint point = event->pos();
 
1558
            const int itemClicked( this->itemClicked( point ) );
 
1559
            if( itemClicked < 0 ) return false;
 
1560
 
 
1561
            _titleAnimationData->reset();
 
1562
 
 
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 );
 
1568
 
 
1569
            // get tab geometry
 
1570
            QRect geometry( _itemData[itemClicked]._boundingRect );
 
1571
 
 
1572
            // remove space used for buttons
 
1573
            if( _itemData.count() > 1  )
 
1574
            {
 
1575
 
 
1576
                geometry.adjust( 0, 0,  - configuration().buttonSize() - layoutMetric(LM_TitleEdgeRight), 0 );
 
1577
 
 
1578
            } else if( !( isActive() && configuration().drawTitleOutline() ) ) {
 
1579
 
 
1580
 
 
1581
                geometry.adjust(
 
1582
                    buttonsLeftWidth() + layoutMetric( LM_TitleEdgeLeft ) , 0,
 
1583
                    -( buttonsRightWidth() + layoutMetric( LM_TitleEdgeRight )), 0 );
 
1584
 
 
1585
            }
 
1586
 
 
1587
            drag->setPixmap( itemDragPixmap( itemClicked, geometry ) );
 
1588
 
 
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 ) );
 
1592
 
 
1593
            // make sure the horizontal hotspot position is not too far away (more than 1px)
 
1594
            // from the pixmap
 
1595
            if( hotSpot.x() < -1 ) hotSpot.setX(-1);
 
1596
            if( hotSpot.x() > geometry.width() ) hotSpot.setX( geometry.width() );
 
1597
 
 
1598
            drag->setHotSpot( hotSpot );
 
1599
 
 
1600
            _dragStartTimer.start( 50, this );
 
1601
            drag->exec( Qt::MoveAction );
 
1602
 
 
1603
            // detach tab from window
 
1604
            if( drag->target() == 0 && _itemData.count() > 1 )
 
1605
            {
 
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 )))
 
1615
                    );
 
1616
            }
 
1617
 
 
1618
            // reset button
 
1619
            _mouseButton = Qt::NoButton;
 
1620
            accepted = true;
 
1621
 
 
1622
        }
 
1623
 
 
1624
        return accepted;
 
1625
 
 
1626
    }
 
1627
 
 
1628
    //_____________________________________________________________
 
1629
    bool Client::dragEnterEvent( QDragEnterEvent* event )
 
1630
    {
 
1631
 
 
1632
        // check if drag enter is allowed
 
1633
        if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) || hideTitleBar() ) return false;
 
1634
 
 
1635
        //
 
1636
        event->acceptProposedAction();
 
1637
        if( event->source() != widget() )
 
1638
        {
 
1639
 
 
1640
            const QPoint position( event->pos() );
 
1641
            _itemData.animate( AnimationEnter, itemClicked( position, true ) );
 
1642
 
 
1643
        } else if( _itemData.count() > 1 )  {
 
1644
 
 
1645
            const QPoint position( event->pos() );
 
1646
            const int itemClicked( this->itemClicked( position, false ) );
 
1647
            _itemData.animate( AnimationEnter|AnimationSameTarget, itemClicked );
 
1648
 
 
1649
        }
 
1650
 
 
1651
        return true;
 
1652
 
 
1653
    }
 
1654
 
 
1655
    //_____________________________________________________________
 
1656
    bool Client::dragLeaveEvent( QDragLeaveEvent* )
 
1657
    {
 
1658
 
 
1659
        if( _itemData.animationType() & AnimationSameTarget )
 
1660
        {
 
1661
 
 
1662
            if( _dragStartTimer.isActive() ) _dragStartTimer.stop();
 
1663
            _itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem );
 
1664
 
 
1665
        } else if( _itemData.isAnimated() ) {
 
1666
 
 
1667
            _itemData.animate( AnimationLeave );
 
1668
 
 
1669
        }
 
1670
 
 
1671
 
 
1672
        return true;
 
1673
 
 
1674
    }
 
1675
 
 
1676
    //_____________________________________________________________
 
1677
    bool Client::dragMoveEvent( QDragMoveEvent* event )
 
1678
    {
 
1679
 
 
1680
        // check format
 
1681
        if( !event->mimeData()->hasFormat( clientGroupItemDragMimeType() ) ) return false;
 
1682
        if( event->source() != widget() )
 
1683
        {
 
1684
 
 
1685
            const QPoint position( event->pos() );
 
1686
            _itemData.animate( AnimationMove, itemClicked( position, true ) );
 
1687
 
 
1688
        } else if( _itemData.count() > 1 )  {
 
1689
 
 
1690
            if( _dragStartTimer.isActive() ) _dragStartTimer.stop();
 
1691
 
 
1692
            const QPoint position( event->pos() );
 
1693
            const int itemClicked( this->itemClicked( position, false ) );
 
1694
            _itemData.animate( AnimationMove|AnimationSameTarget, itemClicked );
 
1695
 
 
1696
        }
 
1697
 
 
1698
        return false;
 
1699
 
 
1700
    }
 
1701
 
 
1702
    //_____________________________________________________________
 
1703
    bool Client::dropEvent( QDropEvent* event )
 
1704
    {
 
1705
 
 
1706
        const QPoint point = event->pos();
 
1707
        _itemData.animate( AnimationNone );
 
1708
 
 
1709
        const QMimeData *groupData = event->mimeData();
 
1710
        if( !groupData->hasFormat( clientGroupItemDragMimeType() ) ) return false;
 
1711
 
 
1712
        if( widget() == event->source() )
 
1713
        {
 
1714
 
 
1715
            const int from = this->itemClicked( _dragPoint );
 
1716
            int itemClicked( this->itemClicked( point, false ) );
 
1717
 
 
1718
            if( itemClicked > from )
 
1719
            {
 
1720
                itemClicked++;
 
1721
                if( itemClicked >= clientGroupItems().count() )
 
1722
                { itemClicked = -1; }
 
1723
            }
 
1724
 
 
1725
            _itemData.setDirty( true );
 
1726
            moveItemInClientGroup( from, itemClicked );
 
1727
            updateTitleRect();
 
1728
 
 
1729
        } else {
 
1730
 
 
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 );
 
1736
 
 
1737
        }
 
1738
 
 
1739
        _titleAnimationData->reset();
 
1740
        return true;
 
1741
 
 
1742
    }
 
1743
 
 
1744
    //_____________________________________________________________
 
1745
    void Client::timerEvent( QTimerEvent* event )
 
1746
    {
 
1747
 
 
1748
        if( event->timerId() != _dragStartTimer.timerId() )
 
1749
        { return KCommonDecorationUnstable::timerEvent( event ); }
 
1750
 
 
1751
        _dragStartTimer.stop();
 
1752
 
 
1753
        // do nothing if there is only one tab
 
1754
        if( _itemData.count() > 1 )
 
1755
        {
 
1756
            _itemData.animate( AnimationMove|AnimationSameTarget, _sourceItem );
 
1757
            _itemData.animate( AnimationLeave|AnimationSameTarget, _sourceItem );
 
1758
        }
 
1759
 
 
1760
    }
 
1761
 
 
1762
    //_____________________________________________________________
 
1763
    bool Client::closeItem( const Button* button )
 
1764
    {
 
1765
 
 
1766
        for( int i=0; i <  _itemData.count(); i++ )
 
1767
        {
 
1768
            if( button == _itemData[i]._closeButton.data() )
 
1769
            {
 
1770
                _itemData.setDirty( true );
 
1771
                closeClientGroupItem( i );
 
1772
                return true;
 
1773
            }
 
1774
        }
 
1775
        return false;
 
1776
 
 
1777
    }
 
1778
 
 
1779
    //________________________________________________________________
 
1780
    QPixmap Client::itemDragPixmap( int index, const QRect& geometry )
 
1781
    {
 
1782
        const bool itemValid( index >= 0 && index < clientGroupItems().count() );
 
1783
 
 
1784
        QPixmap pixmap( geometry.size() );
 
1785
        QPainter painter( &pixmap );
 
1786
        painter.setRenderHints(QPainter::SmoothPixmapTransform|QPainter::Antialiasing);
 
1787
 
 
1788
        painter.translate( -geometry.topLeft() );
 
1789
 
 
1790
        // render window background
 
1791
        renderWindowBackground( &painter, geometry, widget(), widget()->palette() );
 
1792
 
 
1793
        // darken background if item is inactive
 
1794
        const bool itemActive = !( itemValid && index != visibleClientGroupItem() );
 
1795
        if( !itemActive )
 
1796
        {
 
1797
 
 
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 );
 
1804
 
 
1805
        }
 
1806
 
 
1807
        // render title text
 
1808
        painter.setFont( options()->font(isActive(), false) );
 
1809
        QRect textRect( geometry.adjusted( 0, layoutMetric( LM_TitleEdgeTop )-1, 0, -1 ) );
 
1810
 
 
1811
        if( itemValid )
 
1812
        { textRect.adjust( layoutMetric( LM_TitleBorderLeft ), 0, -layoutMetric(LM_TitleBorderRight), 0 ); }
 
1813
 
 
1814
        const QString caption( itemValid ? clientGroupItems()[index].title() : this->caption() );
 
1815
        renderTitleText(
 
1816
            &painter, textRect, caption,
 
1817
            titlebarTextColor( widget()->palette(), isActive() && itemActive ),
 
1818
            titlebarContrastColor( widget()->palette() ) );
 
1819
 
 
1820
        // floating frame
 
1821
        helper().drawFloatFrame(
 
1822
            &painter, geometry, widget()->palette().window().color(),
 
1823
            true, false,
 
1824
            KDecoration::options()->color(ColorTitleBar)
 
1825
            );
 
1826
 
 
1827
        painter.end();
 
1828
 
 
1829
        // create pixmap mask
 
1830
        QBitmap bitmap( geometry.size() );
 
1831
        {
 
1832
            bitmap.clear();
 
1833
            QPainter painter( &bitmap );
 
1834
            QPainterPath path;
 
1835
            path.addRegion( helper().roundedMask( geometry.translated( -geometry.topLeft() ) ) );
 
1836
            painter.fillPath( path, Qt::color1 );
 
1837
        }
 
1838
 
 
1839
        pixmap.setMask( bitmap );
 
1840
        return pixmap;
 
1841
 
 
1842
    }
 
1843
 
 
1844
    //_________________________________________________________________
 
1845
    void Client::createSizeGrip( void )
 
1846
    {
 
1847
 
 
1848
        assert( !hasSizeGrip() );
 
1849
        if( ( isResizable() && windowId() != 0 ) || isPreview() )
 
1850
        {
 
1851
            _sizeGrip = new SizeGrip( this );
 
1852
            sizeGrip().setVisible( !( isMaximized() || isShade() ) );
 
1853
        }
 
1854
 
 
1855
    }
 
1856
 
 
1857
    //_________________________________________________________________
 
1858
    void Client::deleteSizeGrip( void )
 
1859
    {
 
1860
        assert( hasSizeGrip() );
 
1861
        _sizeGrip->deleteLater();
 
1862
        _sizeGrip = 0;
 
1863
    }
 
1864
 
 
1865
    //_________________________________________________________________
 
1866
    void Client::removeShadowHint( void )
 
1867
    {
 
1868
 
 
1869
        // do nothing if no window id
 
1870
        if( !windowId() ) return;
 
1871
 
 
1872
        // create atom
 
1873
        if( !_shadowAtom )
 
1874
        { _shadowAtom = XInternAtom( QX11Info::display(), "_KDE_NET_WM_SHADOW", False); }
 
1875
 
 
1876
        XDeleteProperty(QX11Info::display(), windowId(), _shadowAtom);
 
1877
    }
 
1878
 
 
1879
}