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 = isSetShade();
00765         button[BtnShade]->turnOn(on);
00766         button[BtnShade]->setBitmap(on ? shade_on_bits : shade_off_bits );
00767         button[BtnShade]->repaint(false);
00768         QToolTip::remove( button[BtnShade] );
00769         QToolTip::add( button[BtnShade], on ? i18n("Unshade") : i18n("Shade"));
00770     }
00771 }
00772 
00773 
00774 void QuartzClient::slotMaximize()
00775 {
00776     if (button[BtnMax])
00777     {
00778         maximize(button[BtnMax]->last_button);
00779     }
00780 }
00781 
00782 
00783 void QuartzClient::slotAbove()
00784 {
00785     setKeepAbove( !keepAbove());
00786     button[BtnAbove]->turnOn(keepAbove());
00787     button[BtnAbove]->repaint(true);
00788 }
00789 
00790     
00791 void QuartzClient::slotBelow()
00792 {
00793     setKeepBelow( !keepBelow());
00794     button[BtnBelow]->turnOn(keepBelow());
00795     button[BtnBelow]->repaint(true);
00796 }
00797 
00798         
00799 void QuartzClient::slotShade()
00800 {
00801     setShade( !isSetShade());
00802 }
00803 
00804 
00805 void QuartzClient::resizeEvent( QResizeEvent* e)
00806 {
00807     calcHiddenButtons();
00808 
00809     if (widget()->isVisibleToTLW())
00810     {
00811         widget()->update(widget()->rect());
00812         int dx = 0;
00813         int dy = 0;
00814 
00815         if ( e->oldSize().width() != width() )
00816            dx = 32 + QABS( e->oldSize().width() -  width() );
00817 
00818         if ( e->oldSize().height() != height() )
00819            dy = 8 + QABS( e->oldSize().height() -  height() );
00820 
00821         if ( dy )
00822            widget()->update( 0, height() - dy + 1, width(), dy );
00823 
00824         if ( dx )
00825         {
00826            widget()->update( width() - dx + 1, 0, dx, height() );
00827            widget()->update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) );
00828            widget()->update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, titlebar->geometry().bottom() ) ) );
00829            // Titlebar needs no paint event
00830            widget()->repaint(titlebar->geometry(), false);
00831         }
00832     }
00833 }
00834 
00835 
00836 void QuartzClient::captionChange()
00837 {
00838     widget()->repaint( titlebar->geometry(), false );
00839 }
00840 
00841 
00842 // Quartz Paint magic goes here.
00843 void QuartzClient::paintEvent( QPaintEvent* )
00844 {
00845     // Never paint if the pixmaps have not been created
00846     if (!quartz_initialized)
00847         return;
00848 
00849     const bool maxFull = (maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows();
00850 
00851     QColorGroup g;
00852     QPainter p(widget());
00853 
00854     // Obtain widget bounds.
00855     QRect r(widget()->rect());
00856     int x = r.x();
00857     int y = r.y();
00858     int x2 = r.width() - 1;
00859     int y2 = r.height() - 1;
00860     int w  = r.width();
00861     int h  = r.height();
00862 
00863     // Draw part of the frame that is the title color
00864 
00865     if( coloredFrame )
00866         g = options()->colorGroup(ColorTitleBar, isActive());
00867     else
00868         g = options()->colorGroup(ColorFrame, isActive());
00869 
00870     // Draw outer highlights and lowlights
00871     p.setPen( g.light().light(120) );
00872     p.drawLine( x, y, x2-1, y );
00873     p.drawLine( x, y+1, x, y2-1 );
00874     p.setPen( g.dark().light(120) );
00875     p.drawLine( x2, y, x2, y2 );
00876     p.drawLine( x, y2, x2, y2 );
00877 
00878     // Fill out the border edges
00879     QColor frameColor;
00880     if ( coloredFrame)
00881         frameColor = g.background().light(130);
00882     else
00883         frameColor = g.background();
00884     if (borderSize > 2) {
00885         p.fillRect(x+1, y+1, w-2, borderSize-2, frameColor); // top
00886         if (!maxFull) {
00887             p.fillRect(x+1, y+h-(borderSize-1), w-2, borderSize-2, frameColor); // bottom
00888             p.fillRect(x+1, y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // left
00889             p.fillRect(x+w-(borderSize), y+borderSize-1, borderSize-1, h-2*(borderSize-1), frameColor); // right
00890         }
00891     }
00892 
00893     // Draw a frame around the wrapped widget.
00894     p.setPen( g.background() );
00895     if (maxFull) {
00896         p.drawLine(x+1, y+titleHeight+(borderSize-1), w-2, y+titleHeight+(borderSize-1));
00897     } else {
00898         p.drawRect( x+(borderSize-1), y+titleHeight+(borderSize-1), w-2*(borderSize-1), h-titleHeight-2*(borderSize-1) );
00899     }
00900 
00901     // Drawing this extra line removes non-drawn areas when shaded
00902     p.drawLine( x+borderSize, y2-borderSize, x2-borderSize, y2-borderSize);
00903 
00904     // Highlight top corner
00905     p.setPen( g.light().light(160) );
00906     p.drawPoint( x, y );
00907     p.setPen( g.light().light(140) );
00908     p.drawPoint( x+1, y );
00909     p.drawPoint( x, y+1 );
00910 
00911     // Draw the title bar.
00912     // ===================
00913     r = titlebar->geometry();
00914 
00915     // Obtain titlebar blend colours
00916     QColor c1 = options()->color(ColorTitleBar, isActive() ).light(130);
00917     QColor c2 = options()->color(ColorTitleBlend, isActive() );
00918 
00919     // Create a disposable pixmap buffer for the titlebar
00920     KPixmap* titleBuffer = new KPixmap;
00921     titleBuffer->resize( maxFull?w-2:(w-2*(borderSize-1)), titleHeight );
00922 
00923     QPainter p2( titleBuffer, this );
00924 
00925     // subtract titleBlocks pixmap width and some
00926     int rightoffset = r.x()+r.width()-titleBlocks->width()-borderSize;
00927 
00928     p2.fillRect( 0, 0, w, r.height(), c1 );
00929     p2.fillRect( rightoffset, 0, maxFull?w-rightoffset:w-rightoffset-2*(borderSize-1), r.height(), c2 );
00930 
00931     // 8 bit displays will be a bit dithered, but they still look ok.
00932     if ( isActive() )
00933         p2.drawPixmap( rightoffset, 0, *titleBlocks );
00934     else
00935         p2.drawPixmap( rightoffset, 0, *ititleBlocks );
00936 
00937     // Draw the title text on the pixmap, and with a smaller font
00938     // for toolwindows than the default.
00939     QFont fnt;
00940     if ( largeButtons ) {
00941         fnt = options()->font(true, false); // font not small
00942     } else {
00943         fnt = options()->font(true, true); // font small
00944         fnt.setWeight( QFont::Normal ); // and disable bold
00945     }
00946     p2.setFont( fnt );
00947 
00948     p2.setPen( options()->color(ColorFont, isActive() ));
00949     p2.drawText(r.x()+4-borderSize, 0, r.width()-3, r.height(),
00950                 AlignLeft | AlignVCenter, caption() );
00951     p2.end();
00952 
00953     p.drawPixmap( maxFull?1:borderSize-1, borderSize-1, *titleBuffer );
00954 
00955     delete titleBuffer;
00956 }
00957 
00958 
00959 void QuartzClient::showEvent(QShowEvent *)
00960 {
00961     calcHiddenButtons();
00962     widget()->show();
00963 }
00964 
00965 
00966 void QuartzClient::mouseDoubleClickEvent( QMouseEvent * e )
00967 {
00968     if (titlebar->geometry().contains( e->pos() ) )
00969        titlebarDblClickOperation();
00970 }
00971 
00972 
00973 void QuartzClient::maximizeChange()
00974 {
00975     if (button[BtnMax]) {
00976         button[BtnMax]->setBitmap((maximizeMode()==MaximizeFull) ? minmax_bits : maximize_bits);
00977         button[BtnMax]->setTipText((maximizeMode()==MaximizeFull) ? i18n("Restore") : i18n("Maximize"));
00978     }
00979 }
00980 
00981 
00982 void QuartzClient::activeChange()
00983 {
00984     for(int i=QuartzClient::BtnHelp; i < QuartzClient::BtnCount; i++)
00985         if(button[i])
00986             button[i]->repaint(false);
00987 
00988     widget()->repaint(false);
00989 }
00990 
00991 
00992 QuartzClient::Position QuartzClient::mousePosition(const QPoint &point) const
00993 {
00994     const int corner = 3*borderSize/2 + 18;
00995     Position pos = PositionCenter;
00996 
00997     QRect r(widget()->rect());
00998 
00999     if(point.y() < (borderSize-1)) {
01000         if(point.x() < corner)                   return PositionTopLeft;
01001         else if(point.x() > (r.right()-corner))  return PositionTopRight;
01002         else                                     return PositionTop;
01003     } else if(point.y() > (r.bottom()-borderSize)) {
01004         if(point.x() < corner)                   return PositionBottomLeft;
01005         else if(point.x() > (r.right()-corner))  return PositionBottomRight;
01006         else                                     return PositionBottom;
01007     } else if(point.x() < borderSize) {
01008         if(point.y() < corner)                   return PositionTopLeft;
01009         else if(point.y() > (r.bottom()-corner)) return PositionBottomLeft;
01010         else                                     return PositionLeft;
01011     } else if(point.x() > (r.right()-borderSize)) {
01012         if(point.y() < corner)                   return PositionTopRight;
01013         else if(point.y() > (r.bottom()-corner)) return PositionBottomRight;
01014         else                                     return PositionRight;
01015     }
01016 
01017     return pos;
01018 }
01019 
01020 
01021 void QuartzClient::borders(int& left, int& right, int& top, int& bottom) const
01022 {
01023     left = borderSize;
01024     right = borderSize;
01025     top =  1 + titleHeight + (borderSize-1);
01026     bottom = borderSize;
01027 
01028     if ((maximizeMode()==MaximizeFull) && !options()->moveResizeMaximizedWindows()) {
01029         left = right = bottom = 0;
01030         top = 1 + titleHeight + (borderSize-1);
01031     }
01032 }
01033 
01034 
01035 void QuartzClient::resize( const QSize& s )
01036 {
01037     widget()->resize( s );
01038 }
01039 
01040 
01041 QSize QuartzClient::minimumSize() const
01042 {
01043     return widget()->minimumSize();
01044 }
01045 
01046 
01047 // The hiding button while shrinking, show button while expanding magic
01048 void QuartzClient::calcHiddenButtons()
01049 {
01050     //Hide buttons in this order:
01051     //Shade, Below, Above, OnAllDesktops, Help, Maximize, Menu, Minimize, Close.
01052     QuartzButton* btnArray[] = { button[BtnShade], button[BtnBelow], button[BtnAbove],
01053                                  button[BtnOnAllDesktops], button[BtnHelp], button[BtnMax],
01054                                  button[BtnMenu], button[BtnIconify], button[BtnClose] };
01055     const int buttons_cnt = sizeof( btnArray ) / sizeof( btnArray[ 0 ] );
01056 
01057     int minwidth  = largeButtons ? 180 : 140;   // Start hiding buttons at this width
01058     int btn_width = largeButtons ? 16 : 10;
01059     int current_width = width();
01060     int count = 0;
01061     int i;
01062 
01063     // Find out how many buttons we have to hide.
01064     while (current_width < minwidth)
01065     {
01066         current_width += btn_width;
01067         count++;
01068     }
01069 
01070     // Bound the number of buttons to hide
01071     if (count > buttons_cnt) count = buttons_cnt;
01072 
01073     // Hide the required buttons...
01074     for(i = 0; i < count; i++)
01075     {
01076         if (btnArray[i] && btnArray[i]->isVisible() )
01077             btnArray[i]->hide();
01078     }
01079 
01080     // Show the rest of the buttons...
01081     for(i = count; i < buttons_cnt; i++)
01082     {
01083         if (btnArray[i] && (!btnArray[i]->isVisible()) )
01084             btnArray[i]->show();
01085     }
01086 }
01087 
01088 
01089 // Make sure the menu button follows double click conventions set in kcontrol
01090 void QuartzClient::menuButtonPressed()
01091 {
01092     QRect menuRect = button[BtnMenu]->rect();
01093     QPoint menuTop ( menuRect.topLeft() );
01094     QPoint menuBottom ( menuRect.bottomRight() );
01095     menuTop += QPoint(-1, 2);
01096     menuBottom += QPoint(1, 2);
01097     menuTop = button[BtnMenu]->mapToGlobal( menuTop );
01098     menuBottom = button[BtnMenu]->mapToGlobal( menuBottom );
01099     KDecorationFactory* f = factory();
01100     showWindowMenu(QRect(menuTop, menuBottom));
01101     if( !f->exists( this )) // 'this' was destroyed
01102         return;
01103     button[BtnMenu]->setDown(false);
01104 }
01105 
01106 bool QuartzClient::eventFilter( QObject* o, QEvent* e )
01107 {
01108     if( o != widget())
01109     return false;
01110     switch( e->type())
01111     {
01112     case QEvent::Resize:
01113         resizeEvent(static_cast< QResizeEvent* >( e ) );
01114         return true;
01115     case QEvent::Paint:
01116         paintEvent(static_cast< QPaintEvent* >( e ) );
01117         return true;
01118     case QEvent::MouseButtonDblClick:
01119         mouseDoubleClickEvent(static_cast< QMouseEvent* >( e ) );
01120         return true;
01121     case QEvent::MouseButtonPress:
01122         processMousePressEvent(static_cast< QMouseEvent* >( e ) );
01123         return true;
01124     default:
01125         break;
01126     }
01127     return false;
01128 }
01129 
01130 }
01131 
01132 // Extended KWin plugin interface
01134 extern "C"
01135 {
01136     KDE_EXPORT KDecorationFactory *create_factory()
01137     {
01138         Quartz::clientHandler = new Quartz::QuartzHandler();
01139         return Quartz::clientHandler;
01140     }
01141 }
01142 
01143 
01144 
01145 #include "quartz.moc"
01146 // vim: ts=4
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.90.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Apr 4 11:21:59 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003