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