kwin/clients/quartz/quartz.cpp
Dirk Mueller 5b5ab45fbf fix export's
svn path=/trunk/kdebase/kwin/; revision=364795
2004-11-21 16:19:09 +00:00

1168 lines
34 KiB
C++

/*
*
* Gallium-Quartz KWin client
*
* Copyright 2001
* Karol Szwed <gallium@kde.org>
* http://gallium.n3.net/
*
* Based on the KDE default client.
*
* Includes mini titlebars for ToolWindow Support.
* Button positions are now customizable.
*
*/
#include <kconfig.h>
#include <kdrawutil.h>
#include <kglobal.h>
#include <klocale.h>
#include <kpixmapeffect.h>
#include <qbitmap.h>
#include <qcursor.h>
#include <qdrawutil.h>
#include <qimage.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qtooltip.h>
#include <qapplication.h>
#include "quartz.h"
namespace Quartz {
static const unsigned char iconify_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00,
0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const unsigned char close_bits[] = {
0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00,
0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00};
static const unsigned char maximize_bits[] = {
0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00};
static const unsigned char minmax_bits[] = {
0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00,
0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00};
static const unsigned char question_bits[] = {
0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00};
static const unsigned char pindown_white_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03,
0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char pindown_gray_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char pindown_dgray_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20,
0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e,
0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char pinup_white_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11,
0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char pinup_gray_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char pinup_dgray_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e,
0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char above_on_bits[] = {
0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char above_off_bits[] = {
0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const unsigned char below_on_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00,
0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00};
static const unsigned char below_off_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01,
0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00};
static const unsigned char shade_on_bits[] = {
0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01,
0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00};
static const unsigned char shade_off_bits[] = {
0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
///////////////////////////////////////////////////////////////////////////
// Titlebar button positions
bool onAllDesktopsButtonOnLeft = true;
bool coloredFrame = true;
KPixmap* titleBlocks = NULL;
KPixmap* ititleBlocks = NULL;
KPixmap* pinDownPix = NULL;
KPixmap* pinUpPix = NULL;
KPixmap* ipinDownPix = NULL;
KPixmap* ipinUpPix = NULL;
static int normalTitleHeight;
static int toolTitleHeight;
static int borderWidth;
bool quartz_initialized = false;
QuartzHandler* clientHandler;
///////////////////////////////////////////////////////////////////////////
QuartzHandler::QuartzHandler()
{
quartz_initialized = false;
readConfig();
createPixmaps();
quartz_initialized = true;
}
QuartzHandler::~QuartzHandler()
{
quartz_initialized = false;
freePixmaps();
}
KDecoration* QuartzHandler::createDecoration( KDecorationBridge* bridge )
{
return new QuartzClient( bridge, this );
}
bool QuartzHandler::reset(unsigned long changed)
{
quartz_initialized = false;
freePixmaps();
readConfig();
createPixmaps();
quartz_initialized = true;
// Do we need to "hit the wooden hammer" ?
bool needHardReset = true;
if (changed & SettingColors)
{
needHardReset = false;
}
if (needHardReset) {
return true;
} else {
resetDecorations(changed);
return false;
}
return true;
}
bool QuartzHandler::supports( Ability ability )
{
switch( ability )
{
case AbilityAnnounceButtons:
case AbilityButtonMenu:
case AbilityButtonOnAllDesktops:
case AbilityButtonHelp:
case AbilityButtonMinimize:
case AbilityButtonMaximize:
case AbilityButtonClose:
case AbilityButtonAboveOthers:
case AbilityButtonBelowOthers:
case AbilityButtonShade:
return true;
default:
return false;
};
}
void QuartzHandler::readConfig()
{
KConfig conf("kwinquartzrc");
conf.setGroup("General");
coloredFrame = conf.readBoolEntry( "UseTitleBarBorderColors", true );
// A small hack to make the on all desktops button look nicer
onAllDesktopsButtonOnLeft = KDecoration::options()->titleButtonsLeft().contains( 'S' );
if ( QApplication::reverseLayout() )
onAllDesktopsButtonOnLeft = !onAllDesktopsButtonOnLeft;
switch(options()->preferredBorderSize(this)) {
case BorderLarge:
borderWidth = 8;
break;
case BorderVeryLarge:
borderWidth = 12;
break;
case BorderHuge:
borderWidth = 18;
break;
case BorderVeryHuge:
borderWidth = 27;
break;
case BorderOversized:
borderWidth = 40;
break;
case BorderTiny:
case BorderNormal:
default:
borderWidth = 4;
}
normalTitleHeight = QFontMetrics(options()->font(true)).height();
if (normalTitleHeight < 18) normalTitleHeight = 18;
if (normalTitleHeight < borderWidth) normalTitleHeight = borderWidth;
toolTitleHeight = QFontMetrics(options()->font(true, true)).height();
if (toolTitleHeight < 12) toolTitleHeight = 12;
if (toolTitleHeight < borderWidth) toolTitleHeight = borderWidth;
}
// This does the colour transition magic. (You say "Oh, is that it?")
// This may be made configurable at a later stage
void QuartzHandler::drawBlocks( KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2 )
{
QPainter px;
px.begin( pi );
// Draw a background gradient first
KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient);
int factor = (pi->height()-2)/4;
int square = factor - (factor+2)/4;
int x = pi->width() - 5*factor - square;
int y = (pi->height() - 4*factor)/2;
px.fillRect( x, y, square, square, c1.light(120) );
px.fillRect( x, y+factor, square, square, c1 );
px.fillRect( x, y+2*factor, square, square, c1.light(110) );
px.fillRect( x, y+3*factor, square, square, c1 );
px.fillRect( x+factor, y, square, square, c1.light(110) );
px.fillRect( x+factor, y+factor, square, square, c2.light(110) );
px.fillRect( x+factor, y+2*factor, square, square, c1.light(120) );
px.fillRect( x+factor, y+3*factor, square, square, c2.light(130) );
px.fillRect( x+2*factor, y+factor, square, square, c1.light(110) );
px.fillRect( x+2*factor, y+2*factor, square, square, c2.light(120) );
px.fillRect( x+2*factor, y+3*factor, square, square, c2.light(150) );
px.fillRect( x+3*factor, y, square, square, c1.dark(110) );
px.fillRect( x+3*factor, y+2*factor, square, square, c2.light(120) );
px.fillRect( x+3*factor, y+3*factor, square, square, c1.dark(120) );
px.fillRect( x+4*factor, y+factor, square, square, c1.light(110) );
px.fillRect( x+4*factor, y+3*factor, square, square, c1.dark(110) );
px.fillRect( x+5*factor, y+2*factor, square, square, c2.light(120));
px.fillRect( x+5*factor, y+3*factor, square, square, c2.light(110) );
}
// This paints the button pixmaps upon loading the style.
void QuartzHandler::createPixmaps()
{
// Obtain titlebar blend colours, and create the block stuff on pixmaps.
QColorGroup g2 = options()->colorGroup(ColorTitleBlend, true);
QColor c2 = g2.background();
g2 = options()->colorGroup(ColorTitleBar, true );
QColor c = g2.background().light(130);
titleBlocks = new KPixmap();
titleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight );
drawBlocks( titleBlocks, *titleBlocks, c, c2 );
g2 = options()->colorGroup(ColorTitleBlend, false);
c2 = g2.background();
g2 = options()->colorGroup(ColorTitleBar, false );
c = g2.background().light(130);
ititleBlocks = new KPixmap();
ititleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight );
drawBlocks( ititleBlocks, *ititleBlocks, c, c2 );
// Set the on all desktops pin pixmaps;
QColorGroup g;
QPainter p;
g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, true );
c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background();
g2 = options()->colorGroup( ColorButtonBg, true );
pinUpPix = new KPixmap();
pinUpPix->resize(16, 16);
p.begin( pinUpPix );
p.fillRect( 0, 0, 16, 16, c);
kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits,
pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL );
p.end();
pinDownPix = new KPixmap();
pinDownPix->resize(16, 16);
p.begin( pinDownPix );
p.fillRect( 0, 0, 16, 16, c);
kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits,
pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL );
p.end();
// Inactive pins
g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, false );
c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background();
g2 = options()->colorGroup( ColorButtonBg, false );
ipinUpPix = new KPixmap();
ipinUpPix->resize(16, 16);
p.begin( ipinUpPix );
p.fillRect( 0, 0, 16, 16, c);
kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits,
pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL );
p.end();
ipinDownPix = new KPixmap();
ipinDownPix->resize(16, 16);
p.begin( ipinDownPix );
p.fillRect( 0, 0, 16, 16, c);
kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits,
pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL );
p.end();
}
void QuartzHandler::freePixmaps()
{
delete titleBlocks;
delete ititleBlocks;
// On all desktops pin images
delete pinUpPix;
delete ipinUpPix;
delete pinDownPix;
delete ipinDownPix;
}
QValueList< QuartzHandler::BorderSize > QuartzHandler::borderSizes() const
{ // the list must be sorted
return QValueList< BorderSize >() << BorderNormal << BorderLarge <<
BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized;
}
QuartzButton::QuartzButton(QuartzClient *parent, const char *name, bool largeButton,
bool isLeftButton, bool isOnAllDesktopsButton, const unsigned char *bitmap,
const QString& tip, const int realizeBtns)
: QButton(parent->widget(), name),
last_button(NoButton)
{
setTipText(tip);
setCursor(ArrowCursor);
// Eliminate any possible background flicker
setBackgroundMode( QWidget::NoBackground );
setToggleButton( isOnAllDesktopsButton );
realizeButtons = realizeBtns;
deco = NULL;
large = largeButton;
if ( QApplication::reverseLayout() )
isLeft = !isLeftButton;
else
isLeft = isLeftButton;
isOnAllDesktops = isOnAllDesktopsButton;
client = parent;
if ( large )
setFixedSize(normalTitleHeight-2, normalTitleHeight-2);
else
setFixedSize(toolTitleHeight-2, toolTitleHeight-2);
if(bitmap)
setBitmap(bitmap);
}
QuartzButton::~QuartzButton()
{
delete deco;
}
QSize QuartzButton::sizeHint() const
{
if ( large )
return( QSize(normalTitleHeight-2, normalTitleHeight-2) );
else
return( QSize(toolTitleHeight-2, toolTitleHeight-2) );
}
void QuartzButton::setBitmap(const unsigned char *bitmap)
{
delete deco;
deco = new QBitmap(10, 10, bitmap, true);
deco->setMask( *deco );
repaint( false );
}
void QuartzButton::setTipText(const QString &tip) {
if(KDecoration::options()->showTooltips()) {
QToolTip::remove(this );
QToolTip::add(this, tip );
}
}
void QuartzButton::drawButton(QPainter *p)
{
// Never paint if the pixmaps have not been created
if (!quartz_initialized)
return;
QColor c;
if (isLeft)
c = KDecoration::options()->color(KDecoration::ColorTitleBar, client->isActive()).light(130);
else
c = KDecoration::options()->color(KDecoration::ColorTitleBlend, client->isActive());
// Fill the button background with an appropriate color
p->fillRect(0, 0, width(), height(), c );
// If we have a decoration bitmap, then draw that
// otherwise we paint a menu button (with mini icon), or a onAllDesktops button.
if( deco )
{
int xOff = (width()-10)/2;
int yOff = (height()-10)/2;
p->setPen( Qt::black );
p->drawPixmap(isDown() ? xOff+2: xOff+1, isDown() ? yOff+2 : yOff+1, *deco);
p->setPen( KDecoration::options()->color(KDecoration::ColorButtonBg, client->isActive()).light(150) );
p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco);
} else
{
QPixmap btnpix;
int Offset = 0;
if (isOnAllDesktops)
{
if (isDown())
Offset = 1;
// Select the right onAllDesktops button to paint
if (client->isActive())
btnpix = isOn() ? *pinDownPix : *pinUpPix;
else
btnpix = isOn() ? *ipinDownPix : *ipinUpPix;
} else
btnpix = client->icon().pixmap( QIconSet::Small, QIconSet::Normal);
// Shrink the miniIcon for tiny titlebars.
if ( height() < 16)
{
QPixmap tmpPix;
// Smooth scale the image
tmpPix.convertFromImage( btnpix.convertToImage().smoothScale(height(), height()));
p->drawPixmap( 0, 0, tmpPix );
} else {
Offset += (height() - 16)/2;
p->drawPixmap( Offset, Offset, btnpix );
}
}
}
// Make the protected member public
void QuartzButton::turnOn( bool isOn )
{
if ( isToggleButton() )
setOn( isOn );
}
void QuartzButton::mousePressEvent( QMouseEvent* e )
{
last_button = e->button();
QMouseEvent me( e->type(), e->pos(), e->globalPos(),
(e->button()&realizeButtons)?LeftButton:NoButton, e->state() );
QButton::mousePressEvent( &me );
}
void QuartzButton::mouseReleaseEvent( QMouseEvent* e )
{
last_button = e->button();
QMouseEvent me( e->type(), e->pos(), e->globalPos(),
(e->button()&realizeButtons)?LeftButton:NoButton, e->state() );
QButton::mouseReleaseEvent( &me );
}
///////////////////////////////////////////////////////////////////////////
QuartzClient::QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory)
: KDecoration (bridge, factory)
{
}
void QuartzClient::init()
{
connect( this, SIGNAL( keepAboveChanged( bool )), SLOT( keepAboveChange( bool )));
connect( this, SIGNAL( keepBelowChanged( bool )), SLOT( keepBelowChange( bool )));
createMainWidget(WNoAutoErase | WStaticContents);
widget()->installEventFilter( this );
// No flicker thanks
widget()->setBackgroundMode( QWidget::NoBackground );
// Set button pointers to NULL so we can track things
for(int i=0; i < QuartzClient::BtnCount; i++)
button[i] = NULL;
// Finally, toolWindows look small
if ( isTool() ) {
titleHeight = toolTitleHeight;
largeButtons = false;
}
else {
titleHeight = normalTitleHeight;
largeButtons = true;
}
borderSize = borderWidth;
// Pack the fake window window within a grid
QGridLayout* g = new QGridLayout(widget(), 0, 0, 0);
g->setResizeMode(QLayout::FreeResize);
g->addRowSpacing(0, borderSize-1); // Top grab bar
if( isPreview())
g->addWidget(new QLabel( i18n( "<center><b>Quartz preview</b></center>" ), widget()), 3, 1);
else
g->addItem(new QSpacerItem( 0, 0 ), 3, 1); // no widget in the middle
// without the next line, unshade flickers
g->addItem( new QSpacerItem( 0, 0, QSizePolicy::Fixed,
QSizePolicy::Expanding ) );
g->setRowStretch(3, 10); // Wrapped window
g->addRowSpacing(2, 1); // line under titlebar
g->addRowSpacing(4, borderSize); // bottom handles
g->addColSpacing(0, borderSize);
g->addColSpacing(2, borderSize);
// Pack the titlebar HBox with items
hb = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0);
hb->setResizeMode( QLayout::FreeResize );
g->addLayout ( hb, 1, 1 );
addClientButtons( options()->titleButtonsLeft() );
titlebar = new QSpacerItem( 10, titleHeight, QSizePolicy::Expanding, QSizePolicy::Minimum );
hb->addItem(titlebar);
hb->addSpacing(2);
addClientButtons( options()->titleButtonsRight(), false );
hb->addSpacing(2);
}
void QuartzClient::reset( unsigned long changed )
{
if (changed & SettingColors || changed & SettingFont)
{
// repaint the whole thing
widget()->repaint(false);
}
}
const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask
| NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
| NET::UtilityMask | NET::SplashMask;
bool QuartzClient::isTool()
{
NET::WindowType type = windowType( SUPPORTED_WINDOW_TYPES_MASK );
return ((type==NET::Toolbar)||(type==NET::Utility)||(type==NET::Menu));
}
void QuartzClient::addClientButtons( const QString& s, bool isLeft )
{
if (s.length() > 0)
for(unsigned int i = 0; i < s.length(); i++)
{
switch( s[i].latin1() )
{
// Menu button
case 'M':
if (!button[BtnMenu])
{
button[BtnMenu] = new QuartzButton(this, "menu",
largeButtons, isLeft, false, NULL, i18n("Menu"), LeftButton|RightButton);
connect( button[BtnMenu], SIGNAL(pressed()),
this, SLOT(menuButtonPressed()) );
hb->addWidget( button[BtnMenu] );
}
break;
// On all desktops button
case 'S':
if (!button[BtnOnAllDesktops])
{
button[BtnOnAllDesktops] = new QuartzButton(this, "on_all_desktops",
largeButtons, isLeft, true, NULL, isOnAllDesktops()?i18n("Not on all desktops"):i18n("On all desktops"));
button[BtnOnAllDesktops]->turnOn( isOnAllDesktops() );
connect( button[BtnOnAllDesktops], SIGNAL(clicked()),
this, SLOT(toggleOnAllDesktops()) );
hb->addSpacing(1);
hb->addWidget( button[BtnOnAllDesktops] );
hb->addSpacing(1);
}
break;
// Help button
case 'H':
if( providesContextHelp() && (!button[BtnHelp]) )
{
button[BtnHelp] = new QuartzButton(this, "help",
largeButtons, isLeft, true, question_bits, i18n("Help"));
connect( button[BtnHelp], SIGNAL( clicked() ),
this, SLOT(showContextHelp()));
hb->addWidget( button[BtnHelp] );
}
break;
// Minimize button
case 'I':
if ( (!button[BtnIconify]) && isMinimizable())
{
button[BtnIconify] = new QuartzButton(this, "iconify",
largeButtons, isLeft, true, iconify_bits, i18n("Minimize"));
connect( button[BtnIconify], SIGNAL( clicked()),
this, SLOT(minimize()) );
hb->addWidget( button[BtnIconify] );
}
break;
// Maximize button
case 'A':
if ( (!button[BtnMax]) && isMaximizable())
{
button[BtnMax] = new QuartzButton(this, "maximize",
largeButtons, isLeft, true, maximize_bits, i18n("Maximize"), LeftButton|MidButton|RightButton);
connect( button[BtnMax], SIGNAL( clicked()),
this, SLOT(slotMaximize()) );
hb->addWidget( button[BtnMax] );
}
break;
// Close button
case 'X':
if (!button[BtnClose] && isCloseable())
{
button[BtnClose] = new QuartzButton(this, "close",
largeButtons, isLeft, true, close_bits, i18n("Close"));
connect( button[BtnClose], SIGNAL( clicked()),
this, SLOT(closeWindow()) );
hb->addWidget( button[BtnClose] );
}
break;
// Above button
case 'F':
if ( (!button[BtnAbove]))
{
button[BtnAbove] = new QuartzButton(this, "above",
largeButtons, isLeft, true,
keepAbove() ? above_on_bits : above_off_bits,
i18n("Keep Above Others"));
connect( button[BtnAbove], SIGNAL( clicked()),
this, SLOT(slotAbove()) );
hb->addWidget( button[BtnAbove] );
}
break;
// Below button
case 'B':
if ( (!button[BtnBelow]))
{
button[BtnBelow] = new QuartzButton(this, "below",
largeButtons, isLeft, true,
keepBelow() ? below_on_bits : below_off_bits,
i18n("Keep Below Others"));
connect( button[BtnBelow], SIGNAL( clicked()),
this, SLOT(slotBelow()) );
hb->addWidget( button[BtnBelow] );
}
break;
// Shade button
case 'L':
if ( (!button[BtnShade]) && isShadeable())
{
button[BtnShade] = new QuartzButton(this, "shade",
largeButtons, isLeft, true,
isSetShade() ? shade_on_bits : shade_off_bits,
isSetShade() ? i18n( "Unshade" ) : i18n("Shade"));
connect( button[BtnShade], SIGNAL( clicked()),
this, SLOT(slotShade()) );
hb->addWidget( button[BtnShade] );
}
break;
}
}
}
void QuartzClient::iconChange()
{
if (button[BtnMenu] && button[BtnMenu]->isVisible())
button[BtnMenu]->repaint(false);
}
void QuartzClient::desktopChange()
{
if (button[BtnOnAllDesktops])
{
button[BtnOnAllDesktops]->turnOn(isOnAllDesktops());
button[BtnOnAllDesktops]->repaint(false);
button[BtnOnAllDesktops]->setTipText(isOnAllDesktops() ? i18n("Not on all desktops") : i18n("On all desktops"));
}
}
void QuartzClient::keepAboveChange( bool above )
{
if (button[BtnAbove]) {
button[BtnAbove]->setBitmap( above ? above_on_bits : above_off_bits );
button[BtnAbove]->repaint(false);
}
}
void QuartzClient::keepBelowChange( bool below )
{
if (button[BtnBelow]) {
button[BtnBelow]->setBitmap( below ? below_on_bits : below_off_bits );
button[BtnBelow]->repaint(false);
}
}
void QuartzClient::shadeChange()
{
if (button[BtnShade]) {
bool on = isShade();
button[BtnShade]->turnOn(on);
button[BtnShade]->repaint(false);
QToolTip::remove( button[BtnShade] );
QToolTip::add( button[BtnShade], on ? i18n("Unshade") : i18n("Shade"));
}
}
void QuartzClient::slotMaximize()
{
if (button[BtnMax])
{
maximize(button[BtnMax]->last_button);
}
}
void QuartzClient::slotAbove()
{
setKeepAbove( !keepAbove());
button[BtnAbove]->turnOn(keepAbove());
button[BtnAbove]->repaint(true);
}
void QuartzClient::slotBelow()
{
setKeepBelow( !keepBelow());
button[BtnBelow]->turnOn(keepBelow());
button[BtnBelow]->repaint(true);
}
void QuartzClient::slotShade()
{
setShade( !isSetShade());
button[BtnShade]->setBitmap(isSetShade() ? shade_on_bits : shade_off_bits );
button[BtnShade]->repaint(true);
}
void QuartzClient::resizeEvent( QResizeEvent* e)
{
calcHiddenButtons();
if (widget()->isVisibleToTLW())
{
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
widget()->repaint(titlebar->geometry(), false);
}
}
}
void QuartzClient::captionChange()
{
widget()->repaint( titlebar->geometry(), false );
}
// Quartz Paint magic goes here.
void QuartzClient::paintEvent( QPaintEvent* )
{
// Never paint if the pixmaps have not been created
if (!quartz_initialized)
return;
const bool maxFull = (maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows();
QColorGroup g;
QPainter p(widget());
// Obtain widget bounds.
QRect r(widget()->rect());
int x = r.x();
int y = r.y();
int x2 = r.width() - 1;
int y2 = r.height() - 1;
int w = r.width();
int h = r.height();
// Draw part of the frame that is the title color
if( coloredFrame )
g = options()->colorGroup(ColorTitleBar, isActive());
else
g = options()->colorGroup(ColorFrame, isActive());
// Draw outer highlights and lowlights
p.setPen( g.light().light(120) );
p.drawLine( x, y, x2-1, y );
p.drawLine( x, y+1, x, y2-1 );
p.setPen( g.dark().light(120) );
p.drawLine( x2, y, x2, y2 );
p.drawLine( x, y2, x2, y2 );
// Fill out the border edges
QColor frameColor;
if ( coloredFrame)
frameColor = g.background().light(130);
else
frameColor = g.background();
if (borderSize > 2) {
p.fillRect(x+1, y+1, w-2, borderSize-2, frameColor); // top
if (!maxFull) {
p.fillRect(x+1, y+h-(borderSize-1), w-2, borderSize-2, frameColor); // bottom
p.fillRect(x+1, y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // left
p.fillRect(x+w-(borderSize), y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // right
}
}
// Draw a frame around the wrapped widget.
p.setPen( g.background() );
if (maxFull) {
p.drawLine(x+1, y+titleHeight+(borderSize-1), w-2, y+titleHeight+(borderSize-1));
} else {
p.drawRect( x+(borderSize-1), y+titleHeight+(borderSize-1), w-2*(borderSize-1), h-titleHeight-2*(borderSize-1) );
}
// Drawing this extra line removes non-drawn areas when shaded
p.drawLine( x+borderSize, y2-borderSize, x2-borderSize, y2-borderSize);
// Highlight top corner
p.setPen( g.light().light(160) );
p.drawPoint( x, y );
p.setPen( g.light().light(140) );
p.drawPoint( x+1, y );
p.drawPoint( x, y+1 );
// Draw the title bar.
// ===================
r = titlebar->geometry();
// Obtain titlebar blend colours
QColor c1 = options()->color(ColorTitleBar, isActive() ).light(130);
QColor c2 = options()->color(ColorTitleBlend, isActive() );
// Create a disposable pixmap buffer for the titlebar
KPixmap* titleBuffer = new KPixmap;
titleBuffer->resize( maxFull?w-2:(w-2*(borderSize-1)), titleHeight );
QPainter p2( titleBuffer, this );
// subtract titleBlocks pixmap width and some
int rightoffset = r.x()+r.width()-titleBlocks->width()-borderSize;
p2.fillRect( 0, 0, w, r.height(), c1 );
p2.fillRect( rightoffset, 0, maxFull?w-rightoffset:w-rightoffset-2*(borderSize-1), r.height(), c2 );
// 8 bit displays will be a bit dithered, but they still look ok.
if ( isActive() )
p2.drawPixmap( rightoffset, 0, *titleBlocks );
else
p2.drawPixmap( rightoffset, 0, *ititleBlocks );
// Draw the title text on the pixmap, and with a smaller font
// for toolwindows than the default.
QFont fnt;
if ( largeButtons ) {
fnt = options()->font(true, false); // font not small
} else {
fnt = options()->font(true, true); // font small
fnt.setWeight( QFont::Normal ); // and disable bold
}
p2.setFont( fnt );
p2.setPen( options()->color(ColorFont, isActive() ));
p2.drawText(r.x()+4-borderSize, 0, r.width()-3, r.height(),
AlignLeft | AlignVCenter, caption() );
p2.end();
p.drawPixmap( maxFull?1:borderSize-1, borderSize-1, *titleBuffer );
delete titleBuffer;
}
void QuartzClient::showEvent(QShowEvent *)
{
calcHiddenButtons();
widget()->show();
}
void QuartzClient::mouseDoubleClickEvent( QMouseEvent * e )
{
if (titlebar->geometry().contains( e->pos() ) )
titlebarDblClickOperation();
}
void QuartzClient::maximizeChange()
{
if (button[BtnMax]) {
button[BtnMax]->setBitmap((maximizeMode()==MaximizeFull) ? minmax_bits : maximize_bits);
button[BtnMax]->setTipText((maximizeMode()==MaximizeFull) ? i18n("Restore") : i18n("Maximize"));
}
}
void QuartzClient::activeChange()
{
for(int i=QuartzClient::BtnHelp; i < QuartzClient::BtnCount; i++)
if(button[i])
button[i]->repaint(false);
widget()->repaint(false);
}
QuartzClient::Position QuartzClient::mousePosition(const QPoint &point) const
{
const int corner = 3*borderSize/2 + 18;
Position pos = PositionCenter;
QRect r(widget()->rect());
if(point.y() < (borderSize-1)) {
if(point.x() < corner) return PositionTopLeft;
else if(point.x() > (r.right()-corner)) return PositionTopRight;
else return PositionTop;
} else if(point.y() > (r.bottom()-borderSize)) {
if(point.x() < corner) return PositionBottomLeft;
else if(point.x() > (r.right()-corner)) return PositionBottomRight;
else return PositionBottom;
} else if(point.x() < borderSize) {
if(point.y() < corner) return PositionTopLeft;
else if(point.y() > (r.bottom()-corner)) return PositionBottomLeft;
else return PositionLeft;
} else if(point.x() > (r.right()-borderSize)) {
if(point.y() < corner) return PositionTopRight;
else if(point.y() > (r.bottom()-corner)) return PositionBottomRight;
else return PositionRight;
}
return pos;
}
void QuartzClient::borders(int& left, int& right, int& top, int& bottom) const
{
left = borderSize;
right = borderSize;
top = 1 + titleHeight + (borderSize-1);
bottom = borderSize;
if ((maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows()) {
left = right = bottom = 0;
top = 1 + titleHeight + (borderSize-1);
}
}
void QuartzClient::resize( const QSize& s )
{
widget()->resize( s );
}
QSize QuartzClient::minimumSize() const
{
return widget()->minimumSize();
}
// The hiding button while shrinking, show button while expanding magic
void QuartzClient::calcHiddenButtons()
{
//Hide buttons in this order:
//Shade, Below, Above, OnAllDesktops, Help, Maximize, Menu, Minimize, Close.
QuartzButton* btnArray[] = { button[BtnShade], button[BtnBelow], button[BtnAbove],
button[BtnOnAllDesktops], button[BtnHelp], button[BtnMax],
button[BtnMenu], button[BtnIconify], button[BtnClose] };
const int buttons_cnt = sizeof( btnArray ) / sizeof( btnArray[ 0 ] );
int minwidth = largeButtons ? 180 : 140; // Start hiding buttons at this width
int btn_width = largeButtons ? 16 : 10;
int current_width = width();
int count = 0;
int i;
// Find out how many buttons we have to hide.
while (current_width < minwidth)
{
current_width += btn_width;
count++;
}
// Bound the number of buttons to hide
if (count > buttons_cnt) count = buttons_cnt;
// Hide the required buttons...
for(i = 0; i < count; i++)
{
if (btnArray[i] && btnArray[i]->isVisible() )
btnArray[i]->hide();
}
// Show the rest of the buttons...
for(i = count; i < buttons_cnt; i++)
{
if (btnArray[i] && (!btnArray[i]->isVisible()) )
btnArray[i]->show();
}
}
// Make sure the menu button follows double click conventions set in kcontrol
void QuartzClient::menuButtonPressed()
{
QRect menuRect = button[BtnMenu]->rect();
QPoint menuTop ( menuRect.topLeft() );
QPoint menuBottom ( menuRect.bottomRight() );
menuTop += QPoint(-1, 2);
menuBottom += QPoint(1, 2);
menuTop = button[BtnMenu]->mapToGlobal( menuTop );
menuBottom = button[BtnMenu]->mapToGlobal( menuBottom );
KDecorationFactory* f = factory();
showWindowMenu(QRect(menuTop, menuBottom));
if( !f->exists( this )) // 'this' was destroyed
return;
button[BtnMenu]->setDown(false);
}
bool QuartzClient::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:
break;
}
return false;
}
}
// Extended KWin plugin interface
/////////////////////////////////
extern "C"
{
KDE_EXPORT KDecorationFactory *create_factory()
{
Quartz::clientHandler = new Quartz::QuartzHandler();
return Quartz::clientHandler;
}
}
#include "quartz.moc"
// vim: ts=4