/* * * Gallium-Quartz KWin client * * Copyright 2001 * Karol Szwed * http://gallium.n3.net/ * * Based on the KDE default client. * * Includes mini titlebars for ToolWindow Support. * Button positions are now customizable. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; } 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( "
Quartz
" ), 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" { KDecorationFactory *create_factory() { Quartz::clientHandler = new Quartz::QuartzHandler(); return Quartz::clientHandler; } } #include "quartz.moc" // vim: ts=4