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.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 29 21:20:51 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003