kwin Library API Documentation

quartz.cpp

00001 /*
00002  *
00003  * Gallium-Quartz KWin client
00004  *
00005  * Copyright 2001
00006  *   Karol Szwed <gallium@kde.org>
00007  *   http://gallium.n3.net/
00008  *
00009  * Based on the KDE default client.
00010  *
00011  * Includes mini titlebars for ToolWindow Support.
00012  * Button positions are now customizable.
00013  *
00014  */
00015 
00016 #include <kconfig.h>
00017 #include <kdrawutil.h>
00018 #include <kglobal.h>
00019 #include <klocale.h>
00020 #include <kpixmapeffect.h>
00021 #include <qbitmap.h>
00022 #include <qcursor.h>
00023 #include <qdrawutil.h>
00024 #include <qimage.h>
00025 #include <qlabel.h>
00026 #include <qlayout.h>
00027 #include <qtooltip.h>
00028 #include <qapplication.h>
00029 
00030 #include "quartz.h"
00031 
00032 
00033 namespace Quartz {
00034 
00035 static const unsigned char iconify_bits[] = {
00036   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00,
00037   0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
00038 
00039 static const unsigned char close_bits[] = {
00040   0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00,
00041   0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00};
00042 
00043 static const unsigned char maximize_bits[] = {
00044   0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
00045   0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00};
00046 
00047 static const unsigned char minmax_bits[] = {
00048   0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00,
00049   0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00};
00050 
00051 static const unsigned char question_bits[] = {
00052   0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
00053   0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00};
00054 
00055 static const unsigned char pindown_white_bits[] = {
00056   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x80, 0x1f, 0xa0, 0x03,
00057   0xb0, 0x01, 0x30, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x20, 0x00, 0x00, 0x00,
00058   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00059 
00060 static const unsigned char pindown_gray_bits[] = {
00061   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
00062   0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x80, 0x07, 0xc0, 0x03, 0xe0, 0x01,
00063   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00064 
00065 static const unsigned char pindown_dgray_bits[] = {
00066   0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x10, 0x70, 0x20, 0x50, 0x20,
00067   0x48, 0x30, 0xc8, 0x38, 0x08, 0x1f, 0x08, 0x18, 0x10, 0x1c, 0x10, 0x0e,
00068   0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00069 
00070 static const unsigned char pinup_white_bits[] = {
00071   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x11,
00072   0x3f, 0x15, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00073   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00074 
00075 static const unsigned char pinup_gray_bits[] = {
00076   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00077   0x80, 0x0a, 0xbf, 0x0a, 0x80, 0x15, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
00078   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00079 
00080 static const unsigned char pinup_dgray_bits[] = {
00081   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x40, 0x31, 0x40, 0x2e,
00082   0x40, 0x20, 0x40, 0x20, 0x7f, 0x2a, 0x40, 0x3f, 0xc0, 0x31, 0xc0, 0x20,
00083   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00084 
00085 static const unsigned char above_on_bits[] = {
00086   0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x30, 0x00, 0xfc, 0x00, 0x78, 0x00,
00087   0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00088 
00089 static const unsigned char above_off_bits[] = {
00090   0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01,
00091   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00092 
00093 static const unsigned char below_on_bits[] = {
00094   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x78, 0x00, 0xfc, 0x00,
00095   0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00};
00096 
00097 static const unsigned char below_off_bits[] = {
00098   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01,
00099   0x30, 0x00, 0xfc, 0x00, 0x78, 0x00, 0x30, 0x00};
00100 
00101 static const unsigned char shade_on_bits[] = {
00102   0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x02, 0x01, 0x02, 0x01,
00103   0x02, 0x01, 0x02, 0x01, 0xfe, 0x01, 0x00, 0x00};
00104 
00105 static const unsigned char shade_off_bits[] = {
00106   0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
00107   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00108 
00109 
00111 
00112 // Titlebar button positions
00113 bool onAllDesktopsButtonOnLeft = true;
00114 bool coloredFrame       = true;
00115 
00116 KPixmap* titleBlocks    = NULL;
00117 KPixmap* ititleBlocks   = NULL;
00118 KPixmap* pinDownPix     = NULL;
00119 KPixmap* pinUpPix       = NULL;
00120 KPixmap* ipinDownPix    = NULL;
00121 KPixmap* ipinUpPix      = NULL;
00122 static int normalTitleHeight;
00123 static int toolTitleHeight;
00124 static int borderWidth;
00125 
00126 bool quartz_initialized = false;
00127 QuartzHandler* clientHandler;
00128 
00130 
00131 
00132 QuartzHandler::QuartzHandler()
00133 {
00134     quartz_initialized = false;
00135     readConfig();
00136     createPixmaps();
00137     quartz_initialized = true;
00138 }
00139 
00140 
00141 QuartzHandler::~QuartzHandler()
00142 {
00143     quartz_initialized = false;
00144     freePixmaps();
00145 }
00146 
00147 
00148 KDecoration* QuartzHandler::createDecoration( KDecorationBridge* bridge )
00149 {
00150     return new QuartzClient( bridge, this );
00151 }
00152 
00153 
00154 bool QuartzHandler::reset(unsigned long changed)
00155 {
00156     quartz_initialized = false;
00157     freePixmaps();
00158     readConfig();
00159     createPixmaps();
00160     quartz_initialized = true;
00161 
00162     // Do we need to "hit the wooden hammer" ?
00163     bool needHardReset = true;
00164     if (changed & SettingColors)
00165     {
00166         needHardReset = false;
00167     }
00168 
00169     if (needHardReset) {
00170         return true;
00171     } else {
00172         resetDecorations(changed);
00173         return false;
00174     }
00175     return true;
00176 }
00177 
00178 
00179 void QuartzHandler::readConfig()
00180 {
00181     KConfig conf("kwinquartzrc");
00182     conf.setGroup("General");
00183     coloredFrame = conf.readBoolEntry( "UseTitleBarBorderColors", true );
00184 
00185     // A small hack to make the on all desktops button look nicer
00186     onAllDesktopsButtonOnLeft = KDecoration::options()->titleButtonsLeft().contains( 'S' );
00187         if ( QApplication::reverseLayout() )
00188             onAllDesktopsButtonOnLeft = !onAllDesktopsButtonOnLeft;
00189     switch(options()->preferredBorderSize(this)) {
00190     case BorderLarge:
00191         borderWidth = 8;
00192         break;
00193     case BorderVeryLarge:
00194         borderWidth = 12;
00195         break;
00196     case BorderHuge:
00197         borderWidth = 18;
00198         break;
00199     case BorderVeryHuge:
00200         borderWidth = 27;
00201         break;
00202     case BorderOversized:
00203         borderWidth = 40;
00204         break;
00205     case BorderTiny:
00206     case BorderNormal:
00207     default:
00208         borderWidth = 4;
00209     }
00210 
00211     normalTitleHeight = QFontMetrics(options()->font(true)).height();
00212     if (normalTitleHeight < 18) normalTitleHeight = 18;
00213     if (normalTitleHeight < borderWidth) normalTitleHeight = borderWidth;
00214 
00215     toolTitleHeight = QFontMetrics(options()->font(true, true)).height();
00216     if (toolTitleHeight < 12) toolTitleHeight = 12;
00217     if (toolTitleHeight < borderWidth) toolTitleHeight = borderWidth;
00218 }
00219 
00220 
00221 // This does the colour transition magic. (You say "Oh, is that it?")
00222 // This may be made configurable at a later stage
00223 void QuartzHandler::drawBlocks( KPixmap *pi, KPixmap &p, const QColor &c1, const QColor &c2 )
00224 {
00225     QPainter px;
00226 
00227     px.begin( pi );
00228 
00229     // Draw a background gradient first
00230     KPixmapEffect::gradient(p, c1, c2, KPixmapEffect::HorizontalGradient);
00231 
00232     int factor = (pi->height()-2)/4;
00233     int square = factor - (factor+2)/4;
00234 
00235     int x = pi->width() - 5*factor - square;
00236     int y = (pi->height() - 4*factor)/2;
00237 
00238     px.fillRect( x, y,          square, square, c1.light(120) );
00239     px.fillRect( x, y+factor,   square, square, c1 );
00240     px.fillRect( x, y+2*factor, square, square, c1.light(110) );
00241     px.fillRect( x, y+3*factor, square, square, c1 );
00242 
00243     px.fillRect( x+factor, y,          square, square, c1.light(110) );
00244     px.fillRect( x+factor, y+factor,   square, square, c2.light(110) );
00245     px.fillRect( x+factor, y+2*factor, square, square, c1.light(120) );
00246     px.fillRect( x+factor, y+3*factor, square, square, c2.light(130) );
00247 
00248     px.fillRect( x+2*factor, y+factor,   square, square, c1.light(110) );
00249     px.fillRect( x+2*factor, y+2*factor, square, square, c2.light(120) );
00250     px.fillRect( x+2*factor, y+3*factor, square, square, c2.light(150) );
00251 
00252     px.fillRect( x+3*factor, y,          square, square, c1.dark(110) );
00253     px.fillRect( x+3*factor, y+2*factor, square, square, c2.light(120) );
00254     px.fillRect( x+3*factor, y+3*factor, square, square, c1.dark(120) );
00255 
00256     px.fillRect( x+4*factor, y+factor,   square, square, c1.light(110) );
00257     px.fillRect( x+4*factor, y+3*factor, square, square, c1.dark(110) );
00258 
00259     px.fillRect( x+5*factor, y+2*factor, square, square, c2.light(120));
00260     px.fillRect( x+5*factor, y+3*factor, square, square, c2.light(110) );
00261 }
00262 
00263 
00264 // This paints the button pixmaps upon loading the style.
00265 void QuartzHandler::createPixmaps()
00266 {
00267     // Obtain titlebar blend colours, and create the block stuff on pixmaps.
00268     QColorGroup g2 = options()->colorGroup(ColorTitleBlend, true);
00269     QColor c2 = g2.background();
00270     g2 = options()->colorGroup(ColorTitleBar, true );
00271     QColor c = g2.background().light(130);
00272 
00273     titleBlocks = new KPixmap();
00274     titleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight );
00275     drawBlocks( titleBlocks, *titleBlocks, c, c2 );
00276 
00277     g2 = options()->colorGroup(ColorTitleBlend, false);
00278     c2 = g2.background();
00279     g2 = options()->colorGroup(ColorTitleBar, false );
00280     c = g2.background().light(130);
00281 
00282     ititleBlocks = new KPixmap();
00283     ititleBlocks->resize( normalTitleHeight*25/18, normalTitleHeight );
00284     drawBlocks( ititleBlocks, *ititleBlocks, c, c2 );
00285 
00286     // Set the on all desktops pin pixmaps;
00287     QColorGroup g;
00288     QPainter p;
00289 
00290     g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, true );
00291     c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background();
00292     g2 = options()->colorGroup( ColorButtonBg, true );
00293 
00294     pinUpPix = new KPixmap();
00295     pinUpPix->resize(16, 16);
00296     p.begin( pinUpPix );
00297     p.fillRect( 0, 0, 16, 16, c);
00298     kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits,
00299                     pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL );
00300     p.end();
00301 
00302     pinDownPix = new KPixmap();
00303     pinDownPix->resize(16, 16);
00304     p.begin( pinDownPix );
00305     p.fillRect( 0, 0, 16, 16, c);
00306     kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits,
00307                     pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL );
00308     p.end();
00309 
00310 
00311     // Inactive pins
00312     g = options()->colorGroup( onAllDesktopsButtonOnLeft ? ColorTitleBar : ColorTitleBlend, false );
00313     c = onAllDesktopsButtonOnLeft ? g.background().light(130) : g.background();
00314     g2 = options()->colorGroup( ColorButtonBg, false );
00315 
00316     ipinUpPix = new KPixmap();
00317     ipinUpPix->resize(16, 16);
00318     p.begin( ipinUpPix );
00319     p.fillRect( 0, 0, 16, 16, c);
00320     kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pinup_white_bits,
00321                     pinup_gray_bits, NULL, NULL, pinup_dgray_bits, NULL );
00322     p.end();
00323 
00324     ipinDownPix = new KPixmap();
00325     ipinDownPix->resize(16, 16);
00326     p.begin( ipinDownPix );
00327     p.fillRect( 0, 0, 16, 16, c);
00328     kColorBitmaps( &p, g2, 0, 1, 16, 16, true, pindown_white_bits,
00329                     pindown_gray_bits, NULL, NULL, pindown_dgray_bits, NULL );
00330     p.end();
00331 }
00332 
00333 
00334 void QuartzHandler::freePixmaps()
00335 {
00336     delete titleBlocks;
00337     delete ititleBlocks;
00338 
00339     // On all desktops pin images
00340     delete pinUpPix;
00341     delete ipinUpPix;
00342     delete pinDownPix;
00343     delete ipinDownPix;
00344 }
00345 
00346 
00347 QValueList< QuartzHandler::BorderSize > QuartzHandler::borderSizes() const
00348 { // the list must be sorted
00349   return QValueList< BorderSize >() << BorderNormal << BorderLarge <<
00350       BorderVeryLarge <<  BorderHuge << BorderVeryHuge << BorderOversized;
00351 }
00352 
00353 
00354 QuartzButton::QuartzButton(QuartzClient *parent, const char *name, bool largeButton,
00355         bool isLeftButton, bool isOnAllDesktopsButton, const unsigned char *bitmap,
00356         const QString& tip, const int realizeBtns)
00357     : QButton(parent->widget(), name),
00358       last_button(NoButton)
00359 {
00360     setTipText(tip);
00361     setCursor(ArrowCursor);
00362 
00363     // Eliminate any possible background flicker
00364     setBackgroundMode( QWidget::NoBackground );
00365     setToggleButton( isOnAllDesktopsButton );
00366 
00367     realizeButtons = realizeBtns;
00368 
00369     deco     = NULL;
00370         large    = largeButton;
00371         if ( QApplication::reverseLayout() )
00372              isLeft      = !isLeftButton;
00373          else
00374              isLeft      = isLeftButton;
00375     isOnAllDesktops = isOnAllDesktopsButton;
00376     client   = parent;
00377 
00378     if ( large )
00379        setFixedSize(normalTitleHeight-2, normalTitleHeight-2);
00380     else
00381        setFixedSize(toolTitleHeight-2, toolTitleHeight-2);
00382 
00383     if(bitmap)
00384         setBitmap(bitmap);
00385 }
00386 
00387 
00388 QuartzButton::~QuartzButton()
00389 {
00390     delete deco;
00391 }
00392 
00393 
00394 QSize QuartzButton::sizeHint() const
00395 {
00396    if ( large )
00397       return( QSize(normalTitleHeight-2, normalTitleHeight-2) );
00398    else
00399       return( QSize(toolTitleHeight-2, toolTitleHeight-2) );
00400 }
00401 
00402 
00403 void QuartzButton::setBitmap(const unsigned char *bitmap)
00404 {
00405     delete deco;
00406 
00407     deco = new QBitmap(10, 10, bitmap, true);
00408     deco->setMask( *deco );
00409     repaint( false );
00410 }
00411 
00412 
00413 void QuartzButton::setTipText(const QString &tip) {
00414     if(KDecoration::options()->showTooltips()) {
00415         QToolTip::remove(this );
00416         QToolTip::add(this, tip );
00417     }
00418 }
00419 
00420 
00421 void QuartzButton::drawButton(QPainter *p)
00422 {
00423     // Never paint if the pixmaps have not been created
00424     if (!quartz_initialized)
00425         return;
00426 
00427     QColor c;
00428 
00429     if (isLeft)
00430         c = KDecoration::options()->color(KDecoration::ColorTitleBar, client->isActive()).light(130);
00431     else
00432         c = KDecoration::options()->color(KDecoration::ColorTitleBlend, client->isActive());
00433 
00434     // Fill the button background with an appropriate color
00435     p->fillRect(0, 0, width(), height(), c );
00436 
00437     // If we have a decoration bitmap, then draw that
00438     // otherwise we paint a menu button (with mini icon), or a onAllDesktops button.
00439     if( deco )
00440     {
00441         int xOff = (width()-10)/2;
00442         int yOff = (height()-10)/2;
00443         p->setPen( Qt::black );
00444         p->drawPixmap(isDown() ? xOff+2: xOff+1, isDown() ? yOff+2 : yOff+1, *deco);
00445         p->setPen( KDecoration::options()->color(KDecoration::ColorButtonBg, client->isActive()).light(150) );
00446         p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, *deco);
00447     } else
00448         {
00449             QPixmap btnpix;
00450             int Offset = 0;
00451 
00452             if (isOnAllDesktops)
00453             {
00454                 if (isDown())
00455                     Offset = 1;
00456 
00457                 // Select the right onAllDesktops button to paint
00458                 if (client->isActive())
00459                     btnpix = isOn() ? *pinDownPix : *pinUpPix;
00460                 else
00461                     btnpix = isOn() ? *ipinDownPix : *ipinUpPix;
00462 
00463             } else
00464                 btnpix = client->icon().pixmap( QIconSet::Small, QIconSet::Normal);
00465 
00466             // Shrink the miniIcon for tiny titlebars.
00467             if ( height() < 16)
00468             {
00469                 QPixmap tmpPix;
00470 
00471                 // Smooth scale the image
00472                 tmpPix.convertFromImage( btnpix.convertToImage().smoothScale(height(), height()));
00473                 p->drawPixmap( 0, 0, tmpPix );
00474             } else {
00475                 Offset += (height() - 16)/2;
00476                 p->drawPixmap( Offset, Offset, btnpix );
00477             }
00478     }
00479 }
00480 
00481 
00482 // Make the protected member public
00483 void QuartzButton::turnOn( bool isOn )
00484 {
00485     if ( isToggleButton() )
00486         setOn( isOn );
00487 }
00488 
00489 
00490 void QuartzButton::mousePressEvent( QMouseEvent* e )
00491 {
00492     last_button = e->button();
00493     QMouseEvent me( e->type(), e->pos(), e->globalPos(),
00494                     (e->button()&realizeButtons)?LeftButton:NoButton, e->state() );
00495     QButton::mousePressEvent( &me );
00496 }
00497 
00498 
00499 void QuartzButton::mouseReleaseEvent( QMouseEvent* e )
00500 {
00501     last_button = e->button();
00502     QMouseEvent me( e->type(), e->pos(), e->globalPos(),
00503                     (e->button()&realizeButtons)?LeftButton:NoButton, e->state() );
00504     QButton::mouseReleaseEvent( &me );
00505 }
00506 
00507 
00509 
00510 QuartzClient::QuartzClient(KDecorationBridge* bridge, KDecorationFactory* factory)
00511     : KDecoration (bridge, factory)
00512 {
00513 }
00514 
00515 
00516 void QuartzClient::init()
00517 {
00518     connect( this, SIGNAL( keepAboveChanged( bool )), SLOT( keepAboveChange( bool )));
00519     connect( this, SIGNAL( keepBelowChanged( bool )), SLOT( keepBelowChange( bool )));
00520 
00521     createMainWidget(WNoAutoErase | WStaticContents);
00522 
00523     widget()->installEventFilter( this );
00524 
00525     // No flicker thanks
00526     widget()->setBackgroundMode( QWidget::NoBackground );
00527 
00528     // Set button pointers to NULL so we can track things
00529     for(int i=0; i < QuartzClient::BtnCount; i++)
00530         button[i] = NULL;
00531 
00532     // Finally, toolWindows look small
00533     if ( isTool() ) {
00534         titleHeight  = toolTitleHeight;
00535         largeButtons = false;
00536     }
00537     else {
00538         titleHeight = normalTitleHeight;
00539         largeButtons = true;
00540     }
00541 
00542     borderSize = borderWidth;
00543 
00544     // Pack the fake window window within a grid
00545     QGridLayout* g = new QGridLayout(widget(), 0, 0, 0);
00546     g->setResizeMode(QLayout::FreeResize);
00547     g->addRowSpacing(0, borderSize-1);       // Top grab bar
00548     if( isPreview())
00549     g->addWidget(new QLabel( i18n( "<center><b>Quartz</b></center>" ), widget()), 3, 1);
00550     else
00551     g->addItem(new QSpacerItem( 0, 0 ), 3, 1); // no widget in the middle
00552 
00553     // without the next line, unshade flickers
00554     g->addItem( new QSpacerItem( 0, 0, QSizePolicy::Fixed,
00555                 QSizePolicy::Expanding ) );
00556     g->setRowStretch(3, 10);      // Wrapped window
00557     g->addRowSpacing(2, 1);       // line under titlebar
00558     g->addRowSpacing(4, borderSize);       // bottom handles
00559     g->addColSpacing(0, borderSize);
00560     g->addColSpacing(2, borderSize);
00561 
00562     // Pack the titlebar HBox with items
00563     hb = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0);
00564     hb->setResizeMode( QLayout::FreeResize );
00565     g->addLayout ( hb, 1, 1 );
00566 
00567     addClientButtons( options()->titleButtonsLeft() );
00568 
00569     titlebar = new QSpacerItem( 10, titleHeight, QSizePolicy::Expanding, QSizePolicy::Minimum );
00570     hb->addItem(titlebar);
00571     hb->addSpacing(2);
00572 
00573     addClientButtons( options()->titleButtonsRight(), false );
00574 
00575     hb->addSpacing(2);
00576 }
00577 
00578 void QuartzClient::reset( unsigned long changed )
00579 {
00580     if (changed & SettingColors || changed & SettingFont)
00581     {
00582         // repaint the whole thing
00583         widget()->repaint(false);
00584     }
00585 }
00586 
00587 const int SUPPORTED_WINDOW_TYPES_MASK = NET::NormalMask | NET::DesktopMask | NET::DockMask
00588     | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask
00589     | NET::UtilityMask | NET::SplashMask;
00590 
00591 bool QuartzClient::isTool()
00592 {
00593     NET::WindowType type = windowType( SUPPORTED_WINDOW_TYPES_MASK );
00594     return ((type==NET::Toolbar)||(type==NET::Utility)||(type==NET::Menu));
00595 }
00596 
00597 
00598 void QuartzClient::addClientButtons( const QString& s, bool isLeft )
00599 {
00600     if (s.length() > 0)
00601         for(unsigned int i = 0; i < s.length(); i++)
00602         {
00603             switch( s[i].latin1() )
00604             {
00605                 // Menu button
00606                 case 'M':
00607                     if (!button[BtnMenu])
00608                     {
00609                         button[BtnMenu] = new QuartzButton(this, "menu",
00610                                  largeButtons, isLeft, false, NULL, i18n("Menu"), LeftButton|RightButton);
00611                         connect( button[BtnMenu], SIGNAL(pressed()),
00612                                  this, SLOT(menuButtonPressed()) );
00613                         hb->addWidget( button[BtnMenu] );
00614                     }
00615                     break;
00616 
00617                 // On all desktops button
00618                 case 'S':
00619                     if (!button[BtnOnAllDesktops])
00620                     {
00621                         button[BtnOnAllDesktops] = new QuartzButton(this, "on_all_desktops",
00622                                  largeButtons, isLeft, true, NULL, isOnAllDesktops()?i18n("Not on all desktops"):i18n("On all desktops"));
00623                         button[BtnOnAllDesktops]->turnOn( isOnAllDesktops() );
00624                         connect( button[BtnOnAllDesktops], SIGNAL(clicked()),
00625                                  this, SLOT(toggleOnAllDesktops()) );
00626                         hb->addSpacing(1);
00627                         hb->addWidget( button[BtnOnAllDesktops] );
00628                         hb->addSpacing(1);
00629                     }
00630                     break;
00631 
00632                 // Help button
00633                 case 'H':
00634                     if( providesContextHelp() && (!button[BtnHelp]) )
00635                     {
00636                         button[BtnHelp] = new QuartzButton(this, "help",
00637                                  largeButtons, isLeft, true, question_bits, i18n("Help"));
00638                         connect( button[BtnHelp], SIGNAL( clicked() ),
00639                                  this, SLOT(showContextHelp()));
00640                         hb->addWidget( button[BtnHelp] );
00641                     }
00642                     break;
00643 
00644                 // Minimize button
00645                 case 'I':
00646                     if ( (!button[BtnIconify]) && isMinimizable())
00647                     {
00648                         button[BtnIconify] = new QuartzButton(this, "iconify",
00649                                  largeButtons, isLeft, true, iconify_bits, i18n("Minimize"));
00650                         connect( button[BtnIconify], SIGNAL( clicked()),
00651                                  this, SLOT(minimize()) );
00652                         hb->addWidget( button[BtnIconify] );
00653                     }
00654                     break;
00655 
00656                 // Maximize button
00657                 case 'A':
00658                     if ( (!button[BtnMax]) && isMaximizable())
00659                     {
00660                         button[BtnMax]  = new QuartzButton(this, "maximize",
00661                                  largeButtons, isLeft, true, maximize_bits, i18n("Maximize"), LeftButton|MidButton|RightButton);
00662                         connect( button[BtnMax], SIGNAL( clicked()),
00663                                  this, SLOT(slotMaximize()) );
00664                         hb->addWidget( button[BtnMax] );
00665                     }
00666                     break;
00667 
00668                 // Close button
00669                 case 'X':
00670                     if (!button[BtnClose] && isCloseable())
00671                     {
00672                         button[BtnClose] = new QuartzButton(this, "close",
00673                                  largeButtons, isLeft, true, close_bits, i18n("Close"));
00674                         connect( button[BtnClose], SIGNAL( clicked()),
00675                                  this, SLOT(closeWindow()) );
00676                         hb->addWidget( button[BtnClose] );
00677                     }
00678                     break;
00679 
00680                 // Above button
00681                 case 'F':
00682                     if ( (!button[BtnAbove]))
00683                     {
00684                         button[BtnAbove]  = new QuartzButton(this, "above",
00685                                 largeButtons, isLeft, true,
00686                                 keepAbove() ? above_on_bits : above_off_bits,
00687                                 i18n("Keep Above Others"));
00688                         connect( button[BtnAbove], SIGNAL( clicked()),
00689                                  this, SLOT(slotAbove()) );
00690                         hb->addWidget( button[BtnAbove] );
00691                     }
00692                     break;
00693                         
00694                 // Below button
00695                 case 'B':
00696                     if ( (!button[BtnBelow]))
00697                     {
00698                         button[BtnBelow]  = new QuartzButton(this, "below",
00699                                 largeButtons, isLeft, true,
00700                                 keepBelow() ? below_on_bits : below_off_bits,
00701                                 i18n("Keep Below Others"));
00702                         connect( button[BtnBelow], SIGNAL( clicked()),
00703                                  this, SLOT(slotBelow()) );
00704                         hb->addWidget( button[BtnBelow] );
00705                     }
00706                     break;
00707                             
00708                 // Shade button
00709                 case 'L':
00710                     if ( (!button[BtnShade]) && isShadeable())
00711                     {
00712                         button[BtnShade]  = new QuartzButton(this, "shade",
00713                                 largeButtons, isLeft, true,
00714                                 isSetShade() ? shade_on_bits : shade_off_bits,
00715                                 isSetShade() ? i18n( "Unshade" ) : i18n("Shade"));
00716                         connect( button[BtnShade], SIGNAL( clicked()),
00717                                  this, SLOT(slotShade()) );
00718                         hb->addWidget( button[BtnShade] );
00719                     }
00720                     break;
00721             }
00722         }
00723 }
00724 
00725 
00726 void QuartzClient::iconChange()
00727 {
00728     if (button[BtnMenu] && button[BtnMenu]->isVisible())
00729         button[BtnMenu]->repaint(false);
00730 }
00731 
00732 
00733 void QuartzClient::desktopChange()
00734 {
00735     if (button[BtnOnAllDesktops])
00736     {
00737         button[BtnOnAllDesktops]->turnOn(isOnAllDesktops());
00738         button[BtnOnAllDesktops]->repaint(false);
00739         button[BtnOnAllDesktops]->setTipText(isOnAllDesktops() ? i18n("Not on all desktops") : i18n("On all desktops"));
00740     }
00741 }
00742 
00743 
00744 void QuartzClient::keepAboveChange( bool above )
00745 {
00746     if (button[BtnAbove]) {
00747         button[BtnAbove]->setBitmap( above ? above_on_bits : above_off_bits );
00748         button[BtnAbove]->repaint(false);
00749     }
00750 }
00751 
00752         
00753 void QuartzClient::keepBelowChange( bool below )
00754 {
00755     if (button[BtnBelow]) {
00756         button[BtnBelow]->setBitmap( below ? below_on_bits : below_off_bits );
00757         button[BtnBelow]->repaint(false);
00758     }
00759 }
00760 
00761 void QuartzClient::shadeChange()
00762 {
00763     if (button[BtnShade]) {
00764         bool on = isShade();
00765         button[BtnShade]->turnOn(on);
00766         button[BtnShade]->repaint(false);
00767         QToolTip::remove( button[BtnShade] );
00768         QToolTip::add( button[BtnShade], on ? i18n("Unshade") : i18n("Shade"));
00769     }
00770 }
00771 
00772 
00773 void QuartzClient::slotMaximize()
00774 {
00775     if (button[BtnMax])
00776     {
00777         maximize(button[BtnMax]->last_button);
00778     }
00779 }
00780 
00781 
00782 void QuartzClient::slotAbove()
00783 {
00784     setKeepAbove( !keepAbove());
00785     button[BtnAbove]->turnOn(keepAbove());
00786     button[BtnAbove]->repaint(true);
00787 }
00788 
00789     
00790 void QuartzClient::slotBelow()
00791 {
00792     setKeepBelow( !keepBelow());
00793     button[BtnBelow]->turnOn(keepBelow());
00794     button[BtnBelow]->repaint(true);
00795 }
00796 
00797         
00798 void QuartzClient::slotShade()
00799 {
00800     setShade( !isSetShade());
00801     button[BtnShade]->setBitmap(isSetShade() ? shade_on_bits : shade_off_bits );
00802     button[BtnShade]->repaint(true);
00803 }
00804 
00805 
00806 void QuartzClient::resizeEvent( QResizeEvent* e)
00807 {
00808     calcHiddenButtons();
00809 
00810     if (widget()->isVisibleToTLW())
00811     {
00812         widget()->update(widget()->rect());
00813         int dx = 0;
00814         int dy = 0;
00815 
00816         if ( e->oldSize().width() != width() )
00817            dx = 32 + QABS( e->oldSize().width() -  width() );
00818 
00819         if ( e->oldSize().height() != height() )
00820            dy = 8 + QABS( e->oldSize().height() -  height() );
00821 
00822         if ( dy )
00823            widget()->update( 0, height() - dy + 1, width(), dy );
00824 
00825         if ( dx )
00826         {
00827            widget()->update( width() - dx + 1, 0, dx, height() );
00828            widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) );
00829            widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, titlebar->geometry().bottom() ) ) );
00830            // Titlebar needs no paint event
00831            widget()->repaint(titlebar->geometry(), false);
00832         }
00833     }
00834 }
00835 
00836 
00837 void QuartzClient::captionChange()
00838 {
00839     widget()->repaint( titlebar->geometry(), false );
00840 }
00841 
00842 
00843 // Quartz Paint magic goes here.
00844 void QuartzClient::paintEvent( QPaintEvent* )
00845 {
00846     // Never paint if the pixmaps have not been created
00847     if (!quartz_initialized)
00848         return;
00849 
00850     const bool maxFull = (maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows();
00851 
00852     QColorGroup g;
00853     QPainter p(widget());
00854 
00855     // Obtain widget bounds.
00856     QRect r(widget()->rect());
00857     int x = r.x();
00858     int y = r.y();
00859     int x2 = r.width() - 1;
00860     int y2 = r.height() - 1;
00861     int w  = r.width();
00862     int h  = r.height();
00863 
00864     // Draw part of the frame that is the title color
00865 
00866     if( coloredFrame )
00867         g = options()->colorGroup(ColorTitleBar, isActive());
00868     else
00869         g = options()->colorGroup(ColorFrame, isActive());
00870 
00871     // Draw outer highlights and lowlights
00872     p.setPen( g.light().light(120) );
00873     p.drawLine( x, y, x2-1, y );
00874     p.drawLine( x, y+1, x, y2-1 );
00875     p.setPen( g.dark().light(120) );
00876     p.drawLine( x2, y, x2, y2 );
00877     p.drawLine( x, y2, x2, y2 );
00878 
00879     // Fill out the border edges
00880     QColor frameColor;
00881     if ( coloredFrame)
00882         frameColor = g.background().light(130);
00883     else
00884         frameColor = g.background();
00885     if (borderSize > 2) {
00886         p.fillRect(x+1, y+1, w-2, borderSize-2, frameColor); // top
00887         if (!maxFull) {
00888             p.fillRect(x+1, y+h-(borderSize-1), w-2, borderSize-2, frameColor); // bottom
00889             p.fillRect(x+1, y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // left
00890             p.fillRect(x+w-(borderSize), y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // right
00891         }
00892     }
00893 
00894     // Draw a frame around the wrapped widget.
00895     p.setPen( g.background() );
00896     if (maxFull) {
00897         p.drawLine(x+1, y+titleHeight+(borderSize-1), w-2, y+titleHeight+(borderSize-1));
00898     } else {
00899         p.drawRect( x+(borderSize-1), y+titleHeight+(borderSize-1), w-2*(borderSize-1), h-titleHeight-2*(borderSize-1) );
00900     }
00901 
00902     // Drawing this extra line removes non-drawn areas when shaded
00903     p.drawLine( x+borderSize, y2-borderSize, x2-borderSize, y2-borderSize);
00904 
00905     // Highlight top corner
00906     p.setPen( g.light().light(160) );
00907     p.drawPoint( x, y );
00908     p.setPen( g.light().light(140) );
00909     p.drawPoint( x+1, y );
00910     p.drawPoint( x, y+1 );
00911 
00912     // Draw the title bar.
00913     // ===================
00914     r = titlebar->geometry();
00915 
00916     // Obtain titlebar blend colours
00917     QColor c1 = options()->color(ColorTitleBar, isActive() ).light(130);
00918     QColor c2 = options()->color(ColorTitleBlend, isActive() );
00919 
00920     // Create a disposable pixmap buffer for the titlebar
00921     KPixmap* titleBuffer = new KPixmap;
00922     titleBuffer->resize( maxFull?w-2:(w-2*(borderSize-1)), titleHeight );
00923 
00924     QPainter p2( titleBuffer, this );
00925 
00926     // subtract titleBlocks pixmap width and some
00927     int rightoffset = r.x()+r.width()-titleBlocks->width()-borderSize;
00928 
00929     p2.fillRect( 0, 0, w, r.height(), c1 );
00930     p2.fillRect( rightoffset, 0, maxFull?w-rightoffset:w-rightoffset-2*(borderSize-1), r.height(), c2 );
00931 
00932     // 8 bit displays will be a bit dithered, but they still look ok.
00933     if ( isActive() )
00934         p2.drawPixmap( rightoffset, 0, *titleBlocks );
00935     else
00936         p2.drawPixmap( rightoffset, 0, *ititleBlocks );
00937 
00938     // Draw the title text on the pixmap, and with a smaller font
00939     // for toolwindows than the default.
00940     QFont fnt;
00941     if ( largeButtons ) {
00942         fnt = options()->font(true, false); // font not small
00943     } else {
00944         fnt = options()->font(true, true); // font small
00945         fnt.setWeight( QFont::Normal ); // and disable bold
00946     }
00947     p2.setFont( fnt );
00948 
00949     p2.setPen( options()->color(ColorFont, isActive() ));
00950     p2.drawText(r.x()+4-borderSize, 0, r.width()-3, r.height(),
00951                 AlignLeft | AlignVCenter, caption() );
00952     p2.end();
00953 
00954     p.drawPixmap( maxFull?1:borderSize-1, borderSize-1, *titleBuffer );
00955 
00956     delete titleBuffer;
00957 }
00958 
00959 
00960 void QuartzClient::showEvent(QShowEvent *)
00961 {
00962     calcHiddenButtons();
00963     widget()->show();
00964 }
00965 
00966 
00967 void QuartzClient::mouseDoubleClickEvent( QMouseEvent * e )
00968 {
00969     if (titlebar->geometry().contains( e->pos() ) )
00970        titlebarDblClickOperation();
00971 }
00972 
00973 
00974 void QuartzClient::maximizeChange()
00975 {
00976     if (button[BtnMax]) {
00977         button[BtnMax]->setBitmap((maximizeMode()==MaximizeFull) ? minmax_bits : maximize_bits);
00978         button[BtnMax]->setTipText((maximizeMode()==MaximizeFull) ? i18n("Restore") : i18n("Maximize"));
00979     }
00980 }
00981 
00982 
00983 void QuartzClient::activeChange()
00984 {
00985     for(int i=QuartzClient::BtnHelp; i < QuartzClient::BtnCount; i++)
00986         if(button[i])
00987             button[i]->repaint(false);
00988 
00989     widget()->repaint(false);
00990 }
00991 
00992 
00993 QuartzClient::Position QuartzClient::mousePosition(const QPoint &point) const
00994 {
00995     const int corner = 3*borderSize/2 + 18;
00996     Position pos = PositionCenter;
00997 
00998     QRect r(widget()->rect());
00999 
01000     if(point.y() < (borderSize-1)) {
01001         if(point.x() < corner)                   return PositionTopLeft;
01002         else if(point.x() > (r.right()-corner))  return PositionTopRight;
01003         else                                     return PositionTop;
01004     } else if(point.y() > (r.bottom()-borderSize)) {
01005         if(point.x() < corner)                   return PositionBottomLeft;
01006         else if(point.x() > (r.right()-corner))  return PositionBottomRight;
01007         else                                     return PositionBottom;
01008     } else if(point.x() < borderSize) {
01009         if(point.y() < corner)                   return PositionTopLeft;
01010         else if(point.y() > (r.bottom()-corner)) return PositionBottomLeft;
01011         else                                     return PositionLeft;
01012     } else if(point.x() > (r.right()-borderSize)) {
01013         if(point.y() < corner)                   return PositionTopRight;
01014         else if(point.y() > (r.bottom()-corner)) return PositionBottomRight;
01015         else                                     return PositionRight;
01016     }
01017 
01018     return pos;
01019 }
01020 
01021 
01022 void QuartzClient::borders(int& left, int& right, int& top, int& bottom) const
01023 {
01024     left = borderSize;
01025     right = borderSize;
01026     top =  1 + titleHeight + (borderSize-1);
01027     bottom = borderSize;
01028 
01029     if ((maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows()) {
01030         left = right = bottom = 0;
01031         top = 1 + titleHeight + (borderSize-1);
01032     }
01033 }
01034 
01035 
01036 void QuartzClient::resize( const QSize& s )
01037 {
01038     widget()->resize( s );
01039 }
01040 
01041 
01042 QSize QuartzClient::minimumSize() const
01043 {
01044     return widget()->minimumSize();
01045 }
01046 
01047 
01048 // The hiding button while shrinking, show button while expanding magic
01049 void QuartzClient::calcHiddenButtons()
01050 {
01051     //Hide buttons in this order:
01052     //Shade, Below, Above, OnAllDesktops, Help, Maximize, Menu, Minimize, Close.
01053     QuartzButton* btnArray[] = { button[BtnShade], button[BtnBelow], button[BtnAbove],
01054                                  button[BtnOnAllDesktops], button[BtnHelp], button[BtnMax],
01055                                  button[BtnMenu], button[BtnIconify], button[BtnClose] };
01056     const int buttons_cnt = sizeof( btnArray ) / sizeof( btnArray[ 0 ] );
01057 
01058     int minwidth  = largeButtons ? 180 : 140;   // Start hiding buttons at this width
01059     int btn_width = largeButtons ? 16 : 10;
01060     int current_width = width();
01061     int count = 0;
01062     int i;
01063 
01064     // Find out how many buttons we have to hide.
01065     while (current_width < minwidth)
01066     {
01067         current_width += btn_width;
01068         count++;
01069     }
01070 
01071     // Bound the number of buttons to hide
01072     if (count > buttons_cnt) count = buttons_cnt;
01073 
01074     // Hide the required buttons...
01075     for(i = 0; i < count; i++)
01076     {
01077         if (btnArray[i] && btnArray[i]->isVisible() )
01078             btnArray[i]->hide();
01079     }
01080 
01081     // Show the rest of the buttons...
01082     for(i = count; i < buttons_cnt; i++)
01083     {
01084         if (btnArray[i] && (!btnArray[i]->isVisible()) )
01085             btnArray[i]->show();
01086     }
01087 }
01088 
01089 
01090 // Make sure the menu button follows double click conventions set in kcontrol
01091 void QuartzClient::menuButtonPressed()
01092 {
01093     QRect menuRect = button[BtnMenu]->rect();
01094     QPoint menuTop ( menuRect.topLeft() );
01095     QPoint menuBottom ( menuRect.bottomRight() );
01096     menuTop += QPoint(-1, 2);
01097     menuBottom += QPoint(1, 2);
01098     menuTop = button[BtnMenu]->mapToGlobal( menuTop );
01099     menuBottom = button[BtnMenu]->mapToGlobal( menuBottom );
01100     KDecorationFactory* f = factory();
01101     showWindowMenu(QRect(menuTop, menuBottom));
01102     if( !f->exists( this )) // 'this' was destroyed
01103         return;
01104     button[BtnMenu]->setDown(false);
01105 }
01106 
01107 bool QuartzClient::eventFilter( QObject* o, QEvent* e )
01108 {
01109     if( o != widget())
01110     return false;
01111     switch( e->type())
01112     {
01113     case QEvent::Resize:
01114         resizeEvent(static_cast< QResizeEvent* >( e ) );
01115         return true;
01116     case QEvent::Paint:
01117         paintEvent(static_cast< QPaintEvent* >( e ) );
01118         return true;
01119     case QEvent::MouseButtonDblClick:
01120         mouseDoubleClickEvent(static_cast< QMouseEvent* >( e ) );
01121         return true;
01122     case QEvent::MouseButtonPress:
01123         processMousePressEvent(static_cast< QMouseEvent* >( e ) );
01124         return true;
01125     default:
01126         break;
01127     }
01128     return false;
01129 }
01130 
01131 }
01132 
01133 // Extended KWin plugin interface
01135 extern "C"
01136 {
01137     KDecorationFactory *create_factory()
01138     {
01139         Quartz::clientHandler = new Quartz::QuartzHandler();
01140         return Quartz::clientHandler;
01141     }
01142 }
01143 
01144 
01145 
01146 #include "quartz.moc"
01147 // vim: ts=4
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Sep 25 20:35:06 2004 by doxygen 1.3.8-20040913 written by Dimitri van Heesch, © 1997-2003