add more spacing
[personal-kdebase.git] / workspace / kwin / clients / keramik / keramik.cpp
blobfc14878aac169908f4edb05c755fa3fb7c445a0b
1 /*
3 * Keramik KWin client (version 0.8)
5 * Copyright (C) 2002 Fredrik H�lund <fredrik@kde.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the license, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
23 #include "keramik.h"
25 #include <kconfiggroup.h>
26 #include <klocale.h>
27 #include <kiconeffect.h>
29 #include <QPainter>
30 #include <QLayout>
31 #include <QBitmap>
32 #include <QStyle>
33 #include <QWidget>
34 #include <QLabel>
35 #include <QEvent>
36 #include <QApplication>
38 #include <X11/Xlib.h>
40 #include "keramik.moc"
42 // -------------------------------------------------------------------------------------------
44 static void flip( QPixmap *&pix )
46 QPixmap *tmp = new QPixmap( pix->transformed( QMatrix(-1,0,0,1,pix->width(),0) ) );
47 delete pix;
48 pix = tmp;
51 static void flip( QBitmap *&pix )
53 QBitmap *tmp = new QBitmap( pix->transformed( QMatrix(-1,0,0,1,pix->width(),0) ) );
54 delete pix;
55 pix = tmp;
58 namespace Keramik
61 const int buttonMargin = 9; // Margin between the window edge and the buttons
62 const int buttonSpacing = 4; // Spacing between the titlebar buttons
63 const int iconSpacing = 5; // Spacing between the icon and the text label
65 // Titlebar button bitmaps
66 const unsigned char menu_bits[] = {
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0xf0, 0x07, 0x00,
69 0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 0x00, 0x00, 0x00};
73 const unsigned char on_all_desktops_bits[] = {
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00,
76 0xf0, 0x0f, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00};
80 const unsigned char not_on_all_desktops_bits[] = {
81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00,
83 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00};
87 const unsigned char help_bits[] = {
88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
89 0xf0, 0x07, 0x00, 0x30, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00,
90 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00,
91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
92 0x00, 0x00, 0x00};
94 const unsigned char minimize_bits[] = {
95 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00};
101 const unsigned char maximize_bits[] = {
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0xf0, 0x0f, 0x00,
104 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00};
108 const unsigned char restore_bits[] = {
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00,
111 0xf0, 0x0f, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00};
115 const unsigned char close_bits[] = {
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x30, 0x0c, 0x00, 0x70, 0x0e, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00,
118 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0x70, 0x0e, 0x00, 0x30, 0x0c, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00};
122 const unsigned char above_on_bits[] = {
123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
124 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00 };
129 const unsigned char above_off_bits[] = {
130 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00,
131 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00 };
136 const unsigned char below_on_bits[] = {
137 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
138 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00,
139 0xe0, 0x07, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
140 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
141 0x00, 0x00, 0x00 };
143 const unsigned char below_off_bits[] = {
144 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
145 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
146 0x80, 0x01, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00,
147 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148 0x00, 0x00, 0x00 };
150 const unsigned char shade_on_bits[] = {
151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
152 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00,
153 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0x10, 0x08, 0x00, 0xf0, 0x0f, 0x00,
154 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
155 0x00, 0x00, 0x00 };
157 const unsigned char shade_off_bits[] = {
158 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
159 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
160 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00 };
164 KeramikHandler *clientHandler = NULL;
165 bool keramik_initialized = false;
169 // -------------------------------------------------------------------------------------------
173 KeramikHandler::KeramikHandler()
175 for ( int i = 0; i < NumTiles; i++ ) {
176 activeTiles[i] = NULL;
177 inactiveTiles[i] = NULL;
180 settings_cache = NULL;
182 // Create the button deco bitmaps
183 buttonDecos[ Menu ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), menu_bits ) );
184 buttonDecos[ OnAllDesktops ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), on_all_desktops_bits ) );
185 buttonDecos[ NotOnAllDesktops ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), not_on_all_desktops_bits ) );
186 buttonDecos[ Help ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), help_bits ) );
187 buttonDecos[ Minimize ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), minimize_bits ) );
188 buttonDecos[ Maximize ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), maximize_bits ) );
189 buttonDecos[ Restore ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), restore_bits ) );
190 buttonDecos[ Close ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), close_bits ) );
191 buttonDecos[ AboveOn ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), above_on_bits ) );
192 buttonDecos[ AboveOff ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), above_off_bits ) );
193 buttonDecos[ BelowOn ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), below_on_bits ) );
194 buttonDecos[ BelowOff ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), below_off_bits ) );
195 buttonDecos[ ShadeOn ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), shade_on_bits ) );
196 buttonDecos[ ShadeOff ] = new QBitmap( QBitmap::fromData( QSize( 17, 17 ), shade_off_bits ) );
198 // Selfmask the bitmaps
199 for ( int i = 0; i < NumButtonDecos; i++ )
200 buttonDecos[i]->setMask( *buttonDecos[i] );
202 // Flip the bitmaps horizontally in right-to-left mode
203 if ( QApplication::isRightToLeft() ) {
204 for ( int i = 0; i < Help; ++i )
205 ::flip( buttonDecos[i] );
207 for ( int i = Help + 1; i < NumButtonDecos; ++i )
208 ::flip( buttonDecos[i] );
211 readConfig();
212 createPixmaps();
214 keramik_initialized = true;
218 KeramikHandler::~KeramikHandler()
220 keramik_initialized = false;
221 destroyPixmaps();
223 for ( int i = 0; i < NumButtonDecos; i++ )
224 delete buttonDecos[i];
226 delete settings_cache;
228 clientHandler = NULL;
232 void KeramikHandler::createPixmaps()
234 int heightOffset;
235 int widthOffset;
236 switch(options()->preferredBorderSize(this)) {
237 case BorderLarge:
238 widthOffset = 4;
239 heightOffset = 0;
240 break;
241 case BorderVeryLarge:
242 widthOffset = 8;
243 heightOffset = 0;
244 break;
245 case BorderHuge:
246 widthOffset = 14;
247 heightOffset = 0;
248 break;
249 case BorderVeryHuge:
250 widthOffset = 23;
251 heightOffset = 10;
252 break;
253 case BorderOversized:
254 widthOffset = 36;
255 heightOffset = 25;
256 break;
257 case BorderTiny:
258 case BorderNormal:
259 default:
260 widthOffset = 0;
261 heightOffset = 0;
263 int fontHeight = QFontMetrics(options()->font(true)).height();
264 if (fontHeight > heightOffset + 20)
265 heightOffset = fontHeight - 20;
267 QString size = (heightOffset < 8) ? "" : (heightOffset < 20) ? "-large" : "-huge";
269 QColor titleColor, captionColor, buttonColor;
270 QImage *titleCenter = NULL, *captionLeft = NULL,
271 *captionRight = NULL, *captionCenter = NULL;
274 // Active tiles
275 // -------------------------------------------------------------------------
276 captionColor = KDecoration::options()->color( ColorTitleBar, true );
277 titleColor = KDecoration::options()->color( ColorTitleBlend, true );
279 // Load the titlebar corners.
280 activeTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor );
281 activeTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor );
283 // Load the titlebar center tile image (this will be used as
284 // the background for the caption bubble tiles).
285 titleCenter = loadImage( "titlebar-center", titleColor );
287 // Load the small version of the caption bubble corner & center images.
288 captionLeft = loadImage( "caption-small-left", captionColor );
289 captionRight = loadImage( "caption-small-right", captionColor );
290 captionCenter = loadImage( "caption-small-center", captionColor );
292 // Create the caption bubble tiles (by blending the images onto the titlebar)
293 activeTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter );
294 activeTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter );
295 activeTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter );
297 delete captionLeft;
298 delete captionRight;
299 delete captionCenter;
301 // Now do the same with the large version
302 captionLeft = loadImage( "caption-large-left", captionColor );
303 captionRight = loadImage( "caption-large-right", captionColor );
304 captionCenter = loadImage( "caption-large-center", captionColor );
306 activeTiles[ CaptionLargeLeft ] = composite( captionLeft, titleCenter );
307 activeTiles[ CaptionLargeRight ] = composite( captionRight, titleCenter );
308 activeTiles[ CaptionLargeCenter ] = composite( captionCenter, titleCenter );
310 delete captionLeft;
311 delete captionRight;
312 delete captionCenter;
314 // Create the titlebar center tile
315 activeTiles[ TitleCenter ] = new QPixmap( QPixmap::fromImage( *titleCenter ) );
317 delete titleCenter;
319 // Load the left & right border pixmaps
320 activeTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor );
321 activeTiles[ BorderRight ] = loadPixmap( "border-right", titleColor );
323 // Load the bottom grabbar pixmaps
324 if ( largeGrabBars ) {
325 activeTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor );
326 activeTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor );
327 activeTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor );
328 } else {
329 activeTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor );
330 activeTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor );
331 activeTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor );
334 // Inactive tiles
335 // -------------------------------------------------------------------------
336 captionColor = KDecoration::options()->color( ColorTitleBar, false );
337 titleColor = KDecoration::options()->color( ColorTitleBlend, false );
339 inactiveTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor );
340 inactiveTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor );
342 titleCenter = loadImage( "titlebar-center", titleColor );
344 captionLeft = loadImage( "caption-small-left", captionColor );
345 captionRight = loadImage( "caption-small-right", captionColor );
346 captionCenter = loadImage( "caption-small-center", captionColor );
348 inactiveTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter );
349 inactiveTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter );
350 inactiveTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter );
352 delete captionLeft;
353 delete captionRight;
354 delete captionCenter;
356 inactiveTiles[ TitleCenter ] = new QPixmap( QPixmap::fromImage( *titleCenter ) );
358 delete titleCenter;
360 inactiveTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor );
361 inactiveTiles[ BorderRight ] = loadPixmap( "border-right", titleColor );
363 if ( largeGrabBars ) {
364 inactiveTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor );
365 inactiveTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor );
366 inactiveTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor );
367 } else {
368 inactiveTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor );
369 inactiveTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor );
370 inactiveTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor );
373 // Buttons
374 // -------------------------------------------------------------------------
375 buttonColor = QColor(); //KDecoration::options()->color( ButtonBg, true );
377 titleButtonRound = loadPixmap( "titlebutton-round"+size, buttonColor );
378 titleButtonSquare = loadPixmap( "titlebutton-square"+size, buttonColor );
381 // Prepare the tiles for use
382 // -------------------------------------------------------------------------
383 if ( QApplication::isRightToLeft() ) {
385 // Fix lighting
386 flip( activeTiles[CaptionSmallLeft], activeTiles[CaptionSmallRight] );
387 flip( inactiveTiles[CaptionSmallLeft], inactiveTiles[CaptionSmallRight] );
389 flip( activeTiles[CaptionLargeLeft], activeTiles[CaptionLargeRight] );
391 flip( activeTiles[TitleLeft], activeTiles[TitleRight] );
392 flip( inactiveTiles[TitleLeft], inactiveTiles[TitleRight] );
394 flip( activeTiles[BorderLeft], activeTiles[BorderRight] );
395 flip( inactiveTiles[BorderLeft], inactiveTiles[BorderRight] );
397 flip( activeTiles[GrabBarLeft], activeTiles[GrabBarRight] );
398 flip( inactiveTiles[GrabBarLeft], inactiveTiles[GrabBarRight] );
400 ::flip( titleButtonRound );
401 ::flip( titleButtonSquare );
404 // Pretile the center & border tiles for optimal performance
405 pretile( activeTiles[ CaptionSmallCenter ], 64, Qt::Horizontal );
406 pretile( activeTiles[ CaptionLargeCenter ], 64, Qt::Horizontal );
407 pretile( activeTiles[ TitleCenter ], 64, Qt::Horizontal );
408 pretile( activeTiles[ GrabBarCenter ], 128, Qt::Horizontal );
409 pretile( activeTiles[ BorderLeft ], 128, Qt::Vertical );
410 pretile( activeTiles[ BorderRight ], 128, Qt::Vertical );
412 pretile( inactiveTiles[ CaptionSmallCenter ], 64, Qt::Horizontal );
413 pretile( inactiveTiles[ TitleCenter ], 64, Qt::Horizontal );
414 pretile( inactiveTiles[ GrabBarCenter ], 128, Qt::Horizontal );
415 pretile( inactiveTiles[ BorderLeft ], 128, Qt::Vertical );
416 pretile( inactiveTiles[ BorderRight ], 128, Qt::Vertical );
418 if (heightOffset > 0) {
419 addHeight (heightOffset, activeTiles[TitleLeft]);
420 addHeight (heightOffset, activeTiles[TitleCenter]);
421 addHeight (heightOffset, activeTiles[TitleRight]);
422 addHeight (heightOffset, activeTiles[CaptionSmallLeft]);
423 addHeight (heightOffset, activeTiles[CaptionSmallCenter]);
424 addHeight (heightOffset, activeTiles[CaptionSmallRight]);
425 addHeight (heightOffset, activeTiles[CaptionLargeLeft]);
426 addHeight (heightOffset, activeTiles[CaptionLargeCenter]);
427 addHeight (heightOffset, activeTiles[CaptionLargeRight]);
429 addHeight (heightOffset, inactiveTiles[TitleLeft]);
430 addHeight (heightOffset, inactiveTiles[TitleCenter]);
431 addHeight (heightOffset, inactiveTiles[TitleRight]);
432 addHeight (heightOffset, inactiveTiles[CaptionSmallLeft]);
433 addHeight (heightOffset, inactiveTiles[CaptionSmallCenter]);
434 addHeight (heightOffset, inactiveTiles[CaptionSmallRight]);
437 if (widthOffset > 0) {
438 addWidth (widthOffset, activeTiles[BorderLeft], true, activeTiles[GrabBarCenter]);
439 addWidth (widthOffset, activeTiles[BorderRight], false, activeTiles[GrabBarCenter]);
440 addWidth (widthOffset, inactiveTiles[BorderLeft], true, inactiveTiles[GrabBarCenter]);
441 addWidth (widthOffset, inactiveTiles[BorderRight], false, inactiveTiles[GrabBarCenter]);
443 if (largeGrabBars)
444 widthOffset = widthOffset*3/2;
446 addHeight (widthOffset, activeTiles[GrabBarLeft]);
447 addHeight (widthOffset, activeTiles[GrabBarCenter]);
448 addHeight (widthOffset, activeTiles[GrabBarRight]);
449 addHeight (widthOffset, inactiveTiles[GrabBarLeft]);
450 addHeight (widthOffset, inactiveTiles[GrabBarCenter]);
451 addHeight (widthOffset, inactiveTiles[GrabBarRight]);
457 void KeramikHandler::destroyPixmaps()
459 for ( int i = 0; i < NumTiles; i++ ) {
460 delete activeTiles[i];
461 delete inactiveTiles[i];
462 activeTiles[i] = NULL;
463 inactiveTiles[i] = NULL;
466 delete titleButtonRound;
467 delete titleButtonSquare;
471 void KeramikHandler::addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix) {
472 int w = pix->width()+width;
473 int h = pix->height();
475 QPixmap *tmp = new QPixmap (w, h);
476 tmp->fill ();
477 QPainter p;
478 p.begin (tmp);
480 for (int i = 0; i < h; i++)
481 p.drawPixmap (0, i, *bottomPix, i%2, 0, w,1);
483 if (left)
484 p.drawPixmap(0, 0, *pix);
485 else
486 p.drawPixmap(width, 0, *pix);
488 p.end();
490 delete pix;
491 pix = tmp;
495 void KeramikHandler::addHeight (int height, QPixmap *&pix) {
496 int w = pix->width();
497 int h = pix->height()+height;
499 QPixmap *tmp = new QPixmap (w, h);
500 QPainter p;
501 p.begin (tmp);
502 if (pix->height() > 10) {
503 p.drawPixmap(0, 0, *pix, 0, 0, w, 11);
504 for (int i = 0; i < height; i+=2)
505 p.drawPixmap(0, 11+i, *pix, 0, 11, w, 2);
506 p.drawPixmap(0, 11+height, *pix, 0, 11, w, -1);
508 else {
509 int lines = h-3;
510 int factor = pix->height()-3;
511 for (int i = 0; i < lines; i++)
512 p.drawPixmap(0, i, *pix, 0, i*factor/lines, w, 1);
513 p.drawPixmap(0, lines, *pix, 0, factor, w, 3);
515 p.end();
517 delete pix;
518 pix = tmp;
522 void KeramikHandler::flip( QPixmap *&pix1, QPixmap *&pix2 )
524 // Flip the pixmaps horizontally
525 QPixmap *tmp = new QPixmap( pix1->transformed( QMatrix(-1,0,0,1,pix1->width(),0) ) );
527 delete pix1;
528 pix1 = new QPixmap( pix2->transformed( QMatrix(-1,0,0,1,pix2->width(),0) ) );
530 delete pix2;
531 pix2 = tmp;
535 void KeramikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir )
537 QPixmap *newpix;
538 QPainter p;
540 if ( dir == Qt::Horizontal )
541 newpix = new QPixmap( size, pix->height() );
542 else
543 newpix = new QPixmap( pix->width(), size );
545 p.begin( newpix );
546 p.drawTiledPixmap( newpix->rect(), *pix ) ;
547 p.end();
549 delete pix;
550 pix = newpix;
554 void KeramikHandler::readConfig()
556 KConfig *c = new KConfig( "kwinkeramikrc" );
557 KConfigGroup cg(c, "General");
558 showIcons = cg.readEntry( "ShowAppIcons", true);
559 shadowedText = cg.readEntry( "UseShadowedText", true);
560 smallCaptionBubbles = cg.readEntry( "SmallCaptionBubbles", false);
561 largeGrabBars = cg.readEntry( "LargeGrabBars", true);
563 if ( ! settings_cache ) {
564 settings_cache = new SettingsCache;
565 settings_cache->largeGrabBars = largeGrabBars;
566 settings_cache->smallCaptionBubbles = smallCaptionBubbles;
569 delete c;
573 QPixmap *KeramikHandler::composite( QImage *over, QImage *under )
575 QImage dest( over->width(), over->height(), QImage::Format_ARGB32_Premultiplied );
577 QPainter p( &dest );
578 p.setCompositionMode( QPainter::CompositionMode_Source );
579 p.fillRect( dest.rect(), Qt::transparent );
580 p.drawImage( 0, dest.height() - under->height(), *under );
581 p.setCompositionMode( QPainter::CompositionMode_SourceOver );
582 p.drawImage( 0, 0, *over );
583 p.end();
585 // Create the final pixmap and return it
586 return new QPixmap( QPixmap::fromImage( dest ) );
590 QImage *KeramikHandler::loadImage( const QString &name, const QColor &col )
592 if ( col.isValid() ) {
593 QImage *img = new QImage( ":/pics/" + name + ".png" );
594 KIconEffect::colorize( *img, col, 1.0 );
595 return img;
596 } else
597 return new QImage( ":/pics/" + name + ".png" );
601 QPixmap *KeramikHandler::loadPixmap( const QString &name, const QColor &col )
603 QImage *img = loadImage( name, col );
604 QPixmap *pix = new QPixmap( QPixmap::fromImage( *img ) );
605 delete img;
607 return pix;
611 bool KeramikHandler::reset( unsigned long changed )
613 keramik_initialized = false;
615 bool needHardReset = false;
616 bool pixmapsInvalid = false;
618 // Re-read the config file
619 readConfig();
621 if ( changed & SettingBorder )
623 pixmapsInvalid = true;
624 needHardReset = true;
626 if ( changed & SettingFont )
628 pixmapsInvalid = true;
629 needHardReset = true;
631 // Check if the color scheme has changed
632 if ( changed & SettingColors )
634 pixmapsInvalid = true;
636 // Check if button positions have changed
638 if ( changed & SettingButtons ) {
639 needHardReset = true;
642 // Check if tooltips options have changed
643 if ( changed & SettingTooltips ) {
644 needHardReset = true;
647 if ( (settings_cache->largeGrabBars != largeGrabBars) ) {
648 pixmapsInvalid = true;
649 needHardReset = true;
652 if ( (settings_cache->smallCaptionBubbles != smallCaptionBubbles) ) {
653 needHardReset = true;
656 // Update our config cache
657 settings_cache->largeGrabBars = largeGrabBars;
658 settings_cache->smallCaptionBubbles = smallCaptionBubbles;
660 // Do we need to recreate the pixmaps?
661 if ( pixmapsInvalid ) {
662 destroyPixmaps();
663 createPixmaps();
666 keramik_initialized = true;
668 // Do we need to "hit the wooden hammer" ?
669 if ( !needHardReset )
670 resetDecorations( changed );
671 return needHardReset;
675 bool KeramikHandler::supports( Ability ability ) const
677 switch( ability )
679 // announce
680 case AbilityAnnounceButtons:
681 case AbilityAnnounceColors:
682 // buttons
683 case AbilityButtonMenu:
684 case AbilityButtonOnAllDesktops:
685 case AbilityButtonSpacer:
686 case AbilityButtonHelp:
687 case AbilityButtonMinimize:
688 case AbilityButtonMaximize:
689 case AbilityButtonClose:
690 case AbilityButtonAboveOthers:
691 case AbilityButtonBelowOthers:
692 case AbilityButtonShade:
693 // colors
694 case AbilityColorTitleBack:
695 case AbilityColorTitleBlend:
696 case AbilityColorTitleFore:
697 return true;
698 default:
699 return false;
704 const QPixmap *KeramikHandler::tile( TilePixmap tilePix, bool active ) const
706 return ( active ? activeTiles[ tilePix ] : inactiveTiles[ tilePix ] );
709 KDecoration* KeramikHandler::createDecoration( KDecorationBridge* bridge )
711 return new KeramikClient( bridge, this );
714 QList< KeramikHandler::BorderSize > KeramikHandler::borderSizes() const
715 { // the list must be sorted
716 return QList< BorderSize >() << BorderNormal << BorderLarge <<
717 BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized;
721 // -------------------------------------------------------------------------------------------
725 KeramikButton::KeramikButton( KeramikClient* c, Button btn, const QString &tip, const int realizeBtns )
726 : QAbstractButton( c->widget() ),
727 client( c ), button( btn ), hover( false ), lastbutton( Qt::NoButton )
729 realizeButtons = realizeBtns;
731 this->setToolTip( tip ); // FRAME
732 setAttribute( Qt::WA_NoSystemBackground );
733 setCursor( Qt::ArrowCursor );
734 int size = clientHandler->roundButton()->height();
735 setFixedSize( size, size );
737 setCheckable( (button == OnAllDesktopsButton) );
741 KeramikButton::~KeramikButton()
743 // Empty.
747 void KeramikButton::enterEvent( QEvent *e )
749 QAbstractButton::enterEvent( e );
751 hover = true;
752 repaint();
756 void KeramikButton::leaveEvent( QEvent *e )
758 QAbstractButton::leaveEvent( e );
760 hover = false;
761 repaint();
765 void KeramikButton::mousePressEvent( QMouseEvent *e )
767 lastbutton = e->button();
768 QMouseEvent me( e->type(), e->pos(), e->globalPos(),
769 (e->button()&realizeButtons)?Qt::LeftButton : Qt::NoButton,
770 (e->button()&realizeButtons)?Qt::LeftButton : Qt::NoButton,
771 e->modifiers() );
772 QAbstractButton::mousePressEvent( &me );
776 void KeramikButton::mouseReleaseEvent( QMouseEvent *e )
778 lastbutton = e->button();
779 QMouseEvent me( e->type(), e->pos(), e->globalPos(),
780 (e->button()&realizeButtons)?Qt::LeftButton : Qt::NoButton,
781 (e->button()&realizeButtons)?Qt::LeftButton : Qt::NoButton,
782 e->modifiers() );
783 QAbstractButton::mouseReleaseEvent( &me );
787 void KeramikButton::paintEvent( QPaintEvent * )
789 const QPixmap *pix;
790 const QBitmap *deco;
791 int size = clientHandler->roundButton()->height();
793 QPainter p( this );
795 // Get the bevel from the client handler
796 if ( button == MenuButton || button == OnAllDesktopsButton || button == HelpButton )
797 pix = clientHandler->roundButton();
798 else
799 pix = clientHandler->squareButton();
801 // Draw the button background
802 const QPixmap *background = clientHandler->tile( TitleCenter, client->isActive() );
803 p.drawPixmap( 0, 0, *background,
804 0, (background->height()-size+1)/2, size, size );
806 if ( isDown() ) {
807 // Pressed
808 p.drawPixmap( QPoint(), *pix, QStyle::visualRect( QApplication::layoutDirection(), pix->rect(), QRect(2*size, 0, size, size) ) );
809 p.translate( QApplication::isRightToLeft() ? -1 : 1, 1 );
810 } else if ( hover )
811 // Mouse over
812 p.drawPixmap( QPoint(), *pix, QStyle::visualRect( QApplication::layoutDirection(), pix->rect(), QRect(size, 0, size, size) ) );
813 else
814 // Normal
815 p.drawPixmap( QPoint(), *pix, QStyle::visualRect( QApplication::layoutDirection(), pix->rect(), QRect(0, 0, size, size) ) );
818 // Draw the button deco on the bevel
819 switch ( button ) {
820 case MenuButton:
821 deco = clientHandler->buttonDeco( Menu );
822 break;
824 case OnAllDesktopsButton:
825 deco = clientHandler->buttonDeco( client->isOnAllDesktops() ? NotOnAllDesktops : OnAllDesktops );
826 break;
828 case HelpButton:
829 deco = clientHandler->buttonDeco( Help );
830 // The '?' won't be flipped around in the ctor, so we need to
831 // shift it to the right to compensate for the button shadow
832 // being on the left side of the button in RTL mode.
833 if ( QApplication::isRightToLeft() )
834 p.translate( 2, 0 );
835 break;
837 case MinButton:
838 deco = clientHandler->buttonDeco( Minimize );
839 break;
841 case MaxButton:
842 deco = clientHandler->buttonDeco( client->maximizeMode() == KeramikClient::MaximizeFull ? Restore : Maximize );
843 break;
845 case CloseButton:
846 deco = clientHandler->buttonDeco( Close );
847 break;
849 case AboveButton:
850 deco = clientHandler->buttonDeco( client->keepAbove() ? AboveOn : AboveOff );
851 break;
853 case BelowButton:
854 deco = clientHandler->buttonDeco( client->keepBelow() ? BelowOn : BelowOff );
855 break;
857 case ShadeButton:
858 deco = clientHandler->buttonDeco( client->isSetShade() ? ShadeOn : ShadeOff );
859 break;
861 default:
862 deco = NULL;
865 p.setPen( Qt::black ); // ### hardcoded color
866 if (deco)
867 p.drawPixmap( (size-17)/2, (size-17)/2, *deco );
872 // ------------------------------------------------------------------------------------------
876 KeramikClient::KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory )
877 : KDecoration( bridge, factory ),
878 activeIcon( NULL ), inactiveIcon( NULL ), captionBufferDirty( true ), maskDirty( true )
882 void KeramikClient::init()
884 connect( this, SIGNAL( keepAboveChanged( bool )), SLOT( keepAboveChange( bool )));
885 connect( this, SIGNAL( keepBelowChanged( bool )), SLOT( keepBelowChange( bool )));
887 createMainWidget();
888 widget()->setAttribute( Qt::WA_StaticContents );
889 widget()->installEventFilter( this );
891 // Minimize flicker
892 widget()->setAttribute( Qt::WA_NoSystemBackground );
894 for ( int i=0; i < NumButtons; i++ )
895 button[i] = NULL;
897 createLayout();
900 void KeramikClient::createLayout()
903 QVBoxLayout *mainLayout = new QVBoxLayout( widget() );
904 QBoxLayout *titleLayout = new QBoxLayout( QBoxLayout::LeftToRight );
905 QHBoxLayout *windowLayout = new QHBoxLayout();
907 mainLayout->setMargin( 0 );
908 mainLayout->setSpacing( 0 );
910 titleLayout->setMargin( 0 );
911 titleLayout->setSpacing( 0 );
913 windowLayout->setMargin( 0 );
914 windowLayout->setSpacing( 0 );
916 largeTitlebar = ( !maximizedVertical() && clientHandler->largeCaptionBubbles() );
917 largeCaption = ( isActive() && largeTitlebar );
919 int grabBarHeight = clientHandler->grabBarHeight();
920 int topSpacing = ( largeTitlebar ? 4 : 1 );
921 int leftBorderWidth = clientHandler->tile( BorderLeft, true )->width();
922 int rightBorderWidth = clientHandler->tile( BorderRight, true )->width();
923 topSpacer = new QSpacerItem( 10, topSpacing,
924 QSizePolicy::Expanding, QSizePolicy::Minimum );
926 mainLayout->addItem( topSpacer );
928 mainLayout->addLayout( titleLayout ); // Titlebar
929 mainLayout->addLayout( windowLayout, 1 ); // Left border + window + right border
930 mainLayout->addSpacing( grabBarHeight ); // Bottom grab bar
932 titleLayout->setSpacing( buttonSpacing );
934 titleLayout->addSpacing( buttonMargin ); // Left button margin
935 addButtons( titleLayout, options()->customButtonPositions() ?
936 options()->titleButtonsLeft() : KDecorationOptions::defaultTitleButtonsLeft() );
938 titlebar = new QSpacerItem( 10, clientHandler->titleBarHeight(largeTitlebar)
939 - topSpacing, QSizePolicy::Expanding, QSizePolicy::Minimum );
940 titleLayout->addItem( titlebar );
942 titleLayout->addSpacing( buttonSpacing );
943 addButtons( titleLayout, options()->customButtonPositions() ?
944 options()->titleButtonsRight() : KDecorationOptions::defaultTitleButtonsRight() );
945 titleLayout->addSpacing( buttonMargin - 1 ); // Right button margin
947 windowLayout->addSpacing( leftBorderWidth ); // Left border
948 if( isPreview())
950 QWidget *previewWidget = new QLabel( i18n( "<center><b>Keramik preview</b></center>" ), widget() );
951 previewWidget->setAutoFillBackground( true );
952 windowLayout->addWidget( previewWidget );
954 else
955 windowLayout->addItem( new QSpacerItem( 0, 0 )); //no widget in the middle
956 windowLayout->addSpacing( rightBorderWidth ); // Right border
960 KeramikClient::~KeramikClient()
962 delete activeIcon;
963 delete inactiveIcon;
965 activeIcon = inactiveIcon = NULL;
969 void KeramikClient::reset( unsigned long )
971 if ( clientHandler->largeCaptionBubbles() && !largeTitlebar )
973 // We're switching from small caption bubbles to large
974 if ( !maximizedVertical() ) {
975 topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum );
976 largeTitlebar = true;
977 largeCaption = isActive();
979 widget()->layout()->activate();
981 // Compensate for the titlebar size change
983 // TODO This is wrong, this may break size increments (see bug #53784).
984 // FRAME
985 widget()->setGeometry( widget()->x(), widget()->y() - 3, width(), height() + 3 );
988 else if ( !clientHandler->largeCaptionBubbles() && largeTitlebar )
990 // We're switching from large caption bubbles to small
991 topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum );
992 largeTitlebar = largeCaption = false;
994 widget()->layout()->activate();
996 // Compensate for the titlebar size change
997 // FRAME
998 widget()->setGeometry( widget()->x(), widget()->y() + 3, width(), height() - 3 );
1001 calculateCaptionRect();
1003 captionBufferDirty = maskDirty = true;
1005 // Only repaint the window if it's visible
1006 // (i.e. not minimized and on the current desktop)
1007 if ( widget()->isVisible() ) {
1008 widget()->repaint();
1010 for ( int i = 0; i < NumButtons; i++ )
1011 if ( button[i] ) button[i]->repaint();
1016 void KeramikClient::addButtons( QBoxLayout *layout, const QString &s )
1018 for ( int i=0; i < s.length(); i++ )
1020 switch ( s[i].toLatin1() )
1022 // Menu button
1023 case 'M' :
1024 if ( !button[MenuButton] ) {
1025 button[MenuButton] = new KeramikButton( this, MenuButton, i18n("Menu"), Qt::LeftButton|Qt::RightButton );
1026 connect( button[MenuButton], SIGNAL( pressed() ), SLOT( menuButtonPressed() ) );
1027 layout->addWidget( button[MenuButton] );
1029 break;
1031 // OnAllDesktops button
1032 case 'S' :
1033 if ( !button[OnAllDesktopsButton] ) {
1034 button[OnAllDesktopsButton] = new KeramikButton( this,
1035 OnAllDesktopsButton, isOnAllDesktops()?i18n("Not on all desktops"):i18n("On all desktops") );
1036 if(isOnAllDesktops())
1037 button[OnAllDesktopsButton]->toggle();
1038 connect( button[OnAllDesktopsButton], SIGNAL( clicked() ), SLOT( toggleOnAllDesktops() ) );
1039 layout->addWidget( button[OnAllDesktopsButton] );
1041 break;
1043 // Help button
1044 case 'H' :
1045 if ( !button[HelpButton] && providesContextHelp() ) {
1046 button[HelpButton] = new KeramikButton( this, HelpButton, i18n("Help") );
1047 connect( button[HelpButton], SIGNAL( clicked() ), SLOT( showContextHelp() ) );
1048 layout->addWidget( button[HelpButton] );
1050 break;
1052 // Minimize button
1053 case 'I' :
1054 if ( !button[MinButton] && isMinimizable() ) {
1055 button[MinButton] = new KeramikButton( this, MinButton, i18n("Minimize") );
1056 connect( button[MinButton], SIGNAL( clicked() ), SLOT( minimize() ) );
1057 layout->addWidget( button[MinButton] );
1059 break;
1061 // Maximize button
1062 case 'A' :
1063 if ( !button[MaxButton] && isMaximizable() ) {
1064 button[MaxButton] = new KeramikButton( this, MaxButton, i18n("Maximize"), Qt::LeftButton|Qt::MidButton|Qt::RightButton );
1065 connect( button[MaxButton], SIGNAL( clicked() ), SLOT( slotMaximize() ) );
1066 layout->addWidget( button[MaxButton] );
1068 break;
1070 // Close button
1071 case 'X' :
1072 if ( !button[CloseButton] && isCloseable() ) {
1073 button[CloseButton] = new KeramikButton( this, CloseButton, i18n("Close") );
1074 connect( button[CloseButton], SIGNAL( clicked() ), SLOT( closeWindow() ) );
1075 layout->addWidget( button[CloseButton] );
1077 break;
1079 // Above button
1080 case 'F' :
1081 if ( !button[AboveButton]) {
1082 button[AboveButton] = new KeramikButton( this, AboveButton, i18n("Keep Above Others") );
1083 connect( button[AboveButton], SIGNAL( clicked() ), SLOT( slotAbove() ) );
1084 layout->addWidget( button[AboveButton] );
1086 break;
1088 // Below button
1089 case 'B' :
1090 if ( !button[BelowButton]) {
1091 button[BelowButton] = new KeramikButton( this, BelowButton, i18n("Keep Below Others") );
1092 connect( button[BelowButton], SIGNAL( clicked() ), SLOT( slotBelow() ) );
1093 layout->addWidget( button[BelowButton] );
1095 break;
1097 // Shade button
1098 case 'L' :
1099 if ( !button[ShadeButton] && isShadeable() ) {
1100 button[ShadeButton] = new KeramikButton( this, ShadeButton,
1101 isSetShade() ? i18n("Unshade") : i18n( "Shade" ));
1102 connect( button[ShadeButton], SIGNAL( clicked() ), SLOT( slotShade() ) );
1103 layout->addWidget( button[ShadeButton] );
1105 break;
1107 // Additional spacing
1108 case '_' :
1109 layout->addSpacing( buttonSpacing );
1110 break;
1116 void KeramikClient::updateMask()
1118 if ( !keramik_initialized )
1119 return;
1121 // To maximize performance this code uses precalculated bounding rects
1122 // to set the window mask. This saves us from having to allocate a 1bpp
1123 // pixmap, paint the mask on it and then have the X server iterate
1124 // over the pixels to compute the bounding rects from it.
1126 QRegion r;
1127 register int w, y = 0;
1128 int nrects;
1130 if ( QApplication::isRightToLeft() ) {
1132 // If the caption bubble is visible and extends above the titlebar
1133 if ( largeCaption && captionRect.width() >= 25 ) {
1134 register int x = captionRect.left();
1135 w = captionRect.width();
1136 r += QRegion( x + 11, y++, w - 19, 1 );
1137 r += QRegion( x + 9, y++, w - 15, 1 );
1138 r += QRegion( x + 7, y++, w - 12, 1 );
1139 } else {
1140 nrects = 8;
1142 // Do we have a large titlebar with a retracted caption bubble?
1143 // (i.e. the style is set to use large caption bubbles, we're
1144 // not maximized and not active)
1145 if ( largeTitlebar )
1146 y = 3;
1149 w = width(); // FRAME
1151 // The rounded titlebar corners
1152 r += QRegion( 9, y++, w - 17, 1 );
1153 r += QRegion( 7, y++, w - 13, 1 );
1154 r += QRegion( 5, y++, w - 9, 1 );
1155 r += QRegion( 4, y++, w - 7, 1 );
1156 r += QRegion( 3, y++, w - 5, 1 );
1157 r += QRegion( 2, y++, w - 4, 1 );
1158 r += QRegion( 1, y++, w - 2, 2 );
1159 } else {
1161 // If the caption bubble is visible and extends above the titlebar
1162 if ( largeCaption && captionRect.width() >= 25 ) {
1163 nrects = 11;
1164 register int x = captionRect.left();
1165 w = captionRect.width();
1166 r += QRegion( x + 8, y++, w - 19, 1 );
1167 r += QRegion( x + 6, y++, w - 15, 1 );
1168 r += QRegion( x + 5, y++, w - 12, 1 );
1169 } else {
1170 nrects = 8;
1172 // Do we have a large titlebar with a retracted caption bubble?
1173 // (i.e. the style is set to use large caption bubbles, we're
1174 // not maximized and not active)
1175 if ( largeTitlebar )
1176 y = 3;
1179 w = width(); // FRAME
1181 // The rounded titlebar corners
1182 r += QRegion( 8, y++, w - 17, 1 );
1183 r += QRegion( 6, y++, w - 13, 1 );
1184 r += QRegion( 4, y++, w - 9, 1 );
1185 r += QRegion( 3, y++, w - 7, 1 );
1186 r += QRegion( 2, y++, w - 5, 1 );
1187 r += QRegion( 2, y++, w - 4, 1 );
1188 r += QRegion( 1, y++, w - 2, 2 );
1191 y++;
1193 // The part of the window below the titlebar
1194 r += QRegion( 0, y, w, height() - y );
1196 setMask( r, YXBanded );
1198 maskDirty = false;
1202 void KeramikClient::updateCaptionBuffer()
1204 if ( !keramik_initialized )
1205 return;
1207 bool active = isActive();
1208 QPixmap *icon = NULL;
1210 if ( captionBuffer.size() != captionRect.size() )
1211 captionBuffer = QPixmap( captionRect.size() );
1213 if ( captionBuffer.isNull() )
1214 return;
1216 QPainter p( &captionBuffer );
1218 // Draw the caption bubble
1219 if ( active && largeCaption ) {
1220 p.drawPixmap( 0, 0, *clientHandler->tile( CaptionLargeLeft, true ) );
1221 p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(),
1222 *clientHandler->tile( CaptionLargeCenter, true ) );
1223 p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionLargeRight, true ) );
1224 } else {
1225 p.drawPixmap( 0, 0, *clientHandler->tile( CaptionSmallLeft, active ) );
1226 p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(),
1227 *clientHandler->tile( CaptionSmallCenter, active ) );
1228 p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionSmallRight, active ) );
1231 if ( clientHandler->showAppIcons() )
1233 QStyle *style = QApplication::style();
1234 if ( active ) {
1235 if ( ! activeIcon )
1236 activeIcon = new QPixmap( this->icon().pixmap( style->pixelMetric( QStyle::PM_SmallIconSize ), QIcon::Normal )); // FRAME
1237 icon = activeIcon;
1238 } else {
1239 if ( ! inactiveIcon ) {
1240 QImage img = this->icon().pixmap( style->pixelMetric( QStyle::PM_SmallIconSize ), QIcon::Normal ).toImage();
1241 KIconEffect::semiTransparent( img );
1242 inactiveIcon = new QPixmap( QPixmap::fromImage( img ) );
1244 icon = inactiveIcon;
1248 p.setFont( options()->font( active ) );
1249 int tw = p.fontMetrics().width( caption() ) +
1250 ( clientHandler->showAppIcons() ? 16 + iconSpacing : 0 );
1252 int xpos = qMax( (captionRect.width() - tw) / 3, 8 );
1253 QRect tr = QStyle::visualRect( QApplication::layoutDirection(), captionBuffer.rect(),
1254 QRect(xpos, 1, captionRect.width() - xpos - 10, captionRect.height() - 4) );
1256 //p.setPen( Qt::red ); // debug
1257 //p.drawRect( tr ); // debug
1259 // Application icon
1260 if ( clientHandler->showAppIcons() )
1262 QRect iconRect = QStyle::visualRect( QApplication::layoutDirection(), tr,
1263 QRect(tr.x(), 1 + (captionRect.height() - 4 - 16) / 2, 16, 16) );
1264 QRect r( icon->rect() );
1265 r.moveCenter( iconRect.center() );
1267 if ( tr.width() > 16 ) {
1268 p.drawPixmap( r, *icon );
1269 } else {
1270 QRect sr( 0, 0, icon->width(), icon->height() );
1272 if ( QApplication::isRightToLeft() )
1273 sr.adjust( icon->width() - tr.width(), 0, 0, 0 );
1274 else
1275 sr.adjust( 0, 0, -( icon->width() - tr.width() ), 0 );
1277 p.drawPixmap( r.x() + sr.x(), r.y() + sr.y(), *icon,
1278 sr.x(), sr.y(), sr.width(), sr.height() );
1281 //p.drawRect( r ); // debug
1283 if ( QApplication::isRightToLeft() )
1284 tr.adjust( 0, 0, -(16 + iconSpacing), 0 );
1285 else
1286 tr.adjust( (16 + iconSpacing), 0, 0, 0 );
1289 // Draw the titlebar text
1290 int flags = Qt::AlignVCenter | Qt::TextSingleLine;
1291 flags |= ( QApplication::isRightToLeft() ? Qt::AlignRight : Qt::AlignLeft );
1293 if ( clientHandler->useShadowedText() )
1295 p.translate( QApplication::isRightToLeft() ? -1 : 1, 1 );
1296 //p.setPen( options()->color(ColorTitleBar, active).dark() );
1297 if (qGray(options()->color(ColorFont, active).rgb()) < 100)
1298 p.setPen( QColor(200,200,200) );
1299 else
1300 p.setPen( Qt::black );
1301 p.drawText( tr, flags, caption() );
1302 p.translate( QApplication::isRightToLeft() ? 1 : -1, -1 );
1305 p.setPen( options()->color( ColorFont, active ) );
1306 p.drawText( tr, flags, caption() );
1308 captionBufferDirty = false;
1312 void KeramikClient::calculateCaptionRect()
1314 QFontMetrics fm( options()->font(isActive()) );
1315 int cw = fm.width( caption() ) + 95;
1316 int titleBaseY = ( largeTitlebar ? 3 : 0 );
1318 if ( clientHandler->showAppIcons() )
1319 cw += 16 + 4; // icon width + space
1321 cw = qMin( cw, titlebar->geometry().width() );
1322 captionRect = QStyle::visualRect( QApplication::layoutDirection(), titlebar->geometry(),
1323 QRect(titlebar->geometry().x(), (largeCaption ? 0 : titleBaseY),
1324 cw, clientHandler->titleBarHeight(largeCaption) ) );
1328 void KeramikClient::captionChange()
1330 QRect r( captionRect );
1331 calculateCaptionRect();
1333 if ( r.size() != captionRect.size() )
1334 maskDirty = true;
1336 captionBufferDirty = true;
1338 widget()->repaint( r | captionRect );
1342 void KeramikClient::iconChange()
1344 if ( clientHandler->showAppIcons() ) {
1346 // Force updateCaptionBuffer() to recreate the cached icons
1347 delete activeIcon;
1349 delete inactiveIcon;
1351 activeIcon = inactiveIcon = NULL;
1353 captionBufferDirty = true;
1354 widget()->repaint( captionRect );
1359 void KeramikClient::activeChange()
1361 bool active = isActive();
1362 // Note: It's assumed that the same font will always be used for both active
1363 // and inactive windows, since the fonts kcm hasn't supported setting
1364 // different fonts for different window states for some time.
1365 if ( largeTitlebar ) {
1366 largeCaption = ( active && !maximizedVertical() );
1367 calculateCaptionRect();
1368 maskDirty = true;
1371 captionBufferDirty = true;
1373 widget()->repaint();
1375 for ( int i=0; i < NumButtons; i++ )
1376 if ( button[i] ) button[i]->repaint();
1380 void KeramikClient::maximizeChange()
1382 if ( clientHandler->largeCaptionBubbles() )
1384 if ( maximizeMode() & MaximizeVertical ) {
1385 // We've been maximized - shrink the titlebar by 3 pixels
1386 topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum );
1387 largeCaption = largeTitlebar = false;
1389 calculateCaptionRect();
1390 captionBufferDirty = maskDirty = true;
1392 widget()->layout()->activate();
1393 widget()->repaint();
1394 } else if (( maximizeMode() & MaximizeVertical ) == 0 && !largeTitlebar ) {
1395 // We've been restored - enlarge the titlebar by 3 pixels
1396 topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum );
1397 largeCaption = largeTitlebar = true;
1399 calculateCaptionRect();
1400 captionBufferDirty = maskDirty = true;
1402 widget()->layout()->activate();
1403 widget()->repaint();
1407 if ( button[ MaxButton ] ) {
1408 button[ MaxButton ]->setToolTip( maximizeMode() == MaximizeFull ? i18n("Restore") : i18n("Maximize") );
1409 button[ MaxButton ]->repaint();
1414 void KeramikClient::desktopChange()
1416 if ( button[ OnAllDesktopsButton ] )
1418 button[ OnAllDesktopsButton ]->repaint();
1419 button[ OnAllDesktopsButton ]->setToolTip( isOnAllDesktops() ? i18n("Not on all desktops") : i18n("On all desktops") );
1424 void KeramikClient::shadeChange()
1426 if ( button[ ShadeButton ] )
1428 button[ ShadeButton ]->repaint();
1429 button[ ShadeButton ]->setToolTip( isSetShade() ? i18n("Unshade") : i18n("Shade") );
1434 void KeramikClient::keepAboveChange( bool )
1436 if ( button[ AboveButton ] )
1437 button[ AboveButton ]->repaint();
1441 void KeramikClient::keepBelowChange( bool )
1443 if ( button[ BelowButton ] )
1444 button[ BelowButton ]->repaint();
1448 void KeramikClient::menuButtonPressed()
1450 QPoint menuTop ( button[MenuButton]->rect().topLeft() );
1451 QPoint menuBottom ( button[MenuButton]->rect().bottomRight() );
1452 menuTop += QPoint(-6, -3);
1453 menuBottom += QPoint(6, 3);
1454 KDecorationFactory* f = factory();
1455 showWindowMenu( QRect( button[MenuButton]->mapToGlobal( menuTop ),
1456 button[MenuButton]->mapToGlobal( menuBottom )) );
1457 if( !f->exists( this )) // 'this' was destroyed
1458 return;
1459 button[MenuButton]->setDown(false);
1463 void KeramikClient::slotMaximize()
1465 maximize( button[ MaxButton ]->lastButton() );
1469 void KeramikClient::slotAbove()
1471 setKeepAbove( !keepAbove());
1472 button[ AboveButton ]->repaint();
1476 void KeramikClient::slotBelow()
1478 setKeepBelow( !keepBelow());
1479 button[ BelowButton ]->repaint();
1483 void KeramikClient::slotShade()
1485 setShade( !isSetShade());
1486 button[ ShadeButton ]->repaint();
1490 void KeramikClient::paintEvent( QPaintEvent *e )
1492 if ( !keramik_initialized )
1493 return;
1495 QPainter p( widget());
1496 QRect updateRect( e->rect() );
1497 bool active = isActive();
1499 int titleBaseY = ( largeTitlebar ? 3 : 0 );
1500 int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar );
1501 int grabBarHeight = clientHandler->grabBarHeight();
1502 int leftBorderWidth = clientHandler->tile( BorderLeft, active )->width();
1503 int rightBorderWidth = clientHandler->tile( BorderRight, active )->width();
1505 if ( maskDirty )
1506 updateMask();
1508 // Titlebar
1509 // -----------------------------------------------------------------------
1510 if ( updateRect.y() < titleBarHeight )
1512 int titleBarBaseHeight = titleBarHeight - titleBaseY;
1514 if ( captionBufferDirty )
1515 updateCaptionBuffer();
1517 // Top left corner
1518 if ( updateRect.x() < 15 )
1519 p.drawPixmap( 0, titleBaseY,
1520 *clientHandler->tile( TitleLeft, active ) );
1522 // Space between the top left corner and the caption bubble
1523 if ( updateRect.x() < captionRect.left() && updateRect.right() >= 15 ) {
1524 int x1 = qMax( 15, updateRect.x() );
1525 int x2 = qMin( captionRect.left(), updateRect.right() );
1527 p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight,
1528 *clientHandler->tile( TitleCenter, active ) );
1531 // Caption bubble
1532 if ( updateRect.x() <= captionRect.right() && updateRect.right() > 15 ) {
1533 if ( captionRect.width() >= 25 )
1534 p.drawPixmap( captionRect.left(), active ? 0 : titleBaseY, captionBuffer );
1535 else
1536 p.drawTiledPixmap( captionRect.x(), titleBaseY, captionRect.width(),
1537 titleBarBaseHeight, *clientHandler->tile( TitleCenter, active ) );
1540 // Space between the caption bubble and the top right corner
1541 if ( updateRect.right() > captionRect.right() && updateRect.x() < width() - 15 ) { // FRAME
1542 int x1 = qMax( captionRect.right() + 1, updateRect.x() );
1543 int x2 = qMin( width() - 15, updateRect.right() );
1545 p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight,
1546 *clientHandler->tile( TitleCenter, active ) );
1549 // Top right corner
1550 if ( updateRect.right() >= width() - 15 )
1551 p.drawPixmap( width() - 15, titleBaseY,
1552 *clientHandler->tile( TitleRight, active ) );
1555 // Borders
1556 // -----------------------------------------------------------------------
1557 if ( updateRect.bottom() >= titleBarHeight &&
1558 updateRect.top() < height() - grabBarHeight )
1560 int top = qMax( titleBarHeight, updateRect.top() );
1561 int bottom = qMin( updateRect.bottom(), height() - grabBarHeight );
1563 // Left border
1564 if ( updateRect.x() < leftBorderWidth )
1565 p.drawTiledPixmap( 0, top, leftBorderWidth, bottom - top + 1,
1566 *clientHandler->tile( BorderLeft, active ) );
1568 // Right border
1569 if ( e->rect().right() > width() - rightBorderWidth - 1 )
1570 p.drawTiledPixmap( width() - rightBorderWidth, top, rightBorderWidth,
1571 bottom - top + 1, *clientHandler->tile( BorderRight, active ) );
1574 // Bottom grab bar
1575 // -----------------------------------------------------------------------
1576 if ( updateRect.bottom() >= height() - grabBarHeight ) {
1577 // Bottom left corner
1578 if ( updateRect.x() < 9 )
1579 p.drawPixmap( 0, height() - grabBarHeight,
1580 *clientHandler->tile( GrabBarLeft, active ) );
1582 // Space between the left corner and the right corner
1583 if ( updateRect.x() < width() - 9 ) {
1584 int x1 = qMax( 9, updateRect.x() );
1585 int x2 = qMin( width() - 9, updateRect.right() );
1587 p.drawTiledPixmap( x1, height() - grabBarHeight, x2 - x1 + 1,
1588 grabBarHeight, *clientHandler->tile( GrabBarCenter, active ) );
1591 // Bottom right corner
1592 if ( updateRect.right() > width() - 9 )
1593 p.drawPixmap( width() - 9, height() - grabBarHeight,
1594 *clientHandler->tile( GrabBarRight, active ) );
1597 // Extra drawline for the 1 pixel empty space QLayout leaves when a window is shaded.
1598 p.setPen( options()->color( ColorTitleBlend, active ) );
1599 p.drawLine( leftBorderWidth, height() - grabBarHeight - 1,
1600 width() - rightBorderWidth - 1, height() - grabBarHeight - 1 );
1604 void KeramikClient::resizeEvent( QResizeEvent *e )
1606 // FRAME Client::resizeEvent( e );
1608 QRect r( captionRect );
1609 calculateCaptionRect();
1611 if ( r.size() != captionRect.size() )
1612 captionBufferDirty = true;
1614 maskDirty = true;
1616 if ( widget()->isVisible() )
1618 widget()->update( widget()->rect() );
1619 int dx = 0;
1620 int dy = 0;
1622 if ( e->oldSize().width() != width() )
1623 dx = 32 + qAbs( e->oldSize().width() - width() );
1625 if ( e->oldSize().height() != height() )
1626 dy = 8 + qAbs( e->oldSize().height() - height() );
1628 if ( dy )
1629 widget()->update( 0, height() - dy + 1, width(), dy );
1631 if ( dx )
1633 widget()->update( width() - dx + 1, 0, dx, height() );
1634 widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) );
1635 widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4,
1636 titlebar->geometry().bottom() ) ) );
1637 // Titlebar needs no paint event
1638 QApplication::postEvent( this, new QPaintEvent( titlebar->geometry() ) );
1644 void KeramikClient::mouseDoubleClickEvent( QMouseEvent *e )
1646 if ( e->button() == Qt::LeftButton
1647 && QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) )
1648 titlebarDblClickOperation();
1652 KeramikClient::Position KeramikClient::mousePosition( const QPoint &p ) const
1654 int titleBaseY = (largeTitlebar ? 3 : 0);
1656 int leftBorder = clientHandler->tile( BorderLeft, true )->width();
1657 int rightBorder = width() - clientHandler->tile( BorderRight, true )->width() - 1;
1658 int bottomBorder = height() - clientHandler->grabBarHeight() - 1;
1659 int bottomCornerSize = 3*clientHandler->tile( BorderRight, true )->width()/2 + 24;
1661 // Test if the mouse is over the titlebar area
1662 if ( p.y() < titleBaseY + 11 ) {
1663 // Test for the top left corner
1664 if ( p.x() < leftBorder + 11 ) {
1665 if ( (p.y() < titleBaseY + 3 && p.x() < leftBorder + 11) ||
1666 (p.y() < titleBaseY + 6 && p.x() < leftBorder + 6) ||
1667 (p.y() < titleBaseY + 11 && p.x() < leftBorder + 3) )
1668 return PositionTopLeft;
1671 // Test for the top right corner
1672 if ( p.x() > rightBorder - 11 ) {
1673 if ( (p.y() < titleBaseY + 3 && p.x() > rightBorder - 11) ||
1674 (p.y() < titleBaseY + 6 && p.x() > rightBorder - 6) ||
1675 (p.y() < titleBaseY + 11 && p.x() > rightBorder - 3) )
1676 return PositionTopRight;
1679 // Test for the top border
1680 if ( p.y() <= 3 || (p.y() <= titleBaseY+3 &&
1681 (p.x() < captionRect.left() || p.x() > captionRect.right()) ) )
1682 return PositionTop;
1684 // The cursor must be over the center of the titlebar.
1685 return PositionCenter;
1688 // Test the sides
1689 else if ( p.y() < bottomBorder ) {
1690 // Test for the left side
1691 if ( p.x() < leftBorder ) {
1692 if ( p.y() < height() - bottomCornerSize )
1693 return PositionLeft;
1694 else
1695 return PositionBottomLeft;
1698 // Test for the right side
1699 else if ( p.x() > rightBorder ) {
1700 if ( p.y() < height() - bottomCornerSize )
1701 return PositionRight;
1702 else
1703 return PositionBottomRight;
1706 // The cursor must be over the center of the window
1707 return PositionCenter;
1710 // Test the grab bar / bottom border
1711 else {
1712 // Test for the bottom left corner
1713 if ( p.x() < bottomCornerSize )
1714 return PositionBottomLeft;
1716 // Test for the bottom right corner
1717 else if ( p.x() > width() - bottomCornerSize - 1 )
1718 return PositionBottomRight;
1720 // The cursor must be over the bottom border
1721 return PositionBottom;
1724 // We should never get here
1725 return PositionCenter;
1729 void KeramikClient::resize( const QSize& s )
1731 widget()->resize( s );
1735 void KeramikClient::borders( int& left, int& right, int& top, int& bottom ) const
1737 int titleBarHeight = clientHandler->titleBarHeight( clientHandler->largeCaptionBubbles() );
1738 int grabBarHeight = clientHandler->grabBarHeight();
1739 int leftBorderWidth = clientHandler->tile( BorderLeft, isActive() )->width();
1740 int rightBorderWidth = clientHandler->tile( BorderRight, isActive() )->width();
1742 left = leftBorderWidth;
1743 right = rightBorderWidth;
1744 top = titleBarHeight;
1745 bottom = grabBarHeight;
1747 if ( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows())
1748 left = right = 0;
1749 if( maximizeMode() & MaximizeVertical)
1751 top = clientHandler->titleBarHeight( false );
1752 if( !options()->moveResizeMaximizedWindows())
1753 bottom = 0;
1758 QSize KeramikClient::minimumSize() const
1760 return widget()->minimumSize();
1764 bool KeramikClient::eventFilter( QObject* o, QEvent* e )
1766 if ( o != widget() )
1767 return false;
1769 switch ( e->type() )
1771 case QEvent::Resize:
1772 resizeEvent( static_cast< QResizeEvent* >( e ) );
1773 return true;
1775 case QEvent::Paint:
1776 paintEvent( static_cast< QPaintEvent* >( e ) );
1777 return true;
1779 case QEvent::MouseButtonDblClick:
1780 mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) );
1781 return true;
1783 case QEvent::MouseButtonPress:
1784 processMousePressEvent( static_cast< QMouseEvent* >( e ) );
1785 return true;
1787 default:
1788 return false;
1792 } // namespace Keramik
1796 // -------------------------------------------------------------------------------------------
1800 extern "C"
1802 KDE_EXPORT KDecorationFactory *create_factory()
1804 Keramik::clientHandler = new Keramik::KeramikHandler();
1805 return Keramik::clientHandler;
1811 // vim: set noet ts=4 sw=4: