kwin Library API Documentation

keramik.cpp

00001 /* 00002 * $Id: keramik.cpp,v 1.31 2004/01/09 18:20:29 giessl Exp $ 00003 * 00004 * Keramik KWin client (version 0.8) 00005 * 00006 * Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org> 00007 * 00008 * This program is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the license, or 00011 * (at your option) any later version. 00012 * 00013 * This program is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with this program; see the file COPYING. If not, write to 00020 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00021 * Boston, MA 02111-1307, USA. 00022 */ 00023 00024 #include <kconfig.h> 00025 #include <klocale.h> 00026 #include <kiconeffect.h> 00027 00028 #include <qpainter.h> 00029 #include <qlayout.h> 00030 #include <qbitmap.h> 00031 #include <qstyle.h> 00032 #include <qtooltip.h> 00033 #include <qwidget.h> 00034 #include <qlabel.h> 00035 00036 #include <X11/Xlib.h> 00037 00038 #include "keramik.h" 00039 #include "keramik.moc" 00040 00041 00042 00043 // ------------------------------------------------------------------------------------------- 00044 00045 00046 00047 namespace Keramik 00048 { 00049 00050 const int buttonMargin = 9; // Margin between the window edge and the buttons 00051 const int buttonSpacing = 4; // Spacing between the titlebar buttons 00052 const int iconSpacing = 5; // Spacing between the icon and the text label 00053 00054 // Default button layout 00055 const char default_left[] = "M"; 00056 const char default_right[] = "HIAX"; 00057 00058 // Titlebar button bitmaps 00059 const unsigned char menu_bits[] = { 00060 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00061 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0xf0, 0x07, 0x00, 00062 0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 00063 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00064 0x00, 0x00, 0x00}; 00065 00066 const unsigned char on_all_desktops_bits[] = { 00067 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00068 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00, 00069 0xf0, 0x0f, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 00070 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00071 0x00, 0x00, 0x00}; 00072 00073 const unsigned char not_on_all_desktops_bits[] = { 00074 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00075 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 00076 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00077 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00078 0x00, 0x00, 0x00}; 00079 00080 const unsigned char help_bits[] = { 00081 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 00082 0xf0, 0x07, 0x00, 0x30, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00, 00083 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 00084 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00085 0x00, 0x00, 0x00}; 00086 00087 const unsigned char minimize_bits[] = { 00088 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00089 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00090 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 00091 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00092 0x00, 0x00, 0x00}; 00093 00094 const unsigned char maximize_bits[] = { 00095 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00096 0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0xf0, 0x0f, 0x00, 00097 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 00098 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00099 0x00, 0x00, 0x00}; 00100 00101 const unsigned char restore_bits[] = { 00102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00103 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 00104 0xf0, 0x0f, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00, 00105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00106 0x00, 0x00, 0x00}; 00107 00108 const unsigned char close_bits[] = { 00109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00110 0x30, 0x0c, 0x00, 0x70, 0x0e, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 00111 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0x70, 0x0e, 0x00, 0x30, 0x0c, 0x00, 00112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00113 0x00, 0x00, 0x00}; 00114 00115 00116 KeramikHandler *clientHandler = NULL; 00117 bool keramik_initialized = false; 00118 00119 00120 00121 // ------------------------------------------------------------------------------------------- 00122 00123 00124 00125 KeramikHandler::KeramikHandler() 00126 { 00127 for ( int i = 0; i < NumTiles; i++ ) { 00128 activeTiles[i] = NULL; 00129 inactiveTiles[i] = NULL; 00130 } 00131 00132 settings_cache = NULL; 00133 00134 imageDb = KeramikImageDb::instance(); 00135 00136 // Create the button deco bitmaps 00137 buttonDecos[ Menu ] = new QBitmap( 17, 17, menu_bits, true ); 00138 buttonDecos[ OnAllDesktops ] = new QBitmap( 17, 17, on_all_desktops_bits, true ); 00139 buttonDecos[ NotOnAllDesktops ] = new QBitmap( 17, 17, not_on_all_desktops_bits, true ); 00140 buttonDecos[ Help ] = new QBitmap( 17, 17, help_bits, true ); 00141 buttonDecos[ Minimize ] = new QBitmap( 17, 17, minimize_bits, true ); 00142 buttonDecos[ Maximize ] = new QBitmap( 17, 17, maximize_bits, true ); 00143 buttonDecos[ Restore ] = new QBitmap( 17, 17, restore_bits, true ); 00144 buttonDecos[ Close ] = new QBitmap( 17, 17, close_bits, true ); 00145 00146 // Selfmask the bitmaps 00147 for ( int i = 0; i < NumButtonDecos; i++ ) 00148 buttonDecos[i]->setMask( *buttonDecos[i] ); 00149 00150 // Flip the bitmaps horizontally in right-to-left mode 00151 if ( QApplication::reverseLayout() ) { 00152 for ( int i = 0; i < Help; i++ ) 00153 flip( reinterpret_cast<QPixmap**>(buttonDecos)[i] ); 00154 00155 for ( int i = Help + 1; i < NumButtonDecos; i++ ) 00156 flip( reinterpret_cast<QPixmap**>(buttonDecos)[i] ); 00157 } 00158 00159 readConfig(); 00160 createPixmaps(); 00161 00162 keramik_initialized = true; 00163 } 00164 00165 00166 KeramikHandler::~KeramikHandler() 00167 { 00168 keramik_initialized = false; 00169 destroyPixmaps(); 00170 00171 for ( int i = 0; i < NumButtonDecos; i++ ) 00172 delete buttonDecos[i]; 00173 00174 delete settings_cache; 00175 00176 KeramikImageDb::release(); 00177 imageDb = NULL; 00178 clientHandler = NULL; 00179 } 00180 00181 00182 void KeramikHandler::createPixmaps() 00183 { 00184 int heightOffset; 00185 int widthOffset; 00186 switch(options()->preferredBorderSize(this)) { 00187 case BorderLarge: 00188 widthOffset = 4; 00189 heightOffset = 0; 00190 break; 00191 case BorderVeryLarge: 00192 widthOffset = 8; 00193 heightOffset = 0; 00194 break; 00195 case BorderHuge: 00196 widthOffset = 14; 00197 heightOffset = 0; 00198 break; 00199 case BorderVeryHuge: 00200 widthOffset = 23; 00201 heightOffset = 10; 00202 break; 00203 case BorderOversized: 00204 widthOffset = 36; 00205 heightOffset = 25; 00206 break; 00207 case BorderTiny: 00208 case BorderNormal: 00209 default: 00210 widthOffset = 0; 00211 heightOffset = 0; 00212 } 00213 int fontHeight = QFontMetrics(options()->font(true)).height(); 00214 if (fontHeight > heightOffset + 20) 00215 heightOffset = fontHeight - 20; 00216 00217 QString size = (heightOffset < 8) ? "" : (heightOffset < 20) ? "-large" : "-huge"; 00218 00219 QColor titleColor, captionColor, buttonColor; 00220 QImage *titleCenter = NULL, *captionLeft = NULL, 00221 *captionRight = NULL, *captionCenter = NULL; 00222 00223 00224 // Active tiles 00225 // ------------------------------------------------------------------------- 00226 captionColor = KDecoration::options()->color( ColorTitleBar, true ); 00227 titleColor = KDecoration::options()->color( ColorTitleBlend, true ); 00228 00229 // Load the titlebar corners. 00230 activeTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); 00231 activeTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); 00232 00233 // Load the titlebar center tile image (this will be used as 00234 // the background for the caption bubble tiles). 00235 titleCenter = loadImage( "titlebar-center", titleColor ); 00236 00237 // Load the small version of the caption bubble corner & center images. 00238 captionLeft = loadImage( "caption-small-left", captionColor ); 00239 captionRight = loadImage( "caption-small-right", captionColor ); 00240 captionCenter = loadImage( "caption-small-center", captionColor ); 00241 00242 // Create the caption bubble tiles (by blending the images onto the titlebar) 00243 activeTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); 00244 activeTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); 00245 activeTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); 00246 00247 delete captionLeft; 00248 delete captionRight; 00249 delete captionCenter; 00250 00251 // Now do the same with the large version 00252 captionLeft = loadImage( "caption-large-left", captionColor ); 00253 captionRight = loadImage( "caption-large-right", captionColor ); 00254 captionCenter = loadImage( "caption-large-center", captionColor ); 00255 00256 activeTiles[ CaptionLargeLeft ] = composite( captionLeft, titleCenter ); 00257 activeTiles[ CaptionLargeRight ] = composite( captionRight, titleCenter ); 00258 activeTiles[ CaptionLargeCenter ] = composite( captionCenter, titleCenter ); 00259 00260 delete captionLeft; 00261 delete captionRight; 00262 delete captionCenter; 00263 00264 // Create the titlebar center tile 00265 activeTiles[ TitleCenter ] = new QPixmap( *titleCenter ); 00266 00267 delete titleCenter; 00268 00269 // Load the left & right border pixmaps 00270 activeTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); 00271 activeTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); 00272 00273 // Load the bottom grabbar pixmaps 00274 if ( largeGrabBars ) { 00275 activeTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); 00276 activeTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); 00277 activeTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); 00278 } else { 00279 activeTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); 00280 activeTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); 00281 activeTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); 00282 } 00283 00284 // Inactive tiles 00285 // ------------------------------------------------------------------------- 00286 captionColor = KDecoration::options()->color( ColorTitleBar, false ); 00287 titleColor = KDecoration::options()->color( ColorTitleBlend, false ); 00288 00289 inactiveTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor ); 00290 inactiveTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor ); 00291 00292 titleCenter = loadImage( "titlebar-center", titleColor ); 00293 00294 captionLeft = loadImage( "caption-small-left", captionColor ); 00295 captionRight = loadImage( "caption-small-right", captionColor ); 00296 captionCenter = loadImage( "caption-small-center", captionColor ); 00297 00298 inactiveTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter ); 00299 inactiveTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter ); 00300 inactiveTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter ); 00301 00302 delete captionLeft; 00303 delete captionRight; 00304 delete captionCenter; 00305 00306 inactiveTiles[ TitleCenter ] = new QPixmap( *titleCenter ); 00307 00308 delete titleCenter; 00309 00310 inactiveTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor ); 00311 inactiveTiles[ BorderRight ] = loadPixmap( "border-right", titleColor ); 00312 00313 if ( largeGrabBars ) { 00314 inactiveTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor ); 00315 inactiveTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor ); 00316 inactiveTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor ); 00317 } else { 00318 inactiveTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor ); 00319 inactiveTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor ); 00320 inactiveTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor ); 00321 } 00322 00323 // Buttons 00324 // ------------------------------------------------------------------------- 00325 buttonColor = QColor(); //KDecoration::options()->color( ButtonBg, true ); 00326 00327 titleButtonRound = loadPixmap( "titlebutton-round"+size, buttonColor ); 00328 titleButtonSquare = loadPixmap( "titlebutton-square"+size, buttonColor ); 00329 00330 00331 // Prepare the tiles for use 00332 // ------------------------------------------------------------------------- 00333 if ( QApplication::reverseLayout() ) { 00334 00335 // Fix lighting 00336 flip( activeTiles[CaptionSmallLeft], activeTiles[CaptionSmallRight] ); 00337 flip( inactiveTiles[CaptionSmallLeft], inactiveTiles[CaptionSmallRight] ); 00338 00339 flip( activeTiles[CaptionLargeLeft], activeTiles[CaptionLargeRight] ); 00340 00341 flip( activeTiles[TitleLeft], activeTiles[TitleRight] ); 00342 flip( inactiveTiles[TitleLeft], inactiveTiles[TitleRight] ); 00343 00344 flip( activeTiles[BorderLeft], activeTiles[BorderRight] ); 00345 flip( inactiveTiles[BorderLeft], inactiveTiles[BorderRight] ); 00346 00347 flip( activeTiles[GrabBarLeft], activeTiles[GrabBarRight] ); 00348 flip( inactiveTiles[GrabBarLeft], inactiveTiles[GrabBarRight] ); 00349 00350 flip( titleButtonRound ); 00351 flip( titleButtonSquare ); 00352 } 00353 00354 // Pretile the center & border tiles for optimal performance 00355 pretile( activeTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); 00356 pretile( activeTiles[ CaptionLargeCenter ], 64, Qt::Horizontal ); 00357 pretile( activeTiles[ TitleCenter ], 64, Qt::Horizontal ); 00358 pretile( activeTiles[ GrabBarCenter ], 128, Qt::Horizontal ); 00359 pretile( activeTiles[ BorderLeft ], 128, Qt::Vertical ); 00360 pretile( activeTiles[ BorderRight ], 128, Qt::Vertical ); 00361 00362 pretile( inactiveTiles[ CaptionSmallCenter ], 64, Qt::Horizontal ); 00363 pretile( inactiveTiles[ TitleCenter ], 64, Qt::Horizontal ); 00364 pretile( inactiveTiles[ GrabBarCenter ], 128, Qt::Horizontal ); 00365 pretile( inactiveTiles[ BorderLeft ], 128, Qt::Vertical ); 00366 pretile( inactiveTiles[ BorderRight ], 128, Qt::Vertical ); 00367 00368 if (heightOffset > 0) { 00369 addHeight (heightOffset, activeTiles[TitleLeft]); 00370 addHeight (heightOffset, activeTiles[TitleCenter]); 00371 addHeight (heightOffset, activeTiles[TitleRight]); 00372 addHeight (heightOffset, activeTiles[CaptionSmallLeft]); 00373 addHeight (heightOffset, activeTiles[CaptionSmallCenter]); 00374 addHeight (heightOffset, activeTiles[CaptionSmallRight]); 00375 addHeight (heightOffset, activeTiles[CaptionLargeLeft]); 00376 addHeight (heightOffset, activeTiles[CaptionLargeCenter]); 00377 addHeight (heightOffset, activeTiles[CaptionLargeRight]); 00378 00379 addHeight (heightOffset, inactiveTiles[TitleLeft]); 00380 addHeight (heightOffset, inactiveTiles[TitleCenter]); 00381 addHeight (heightOffset, inactiveTiles[TitleRight]); 00382 addHeight (heightOffset, inactiveTiles[CaptionSmallLeft]); 00383 addHeight (heightOffset, inactiveTiles[CaptionSmallCenter]); 00384 addHeight (heightOffset, inactiveTiles[CaptionSmallRight]); 00385 } 00386 00387 if (widthOffset > 0) { 00388 addWidth (widthOffset, activeTiles[BorderLeft], true, activeTiles[GrabBarCenter]); 00389 addWidth (widthOffset, activeTiles[BorderRight], false, activeTiles[GrabBarCenter]); 00390 addWidth (widthOffset, inactiveTiles[BorderLeft], true, inactiveTiles[GrabBarCenter]); 00391 addWidth (widthOffset, inactiveTiles[BorderRight], false, inactiveTiles[GrabBarCenter]); 00392 00393 if (largeGrabBars) 00394 widthOffset = widthOffset*3/2; 00395 00396 addHeight (widthOffset, activeTiles[GrabBarLeft]); 00397 addHeight (widthOffset, activeTiles[GrabBarCenter]); 00398 addHeight (widthOffset, activeTiles[GrabBarRight]); 00399 addHeight (widthOffset, inactiveTiles[GrabBarLeft]); 00400 addHeight (widthOffset, inactiveTiles[GrabBarCenter]); 00401 addHeight (widthOffset, inactiveTiles[GrabBarRight]); 00402 } 00403 } 00404 00405 00406 00407 void KeramikHandler::destroyPixmaps() 00408 { 00409 for ( int i = 0; i < NumTiles; i++ ) { 00410 delete activeTiles[i]; 00411 delete inactiveTiles[i]; 00412 activeTiles[i] = NULL; 00413 inactiveTiles[i] = NULL; 00414 } 00415 00416 delete titleButtonRound; 00417 delete titleButtonSquare; 00418 } 00419 00420 00421 void KeramikHandler::addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix) { 00422 int w = pix->width()+width; 00423 int h = pix->height(); 00424 00425 QPixmap *tmp = new QPixmap (w, h); 00426 tmp->fill (); 00427 QPainter p; 00428 p.begin (tmp); 00429 00430 for (int i = 0; i < h; i++) 00431 p.drawPixmap (0, i, *bottomPix, i%2, 0, w,1); 00432 00433 if (left) 00434 p.drawPixmap(0, 0, *pix); 00435 else 00436 p.drawPixmap(width, 0, *pix); 00437 00438 p.end(); 00439 00440 delete pix; 00441 pix = tmp; 00442 } 00443 00444 00445 void KeramikHandler::addHeight (int height, QPixmap *&pix) { 00446 int w = pix->width(); 00447 int h = pix->height()+height; 00448 00449 QPixmap *tmp = new QPixmap (w, h); 00450 QPainter p; 00451 p.begin (tmp); 00452 if (pix->height() > 10) { 00453 p.drawPixmap(0, 0, *pix, 0, 0, w, 11); 00454 for (int i = 0; i < height; i+=2) 00455 p.drawPixmap(0, 11+i, *pix, 0, 11, w, 2); 00456 p.drawPixmap(0, 11+height, *pix, 0, 11, w, -1); 00457 } 00458 else { 00459 int lines = h-3; 00460 int factor = pix->height()-3; 00461 for (int i = 0; i < lines; i++) 00462 p.drawPixmap(0, i, *pix, 0, i*factor/lines, w, 1); 00463 p.drawPixmap(0, lines, *pix, 0, factor, w, 3); 00464 } 00465 p.end(); 00466 00467 delete pix; 00468 pix = tmp; 00469 } 00470 00471 00472 void KeramikHandler::flip( QPixmap *&pix1, QPixmap *&pix2 ) 00473 { 00474 // Flip the pixmaps horizontally 00475 QPixmap *tmp = new QPixmap( pix1->xForm( QWMatrix(-1,0,0,1,pix1->width(),0) ) ); 00476 00477 delete pix1; 00478 pix1 = new QPixmap( pix2->xForm( QWMatrix(-1,0,0,1,pix2->width(),0) ) ); 00479 00480 delete pix2; 00481 pix2 = tmp; 00482 } 00483 00484 00485 void KeramikHandler::flip( QPixmap *&pix ) 00486 { 00487 // Flip the pixmap horizontally 00488 QPixmap *tmp = new QPixmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) ); 00489 delete pix; 00490 pix = tmp; 00491 } 00492 00493 00494 void KeramikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir ) 00495 { 00496 QPixmap *newpix; 00497 QPainter p; 00498 00499 if ( dir == Qt::Horizontal ) 00500 newpix = new QPixmap( size, pix->height() ); 00501 else 00502 newpix = new QPixmap( pix->width(), size ); 00503 00504 p.begin( newpix ); 00505 p.drawTiledPixmap( newpix->rect(), *pix ) ; 00506 p.end(); 00507 00508 delete pix; 00509 pix = newpix; 00510 } 00511 00512 00513 void KeramikHandler::readConfig() 00514 { 00515 KConfig *c = new KConfig( "kwinkeramikrc" ); 00516 00517 c->setGroup( "General" ); 00518 showIcons = c->readBoolEntry( "ShowAppIcons", true ); 00519 shadowedText = c->readBoolEntry( "UseShadowedText", true ); 00520 smallCaptionBubbles = c->readBoolEntry( "SmallCaptionBubbles", false ); 00521 largeGrabBars = c->readBoolEntry( "LargeGrabBars", true ); 00522 00523 if ( ! settings_cache ) { 00524 settings_cache = new SettingsCache; 00525 settings_cache->largeGrabBars = largeGrabBars; 00526 settings_cache->smallCaptionBubbles = smallCaptionBubbles; 00527 } 00528 00529 delete c; 00530 } 00531 00532 00533 QPixmap *KeramikHandler::composite( QImage *over, QImage *under ) 00534 { 00535 QImage dest( over->width(), over->height(), 32 ); 00536 int width = over->width(), height = over->height(); 00537 00538 // Clear the destination image 00539 Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dest.bits() ); 00540 for (int i = 0; i < width * height; i++) 00541 *(data++) = 0; 00542 00543 // Copy the under image (bottom aligned) to the destination image 00544 for (int y1 = height - under->height(), y2 = 0; y1 < height; y1++, y2++ ) 00545 { 00546 register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.scanLine(y1) ); 00547 register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( under->scanLine(y2) ); 00548 00549 for ( int x = 0; x < width; x++ ) 00550 *(dst++) = *(src++); 00551 } 00552 00553 // Blend the over image onto the destination 00554 register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.bits() ); 00555 register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( over->bits() ); 00556 for ( int i = 0; i < width * height; i++ ) 00557 { 00558 int r1 = qRed( *dst ), g1 = qGreen( *dst ), b1 = qBlue( *dst ); 00559 int r2 = qRed( *src ), g2 = qGreen( *src ), b2 = qBlue( *src ); 00560 int a = qAlpha( *src ); 00561 00562 if ( a == 0xff ) 00563 *dst = *src; 00564 00565 else if ( a != 0x00 ) 00566 *dst = qRgba( Q_UINT8( r1 + (((r2 - r1) * a) >> 8) ), 00567 Q_UINT8( g1 + (((g2 - g1) * a) >> 8) ), 00568 Q_UINT8( b1 + (((b2 - b1) * a) >> 8) ), 00569 0xff ); 00570 00571 else if ( qAlpha(*dst) == 0x00 ) 00572 *dst = 0; 00573 00574 src++; dst++; 00575 } 00576 00577 // Create the final pixmap and return it 00578 return new QPixmap( dest ); 00579 } 00580 00581 00582 QImage *KeramikHandler::loadImage( const QString &name, const QColor &col ) 00583 { 00584 if ( col.isValid() ) { 00585 QImage *img = new QImage( imageDb->image(name)->copy() ); 00586 KIconEffect::colorize( *img, col, 1.0 ); 00587 return img; 00588 } else 00589 return new QImage( imageDb->image(name)->copy() ); 00590 } 00591 00592 00593 QPixmap *KeramikHandler::loadPixmap( const QString &name, const QColor &col ) 00594 { 00595 QImage *img = loadImage( name, col ); 00596 QPixmap *pix = new QPixmap( *img ); 00597 delete img; 00598 00599 return pix; 00600 } 00601 00602 00603 bool KeramikHandler::reset( unsigned long changed ) 00604 { 00605 keramik_initialized = false; 00606 00607 bool needHardReset = false; 00608 bool pixmapsInvalid = false; 00609 00610 // Re-read the config file 00611 readConfig(); 00612 00613 if ( changed & SettingBorder ) 00614 { 00615 pixmapsInvalid = true; 00616 needHardReset = true; 00617 } 00618 if ( changed & SettingFont ) 00619 { 00620 pixmapsInvalid = true; 00621 needHardReset = true; 00622 } 00623 // Check if the color scheme has changed 00624 if ( changed & SettingColors ) 00625 { 00626 pixmapsInvalid = true; 00627 } 00628 // Check if button positions have changed 00629 00630 if ( changed & SettingButtons ) { 00631 needHardReset = true; 00632 } 00633 00634 // Check if tooltips options have changed 00635 if ( changed & SettingTooltips ) { 00636 needHardReset = true; 00637 } 00638 00639 if ( (settings_cache->largeGrabBars != largeGrabBars) ) { 00640 pixmapsInvalid = true; 00641 needHardReset = true; 00642 } 00643 00644 if ( (settings_cache->smallCaptionBubbles != smallCaptionBubbles) ) { 00645 needHardReset = true; 00646 } 00647 00648 // Update our config cache 00649 settings_cache->largeGrabBars = largeGrabBars; 00650 settings_cache->smallCaptionBubbles = smallCaptionBubbles; 00651 00652 // Do we need to recreate the pixmaps? 00653 if ( pixmapsInvalid ) { 00654 destroyPixmaps(); 00655 createPixmaps(); 00656 } 00657 00658 keramik_initialized = true; 00659 00660 // Do we need to "hit the wooden hammer" ? 00661 if ( !needHardReset ) 00662 resetDecorations( changed ); 00663 return needHardReset; 00664 } 00665 00666 00667 const QPixmap *KeramikHandler::tile( TilePixmap tilePix, bool active ) const 00668 { 00669 return ( active ? activeTiles[ tilePix ] : inactiveTiles[ tilePix ] ); 00670 } 00671 00672 KDecoration* KeramikHandler::createDecoration( KDecorationBridge* bridge ) 00673 { 00674 return new KeramikClient( bridge, this ); 00675 } 00676 00677 QValueList< KeramikHandler::BorderSize > KeramikHandler::borderSizes() const 00678 { // the list must be sorted 00679 return QValueList< BorderSize >() << BorderNormal << BorderLarge << 00680 BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized; 00681 } 00682 00683 00684 // ------------------------------------------------------------------------------------------- 00685 00686 00687 00688 KeramikButton::KeramikButton( KeramikClient* c, const char *name, Button btn, const QString &tip, const int realizeBtns ) 00689 : QButton( c->widget(), name ), 00690 client( c ), button( btn ), hover( false ), lastbutton( 0 ) 00691 { 00692 realizeButtons = realizeBtns; 00693 00694 QToolTip::add( this, tip ); // FRAME 00695 setBackgroundMode( NoBackground ); 00696 setCursor( arrowCursor ); 00697 int size = clientHandler->roundButton()->height(); 00698 setFixedSize( size, size ); 00699 00700 setToggleButton( (button == OnAllDesktopsButton) ); 00701 } 00702 00703 00704 KeramikButton::~KeramikButton() 00705 { 00706 // Empty. 00707 } 00708 00709 00710 void KeramikButton::enterEvent( QEvent *e ) 00711 { 00712 QButton::enterEvent( e ); 00713 00714 hover = true; 00715 repaint( false ); 00716 } 00717 00718 00719 void KeramikButton::leaveEvent( QEvent *e ) 00720 { 00721 QButton::leaveEvent( e ); 00722 00723 hover = false; 00724 repaint( false ); 00725 } 00726 00727 00728 void KeramikButton::mousePressEvent( QMouseEvent *e ) 00729 { 00730 lastbutton = e->button(); 00731 QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); 00732 QButton::mousePressEvent( &me ); 00733 } 00734 00735 00736 void KeramikButton::mouseReleaseEvent( QMouseEvent *e ) 00737 { 00738 lastbutton = e->button(); 00739 QMouseEvent me( e->type(), e->pos(), e->globalPos(), (e->button()&realizeButtons)?LeftButton:NoButton, e->state() ); 00740 QButton::mouseReleaseEvent( &me ); 00741 } 00742 00743 00744 void KeramikButton::drawButton( QPainter *p ) 00745 { 00746 const QPixmap *pix; 00747 const QBitmap *deco; 00748 int size = clientHandler->roundButton()->height(); 00749 00750 // Get the bevel from the client handler 00751 if ( button == MenuButton || button == OnAllDesktopsButton || button == HelpButton ) 00752 pix = clientHandler->roundButton(); 00753 else 00754 pix = clientHandler->squareButton(); 00755 00756 // Draw the button background 00757 const QPixmap *background = clientHandler->tile( TitleCenter, client->isActive() ); 00758 p->drawPixmap( 0, 0, *background, 00759 0, (background->height()-size+1)/2, size, size ); 00760 00761 if ( isDown() ) { 00762 // Pressed 00763 p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(2*size, 0, size, size), pix->rect() ) ); 00764 p->translate( QApplication::reverseLayout() ? -1 : 1, 1 ); 00765 } else if ( hover ) 00766 // Mouse over 00767 p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(size, 0, size, size), pix->rect() ) ); 00768 else 00769 // Normal 00770 p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(0, 0, size, size), pix->rect() ) ); 00771 00772 00773 // Draw the button deco on the bevel 00774 switch ( button ) { 00775 case MenuButton: 00776 deco = clientHandler->buttonDeco( Menu ); 00777 break; 00778 00779 case OnAllDesktopsButton: 00780 deco = clientHandler->buttonDeco( isOn() ? NotOnAllDesktops : OnAllDesktops ); 00781 break; 00782 00783 case HelpButton: 00784 deco = clientHandler->buttonDeco( Help ); 00785 // The '?' won't be flipped around in the ctor, so we need to 00786 // shift it to the right to compensate for the button shadow 00787 // being on the left side of the button in RTL mode. 00788 if ( QApplication::reverseLayout() ) 00789 p->translate( 2, 0 ); 00790 break; 00791 00792 case MinButton: 00793 deco = clientHandler->buttonDeco( Minimize ); 00794 break; 00795 00796 case MaxButton: 00797 deco = clientHandler->buttonDeco( client->maximizeMode() == KeramikClient::MaximizeFull ? Restore : Maximize ); 00798 break; 00799 00800 case CloseButton: 00801 deco = clientHandler->buttonDeco( Close ); 00802 break; 00803 00804 default: 00805 deco = NULL; 00806 } 00807 00808 p->setPen( Qt::black ); // ### hardcoded color 00809 p->drawPixmap( (size-17)/2, (size-17)/2, *deco ); 00810 } 00811 00812 00813 00814 // ------------------------------------------------------------------------------------------ 00815 00816 00817 00818 KeramikClient::KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory ) 00819 : KDecoration( bridge, factory ), 00820 activeIcon( NULL ), inactiveIcon( NULL ), captionBufferDirty( true ), maskDirty( true ) 00821 { 00822 } 00823 00824 void KeramikClient::init() 00825 { 00826 createMainWidget( WStaticContents | WResizeNoErase | WRepaintNoErase ); 00827 widget()->installEventFilter( this ); 00828 00829 // Minimize flicker 00830 widget()->setBackgroundMode( NoBackground ); 00831 00832 for ( int i=0; i < NumButtons; i++ ) 00833 button[i] = NULL; 00834 00835 createLayout(); 00836 } 00837 00838 void KeramikClient::createLayout() 00839 { 00840 00841 QVBoxLayout *mainLayout = new QVBoxLayout( widget() ); 00842 QBoxLayout *titleLayout = new QBoxLayout( 0, QBoxLayout::LeftToRight, 0, 0, 0 ); 00843 QHBoxLayout *windowLayout = new QHBoxLayout(); 00844 00845 largeTitlebar = ( !maximizedVertical() && clientHandler->largeCaptionBubbles() ); 00846 largeCaption = ( isActive() && largeTitlebar ); 00847 00848 int grabBarHeight = clientHandler->grabBarHeight(); 00849 int topSpacing = ( largeTitlebar ? 4 : 1 ); 00850 int leftBorderWidth = clientHandler->tile( BorderLeft, true )->width(); 00851 int rightBorderWidth = clientHandler->tile( BorderRight, true )->width(); 00852 topSpacer = new QSpacerItem( 10, topSpacing, 00853 QSizePolicy::Expanding, QSizePolicy::Minimum ); 00854 00855 mainLayout->addItem( topSpacer ); 00856 00857 mainLayout->addLayout( titleLayout ); // Titlebar 00858 mainLayout->addLayout( windowLayout, 1 ); // Left border + window + right border 00859 mainLayout->addSpacing( grabBarHeight ); // Bottom grab bar 00860 00861 titleLayout->setSpacing( buttonSpacing ); 00862 00863 titleLayout->addSpacing( buttonMargin ); // Left button margin 00864 addButtons( titleLayout, options()->customButtonPositions() ? 00865 options()->titleButtonsLeft() : QString(default_left) ); 00866 00867 titlebar = new QSpacerItem( 10, clientHandler->titleBarHeight(largeTitlebar) 00868 - topSpacing, QSizePolicy::Expanding, QSizePolicy::Minimum ); 00869 titleLayout->addItem( titlebar ); 00870 00871 titleLayout->addSpacing( buttonSpacing ); 00872 addButtons( titleLayout, options()->customButtonPositions() ? 00873 options()->titleButtonsRight() : QString(default_right) ); 00874 titleLayout->addSpacing( buttonMargin - 1 ); // Right button margin 00875 00876 windowLayout->addSpacing( leftBorderWidth ); // Left border 00877 if( isPreview()) 00878 windowLayout->addWidget( new QLabel( i18n( "<center><b>Keramik</b></center>" ), widget())); 00879 else 00880 windowLayout->addItem( new QSpacerItem( 0, 0 )); //no widget in the middle 00881 windowLayout->addSpacing( rightBorderWidth ); // Right border 00882 } 00883 00884 00885 KeramikClient::~KeramikClient() 00886 { 00887 delete activeIcon; 00888 delete inactiveIcon; 00889 00890 activeIcon = inactiveIcon = NULL; 00891 } 00892 00893 00894 void KeramikClient::reset( unsigned long ) 00895 { 00896 if ( clientHandler->largeCaptionBubbles() && !largeTitlebar ) 00897 { 00898 // We're switching from small caption bubbles to large 00899 if ( !maximizedVertical() ) { 00900 topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); 00901 largeTitlebar = true; 00902 largeCaption = isActive(); 00903 00904 widget()->layout()->activate(); 00905 00906 // Compensate for the titlebar size change 00907 00908 // TODO This is wrong, this may break size increments (see bug #53784). 00909 // FRAME 00910 widget()->setGeometry( widget()->x(), widget()->y() - 3, width(), height() + 3 ); 00911 } 00912 } 00913 else if ( !clientHandler->largeCaptionBubbles() && largeTitlebar ) 00914 { 00915 // We're switching from large caption bubbles to small 00916 topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); 00917 largeTitlebar = largeCaption = false; 00918 00919 widget()->layout()->activate(); 00920 00921 // Compensate for the titlebar size change 00922 // FRAME 00923 widget()->setGeometry( widget()->x(), widget()->y() + 3, width(), height() - 3 ); 00924 } 00925 00926 calculateCaptionRect(); 00927 00928 captionBufferDirty = maskDirty = true; 00929 00930 // Only repaint the window if it's visible 00931 // (i.e. not minimized and on the current desktop) 00932 if ( widget()->isVisible() ) { 00933 widget()->repaint( false ); 00934 00935 for ( int i = 0; i < NumButtons; i++ ) 00936 if ( button[i] ) button[i]->repaint( false ); 00937 } 00938 } 00939 00940 00941 void KeramikClient::addButtons( QBoxLayout *layout, const QString &s ) 00942 { 00943 for ( uint i=0; i < s.length(); i++ ) 00944 { 00945 switch ( s[i].latin1() ) 00946 { 00947 // Menu button 00948 case 'M' : 00949 if ( !button[MenuButton] ) { 00950 button[MenuButton] = new KeramikButton( this, "menu", MenuButton, i18n("Menu"), LeftButton|RightButton ); 00951 connect( button[MenuButton], SIGNAL( pressed() ), SLOT( menuButtonPressed() ) ); 00952 layout->addWidget( button[MenuButton] ); 00953 } 00954 break; 00955 00956 // OnAllDesktops button 00957 case 'S' : 00958 if ( !button[OnAllDesktopsButton] ) { 00959 button[OnAllDesktopsButton] = new KeramikButton( this, "on_all_desktops", 00960 OnAllDesktopsButton, isOnAllDesktops()?i18n("Not On All Desktops"):i18n("On All Desktops") ); 00961 if(isOnAllDesktops()) 00962 button[OnAllDesktopsButton]->toggle(); 00963 connect( button[OnAllDesktopsButton], SIGNAL( clicked() ), SLOT( toggleOnAllDesktops() ) ); 00964 layout->addWidget( button[OnAllDesktopsButton] ); 00965 } 00966 break; 00967 00968 // Help button 00969 case 'H' : 00970 if ( !button[HelpButton] && providesContextHelp() ) { 00971 button[HelpButton] = new KeramikButton( this, "help", HelpButton, i18n("Help") ); 00972 connect( button[HelpButton], SIGNAL( clicked() ), SLOT( showContextHelp() ) ); 00973 layout->addWidget( button[HelpButton] ); 00974 } 00975 break; 00976 00977 // Minimize button 00978 case 'I' : 00979 if ( !button[MinButton] && isMinimizable() ) { 00980 button[MinButton] = new KeramikButton( this, "minimize", MinButton, i18n("Minimize") ); 00981 connect( button[MinButton], SIGNAL( clicked() ), SLOT( minimize() ) ); 00982 layout->addWidget( button[MinButton] ); 00983 } 00984 break; 00985 00986 // Maximize button 00987 case 'A' : 00988 if ( !button[MaxButton] && isMaximizable() ) { 00989 button[MaxButton] = new KeramikButton( this, "maximize", MaxButton, i18n("Maximize"), LeftButton|MidButton|RightButton ); 00990 connect( button[MaxButton], SIGNAL( clicked() ), SLOT( slotMaximize() ) ); 00991 layout->addWidget( button[MaxButton] ); 00992 } 00993 break; 00994 00995 // Close button 00996 case 'X' : 00997 if ( !button[CloseButton] && isCloseable() ) { 00998 button[CloseButton] = new KeramikButton( this, "close", CloseButton, i18n("Close") ); 00999 connect( button[CloseButton], SIGNAL( clicked() ), SLOT( closeWindow() ) ); 01000 layout->addWidget( button[CloseButton] ); 01001 } 01002 break; 01003 01004 // Additional spacing 01005 case '_' : 01006 layout->addSpacing( buttonSpacing ); 01007 break; 01008 } 01009 } 01010 } 01011 01012 01013 void KeramikClient::updateMask() 01014 { 01015 if ( !keramik_initialized ) 01016 return; 01017 01018 // To maximize performance this code uses precalculated bounding rects 01019 // to set the window mask. This saves us from having to allocate a 1bpp 01020 // pixmap, paint the mask on it and then have the X server iterate 01021 // over the pixels to compute the bounding rects from it. 01022 01023 QRegion r; 01024 register int w, y = 0; 01025 int nrects; 01026 01027 if ( QApplication::reverseLayout() ) { 01028 01029 // If the caption bubble is visible and extends above the titlebar 01030 if ( largeCaption && captionRect.width() >= 25 ) { 01031 register int x = captionRect.left(); 01032 w = captionRect.width(); 01033 r += QRegion( x + 11, y++, w - 19, 1 ); 01034 r += QRegion( x + 9, y++, w - 15, 1 ); 01035 r += QRegion( x + 7, y++, w - 12, 1 ); 01036 } else { 01037 nrects = 8; 01038 01039 // Do we have a large titlebar with a retracted caption bubble? 01040 // (i.e. the style is set to use large caption bubbles, we're 01041 // not maximized and not active) 01042 if ( largeTitlebar ) 01043 y = 3; 01044 } 01045 01046 w = width(); // FRAME 01047 01048 // The rounded titlebar corners 01049 r += QRegion( 9, y++, w - 17, 1 ); 01050 r += QRegion( 7, y++, w - 13, 1 ); 01051 r += QRegion( 5, y++, w - 9, 1 ); 01052 r += QRegion( 4, y++, w - 7, 1 ); 01053 r += QRegion( 3, y++, w - 5, 1 ); 01054 r += QRegion( 2, y++, w - 4, 1 ); 01055 r += QRegion( 1, y++, w - 2, 2 ); 01056 } else { 01057 01058 // If the caption bubble is visible and extends above the titlebar 01059 if ( largeCaption && captionRect.width() >= 25 ) { 01060 nrects = 11; 01061 register int x = captionRect.left(); 01062 w = captionRect.width(); 01063 r += QRegion( x + 8, y++, w - 19, 1 ); 01064 r += QRegion( x + 6, y++, w - 15, 1 ); 01065 r += QRegion( x + 5, y++, w - 12, 1 ); 01066 } else { 01067 nrects = 8; 01068 01069 // Do we have a large titlebar with a retracted caption bubble? 01070 // (i.e. the style is set to use large caption bubbles, we're 01071 // not maximized and not active) 01072 if ( largeTitlebar ) 01073 y = 3; 01074 } 01075 01076 w = width(); // FRAME 01077 01078 // The rounded titlebar corners 01079 r += QRegion( 8, y++, w - 17, 1 ); 01080 r += QRegion( 6, y++, w - 13, 1 ); 01081 r += QRegion( 4, y++, w - 9, 1 ); 01082 r += QRegion( 3, y++, w - 7, 1 ); 01083 r += QRegion( 2, y++, w - 5, 1 ); 01084 r += QRegion( 2, y++, w - 4, 1 ); 01085 r += QRegion( 1, y++, w - 2, 2 ); 01086 } 01087 01088 y++; 01089 01090 // The part of the window below the titlebar 01091 r += QRegion( 0, y, w, height() - y ); 01092 01093 setMask( r, YXBanded ); 01094 01095 maskDirty = false; 01096 } 01097 01098 01099 void KeramikClient::updateCaptionBuffer() 01100 { 01101 if ( !keramik_initialized ) 01102 return; 01103 01104 bool active = isActive(); 01105 QPixmap *icon = NULL; 01106 01107 if ( captionBuffer.size() != captionRect.size() ) 01108 captionBuffer.resize( captionRect.size() ); 01109 01110 if ( captionBuffer.isNull() ) 01111 return; 01112 01113 QPainter p( &captionBuffer ); 01114 01115 // Draw the caption bubble 01116 if ( active && largeCaption ) { 01117 p.drawPixmap( 0, 0, *clientHandler->tile( CaptionLargeLeft, true ) ); 01118 p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), 01119 *clientHandler->tile( CaptionLargeCenter, true ) ); 01120 p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionLargeRight, true ) ); 01121 } else { 01122 p.drawPixmap( 0, 0, *clientHandler->tile( CaptionSmallLeft, active ) ); 01123 p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(), 01124 *clientHandler->tile( CaptionSmallCenter, active ) ); 01125 p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionSmallRight, active ) ); 01126 } 01127 01128 if ( clientHandler->showAppIcons() ) 01129 { 01130 if ( active ) { 01131 if ( ! activeIcon ) 01132 activeIcon = new QPixmap( this->icon().pixmap( QIconSet::Small, QIconSet::Normal )); // FRAME 01133 icon = activeIcon; 01134 } else { 01135 if ( ! inactiveIcon ) { 01136 QImage img = this->icon().pixmap( QIconSet::Small, QIconSet::Normal ).convertToImage(); 01137 KIconEffect::semiTransparent( img ); 01138 inactiveIcon = new QPixmap( img ); 01139 } 01140 icon = inactiveIcon; 01141 } 01142 } 01143 01144 p.setFont( options()->font( active ) ); 01145 int tw = p.fontMetrics().width( caption() ) + 01146 ( clientHandler->showAppIcons() ? 16 + iconSpacing : 0 ); 01147 01148 int xpos = QMAX( (captionRect.width() - tw) / 3, 8 ); 01149 QRect tr = QStyle::visualRect( QRect(xpos, 1, captionRect.width() - xpos - 10, 01150 captionRect.height() - 4), captionBuffer.rect() ); 01151 01152 //p.setPen( Qt::red ); // debug 01153 //p.drawRect( tr ); // debug 01154 01155 // Application icon 01156 if ( clientHandler->showAppIcons() ) 01157 { 01158 QRect iconRect = QStyle::visualRect( QRect(tr.x(), 01159 1 + (captionRect.height() - 4 - 16) / 2, 16, 16), tr ); 01160 QRect r( icon->rect() ); 01161 r.moveCenter( iconRect.center() ); 01162 01163 if ( tr.width() > 16 ) { 01164 p.drawPixmap( r, *icon ); 01165 } else { 01166 QRect sr( 0, 0, icon->width(), icon->height() ); 01167 01168 if ( QApplication::reverseLayout() ) 01169 sr.addCoords( icon->width() - tr.width(), 0, 0, 0 ); 01170 else 01171 sr.addCoords( 0, 0, -( icon->width() - tr.width() ), 0 ); 01172 01173 p.drawPixmap( r.x() + sr.x(), r.y() + sr.y(), *icon, 01174 sr.x(), sr.y(), sr.width(), sr.height() ); 01175 } 01176 01177 //p.drawRect( r ); // debug 01178 01179 if ( QApplication::reverseLayout() ) 01180 tr.addCoords( 0, 0, -(16 + iconSpacing), 0 ); 01181 else 01182 tr.addCoords( (16 + iconSpacing), 0, 0, 0 ); 01183 } 01184 01185 // Draw the titlebar text 01186 int flags = AlignVCenter | SingleLine; 01187 flags |= ( QApplication::reverseLayout() ? AlignRight : AlignLeft ); 01188 01189 if ( clientHandler->useShadowedText() ) 01190 { 01191 p.translate( QApplication::reverseLayout() ? -1 : 1, 1 ); 01192 p.setPen( options()->color(ColorTitleBar, active).dark() ); 01193 p.setPen( black ); 01194 p.drawText( tr, flags, caption() ); 01195 p.translate( QApplication::reverseLayout() ? 1 : -1, -1 ); 01196 } 01197 01198 p.setPen( options()->color( ColorFont, active ) ); 01199 p.drawText( tr, flags, caption() ); 01200 01201 captionBufferDirty = false; 01202 } 01203 01204 01205 void KeramikClient::calculateCaptionRect() 01206 { 01207 QFontMetrics fm( options()->font(isActive()) ); 01208 int cw = fm.width( caption() ) + 95; 01209 int titleBaseY = ( largeTitlebar ? 3 : 0 ); 01210 01211 if ( clientHandler->showAppIcons() ) 01212 cw += 16 + 4; // icon width + space 01213 01214 cw = QMIN( cw, titlebar->geometry().width() ); 01215 captionRect = QStyle::visualRect( QRect(titlebar->geometry().x(), (largeCaption ? 0 : titleBaseY), 01216 cw, clientHandler->titleBarHeight(largeCaption) ), 01217 titlebar->geometry() ); 01218 } 01219 01220 01221 void KeramikClient::captionChange() 01222 { 01223 QRect r( captionRect ); 01224 calculateCaptionRect(); 01225 01226 if ( r.size() != captionRect.size() ) 01227 maskDirty = true; 01228 01229 captionBufferDirty = true; 01230 01231 widget()->repaint( r | captionRect, false ); 01232 } 01233 01234 01235 void KeramikClient::iconChange() 01236 { 01237 if ( clientHandler->showAppIcons() ) { 01238 01239 // Force updateCaptionBuffer() to recreate the cached icons 01240 if ( activeIcon ) 01241 delete activeIcon; 01242 01243 if ( inactiveIcon ) 01244 delete inactiveIcon; 01245 01246 activeIcon = inactiveIcon = NULL; 01247 01248 captionBufferDirty = true; 01249 widget()->repaint( captionRect, false ); 01250 } 01251 } 01252 01253 01254 void KeramikClient::activeChange() 01255 { 01256 bool active = isActive(); 01257 // Note: It's assumed that the same font will always be used for both active 01258 // and inactive windows, since the fonts kcm hasn't supported setting 01259 // different fonts for different window states for some time. 01260 if ( largeTitlebar ) { 01261 largeCaption = ( active && !maximizedVertical() ); 01262 calculateCaptionRect(); 01263 maskDirty = true; 01264 } 01265 01266 captionBufferDirty = true; 01267 01268 widget()->repaint( false ); 01269 01270 for ( int i=0; i < NumButtons; i++ ) 01271 if ( button[i] ) button[i]->repaint( false ); 01272 } 01273 01274 01275 void KeramikClient::maximizeChange() 01276 { 01277 if ( clientHandler->largeCaptionBubbles() ) 01278 { 01279 if ( maximizeMode() & MaximizeVertical ) { 01280 // We've been maximized - shrink the titlebar by 3 pixels 01281 topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum ); 01282 largeCaption = largeTitlebar = false; 01283 01284 calculateCaptionRect(); 01285 captionBufferDirty = maskDirty = true; 01286 01287 widget()->layout()->activate(); 01288 widget()->repaint( false ); 01289 } else if (( maximizeMode() & MaximizeVertical ) == 0 && !largeTitlebar ) { 01290 // We've been restored - enlarge the titlebar by 3 pixels 01291 topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum ); 01292 largeCaption = largeTitlebar = true; 01293 01294 calculateCaptionRect(); 01295 captionBufferDirty = maskDirty = true; 01296 01297 widget()->layout()->activate(); 01298 widget()->repaint( false ); 01299 } 01300 } 01301 01302 if ( button[ MaxButton ] ) { 01303 QToolTip::remove( button[ MaxButton ] ); 01304 QToolTip::add( button[ MaxButton ], maximizeMode() == MaximizeFull ? i18n("Restore") : i18n("Maximize") ); 01305 button[ MaxButton ]->repaint(); 01306 } 01307 } 01308 01309 01310 void KeramikClient::desktopChange() 01311 { 01312 if ( button[ OnAllDesktopsButton ] ) 01313 { 01314 QToolTip::remove( button[ OnAllDesktopsButton ] ); 01315 QToolTip::add( button[ OnAllDesktopsButton ], isOnAllDesktops() ? i18n("Not On All Desktops") : i18n("On All Desktops") ); 01316 } 01317 } 01318 01319 01320 void KeramikClient::menuButtonPressed() 01321 { 01322 QPoint menuPoint ( button[MenuButton]->rect().bottomLeft().x() - 6, 01323 button[MenuButton]->rect().bottomLeft().y() + 3 ); 01324 KDecorationFactory* f = factory(); 01325 showWindowMenu( button[MenuButton]->mapToGlobal( menuPoint )); 01326 if( !f->exists( this )) // 'this' was destroyed 01327 return; 01328 button[MenuButton]->setDown(false); 01329 } 01330 01331 01332 void KeramikClient::slotMaximize() 01333 { 01334 switch ( button[ MaxButton ]->lastButton() ) 01335 { 01336 case MidButton: 01337 maximize( maximizeMode() ^ MaximizeVertical ); 01338 break; 01339 01340 case RightButton: 01341 maximize( maximizeMode() ^ MaximizeHorizontal ); 01342 break; 01343 01344 case LeftButton: 01345 maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull ); 01346 break; 01347 } 01348 } 01349 01350 01351 void KeramikClient::paintEvent( QPaintEvent *e ) 01352 { 01353 if ( !keramik_initialized ) 01354 return; 01355 01356 QPainter p( widget()); 01357 QRect updateRect( e->rect() ); 01358 bool active = isActive(); 01359 01360 int titleBaseY = ( largeTitlebar ? 3 : 0 ); 01361 int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar ); 01362 int grabBarHeight = clientHandler->grabBarHeight(); 01363 int leftBorderWidth = clientHandler->tile( BorderLeft, active )->width(); 01364 int rightBorderWidth = clientHandler->tile( BorderRight, active )->width(); 01365 01366 if ( maskDirty ) 01367 updateMask(); 01368 01369 // Titlebar 01370 // ----------------------------------------------------------------------- 01371 if ( updateRect.y() < titleBarHeight ) 01372 { 01373 int titleBarBaseHeight = titleBarHeight - titleBaseY; 01374 01375 if ( captionBufferDirty ) 01376 updateCaptionBuffer(); 01377 01378 // Top left corner 01379 if ( updateRect.x() < 15 ) 01380 p.drawPixmap( 0, titleBaseY, 01381 *clientHandler->tile( TitleLeft, active ) ); 01382 01383 // Space between the top left corner and the caption bubble 01384 if ( updateRect.x() < captionRect.left() && updateRect.right() >= 15 ) { 01385 int x1 = QMAX( 15, updateRect.x() ); 01386 int x2 = QMIN( captionRect.left(), updateRect.right() ); 01387 01388 p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, 01389 *clientHandler->tile( TitleCenter, active ) ); 01390 } 01391 01392 // Caption bubble 01393 if ( updateRect.x() <= captionRect.right() && updateRect.right() > 15 ) { 01394 if ( captionRect.width() >= 25 ) 01395 p.drawPixmap( captionRect.left(), active ? 0 : titleBaseY, captionBuffer ); 01396 else 01397 p.drawTiledPixmap( captionRect.x(), titleBaseY, captionRect.width(), 01398 titleBarBaseHeight, *clientHandler->tile( TitleCenter, active ) ); 01399 } 01400 01401 // Space between the caption bubble and the top right corner 01402 if ( updateRect.right() > captionRect.right() && updateRect.x() < width() - 15 ) { // FRAME 01403 int x1 = QMAX( captionRect.right() + 1, updateRect.x() ); 01404 int x2 = QMIN( width() - 15, updateRect.right() ); 01405 01406 p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight, 01407 *clientHandler->tile( TitleCenter, active ) ); 01408 } 01409 01410 // Top right corner 01411 if ( updateRect.right() >= width() - 15 ) 01412 p.drawPixmap( width() - 15, titleBaseY, 01413 *clientHandler->tile( TitleRight, active ) ); 01414 } 01415 01416 // Borders 01417 // ----------------------------------------------------------------------- 01418 if ( updateRect.bottom() >= titleBarHeight && 01419 updateRect.top() < height() - grabBarHeight ) 01420 { 01421 int top = QMAX( titleBarHeight, updateRect.top() ); 01422 int bottom = QMIN( updateRect.bottom(), height() - grabBarHeight ); 01423 01424 // Left border 01425 if ( updateRect.x() < leftBorderWidth ) 01426 p.drawTiledPixmap( 0, top, leftBorderWidth, bottom - top + 1, 01427 *clientHandler->tile( BorderLeft, active ) ); 01428 01429 // Right border 01430 if ( e->rect().right() > width() - rightBorderWidth - 1 ) 01431 p.drawTiledPixmap( width() - rightBorderWidth, top, rightBorderWidth, 01432 bottom - top + 1, *clientHandler->tile( BorderRight, active ) ); 01433 } 01434 01435 // Bottom grab bar 01436 // ----------------------------------------------------------------------- 01437 if ( updateRect.bottom() >= height() - grabBarHeight ) { 01438 // Bottom left corner 01439 if ( updateRect.x() < 9 ) 01440 p.drawPixmap( 0, height() - grabBarHeight, 01441 *clientHandler->tile( GrabBarLeft, active ) ); 01442 01443 // Space between the left corner and the right corner 01444 if ( updateRect.x() < width() - 9 ) { 01445 int x1 = QMAX( 9, updateRect.x() ); 01446 int x2 = QMIN( width() - 9, updateRect.right() ); 01447 01448 p.drawTiledPixmap( x1, height() - grabBarHeight, x2 - x1 + 1, 01449 grabBarHeight, *clientHandler->tile( GrabBarCenter, active ) ); 01450 } 01451 01452 // Bottom right corner 01453 if ( updateRect.right() > width() - 9 ) 01454 p.drawPixmap( width() - 9, height() - grabBarHeight, 01455 *clientHandler->tile( GrabBarRight, active ) ); 01456 } 01457 01458 // Extra drawline for the 1 pixel empty space QLayout leaves when a window is shaded. 01459 p.setPen( options()->color( ColorTitleBlend, active ) ); 01460 p.drawLine( leftBorderWidth, height() - grabBarHeight - 1, 01461 width() - rightBorderWidth - 1, height() - grabBarHeight - 1 ); 01462 } 01463 01464 01465 void KeramikClient::resizeEvent( QResizeEvent *e ) 01466 { 01467 // FRAME Client::resizeEvent( e ); 01468 01469 QRect r( captionRect ); 01470 calculateCaptionRect(); 01471 01472 if ( r.size() != captionRect.size() ) 01473 captionBufferDirty = true; 01474 01475 maskDirty = true; 01476 01477 if ( widget()->isVisible() ) 01478 { 01479 widget()->update( widget()->rect() ); 01480 int dx = 0; 01481 int dy = 0; 01482 01483 if ( e->oldSize().width() != width() ) 01484 dx = 32 + QABS( e->oldSize().width() - width() ); 01485 01486 if ( e->oldSize().height() != height() ) 01487 dy = 8 + QABS( e->oldSize().height() - height() ); 01488 01489 if ( dy ) 01490 widget()->update( 0, height() - dy + 1, width(), dy ); 01491 01492 if ( dx ) 01493 { 01494 widget()->update( width() - dx + 1, 0, dx, height() ); 01495 widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) ); 01496 widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, 01497 titlebar->geometry().bottom() ) ) ); 01498 // Titlebar needs no paint event 01499 QApplication::postEvent( this, new QPaintEvent( titlebar->geometry(), FALSE ) ); 01500 } 01501 } 01502 } 01503 01504 01505 void KeramikClient::mouseDoubleClickEvent( QMouseEvent *e ) 01506 { 01507 if ( QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) ) 01508 titlebarDblClickOperation(); 01509 } 01510 01511 01512 KeramikClient::Position KeramikClient::mousePosition( const QPoint &p ) const 01513 { 01514 int titleBaseY = (largeTitlebar ? 3 : 0); 01515 01516 int leftBorder = clientHandler->tile( BorderLeft, true )->width(); 01517 int rightBorder = width() - clientHandler->tile( BorderRight, true )->width() - 1; 01518 int bottomBorder = height() - clientHandler->grabBarHeight() - 1; 01519 int bottomCornerSize = 3*clientHandler->tile( BorderRight, true )->width()/2 + 24; 01520 01521 // Test if the mouse is over the titlebar area 01522 if ( p.y() < titleBaseY + 11 ) { 01523 // Test for the top left corner 01524 if ( p.x() < leftBorder + 11 ) { 01525 if ( (p.y() < titleBaseY + 3 && p.x() < leftBorder + 11) || 01526 (p.y() < titleBaseY + 6 && p.x() < leftBorder + 6) || 01527 (p.y() < titleBaseY + 11 && p.x() < leftBorder + 3) ) 01528 return PositionTopLeft; 01529 } 01530 01531 // Test for the top right corner 01532 if ( p.x() > rightBorder - 11 ) { 01533 if ( (p.y() < titleBaseY + 3 && p.x() > rightBorder - 11) || 01534 (p.y() < titleBaseY + 6 && p.x() > rightBorder - 6) || 01535 (p.y() < titleBaseY + 11 && p.x() > rightBorder - 3) ) 01536 return PositionTopRight; 01537 } 01538 01539 // Test for the top border 01540 if ( p.y() <= 3 || (p.y() <= titleBaseY+3 && 01541 (p.x() < captionRect.left() || p.x() > captionRect.right()) ) ) 01542 return PositionTop; 01543 01544 // The cursor must be over the center of the titlebar. 01545 return PositionCenter; 01546 } 01547 01548 // Test the sides 01549 else if ( p.y() < bottomBorder ) { 01550 // Test for the left side 01551 if ( p.x() < leftBorder ) { 01552 if ( p.y() < height() - bottomCornerSize ) 01553 return PositionLeft; 01554 else 01555 return PositionBottomLeft; 01556 } 01557 01558 // Test for the right side 01559 else if ( p.x() > rightBorder ) { 01560 if ( p.y() < height() - bottomCornerSize ) 01561 return PositionRight; 01562 else 01563 return PositionBottomRight; 01564 } 01565 01566 // The cursor must be over the center of the window 01567 return PositionCenter; 01568 } 01569 01570 // Test the grab bar / bottom border 01571 else { 01572 // Test for the bottom left corner 01573 if ( p.x() < bottomCornerSize ) 01574 return PositionBottomLeft; 01575 01576 // Test for the bottom right corner 01577 else if ( p.x() > width() - bottomCornerSize - 1 ) 01578 return PositionBottomRight; 01579 01580 // The cursor must be over the bottom border 01581 return PositionBottom; 01582 } 01583 01584 // We should never get here 01585 return PositionCenter; 01586 } 01587 01588 01589 void KeramikClient::resize( const QSize& s ) 01590 { 01591 widget()->resize( s ); 01592 } 01593 01594 01595 void KeramikClient::borders( int& left, int& right, int& top, int& bottom ) const 01596 { 01597 int titleBarHeight = clientHandler->titleBarHeight( clientHandler->largeCaptionBubbles() ); 01598 int grabBarHeight = clientHandler->grabBarHeight(); 01599 int leftBorderWidth = clientHandler->tile( BorderLeft, isActive() )->width(); 01600 int rightBorderWidth = clientHandler->tile( BorderRight, isActive() )->width(); 01601 01602 left = leftBorderWidth; 01603 right = rightBorderWidth; 01604 top = titleBarHeight; 01605 bottom = grabBarHeight; 01606 01607 if ( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows()) 01608 left = right = 0; 01609 if( maximizeMode() & MaximizeVertical) 01610 { 01611 top = clientHandler->titleBarHeight( false ); 01612 if( !options()->moveResizeMaximizedWindows()) 01613 bottom = 0; 01614 } 01615 } 01616 01617 01618 QSize KeramikClient::minimumSize() const 01619 { 01620 return widget()->minimumSize(); 01621 } 01622 01623 01624 bool KeramikClient::eventFilter( QObject* o, QEvent* e ) 01625 { 01626 if ( o != widget() ) 01627 return false; 01628 01629 switch ( e->type() ) 01630 { 01631 case QEvent::Resize: 01632 resizeEvent( static_cast< QResizeEvent* >( e ) ); 01633 return true; 01634 01635 case QEvent::Paint: 01636 paintEvent( static_cast< QPaintEvent* >( e ) ); 01637 return true; 01638 01639 case QEvent::MouseButtonDblClick: 01640 mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) ); 01641 return true; 01642 01643 case QEvent::MouseButtonPress: 01644 processMousePressEvent( static_cast< QMouseEvent* >( e ) ); 01645 return true; 01646 01647 default: 01648 return false; 01649 } 01650 } 01651 01652 } // namespace Keramik 01653 01654 01655 01656 // ------------------------------------------------------------------------------------------- 01657 01658 01659 01660 extern "C" 01661 { 01662 KDecorationFactory *create_factory() 01663 { 01664 Keramik::clientHandler = new Keramik::KeramikHandler(); 01665 return Keramik::clientHandler; 01666 } 01667 } 01668 01669 01670 01671 // vim: set noet ts=4 sw=4:
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Aug 31 00:02:13 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003