kwin/clients/keramik/keramik.cpp
Sandro Giessl 50a9e9aa2e Enabled the bottom border for shaded windows like it was the case for
Keramik in KDE 3.1. Hope that's ok with everyone.
(sorry for the ugly diff - seems like kate wasn't able to handle the
characters in the empty lines...)

svn path=/trunk/kdebase/kwin/; revision=263476
2003-10-31 17:05:21 +00:00

1663 lines
49 KiB
C++

/*
* $Id$
*
* Keramik KWin client (version 0.8)
*
* Copyright (C) 2002 Fredrik Höglund <fredrik@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the license, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <kconfig.h>
#include <klocale.h>
#include <kiconeffect.h>
#include <qpainter.h>
#include <qlayout.h>
#include <qbitmap.h>
#include <qstyle.h>
#include <qtooltip.h>
#include <qwidget.h>
#include <qlabel.h>
#include <X11/Xlib.h>
#include "keramik.h"
#include "keramik.moc"
// -------------------------------------------------------------------------------------------
namespace Keramik
{
const int buttonMargin = 9; // Margin between the window edge and the buttons
const int buttonSpacing = 4; // Spacing between the titlebar buttons
const int iconSpacing = 5; // Spacing between the icon and the text label
// Default button layout
const char default_left[] = "M";
const char default_right[] = "HIAX";
// Titlebar button bitmaps
const unsigned char menu_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x00, 0xf0, 0x07, 0x00,
0xe0, 0x03, 0x00, 0xc0, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char on_all_desktops_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0xf0, 0x0f, 0x00,
0xf0, 0x0f, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char not_on_all_desktops_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00,
0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char help_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00,
0xf0, 0x07, 0x00, 0x30, 0x06, 0x00, 0x00, 0x07, 0x00, 0x80, 0x03, 0x00,
0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char minimize_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char maximize_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x01, 0x00, 0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0xf0, 0x0f, 0x00,
0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char restore_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xf0, 0x0f, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00,
0xf0, 0x0f, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00, 0x80, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
const unsigned char close_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x0c, 0x00, 0x70, 0x0e, 0x00, 0xe0, 0x07, 0x00, 0xc0, 0x03, 0x00,
0xc0, 0x03, 0x00, 0xe0, 0x07, 0x00, 0x70, 0x0e, 0x00, 0x30, 0x0c, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00};
KeramikHandler *clientHandler = NULL;
bool keramik_initialized = false;
// -------------------------------------------------------------------------------------------
KeramikHandler::KeramikHandler()
{
for ( int i = 0; i < NumTiles; i++ ) {
activeTiles[i] = NULL;
inactiveTiles[i] = NULL;
}
settings_cache = NULL;
imageDb = KeramikImageDb::instance();
// Create the button deco bitmaps
buttonDecos[ Menu ] = new QBitmap( 17, 17, menu_bits, true );
buttonDecos[ OnAllDesktops ] = new QBitmap( 17, 17, on_all_desktops_bits, true );
buttonDecos[ NotOnAllDesktops ] = new QBitmap( 17, 17, not_on_all_desktops_bits, true );
buttonDecos[ Help ] = new QBitmap( 17, 17, help_bits, true );
buttonDecos[ Minimize ] = new QBitmap( 17, 17, minimize_bits, true );
buttonDecos[ Maximize ] = new QBitmap( 17, 17, maximize_bits, true );
buttonDecos[ Restore ] = new QBitmap( 17, 17, restore_bits, true );
buttonDecos[ Close ] = new QBitmap( 17, 17, close_bits, true );
// Selfmask the bitmaps
for ( int i = 0; i < NumButtonDecos; i++ )
buttonDecos[i]->setMask( *buttonDecos[i] );
// Flip the bitmaps horizontally in right-to-left mode
if ( QApplication::reverseLayout() ) {
for ( int i = 0; i < Help; i++ )
flip( reinterpret_cast<QPixmap**>(buttonDecos)[i] );
for ( int i = Help + 1; i < NumButtonDecos; i++ )
flip( reinterpret_cast<QPixmap**>(buttonDecos)[i] );
}
readConfig();
createPixmaps();
keramik_initialized = true;
}
KeramikHandler::~KeramikHandler()
{
keramik_initialized = false;
destroyPixmaps();
for ( int i = 0; i < NumButtonDecos; i++ )
delete buttonDecos[i];
delete settings_cache;
KeramikImageDb::release();
imageDb = NULL;
clientHandler = NULL;
}
void KeramikHandler::createPixmaps()
{
int heightOffset;
int widthOffset;
switch(options()->preferredBorderSize(this)) {
case BorderLarge:
widthOffset = 4;
heightOffset = 0;
break;
case BorderVeryLarge:
widthOffset = 8;
heightOffset = 0;
break;
case BorderHuge:
widthOffset = 14;
heightOffset = 0;
break;
case BorderVeryHuge:
widthOffset = 23;
heightOffset = 10;
break;
case BorderOversized:
widthOffset = 36;
heightOffset = 25;
break;
case BorderTiny:
case BorderNormal:
default:
widthOffset = 0;
heightOffset = 0;
}
int fontHeight = QFontMetrics(options()->font(true)).height();
if (fontHeight > heightOffset + 20)
heightOffset = fontHeight - 20;
QString size = (heightOffset < 8) ? "" : (heightOffset < 20) ? "-large" : "-huge";
QColor titleColor, captionColor, buttonColor;
QImage *titleCenter = NULL, *captionLeft = NULL,
*captionRight = NULL, *captionCenter = NULL;
// Active tiles
// -------------------------------------------------------------------------
captionColor = KDecoration::options()->color( ColorTitleBar, true );
titleColor = KDecoration::options()->color( ColorTitleBlend, true );
// Load the titlebar corners.
activeTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor );
activeTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor );
// Load the titlebar center tile image (this will be used as
// the background for the caption bubble tiles).
titleCenter = loadImage( "titlebar-center", titleColor );
// Load the small version of the caption bubble corner & center images.
captionLeft = loadImage( "caption-small-left", captionColor );
captionRight = loadImage( "caption-small-right", captionColor );
captionCenter = loadImage( "caption-small-center", captionColor );
// Create the caption bubble tiles (by blending the images onto the titlebar)
activeTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter );
activeTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter );
activeTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter );
delete captionLeft;
delete captionRight;
delete captionCenter;
// Now do the same with the large version
captionLeft = loadImage( "caption-large-left", captionColor );
captionRight = loadImage( "caption-large-right", captionColor );
captionCenter = loadImage( "caption-large-center", captionColor );
activeTiles[ CaptionLargeLeft ] = composite( captionLeft, titleCenter );
activeTiles[ CaptionLargeRight ] = composite( captionRight, titleCenter );
activeTiles[ CaptionLargeCenter ] = composite( captionCenter, titleCenter );
delete captionLeft;
delete captionRight;
delete captionCenter;
// Create the titlebar center tile
activeTiles[ TitleCenter ] = new QPixmap( *titleCenter );
delete titleCenter;
// Load the left & right border pixmaps
activeTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor );
activeTiles[ BorderRight ] = loadPixmap( "border-right", titleColor );
// Load the bottom grabbar pixmaps
if ( largeGrabBars ) {
activeTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor );
activeTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor );
activeTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor );
} else {
activeTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor );
activeTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor );
activeTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor );
}
// Inactive tiles
// -------------------------------------------------------------------------
captionColor = KDecoration::options()->color( ColorTitleBar, false );
titleColor = KDecoration::options()->color( ColorTitleBlend, false );
inactiveTiles[ TitleLeft ] = loadPixmap( "titlebar-left", titleColor );
inactiveTiles[ TitleRight ] = loadPixmap( "titlebar-right", titleColor );
titleCenter = loadImage( "titlebar-center", titleColor );
captionLeft = loadImage( "caption-small-left", captionColor );
captionRight = loadImage( "caption-small-right", captionColor );
captionCenter = loadImage( "caption-small-center", captionColor );
inactiveTiles[ CaptionSmallLeft ] = composite( captionLeft, titleCenter );
inactiveTiles[ CaptionSmallRight ] = composite( captionRight, titleCenter );
inactiveTiles[ CaptionSmallCenter ] = composite( captionCenter, titleCenter );
delete captionLeft;
delete captionRight;
delete captionCenter;
inactiveTiles[ TitleCenter ] = new QPixmap( *titleCenter );
delete titleCenter;
inactiveTiles[ BorderLeft ] = loadPixmap( "border-left", titleColor );
inactiveTiles[ BorderRight ] = loadPixmap( "border-right", titleColor );
if ( largeGrabBars ) {
inactiveTiles[ GrabBarLeft ] = loadPixmap( "grabbar-left", titleColor );
inactiveTiles[ GrabBarRight ] = loadPixmap( "grabbar-right", titleColor );
inactiveTiles[ GrabBarCenter ] = loadPixmap( "grabbar-center", titleColor );
} else {
inactiveTiles[ GrabBarLeft ] = loadPixmap( "bottom-left", titleColor );
inactiveTiles[ GrabBarRight ] = loadPixmap( "bottom-right", titleColor );
inactiveTiles[ GrabBarCenter ] = loadPixmap( "bottom-center", titleColor );
}
// Buttons
// -------------------------------------------------------------------------
buttonColor = QColor(); //KDecoration::options()->color( ButtonBg, true );
titleButtonRound = loadPixmap( "titlebutton-round"+size, buttonColor );
titleButtonSquare = loadPixmap( "titlebutton-square"+size, buttonColor );
// Prepare the tiles for use
// -------------------------------------------------------------------------
if ( QApplication::reverseLayout() ) {
// Fix lighting
flip( activeTiles[CaptionSmallLeft], activeTiles[CaptionSmallRight] );
flip( inactiveTiles[CaptionSmallLeft], inactiveTiles[CaptionSmallRight] );
flip( activeTiles[CaptionLargeLeft], activeTiles[CaptionLargeRight] );
flip( activeTiles[TitleLeft], activeTiles[TitleRight] );
flip( inactiveTiles[TitleLeft], inactiveTiles[TitleRight] );
flip( activeTiles[BorderLeft], activeTiles[BorderRight] );
flip( inactiveTiles[BorderLeft], inactiveTiles[BorderRight] );
flip( activeTiles[GrabBarLeft], activeTiles[GrabBarRight] );
flip( inactiveTiles[GrabBarLeft], inactiveTiles[GrabBarRight] );
flip( titleButtonRound );
flip( titleButtonSquare );
}
// Pretile the center & border tiles for optimal performance
pretile( activeTiles[ CaptionSmallCenter ], 64, Qt::Horizontal );
pretile( activeTiles[ CaptionLargeCenter ], 64, Qt::Horizontal );
pretile( activeTiles[ TitleCenter ], 64, Qt::Horizontal );
pretile( activeTiles[ GrabBarCenter ], 128, Qt::Horizontal );
pretile( activeTiles[ BorderLeft ], 128, Qt::Vertical );
pretile( activeTiles[ BorderRight ], 128, Qt::Vertical );
pretile( inactiveTiles[ CaptionSmallCenter ], 64, Qt::Horizontal );
pretile( inactiveTiles[ TitleCenter ], 64, Qt::Horizontal );
pretile( inactiveTiles[ GrabBarCenter ], 128, Qt::Horizontal );
pretile( inactiveTiles[ BorderLeft ], 128, Qt::Vertical );
pretile( inactiveTiles[ BorderRight ], 128, Qt::Vertical );
if (heightOffset > 0) {
addHeight (heightOffset, activeTiles[TitleLeft]);
addHeight (heightOffset, activeTiles[TitleCenter]);
addHeight (heightOffset, activeTiles[TitleRight]);
addHeight (heightOffset, activeTiles[CaptionSmallLeft]);
addHeight (heightOffset, activeTiles[CaptionSmallCenter]);
addHeight (heightOffset, activeTiles[CaptionSmallRight]);
addHeight (heightOffset, activeTiles[CaptionLargeLeft]);
addHeight (heightOffset, activeTiles[CaptionLargeCenter]);
addHeight (heightOffset, activeTiles[CaptionLargeRight]);
addHeight (heightOffset, inactiveTiles[TitleLeft]);
addHeight (heightOffset, inactiveTiles[TitleCenter]);
addHeight (heightOffset, inactiveTiles[TitleRight]);
addHeight (heightOffset, inactiveTiles[CaptionSmallLeft]);
addHeight (heightOffset, inactiveTiles[CaptionSmallCenter]);
addHeight (heightOffset, inactiveTiles[CaptionSmallRight]);
}
if (widthOffset > 0) {
addWidth (widthOffset, activeTiles[BorderLeft], true, activeTiles[GrabBarCenter]);
addWidth (widthOffset, activeTiles[BorderRight], false, activeTiles[GrabBarCenter]);
addWidth (widthOffset, inactiveTiles[BorderLeft], true, inactiveTiles[GrabBarCenter]);
addWidth (widthOffset, inactiveTiles[BorderRight], false, inactiveTiles[GrabBarCenter]);
if (largeGrabBars)
widthOffset = widthOffset*3/2;
addHeight (widthOffset, activeTiles[GrabBarLeft]);
addHeight (widthOffset, activeTiles[GrabBarCenter]);
addHeight (widthOffset, activeTiles[GrabBarRight]);
addHeight (widthOffset, inactiveTiles[GrabBarLeft]);
addHeight (widthOffset, inactiveTiles[GrabBarCenter]);
addHeight (widthOffset, inactiveTiles[GrabBarRight]);
}
}
void KeramikHandler::destroyPixmaps()
{
for ( int i = 0; i < NumTiles; i++ ) {
delete activeTiles[i];
delete inactiveTiles[i];
activeTiles[i] = NULL;
inactiveTiles[i] = NULL;
}
delete titleButtonRound;
delete titleButtonSquare;
}
void KeramikHandler::addWidth (int width, QPixmap *&pix, bool left, QPixmap *bottomPix) {
int w = pix->width()+width;
int h = pix->height();
QPixmap *tmp = new QPixmap (w, h);
tmp->fill ();
QPainter p;
p.begin (tmp);
for (int i = 0; i < h; i++)
p.drawPixmap (0, i, *bottomPix, i%2, 0, w,1);
if (left)
p.drawPixmap(0, 0, *pix);
else
p.drawPixmap(width, 0, *pix);
p.end();
delete pix;
pix = tmp;
}
void KeramikHandler::addHeight (int height, QPixmap *&pix) {
int w = pix->width();
int h = pix->height()+height;
QPixmap *tmp = new QPixmap (w, h);
QPainter p;
p.begin (tmp);
if (pix->height() > 10) {
p.drawPixmap(0, 0, *pix, 0, 0, w, 11);
for (int i = 0; i < height; i+=2)
p.drawPixmap(0, 11+i, *pix, 0, 11, w, 2);
p.drawPixmap(0, 11+height, *pix, 0, 11, w, -1);
}
else {
int lines = h-3;
int factor = pix->height()-3;
for (int i = 0; i < lines; i++)
p.drawPixmap(0, i, *pix, 0, i*factor/lines, w, 1);
p.drawPixmap(0, lines, *pix, 0, factor, w, 3);
}
p.end();
delete pix;
pix = tmp;
}
void KeramikHandler::flip( QPixmap *&pix1, QPixmap *&pix2 )
{
// Flip the pixmaps horizontally
QPixmap *tmp = new QPixmap( pix1->xForm( QWMatrix(-1,0,0,1,pix1->width(),0) ) );
delete pix1;
pix1 = new QPixmap( pix2->xForm( QWMatrix(-1,0,0,1,pix2->width(),0) ) );
delete pix2;
pix2 = tmp;
}
void KeramikHandler::flip( QPixmap *&pix )
{
// Flip the pixmap horizontally
QPixmap *tmp = new QPixmap( pix->xForm( QWMatrix(-1,0,0,1,pix->width(),0) ) );
delete pix;
pix = tmp;
}
void KeramikHandler::pretile( QPixmap *&pix, int size, Qt::Orientation dir )
{
QPixmap *newpix;
QPainter p;
if ( dir == Qt::Horizontal )
newpix = new QPixmap( size, pix->height() );
else
newpix = new QPixmap( pix->width(), size );
p.begin( newpix );
p.drawTiledPixmap( newpix->rect(), *pix ) ;
p.end();
delete pix;
pix = newpix;
}
void KeramikHandler::readConfig()
{
KConfig *c = new KConfig( "kwinkeramikrc" );
c->setGroup( "General" );
showIcons = c->readBoolEntry( "ShowAppIcons", true );
shadowedText = c->readBoolEntry( "UseShadowedText", true );
smallCaptionBubbles = c->readBoolEntry( "SmallCaptionBubbles", false );
largeGrabBars = c->readBoolEntry( "LargeGrabBars", true );
if ( ! settings_cache ) {
settings_cache = new SettingsCache;
settings_cache->largeGrabBars = largeGrabBars;
settings_cache->smallCaptionBubbles = smallCaptionBubbles;
}
delete c;
}
QPixmap *KeramikHandler::composite( QImage *over, QImage *under )
{
QImage dest( over->width(), over->height(), 32 );
int width = over->width(), height = over->height();
// Clear the destination image
Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dest.bits() );
for (int i = 0; i < width * height; i++)
*(data++) = 0;
// Copy the under image (bottom aligned) to the destination image
for (int y1 = height - under->height(), y2 = 0; y1 < height; y1++, y2++ )
{
register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.scanLine(y1) );
register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( under->scanLine(y2) );
for ( int x = 0; x < width; x++ )
*(dst++) = *(src++);
}
// Blend the over image onto the destination
register Q_UINT32 *dst = reinterpret_cast<Q_UINT32*>( dest.bits() );
register Q_UINT32 *src = reinterpret_cast<Q_UINT32*>( over->bits() );
for ( int i = 0; i < width * height; i++ )
{
int r1 = qRed( *dst ), g1 = qGreen( *dst ), b1 = qBlue( *dst );
int r2 = qRed( *src ), g2 = qGreen( *src ), b2 = qBlue( *src );
int a = qAlpha( *src );
if ( a == 0xff )
*dst = *src;
else if ( a != 0x00 )
*dst = qRgba( Q_UINT8( r1 + (((r2 - r1) * a) >> 8) ),
Q_UINT8( g1 + (((g2 - g1) * a) >> 8) ),
Q_UINT8( b1 + (((b2 - b1) * a) >> 8) ),
0xff );
else if ( qAlpha(*dst) == 0x00 )
*dst = 0;
src++; dst++;
}
// Create the final pixmap and return it
return new QPixmap( dest );
}
QImage *KeramikHandler::loadImage( const QString &name, const QColor &col )
{
if ( col.isValid() ) {
QImage *img = new QImage( imageDb->image(name)->copy() );
KIconEffect::colorize( *img, col, 1.0 );
return img;
} else
return new QImage( imageDb->image(name)->copy() );
}
QPixmap *KeramikHandler::loadPixmap( const QString &name, const QColor &col )
{
QImage *img = loadImage( name, col );
QPixmap *pix = new QPixmap( *img );
delete img;
return pix;
}
bool KeramikHandler::reset( unsigned long changed )
{
keramik_initialized = false;
bool needHardReset = false;
bool pixmapsInvalid = false;
// Re-read the config file
readConfig();
if ( changed & SettingBorder )
{
pixmapsInvalid = true;
needHardReset = true;
}
if ( changed & SettingFont )
{
pixmapsInvalid = true;
needHardReset = true;
}
// Check if the color scheme has changed
if ( changed & SettingColors )
{
pixmapsInvalid = true;
}
// Check if button positions have changed
if ( changed & SettingButtons ) {
needHardReset = true;
}
// Check if tooltips options have changed
if ( changed & SettingTooltips ) {
needHardReset = true;
}
if ( (settings_cache->largeGrabBars != largeGrabBars) ) {
pixmapsInvalid = true;
needHardReset = true;
}
if ( (settings_cache->smallCaptionBubbles != smallCaptionBubbles) ) {
needHardReset = true;
}
// Update our config cache
settings_cache->largeGrabBars = largeGrabBars;
settings_cache->smallCaptionBubbles = smallCaptionBubbles;
// Do we need to recreate the pixmaps?
if ( pixmapsInvalid ) {
destroyPixmaps();
createPixmaps();
}
keramik_initialized = true;
// Do we need to "hit the wooden hammer" ?
if ( !needHardReset )
resetDecorations( changed );
return needHardReset;
}
const QPixmap *KeramikHandler::tile( TilePixmap tilePix, bool active ) const
{
return ( active ? activeTiles[ tilePix ] : inactiveTiles[ tilePix ] );
}
KDecoration* KeramikHandler::createDecoration( KDecorationBridge* bridge )
{
return new KeramikClient( bridge, this );
}
QValueList< KeramikHandler::BorderSize > KeramikHandler::borderSizes() const
{ // the list must be sorted
return QValueList< BorderSize >() << BorderNormal << BorderLarge <<
BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized;
}
// -------------------------------------------------------------------------------------------
KeramikButton::KeramikButton( KeramikClient* c, const char *name, Button btn, const QString &tip )
: QButton( c->widget(), name ),
client( c ), button( btn ), hover( false ), lastbutton( 0 )
{
QToolTip::add( this, tip ); // FRAME
setBackgroundMode( NoBackground );
setCursor( arrowCursor );
int size = clientHandler->roundButton()->height();
setFixedSize( size, size );
setToggleButton( (button == OnAllDesktopsButton) );
}
KeramikButton::~KeramikButton()
{
// Empty.
}
void KeramikButton::enterEvent( QEvent *e )
{
QButton::enterEvent( e );
hover = true;
repaint( false );
}
void KeramikButton::leaveEvent( QEvent *e )
{
QButton::leaveEvent( e );
hover = false;
repaint( false );
}
void KeramikButton::mousePressEvent( QMouseEvent *e )
{
lastbutton = e->button();
QMouseEvent me( e->type(), e->pos(), e->globalPos(), LeftButton, e->state() );
QButton::mousePressEvent( &me );
}
void KeramikButton::mouseReleaseEvent( QMouseEvent *e )
{
lastbutton = e->button();
QMouseEvent me( e->type(), e->pos(), e->globalPos(), LeftButton, e->state() );
QButton::mouseReleaseEvent( &me );
}
void KeramikButton::drawButton( QPainter *p )
{
const QPixmap *pix;
const QBitmap *deco;
int size = clientHandler->roundButton()->height();
// Get the bevel from the client handler
if ( button == MenuButton || button == OnAllDesktopsButton || button == HelpButton )
pix = clientHandler->roundButton();
else
pix = clientHandler->squareButton();
// Draw the button background
const QPixmap *background = clientHandler->tile( TitleCenter, client->isActive() );
p->drawPixmap( 0, 0, *background,
0, (background->height()-size+1)/2, size, size );
if ( isDown() ) {
// Pressed
p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(2*size, 0, size, size), pix->rect() ) );
p->translate( QApplication::reverseLayout() ? -1 : 1, 1 );
} else if ( hover )
// Mouse over
p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(size, 0, size, size), pix->rect() ) );
else
// Normal
p->drawPixmap( QPoint(), *pix, QStyle::visualRect( QRect(0, 0, size, size), pix->rect() ) );
// Draw the button deco on the bevel
switch ( button ) {
case MenuButton:
deco = clientHandler->buttonDeco( Menu );
break;
case OnAllDesktopsButton:
deco = clientHandler->buttonDeco( isOn() ? NotOnAllDesktops : OnAllDesktops );
break;
case HelpButton:
deco = clientHandler->buttonDeco( Help );
// The '?' won't be flipped around in the ctor, so we need to
// shift it to the right to compensate for the button shadow
// being on the left side of the button in RTL mode.
if ( QApplication::reverseLayout() )
p->translate( 2, 0 );
break;
case MinButton:
deco = clientHandler->buttonDeco( Minimize );
break;
case MaxButton:
deco = clientHandler->buttonDeco( client->maximizeMode() == KeramikClient::MaximizeFull ? Restore : Maximize );
break;
case CloseButton:
deco = clientHandler->buttonDeco( Close );
break;
default:
deco = NULL;
}
p->setPen( Qt::black ); // ### hardcoded color
p->drawPixmap( (size-17)/2, (size-17)/2, *deco );
}
// ------------------------------------------------------------------------------------------
KeramikClient::KeramikClient( KDecorationBridge* bridge, KDecorationFactory* factory )
: KDecoration( bridge, factory ),
activeIcon( NULL ), inactiveIcon( NULL ), captionBufferDirty( true ), maskDirty( true )
{
}
void KeramikClient::init()
{
createMainWidget( WStaticContents | WResizeNoErase | WRepaintNoErase );
widget()->installEventFilter( this );
// Minimize flicker
widget()->setBackgroundMode( NoBackground );
for ( int i=0; i < NumButtons; i++ )
button[i] = NULL;
createLayout();
}
void KeramikClient::createLayout()
{
QVBoxLayout *mainLayout = new QVBoxLayout( widget() );
QBoxLayout *titleLayout = new QBoxLayout( 0, QBoxLayout::LeftToRight, 0, 0, 0 );
QHBoxLayout *windowLayout = new QHBoxLayout();
largeTitlebar = ( !maximizedVertical() && clientHandler->largeCaptionBubbles() );
largeCaption = ( isActive() && largeTitlebar );
int grabBarHeight = clientHandler->grabBarHeight();
int topSpacing = ( largeTitlebar ? 4 : 1 );
int leftBorderWidth = clientHandler->tile( BorderLeft, true )->width();
int rightBorderWidth = clientHandler->tile( BorderRight, true )->width();
topSpacer = new QSpacerItem( 10, topSpacing,
QSizePolicy::Expanding, QSizePolicy::Minimum );
mainLayout->addItem( topSpacer );
mainLayout->addLayout( titleLayout ); // Titlebar
mainLayout->addLayout( windowLayout, 1 ); // Left border + window + right border
mainLayout->addSpacing( grabBarHeight ); // Bottom grab bar
titleLayout->setSpacing( buttonSpacing );
titleLayout->addSpacing( buttonMargin ); // Left button margin
addButtons( titleLayout, options()->customButtonPositions() ?
options()->titleButtonsLeft() : QString(default_left) );
titlebar = new QSpacerItem( 10, clientHandler->titleBarHeight(largeTitlebar)
- topSpacing, QSizePolicy::Expanding, QSizePolicy::Minimum );
titleLayout->addItem( titlebar );
titleLayout->addSpacing( buttonSpacing );
addButtons( titleLayout, options()->customButtonPositions() ?
options()->titleButtonsRight() : QString(default_right) );
titleLayout->addSpacing( buttonMargin - 1 ); // Right button margin
windowLayout->addSpacing( leftBorderWidth ); // Left border
if( isPreview())
windowLayout->addWidget( new QLabel( i18n( "<center><b>Keramik</b></center>" ), widget()));
else
windowLayout->addItem( new QSpacerItem( 0, 0 )); //no widget in the middle
windowLayout->addSpacing( rightBorderWidth ); // Right border
}
KeramikClient::~KeramikClient()
{
delete activeIcon;
delete inactiveIcon;
activeIcon = inactiveIcon = NULL;
}
void KeramikClient::reset( unsigned long )
{
if ( clientHandler->largeCaptionBubbles() && !largeTitlebar )
{
// We're switching from small caption bubbles to large
if ( !maximizedVertical() ) {
topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum );
largeTitlebar = true;
largeCaption = isActive();
widget()->layout()->activate();
// Compensate for the titlebar size change
// TODO This is wrong, this may break size increments (see bug #53784).
// FRAME
widget()->setGeometry( widget()->x(), widget()->y() - 3, width(), height() + 3 );
}
}
else if ( !clientHandler->largeCaptionBubbles() && largeTitlebar )
{
// We're switching from large caption bubbles to small
topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum );
largeTitlebar = largeCaption = false;
widget()->layout()->activate();
// Compensate for the titlebar size change
// FRAME
widget()->setGeometry( widget()->x(), widget()->y() + 3, width(), height() - 3 );
}
calculateCaptionRect();
captionBufferDirty = maskDirty = true;
// Only repaint the window if it's visible
// (i.e. not minimized and on the current desktop)
if ( widget()->isVisible() ) {
widget()->repaint( false );
for ( int i = 0; i < NumButtons; i++ )
if ( button[i] ) button[i]->repaint( false );
}
}
void KeramikClient::addButtons( QBoxLayout *layout, const QString &s )
{
for ( uint i=0; i < s.length(); i++ )
{
switch ( s[i].latin1() )
{
// Menu button
case 'M' :
if ( !button[MenuButton] ) {
button[MenuButton] = new KeramikButton( this, "menu", MenuButton, i18n("Menu") );
connect( button[MenuButton], SIGNAL( pressed() ), SLOT( menuButtonPressed() ) );
layout->addWidget( button[MenuButton] );
}
break;
// OnAllDesktops button
case 'S' :
if ( !button[OnAllDesktopsButton] ) {
button[OnAllDesktopsButton] = new KeramikButton( this, "on_all_desktops",
OnAllDesktopsButton, i18n("On All Desktops") );
connect( button[OnAllDesktopsButton], SIGNAL( clicked() ), SLOT( toggleOnAllDesktops() ) );
layout->addWidget( button[OnAllDesktopsButton] );
}
break;
// Help button
case 'H' :
if ( !button[HelpButton] && providesContextHelp() ) {
button[HelpButton] = new KeramikButton( this, "help", HelpButton, i18n("Help") );
connect( button[HelpButton], SIGNAL( clicked() ), SLOT( showContextHelp() ) );
layout->addWidget( button[HelpButton] );
}
break;
// Minimize button
case 'I' :
if ( !button[MinButton] && isMinimizable() ) {
button[MinButton] = new KeramikButton( this, "minimize", MinButton, i18n("Minimize") );
connect( button[MinButton], SIGNAL( clicked() ), SLOT( minimize() ) );
layout->addWidget( button[MinButton] );
}
break;
// Maximize button
case 'A' :
if ( !button[MaxButton] && isMaximizable() ) {
button[MaxButton] = new KeramikButton( this, "maximize", MaxButton, i18n("Maximize") );
connect( button[MaxButton], SIGNAL( clicked() ), SLOT( slotMaximize() ) );
layout->addWidget( button[MaxButton] );
}
break;
// Close button
case 'X' :
if ( !button[CloseButton] && isCloseable() ) {
button[CloseButton] = new KeramikButton( this, "close", CloseButton, i18n("Close") );
connect( button[CloseButton], SIGNAL( clicked() ), SLOT( closeWindow() ) );
layout->addWidget( button[CloseButton] );
}
break;
// Additional spacing
case '_' :
layout->addSpacing( buttonSpacing );
break;
}
}
}
void KeramikClient::updateMask()
{
if ( !keramik_initialized )
return;
// To maximize performance this code uses precalculated bounding rects
// to set the window mask. This saves us from having to allocate a 1bpp
// pixmap, paint the mask on it and then have the X server iterate
// over the pixels to compute the bounding rects from it.
QRegion r;
register int w, y = 0;
int nrects;
if ( QApplication::reverseLayout() ) {
// If the caption bubble is visible and extends above the titlebar
if ( largeCaption && captionRect.width() >= 25 ) {
register int x = captionRect.left();
w = captionRect.width();
r += QRegion( x + 11, y++, w - 19, 1 );
r += QRegion( x + 9, y++, w - 15, 1 );
r += QRegion( x + 7, y++, w - 12, 1 );
} else {
nrects = 8;
// Do we have a large titlebar with a retracted caption bubble?
// (i.e. the style is set to use large caption bubbles, we're
// not maximized and not active)
if ( largeTitlebar )
y = 3;
}
w = width(); // FRAME
// The rounded titlebar corners
r += QRegion( 9, y++, w - 17, 1 );
r += QRegion( 7, y++, w - 13, 1 );
r += QRegion( 5, y++, w - 9, 1 );
r += QRegion( 4, y++, w - 7, 1 );
r += QRegion( 3, y++, w - 5, 1 );
r += QRegion( 2, y++, w - 4, 1 );
r += QRegion( 1, y++, w - 2, 2 );
} else {
// If the caption bubble is visible and extends above the titlebar
if ( largeCaption && captionRect.width() >= 25 ) {
nrects = 11;
register int x = captionRect.left();
w = captionRect.width();
r += QRegion( x + 8, y++, w - 19, 1 );
r += QRegion( x + 6, y++, w - 15, 1 );
r += QRegion( x + 5, y++, w - 12, 1 );
} else {
nrects = 8;
// Do we have a large titlebar with a retracted caption bubble?
// (i.e. the style is set to use large caption bubbles, we're
// not maximized and not active)
if ( largeTitlebar )
y = 3;
}
w = width(); // FRAME
// The rounded titlebar corners
r += QRegion( 8, y++, w - 17, 1 );
r += QRegion( 6, y++, w - 13, 1 );
r += QRegion( 4, y++, w - 9, 1 );
r += QRegion( 3, y++, w - 7, 1 );
r += QRegion( 2, y++, w - 5, 1 );
r += QRegion( 2, y++, w - 4, 1 );
r += QRegion( 1, y++, w - 2, 2 );
}
y++;
// The part of the window below the titlebar
r += QRegion( 0, y, w, height() - y );
setMask( r, YXBanded );
maskDirty = false;
}
void KeramikClient::updateCaptionBuffer()
{
if ( !keramik_initialized )
return;
bool active = isActive();
QPixmap *icon = NULL;
if ( captionBuffer.size() != captionRect.size() )
captionBuffer.resize( captionRect.size() );
if ( captionBuffer.isNull() )
return;
QPainter p( &captionBuffer );
// Draw the caption bubble
if ( active && largeCaption ) {
p.drawPixmap( 0, 0, *clientHandler->tile( CaptionLargeLeft, true ) );
p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(),
*clientHandler->tile( CaptionLargeCenter, true ) );
p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionLargeRight, true ) );
} else {
p.drawPixmap( 0, 0, *clientHandler->tile( CaptionSmallLeft, active ) );
p.drawTiledPixmap( 15, 0, captionRect.width() - 30, captionRect.height(),
*clientHandler->tile( CaptionSmallCenter, active ) );
p.drawPixmap( captionRect.width() - 15, 0, *clientHandler->tile( CaptionSmallRight, active ) );
}
if ( clientHandler->showAppIcons() )
{
if ( active ) {
if ( ! activeIcon )
activeIcon = new QPixmap( this->icon().pixmap( QIconSet::Small, QIconSet::Normal )); // FRAME
icon = activeIcon;
} else {
if ( ! inactiveIcon ) {
QImage img = this->icon().pixmap( QIconSet::Small, QIconSet::Normal ).convertToImage();
KIconEffect::semiTransparent( img );
inactiveIcon = new QPixmap( img );
}
icon = inactiveIcon;
}
}
p.setFont( options()->font( active ) );
int tw = p.fontMetrics().width( caption() ) +
( clientHandler->showAppIcons() ? 16 + iconSpacing : 0 );
int xpos = QMAX( (captionRect.width() - tw) / 3, 8 );
QRect tr = QStyle::visualRect( QRect(xpos, 1, captionRect.width() - xpos - 10,
captionRect.height() - 4), captionBuffer.rect() );
//p.setPen( Qt::red ); // debug
//p.drawRect( tr ); // debug
// Application icon
if ( clientHandler->showAppIcons() )
{
QRect iconRect = QStyle::visualRect( QRect(tr.x(),
1 + (captionRect.height() - 4 - 16) / 2, 16, 16), tr );
QRect r( icon->rect() );
r.moveCenter( iconRect.center() );
if ( tr.width() > 16 ) {
p.drawPixmap( r, *icon );
} else {
QRect sr( 0, 0, icon->width(), icon->height() );
if ( QApplication::reverseLayout() )
sr.addCoords( icon->width() - tr.width(), 0, 0, 0 );
else
sr.addCoords( 0, 0, -( icon->width() - tr.width() ), 0 );
p.drawPixmap( r.x() + sr.x(), r.y() + sr.y(), *icon,
sr.x(), sr.y(), sr.width(), sr.height() );
}
//p.drawRect( r ); // debug
if ( QApplication::reverseLayout() )
tr.addCoords( 0, 0, -(16 + iconSpacing), 0 );
else
tr.addCoords( (16 + iconSpacing), 0, 0, 0 );
}
// Draw the titlebar text
int flags = AlignVCenter | SingleLine;
flags |= ( QApplication::reverseLayout() ? AlignRight : AlignLeft );
if ( clientHandler->useShadowedText() )
{
p.translate( QApplication::reverseLayout() ? -1 : 1, 1 );
p.setPen( options()->color(ColorTitleBar, active).dark() );
p.setPen( black );
p.drawText( tr, flags, caption() );
p.translate( QApplication::reverseLayout() ? 1 : -1, -1 );
}
p.setPen( options()->color( ColorFont, active ) );
p.drawText( tr, flags, caption() );
captionBufferDirty = false;
}
void KeramikClient::calculateCaptionRect()
{
QFontMetrics fm( options()->font(isActive()) );
int cw = fm.width( caption() ) + 95;
int titleBaseY = ( largeTitlebar ? 3 : 0 );
if ( clientHandler->showAppIcons() )
cw += 16 + 4; // icon width + space
cw = QMIN( cw, titlebar->geometry().width() );
captionRect = QStyle::visualRect( QRect(titlebar->geometry().x(), (largeCaption ? 0 : titleBaseY),
cw, clientHandler->titleBarHeight(largeCaption) ),
titlebar->geometry() );
}
void KeramikClient::captionChange()
{
QRect r( captionRect );
calculateCaptionRect();
if ( r.size() != captionRect.size() )
maskDirty = true;
captionBufferDirty = true;
widget()->repaint( r | captionRect, false );
}
void KeramikClient::iconChange()
{
if ( clientHandler->showAppIcons() ) {
// Force updateCaptionBuffer() to recreate the cached icons
if ( activeIcon )
delete activeIcon;
if ( inactiveIcon )
delete inactiveIcon;
activeIcon = inactiveIcon = NULL;
captionBufferDirty = true;
widget()->repaint( captionRect, false );
}
}
void KeramikClient::activeChange()
{
bool active = isActive();
// Note: It's assumed that the same font will always be used for both active
// and inactive windows, since the fonts kcm hasn't supported setting
// different fonts for different window states for some time.
if ( largeTitlebar ) {
largeCaption = ( active && !maximizedVertical() );
calculateCaptionRect();
maskDirty = true;
}
captionBufferDirty = true;
widget()->repaint( false );
for ( int i=0; i < NumButtons; i++ )
if ( button[i] ) button[i]->repaint( false );
}
void KeramikClient::maximizeChange()
{
if ( clientHandler->largeCaptionBubbles() )
{
if ( maximizeMode() & MaximizeVertical ) {
// We've been maximized - shrink the titlebar by 3 pixels
topSpacer->changeSize( 10, 1, QSizePolicy::Expanding, QSizePolicy::Minimum );
largeCaption = largeTitlebar = false;
calculateCaptionRect();
captionBufferDirty = maskDirty = true;
widget()->layout()->activate();
widget()->repaint( false );
} else if (( maximizeMode() & MaximizeVertical ) == 0 && !largeTitlebar ) {
// We've been restored - enlarge the titlebar by 3 pixels
topSpacer->changeSize( 10, 4, QSizePolicy::Expanding, QSizePolicy::Minimum );
largeCaption = largeTitlebar = true;
calculateCaptionRect();
captionBufferDirty = maskDirty = true;
widget()->layout()->activate();
widget()->repaint( false );
}
}
if ( button[ MaxButton ] ) {
QToolTip::remove( button[ MaxButton ] );
QToolTip::add( button[ MaxButton ], maximizeMode() == MaximizeFull ? i18n("Restore") : i18n("Maximize") );
button[ MaxButton ]->repaint();
}
}
void KeramikClient::desktopChange()
{
if ( button[ OnAllDesktopsButton ] )
{
QToolTip::remove( button[ OnAllDesktopsButton ] );
QToolTip::add( button[ OnAllDesktopsButton ], isOnAllDesktops() ? i18n("Not On All Desktops") : i18n("On All Desktops") );
}
}
void KeramikClient::menuButtonPressed()
{
QPoint menuPoint ( button[MenuButton]->rect().bottomLeft().x() - 6,
button[MenuButton]->rect().bottomLeft().y() + 3 );
KDecorationFactory* f = factory();
showWindowMenu( button[MenuButton]->mapToGlobal( menuPoint ));
if( !f->exists( this )) // 'this' was destroyed
return;
button[MenuButton]->setDown(false);
}
void KeramikClient::slotMaximize()
{
switch ( button[ MaxButton ]->lastButton() )
{
case MidButton:
maximize( maximizeMode() ^ MaximizeVertical );
break;
case RightButton:
maximize( maximizeMode() ^ MaximizeHorizontal );
break;
case LeftButton:
maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull );
break;
}
}
void KeramikClient::paintEvent( QPaintEvent *e )
{
if ( !keramik_initialized )
return;
QPainter p( widget());
QRect updateRect( e->rect() );
bool active = isActive();
int titleBaseY = ( largeTitlebar ? 3 : 0 );
int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar );
int grabBarHeight = clientHandler->grabBarHeight();
int leftBorderWidth = clientHandler->tile( BorderLeft, active )->width();
int rightBorderWidth = clientHandler->tile( BorderRight, active )->width();
if ( maskDirty )
updateMask();
// Titlebar
// -----------------------------------------------------------------------
if ( updateRect.y() < titleBarHeight )
{
int titleBarBaseHeight = titleBarHeight - titleBaseY;
if ( captionBufferDirty )
updateCaptionBuffer();
// Top left corner
if ( updateRect.x() < 15 )
p.drawPixmap( 0, titleBaseY,
*clientHandler->tile( TitleLeft, active ) );
// Space between the top left corner and the caption bubble
if ( updateRect.x() < captionRect.left() && updateRect.right() >= 15 ) {
int x1 = QMAX( 15, updateRect.x() );
int x2 = QMIN( captionRect.left(), updateRect.right() );
p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight,
*clientHandler->tile( TitleCenter, active ) );
}
// Caption bubble
if ( updateRect.x() <= captionRect.right() && updateRect.right() > 15 ) {
if ( captionRect.width() >= 25 )
p.drawPixmap( captionRect.left(), active ? 0 : titleBaseY, captionBuffer );
else
p.drawTiledPixmap( captionRect.x(), titleBaseY, captionRect.width(),
titleBarBaseHeight, *clientHandler->tile( TitleCenter, active ) );
}
// Space between the caption bubble and the top right corner
if ( updateRect.right() > captionRect.right() && updateRect.x() < width() - 15 ) { // FRAME
int x1 = QMAX( captionRect.right() + 1, updateRect.x() );
int x2 = QMIN( width() - 15, updateRect.right() );
p.drawTiledPixmap( x1, titleBaseY, x2 - x1 + 1, titleBarBaseHeight,
*clientHandler->tile( TitleCenter, active ) );
}
// Top right corner
if ( updateRect.right() >= width() - 15 )
p.drawPixmap( width() - 15, titleBaseY,
*clientHandler->tile( TitleRight, active ) );
}
// Borders
// -----------------------------------------------------------------------
if ( updateRect.bottom() >= titleBarHeight &&
updateRect.top() < height() - grabBarHeight )
{
int top = QMAX( titleBarHeight, updateRect.top() );
int bottom = QMIN( updateRect.bottom(), height() - grabBarHeight );
// Left border
if ( updateRect.x() < leftBorderWidth )
p.drawTiledPixmap( 0, top, leftBorderWidth, bottom - top + 1,
*clientHandler->tile( BorderLeft, active ) );
// Right border
if ( e->rect().right() > width() - rightBorderWidth - 1 )
p.drawTiledPixmap( width() - rightBorderWidth, top, rightBorderWidth,
bottom - top + 1, *clientHandler->tile( BorderRight, active ) );
}
// Bottom grab bar
// -----------------------------------------------------------------------
if ( updateRect.bottom() >= height() - grabBarHeight ) {
// Bottom left corner
if ( updateRect.x() < 9 )
p.drawPixmap( 0, height() - grabBarHeight,
*clientHandler->tile( GrabBarLeft, active ) );
// Space between the left corner and the right corner
if ( updateRect.x() < width() - 9 ) {
int x1 = QMAX( 9, updateRect.x() );
int x2 = QMIN( width() - 9, updateRect.right() );
p.drawTiledPixmap( x1, height() - grabBarHeight, x2 - x1 + 1,
grabBarHeight, *clientHandler->tile( GrabBarCenter, active ) );
}
// Bottom right corner
if ( updateRect.right() > width() - 9 )
p.drawPixmap( width() - 9, height() - grabBarHeight,
*clientHandler->tile( GrabBarRight, active ) );
}
// Extra drawline for the 1 pixel empty space QLayout leaves when a window is shaded.
p.setPen( options()->color( ColorTitleBlend, active ) );
p.drawLine( leftBorderWidth, height() - grabBarHeight - 1,
width() - rightBorderWidth - 1, height() - grabBarHeight - 1 );
}
void KeramikClient::resizeEvent( QResizeEvent *e )
{
// FRAME Client::resizeEvent( e );
QRect r( captionRect );
calculateCaptionRect();
if ( r.size() != captionRect.size() )
captionBufferDirty = true;
maskDirty = true;
if ( widget()->isVisible() )
{
widget()->update( widget()->rect() );
int dx = 0;
int dy = 0;
if ( e->oldSize().width() != width() )
dx = 32 + QABS( e->oldSize().width() - width() );
if ( e->oldSize().height() != height() )
dy = 8 + QABS( e->oldSize().height() - height() );
if ( dy )
widget()->update( 0, height() - dy + 1, width(), dy );
if ( dx )
{
widget()->update( width() - dx + 1, 0, dx, height() );
widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) );
widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4,
titlebar->geometry().bottom() ) ) );
// Titlebar needs no paint event
QApplication::postEvent( this, new QPaintEvent( titlebar->geometry(), FALSE ) );
}
}
}
void KeramikClient::mouseDoubleClickEvent( QMouseEvent *e )
{
if ( QRect( 0, 0, width(), clientHandler->titleBarHeight( largeTitlebar ) ).contains( e->pos() ) )
titlebarDblClickOperation();
}
KeramikClient::MousePosition KeramikClient::mousePosition( const QPoint &p ) const
{
int titleBaseY = (largeTitlebar ? 3 : 0);
int leftBorder = clientHandler->tile( BorderLeft, true )->width();
int rightBorder = width() - clientHandler->tile( BorderRight, true )->width() - 1;
int bottomBorder = height() - clientHandler->grabBarHeight() - 1;
int bottomCornerSize = 3*clientHandler->tile( BorderRight, true )->width()/2 + 24;
// Test if the mouse is over the titlebar area
if ( p.y() < titleBaseY + 11 ) {
// Test for the top left corner
if ( p.x() < leftBorder + 11 ) {
if ( (p.y() < titleBaseY + 3 && p.x() < leftBorder + 11) ||
(p.y() < titleBaseY + 6 && p.x() < leftBorder + 6) ||
(p.y() < titleBaseY + 11 && p.x() < leftBorder + 3) )
return TopLeft2;
}
// Test for the top right corner
if ( p.x() > rightBorder - 11 ) {
if ( (p.y() < titleBaseY + 3 && p.x() > rightBorder - 11) ||
(p.y() < titleBaseY + 6 && p.x() > rightBorder - 6) ||
(p.y() < titleBaseY + 11 && p.x() > rightBorder - 3) )
return TopRight2;
}
// Test for the top border
if ( p.y() <= 3 || (p.y() <= titleBaseY+3 &&
(p.x() < captionRect.left() || p.x() > captionRect.right()) ) )
return Top;
// The cursor must be over the center of the titlebar.
return Center;
}
// Test the sides
else if ( p.y() < bottomBorder ) {
// Test for the left side
if ( p.x() < leftBorder ) {
if ( p.y() < height() - bottomCornerSize )
return Left;
else
return BottomLeft2;
}
// Test for the right side
else if ( p.x() > rightBorder ) {
if ( p.y() < height() - bottomCornerSize )
return Right;
else
return BottomRight2;
}
// The cursor must be over the center of the window
return Center;
}
// Test the grab bar / bottom border
else {
// Test for the bottom left corner
if ( p.x() < bottomCornerSize )
return BottomLeft2;
// Test for the bottom right corner
else if ( p.x() > width() - bottomCornerSize - 1 )
return BottomRight2;
// The cursor must be over the bottom border
return Bottom;
}
// We should never get here
return Nowhere;
}
void KeramikClient::resize( const QSize& s )
{
widget()->resize( s );
}
void KeramikClient::borders( int& left, int& right, int& top, int& bottom ) const
{
int titleBarHeight = clientHandler->titleBarHeight( largeTitlebar );
int grabBarHeight = clientHandler->grabBarHeight();
int leftBorderWidth = clientHandler->tile( BorderLeft, isActive() )->width();
int rightBorderWidth = clientHandler->tile( BorderRight, isActive() )->width();
left = leftBorderWidth;
right = rightBorderWidth;
top = titleBarHeight;
bottom = grabBarHeight;
if ( ( maximizeMode() & MaximizeHorizontal ) && !options()->moveResizeMaximizedWindows())
left = right = 0;
if( ( maximizeMode() & MaximizeVertical ) && !options()->moveResizeMaximizedWindows())
bottom = 0;
}
QSize KeramikClient::minimumSize() const
{
return widget()->minimumSize();
}
bool KeramikClient::eventFilter( QObject* o, QEvent* e )
{
if ( o != widget() )
return false;
switch ( e->type() )
{
case QEvent::Resize:
resizeEvent( static_cast< QResizeEvent* >( e ) );
return true;
case QEvent::Paint:
paintEvent( static_cast< QPaintEvent* >( e ) );
return true;
case QEvent::MouseButtonDblClick:
mouseDoubleClickEvent( static_cast< QMouseEvent* >( e ) );
return true;
case QEvent::MouseButtonPress:
processMousePressEvent( static_cast< QMouseEvent* >( e ) );
return true;
default:
return false;
}
}
} // namespace Keramik
// -------------------------------------------------------------------------------------------
extern "C"
{
KDecorationFactory *create_factory()
{
Keramik::clientHandler = new Keramik::KeramikHandler();
return Keramik::clientHandler;
}
}
// vim: set noet ts=4 sw=4: