kwin Library API Documentation

redmond.cpp

00001 /*
00002  * $Id: redmond.cpp,v 1.23.2.1 2004/01/21 19:16:12 giessl Exp $
00003  *
00004  * Redmond KWin client
00005  *
00006  * Copyright 2001
00007  *   Karol Szwed <gallium@kde.org>
00008  *   http://gallium.n3.net/
00009  *
00010  * Based on the default KWin client.
00011  *
00012  * Updated to support toolwindows 3/2001 (KS)
00013  *
00014  */
00015 
00016 #include "redmond.h"
00017 
00018 #include <qlayout.h>
00019 #include <qdrawutil.h>
00020 #include <qdatetime.h>
00021 #include <kpixmapeffect.h>
00022 #include <kimageeffect.h>
00023 #include <kdrawutil.h>
00024 #include <klocale.h>
00025 
00026 #include <qbitmap.h>
00027 #include <qtooltip.h>
00028 #include <qimage.h>
00029 #include <qlabel.h>
00030 #include <qapplication.h>
00031 
00032 namespace Redmond {
00033 
00034 static const char *kdelogo[] = {
00035 /* columns rows colors chars-per-pixel */
00036 "16 16 8 1",
00037 "   c None",
00038 ".  c #000000",
00039 "+  c #A0A0A4",
00040 "@  c #FFFFFF",
00041 "#  c #585858",
00042 "$  c #C0C0C0",
00043 "%  c #808080",
00044 "&  c #DCDCDC",
00045 "                ",
00046 "     ..    ..   ",
00047 "    .+@.  .@#.  ",
00048 "   .@@@. .@@@#  ",
00049 "   .@@@..$@@$.  ",
00050 "   .@@@.@@@$.   ",
00051 "   .@@@%@@$.    ",
00052 "   .@@@&@@.     ",
00053 "   .@@@@@@.     ",
00054 "   .@@@$@@&.    ",
00055 "   .@@@.@@@.    ",
00056 "   .@@@.+@@@.   ",
00057 "   .@@@..$@@&.  ",
00058 "   .@@%. .@@@.  ",
00059 "   ....   ...   ",
00060 "                "};
00061 
00062 static unsigned char iconify_bits[] = {
00063   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
00064   0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00};
00065 
00066 static unsigned char close_bits[] = {
00067   0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00,
00068   0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00};
00069 
00070 static unsigned char maximize_bits[] = {
00071   0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
00072   0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00};
00073 
00074 static unsigned char minmax_bits[] = {
00075   0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00,
00076   0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00};
00077 
00078 static unsigned char question_bits[] = {
00079   0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
00080   0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00};
00081 
00082 
00083 // Up / Down titlebar button images
00084 static KPixmap *btnPix1;
00085 static KPixmap *iBtnPix1;
00086 static KPixmap *btnDownPix1;
00087 static KPixmap *iBtnDownPix1;
00088 
00089 static KPixmap *miniBtnPix1;
00090 static KPixmap *iMiniBtnPix1;
00091 static KPixmap *miniBtnDownPix1;
00092 static KPixmap *iMiniBtnDownPix1;
00093 
00094 static QPixmap *defaultMenuPix;
00095 static QColor  *btnForeground;
00096 static bool    pixmaps_created = false;
00097 
00098 static int toolTitleHeight;
00099 static int normalTitleHeight;
00100 static int borderWidth;
00101 
00102 static inline const KDecorationOptions *options()
00103 {
00104    return KDecoration::options();
00105 }
00106 
00107 static void drawButtonFrame( KPixmap *pix, const QColorGroup &g, bool sunken )
00108 {
00109     QPainter p;
00110     int x2 = pix->width() - 1;
00111     int y2 = pix->height() - 1;
00112     p.begin(pix);
00113 
00114     // titlebar button frame
00115     p.setPen( sunken ? g.dark().dark(155) : g.light());
00116     p.drawLine(0, 0, x2-1, 0);
00117     p.drawLine(0, 0, 0, y2-1);
00118 
00119     if (sunken)
00120     {
00121        p.setPen( g.mid().dark(135) );
00122        p.drawLine(1, 1, x2-2, 1);
00123        p.drawLine(1, 1, 1, y2-2);
00124     }
00125 
00126     p.setPen( sunken ? g.light() : g.mid().dark(135));
00127     p.drawLine(1, y2-1, x2-1, y2-1);
00128     p.drawLine(x2-1, 1, x2-1, y2-1);
00129 
00130     p.setPen( sunken ? g.light() : g.dark().dark(155));
00131     p.drawLine(0, y2, x2, y2);
00132     p.drawLine(x2, 0, x2, y2);
00133 }
00134 
00135 
00136 static void create_pixmaps ()
00137 {
00138     if (pixmaps_created)
00139         return;
00140 
00141     pixmaps_created = true;
00142 
00143     bool highcolor = QPixmap::defaultDepth() > 8;
00144 
00145     btnPix1 = new KPixmap;
00146     btnDownPix1 = new KPixmap;
00147     iBtnPix1 = new KPixmap;
00148     iBtnDownPix1 = new KPixmap;
00149     miniBtnPix1 = new KPixmap;
00150     miniBtnDownPix1 = new KPixmap;
00151     iMiniBtnPix1 = new KPixmap;
00152     iMiniBtnDownPix1 = new KPixmap;
00153     defaultMenuPix = new QPixmap(kdelogo);
00154 
00155     // buttons (active/inactive, sunken/unsunken)
00156     QColorGroup g = options()->colorGroup(KDecoration::ColorButtonBg, true);
00157     QColor c = g.background();
00158     btnPix1->resize(normalTitleHeight, normalTitleHeight-2);
00159     btnDownPix1->resize(normalTitleHeight, normalTitleHeight-2);
00160     iBtnPix1->resize(normalTitleHeight, normalTitleHeight-2);
00161     iBtnDownPix1->resize(normalTitleHeight, normalTitleHeight-2);
00162 
00163     miniBtnPix1->resize(toolTitleHeight, toolTitleHeight);
00164     miniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight);
00165     iMiniBtnPix1->resize(toolTitleHeight, toolTitleHeight);
00166     iMiniBtnDownPix1->resize(toolTitleHeight, toolTitleHeight);
00167 
00168     if (highcolor && false) {
00169         KPixmapEffect::gradient(*btnPix1, c.light(130), c.dark(130),
00170                                 KPixmapEffect::VerticalGradient);
00171         KPixmapEffect::gradient(*btnDownPix1, c.dark(130), c.light(130),
00172                                 KPixmapEffect::VerticalGradient);
00173 
00174         KPixmapEffect::gradient(*miniBtnPix1, c.light(130), c.dark(130),
00175                                 KPixmapEffect::VerticalGradient);
00176         KPixmapEffect::gradient(*miniBtnDownPix1, c.dark(130), c.light(130),
00177                                 KPixmapEffect::VerticalGradient);
00178 
00179         g = options()->colorGroup(KDecoration::ColorButtonBg, false);
00180         c = g.background();
00181         KPixmapEffect::gradient(*iBtnPix1, c.light(130), c.dark(130),
00182                                 KPixmapEffect::VerticalGradient);
00183         KPixmapEffect::gradient(*iBtnDownPix1, c.dark(130), c.light(130),
00184                                 KPixmapEffect::VerticalGradient);
00185         KPixmapEffect::gradient(*iMiniBtnPix1, c.light(130), c.dark(130),
00186                                 KPixmapEffect::VerticalGradient);
00187         KPixmapEffect::gradient(*iMiniBtnDownPix1, c.dark(130), c.light(130),
00188                                 KPixmapEffect::VerticalGradient);
00189     } else {
00190         btnPix1->fill(c.rgb());
00191         btnDownPix1->fill(c.rgb());
00192         miniBtnPix1->fill(c.rgb());
00193         miniBtnDownPix1->fill(c.rgb());
00194 
00195         g = options()->colorGroup(KDecoration::ColorButtonBg, false);
00196         c = g.background();
00197         iBtnPix1->fill(c.rgb());
00198         iBtnDownPix1->fill(c.rgb());
00199         iMiniBtnPix1->fill(c.rgb());
00200         iMiniBtnDownPix1->fill(c.rgb());
00201     }
00202 
00203     g = options()->colorGroup(KDecoration::ColorButtonBg, true);
00204     drawButtonFrame(btnPix1, g, false);
00205     drawButtonFrame(btnDownPix1, g, true);
00206     drawButtonFrame(miniBtnPix1, g, false);
00207     drawButtonFrame(miniBtnDownPix1, g, true);
00208 
00209     g = options()->colorGroup(KDecoration::ColorButtonBg, false);
00210     drawButtonFrame(iBtnPix1, g, false);
00211     drawButtonFrame(iBtnDownPix1, g, true);
00212     drawButtonFrame(iMiniBtnPix1, g, false);
00213     drawButtonFrame(iMiniBtnDownPix1, g, true);
00214 
00215     // Make sure button pixmaps contrast with the current colour scheme.
00216     if (qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 127)
00217         btnForeground = new QColor(Qt::black);
00218     else
00219         btnForeground = new QColor(Qt::white);
00220 }
00221 
00222 void delete_pixmaps()
00223 {
00224     delete btnPix1;
00225     delete btnDownPix1;
00226     delete iBtnPix1;
00227     delete iBtnDownPix1;
00228     delete miniBtnPix1;
00229     delete miniBtnDownPix1;
00230     delete iMiniBtnPix1;
00231     delete iMiniBtnDownPix1;
00232     delete defaultMenuPix;
00233     delete btnForeground;
00234     pixmaps_created = false;
00235 }
00236 
00237 
00238 RedmondButton::RedmondButton(RedmondDeco *parent, const char *name,
00239       const unsigned char *bitmap, bool menuButton, bool isMini, int size, const QString& tip, const int realizeBtns)
00240     : QButton(parent->widget(), name)
00241 {
00242     // Eliminate background flicker
00243     setBackgroundMode( NoBackground );
00244         setCursor( arrowCursor );
00245 
00246     menuBtn = menuButton;
00247     miniBtn = isMini;
00248     client  = parent;
00249     this->size = size;
00250     realizeButtons = realizeBtns;
00251 
00252     // Use larger button for the menu, or mini-buttons for toolwindows.
00253     if ( isMini || menuButton ) {
00254         setFixedSize(size, size);
00255         resize(size, size);
00256     } else {
00257         setFixedSize(size, size-2);
00258         resize(size, size-2);
00259     }
00260 
00261     if ( bitmap ) {
00262         setBitmap(bitmap);
00263     }
00264 
00265     QToolTip::add(this, tip);
00266 }
00267 
00268 
00269 QSize RedmondButton::sizeHint() const
00270 {
00271     if ( miniBtn || menuBtn )
00272         return( QSize(size, size) );
00273     else
00274         return( QSize(size, size-2) );
00275 }
00276 
00277 
00278 void RedmondButton::reset()
00279 {
00280     repaint(false);
00281 }
00282 
00283 
00284 void RedmondButton::setBitmap(const unsigned char *bitmap)
00285 {
00286     pix.resize(0, 0);
00287     deco = QBitmap(10, 10, bitmap, true);
00288     deco.setMask(deco);
00289     repaint( false );
00290 }
00291 
00292 
00293 void RedmondButton::setPixmap( const QPixmap &p )
00294 {
00295     deco.resize(0, 0);
00296     pix = p;
00297 
00298     if (miniBtn || menuBtn)
00299         setMask(QRect(0, 0, size, size));
00300     else
00301         setMask(QRect(0, 0, size, size-2));
00302 
00303     repaint(false);
00304 }
00305 
00306 
00307 void RedmondButton::mousePressEvent( QMouseEvent* e )
00308 {
00309     last_button = e->button();
00310     QMouseEvent me(e->type(), e->pos(), e->globalPos(),
00311                    (e->button()&realizeButtons)?LeftButton:NoButton, e->state());
00312     QButton::mousePressEvent( &me );
00313 }
00314 
00315 
00316 void RedmondButton::mouseReleaseEvent( QMouseEvent* e )
00317 {
00318     last_button = e->button();
00319     QMouseEvent me ( e->type(), e->pos(), e->globalPos(),
00320                      (e->button()&realizeButtons)?LeftButton:NoButton, e->state() );
00321     QButton::mouseReleaseEvent( &me );
00322 }
00323 
00324 
00325 void RedmondButton::drawButton(QPainter *p)
00326 {
00327    if ( pix.isNull() ) {
00328       if ( client->isActive() ) {
00329          if ( isDown() )
00330             p->drawPixmap(0, 0, miniBtn ? *miniBtnDownPix1 : *btnDownPix1);
00331          else
00332             p->drawPixmap(0, 0, miniBtn ? *miniBtnPix1 : *btnPix1);
00333       } else {
00334          if ( isDown() )
00335             p->drawPixmap(0, 0, miniBtn ? *iMiniBtnDownPix1 : *iBtnDownPix1);
00336          else
00337             p->drawPixmap(0, 0, miniBtn ? *iMiniBtnPix1 : *iBtnPix1);
00338       }
00339 
00340       p->setPen( *btnForeground );
00341       int xOff = (width()-10)/2;
00342       int yOff = (height()-10)/2;
00343       p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco);
00344    } else {
00345       p->fillRect(0, 0, width(), height(),
00346             options()->color(KDecoration::ColorTitleBar, client->isActive()));
00347 
00348       if ( menuBtn && size < 16) {
00349          QPixmap tmpPix;
00350 
00351          // Smooth scale the menu button pixmap
00352          tmpPix.convertFromImage(
00353          pix.convertToImage().smoothScale(size, size));
00354 
00355          p->drawPixmap( 0, 0, tmpPix );
00356       } else {
00357          int xOff = (width() -pix.width() )/2;
00358          int yOff = (height()-pix.height())/2;
00359          p->drawPixmap(xOff, yOff, pix );
00360       }
00361    }
00362 }
00363 
00364 
00365 RedmondDeco::RedmondDeco(KDecorationBridge *b, KDecorationFactory *f)
00366     : KDecoration(b, f)
00367 {
00368 }
00369 
00370 void RedmondDeco::init()
00371 {
00372     createMainWidget(WResizeNoErase);
00373     widget()->installEventFilter(this);
00374 
00375     widget()->setBackgroundMode(NoBackground);
00376 //  bool reverse = QApplication::reverseLayout();
00377 
00378 //  Finally, toolwindows look small
00379 //  if ( isTool() ) {
00380 //      titleHeight = toolTitleHeight+2;
00381 //      smallButtons = true;
00382 //  } else {
00383         titleHeight = normalTitleHeight+2;
00384         smallButtons = false;
00385 //  }
00386 
00387     lastButtonWidth = 0;
00388 
00389     QGridLayout* g = new QGridLayout(widget(), 0, 0, 0);
00390     g->setResizeMode(QLayout::FreeResize);
00391     if (isPreview()) {
00392         g->addWidget(new QLabel(i18n("<center><b>Redmond preview</b></center>"), widget()), 3, 1);
00393     } else {
00394         g->addItem(new QSpacerItem( 0, 0 ), 3, 1); // no widget in the middle
00395     }
00396 
00397     g->addRowSpacing(0, borderWidth);       // Top grab bar
00398     // without the next line, unshade flickers
00399     g->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding));
00400     g->setRowStretch(3, 10);      // Wrapped window
00401     g->addRowSpacing(4, borderWidth);       // bottom handles
00402     g->addRowSpacing(2, 1);       // Line below title bar
00403     g->addColSpacing(0, borderWidth);
00404     g->addColSpacing(2, borderWidth);
00405 
00406     button[BtnMenu] = new RedmondButton(this, "menu", NULL, true, smallButtons, titleHeight-2, i18n("Menu"), LeftButton|RightButton);
00407     button[BtnClose] = new RedmondButton(this, "close", close_bits, false, smallButtons, titleHeight-2, i18n("Close"));
00408     button[BtnMin] = new RedmondButton(this, "iconify", iconify_bits, false, smallButtons, titleHeight-2, i18n("Minimize"));
00409     button[BtnMax] = new RedmondButton(this, "maximize", maximize_bits, false, smallButtons, titleHeight-2, i18n("Maximize"), LeftButton|MidButton|RightButton);
00410 
00411     // Connect required stuff together
00412     connect(button[BtnMenu], SIGNAL(pressed()), this, SLOT(menuButtonPressed()));
00413     connect(button[BtnClose], SIGNAL(clicked()), this, SLOT(closeWindow()));
00414     connect(button[BtnMin], SIGNAL(clicked()), this, SLOT(minimize()));
00415     connect(button[BtnMax], SIGNAL(clicked()), this, SLOT(slotMaximize()));
00416 
00417     // Pack the titleBar hbox with items
00418     hb = new QBoxLayout(0, QBoxLayout::LeftToRight, 0, 0, 0);
00419     hb->setResizeMode(QLayout::FreeResize);
00420     hb->addSpacing(2);
00421     hb->addWidget(button[BtnMenu]);
00422     titlebar = new QSpacerItem(10, titleHeight, QSizePolicy::Expanding, QSizePolicy::Minimum);
00423     hb->addItem(titlebar);
00424     hb->addSpacing(borderWidth/2);
00425 
00426     if ( providesContextHelp() ) {
00427         button[BtnHelp] = new RedmondButton(this, "help", question_bits, false, smallButtons, titleHeight-2, i18n("Help"));
00428         connect( button[BtnHelp], SIGNAL( clicked() ), this, SLOT( showContextHelp() ));
00429         hb->addWidget( button[BtnHelp] );
00430     } else {
00431         button[BtnHelp] = NULL;
00432     }
00433 
00434     hb->addWidget(button[BtnMin]);
00435     hb->addWidget(button[BtnMax]);
00436     hb->addSpacing(borderWidth/2);
00437     hb->addWidget(button[BtnClose]);
00438     hb->addSpacing(2);
00439 
00440     g->addLayout(hb, 1, 1);
00441 
00442     // Hide buttons which are not required
00443     // We can un-hide them if required later
00444     if (!isMinimizable())
00445         button[BtnMin]->hide();
00446     if (!isMaximizable())
00447         button[BtnMax]->hide();
00448     if (!isCloseable())
00449         button[BtnClose]->hide();
00450 
00451     hiddenItems = false;
00452 
00453     // Make sure that the menu button uses the correct mini-icon
00454     iconChange();
00455     widget()->layout()->activate();
00456 }
00457 
00458 
00459 void RedmondDeco::slotReset()
00460 {
00461     // 0 to 3   ( 4 buttons - Help, Max, Iconify, Close )
00462     for(int i = RedmondDeco::BtnHelp; i <= RedmondDeco::BtnClose; i++)
00463         if (button[i])
00464             button[i]->reset();
00465 
00466     // The menu is reset by iconChange()
00467 
00468     widget()->repaint( false );
00469 }
00470 
00471 
00472 void RedmondDeco::iconChange()
00473 {
00474     QPixmap miniIcon = icon().pixmap(QIconSet::Small, QIconSet::Normal);
00475 
00476     if (!miniIcon.isNull())
00477         button[BtnMenu]->setPixmap(miniIcon);
00478     else
00479         button[BtnMenu]->setPixmap(*defaultMenuPix);
00480 
00481     if (button[BtnMenu]->isVisible())
00482         button[BtnMenu]->repaint(false);
00483 }
00484 
00485 
00486 void RedmondDeco::slotMaximize()
00487 {
00488     if ( button[BtnMax]->last_button == MidButton )
00489        maximize( maximizeMode() ^ MaximizeVertical );
00490     else if ( button[BtnMax]->last_button == RightButton )
00491        maximize( maximizeMode() ^ MaximizeHorizontal );
00492     else
00493        maximize( maximizeMode() == MaximizeFull ? MaximizeRestore : MaximizeFull );
00494 }
00495 
00496 
00497 void RedmondDeco::resizeEvent(QResizeEvent *)
00498 {
00499     calcHiddenButtons();
00500 /*
00501     if (isVisibleToTLW()) {
00502         update(rect());
00503         int dx = 0;
00504         int dy = 0;
00505 
00506         if ( e->oldSize().width() != width() )
00507            dx = 32 + QABS( e->oldSize().width() -  width() );
00508 
00509         if ( e->oldSize().height() != height() )
00510            dy = 8 + QABS( e->oldSize().height() -  height() );
00511 
00512         if ( dy )
00513            update( 0, height() - dy + 1, width(), dy );
00514 
00515         if ( dx )
00516         {
00517            update( width() - dx + 1, 0, dx, height() );
00518            update( QRect( QPoint(4,4), titlebar->geometry().bottomLeft() - QPoint(1,0) ) );
00519            update( QRect( titlebar->geometry().topRight(), QPoint( width() - 4, titlebar->geometry().bottom() ) ) );
00520            // Titlebar needs no paint event
00521            QApplication::postEvent( this, new QPaintEvent( titlebar->geometry(), FALSE ) );
00522         }
00523     }
00524  */
00525 }
00526 
00527 
00528 void RedmondDeco::captionChange( const QString& )
00529 {
00530     widget()->repaint( titlebar->geometry(), false );
00531 }
00532 
00533 
00534 void RedmondDeco::paintEvent( QPaintEvent* )
00535 {
00536     bool hicolor = QPixmap::defaultDepth() > 8;
00537     int fontoffset = 1;
00538 
00539     QPainter p(widget());
00540 
00541     // Obtain widget bounds.
00542     QRect r(widget()->rect());
00543     int x = r.x();
00544     int y = r.y();
00545     int x2 = r.width()-1;
00546     int y2 = r.height()-1;
00547     int w  = r.width();
00548     int h  = r.height();
00549 
00550     // Draw part of the frame that is the frame color
00551     // ==============================================
00552     QColorGroup g = options()->colorGroup(KDecoration::ColorFrame, isActive());
00553     p.setPen( g.background() );
00554     p.drawLine( x, y, x2-1, y );
00555     p.drawLine( x, y, x, y2-1 );
00556 
00557     // Draw line under title bar
00558     p.drawLine( x+borderWidth, y+titleHeight+borderWidth, x2-borderWidth, y+titleHeight+borderWidth );
00559     // Draw a hidden line that appears during shading
00560     p.drawLine( x+borderWidth, y2-borderWidth, x2-borderWidth, y2-borderWidth );
00561 
00562     // Fill out the border edges
00563     for (int i = 1; i < borderWidth; i++)
00564         p.drawRect( x+i, y+i, w-2*i, h-2*i );
00565 
00566     // Draw highlights and lowlights
00567     p.setPen(g.light());
00568     for (int i = 1; i <= borderWidth/3; i++) {
00569         p.drawLine( x+i, y+i, x2-i-1, y+i);
00570         p.drawLine( x+i, y+i, x+i, y2-i-1);
00571     }
00572 
00573     p.setPen(g.mid().dark(135));
00574     for (int i = 1; i <= borderWidth/3; i++) {
00575         p.drawLine( x2-i, y+i+1, x2-i, y2-i);
00576         p.drawLine( x+i+1, y2-i, x2-i, y2-i);
00577     }
00578 
00579     // Draw black edges
00580     p.setPen( g.dark().dark(155) );
00581     p.drawLine(x2, y, x2, y2);
00582     p.drawLine(x, y2, x2, y2);
00583 
00584     // Draw the title bar.
00585     // ===================
00586     r = titlebar->geometry();
00587     QFontMetrics fm(options()->font(true));
00588 
00589     // Obtain blend colours.
00590     QColor c1 = options()->color(KDecoration::ColorTitleBar, isActive() );
00591     QColor c2 = options()->color(KDecoration::ColorTitleBlend, isActive() );
00592 
00593     // Paint without a buffer if the colours are the same to
00594     // improve performance, and only draw gradients on hicolor displays.
00595     if ((c1 != c2) && hicolor) {
00596         // KS - Add gradient caching if needed at a later stage.
00597 
00598         // Create a disposable pixmap buffer for the title blend
00599         KPixmap* titleBuffer = new KPixmap;
00600         titleBuffer->resize(w-2*borderWidth, titleHeight);
00601 
00602         if (titleBuffer->depth() > 16) {
00603             KPixmapEffect::gradient(*titleBuffer, c1, c2,
00604                                     KPixmapEffect::HorizontalGradient);
00605         } else {
00606             // This enables dithering on 15 and 16bit displays, preventing
00607             // some pretty horrible banding effects
00608             QImage image = KImageEffect::gradient(titleBuffer->size(), c1, c2,
00609                                     KImageEffect::HorizontalGradient);
00610 
00611             titleBuffer->convertFromImage(image, Qt::OrderedDither);
00612         }
00613 
00614         QPainter p2( titleBuffer, this );
00615 
00616         // Since drawing the gradient is (relatively) slow, it is best
00617         // to draw the title text on the pixmap.
00618 
00619         // Reduce the font size and weight for toolwindows.
00620         QFont fnt = options()->font(true);
00621         if ( smallButtons ) {
00622            fnt.setPointSize( fnt.pointSize() - 2 ); // Shrink font by 2 pt.
00623            fnt.setWeight( QFont::Normal );
00624            fontoffset = 0;
00625         }
00626         p2.setFont( fnt );
00627         p2.setPen( options()->color(KDecoration::ColorFont, isActive() ));
00628         p2.drawText( r.x(), fontoffset, r.width()-3, r.height()-1,
00629                      AlignLeft | AlignVCenter, caption() );
00630         p2.end();
00631 
00632         p.drawPixmap( borderWidth, borderWidth, *titleBuffer );
00633 
00634         delete titleBuffer;
00635 
00636     } else {
00637        // Assume lower ended hardware, so don't use buffers.
00638        // Don't draw a gradient either.
00639        p.fillRect( borderWidth, borderWidth, w-2*borderWidth, titleHeight, c1 );
00640 
00641        // Draw the title text.
00642        QFont fnt = options()->font(true);
00643        if ( smallButtons )
00644        {
00645           fnt.setPointSize( fnt.pointSize() - 2 ); // Shrink font by 2 pt.
00646           fnt.setWeight( QFont::Normal );
00647           fontoffset = 0;
00648        }
00649        p.setFont( fnt );
00650        p.setPen(options()->color(KDecoration::ColorFont, isActive() ));
00651        p.drawText(r.x()+4, r.y()+fontoffset, r.width()-3, r.height()-1,
00652                   AlignLeft | AlignVCenter, caption() );
00653    }
00654 
00655 }
00656 
00657 void RedmondDeco::showEvent(QShowEvent *)
00658 {
00659     calcHiddenButtons();
00660     widget()->show();
00661 }
00662 
00663 
00664 void RedmondDeco::mouseDoubleClickEvent( QMouseEvent * e )
00665 {
00666     if (titlebar->geometry().contains( e->pos() ) )
00667        titlebarDblClickOperation();
00668 }
00669 
00670 
00671 void RedmondDeco::maximizeChange(bool m)
00672 {
00673     button[BtnMax]->setBitmap(m ? minmax_bits : maximize_bits);
00674 }
00675 
00676 void RedmondDeco::calcHiddenButtons()
00677 {
00678    // order of hiding is help, maximize, minimize, close, then menu;
00679    int minWidth = (2 + 4 + (providesContextHelp() ? 2 : 1 )) * normalTitleHeight;
00680 
00681    if (lastButtonWidth > width()) { // Shrinking
00682       lastButtonWidth = width();
00683       if (width() < minWidth) {
00684          hiddenItems = true;
00685 
00686          for(int i = RedmondDeco::BtnHelp; i <= RedmondDeco::BtnMenu; i++) {
00687             if (button[i]) {
00688                if ( !button[i]->isHidden() ) {
00689                   button[i]->hide();
00690                }
00691                minWidth -= button[i]->sizeHint().width();
00692                if (width() >= minWidth) {
00693                   return;
00694                }
00695             }
00696          }
00697       }
00698    } else {
00699       if ( hiddenItems ) { // Expanding
00700          lastButtonWidth = width();
00701          int totalSize = normalTitleHeight*3;
00702 
00703          for (int i = RedmondDeco::BtnMenu; i >= RedmondDeco::BtnHelp; i--) {
00704             if (button[i]) {
00705                if (button[i]->sizeHint().width() + totalSize <= width()) {
00706                   totalSize += button[i]->sizeHint().width();
00707                   button[i]->resize(button[i]->sizeHint());
00708                   button[i]->show();
00709                } else {
00710                   return;
00711                }
00712             }
00713          }
00714 
00715          // all items shown now
00716          hiddenItems = false;
00717       } else {
00718          lastButtonWidth = width();
00719       }
00720    }
00721 }
00722 
00723 RedmondDeco::Position RedmondDeco::mousePosition(const QPoint &p) const
00724 {
00725     Position m = PositionCenter;
00726 
00727     const int range = 14 + 3*borderWidth/2;
00728 
00729     if ( ( p.x() > borderWidth && p.x() < width() - borderWidth )
00730          && ( p.y() > borderWidth && p.y() < height() - borderWidth ) )
00731         m = PositionCenter;
00732     else if ( p.y() <= range && p.x() <= range)
00733         m = PositionTopLeft;
00734     else if ( p.y() >= height()-range && p.x() >= width()-range)
00735         m = PositionBottomRight;
00736     else if ( p.y() >= height()-range && p.x() <= range)
00737         m = PositionBottomLeft;
00738     else if ( p.y() <= range && p.x() >= width()-range)
00739         m = PositionTopRight;
00740     else if ( p.y() <= borderWidth )
00741         m = PositionTop;
00742     else if ( p.y() >= height()-borderWidth )
00743         m = PositionBottom;
00744     else if ( p.x() <= borderWidth )
00745         m = PositionLeft;
00746     else if ( p.x() >= width()-borderWidth )
00747         m = PositionRight;
00748     else
00749         m = PositionCenter;
00750 
00751     return m;
00752 }
00753 
00754 void RedmondDeco::borders(int &l, int &r, int &t, int &b) const
00755 {
00756 //  bool reverse = QApplication::reverseLayout();
00757     l = borderWidth;
00758     r = borderWidth;
00759     t = borderWidth + titlebar->geometry().height() + 1;
00760     b = borderWidth;
00761 }
00762 
00763 void RedmondDeco::resize(const QSize &s)
00764 {
00765     widget()->resize(s);
00766 }
00767 
00768 QSize RedmondDeco::minimumSize() const
00769 {
00770     return QSize(50, 50); // what's good for the goose....
00771 }
00772 
00773 void RedmondDeco::activeChange()
00774 {
00775     QPixmap miniIcon = icon().pixmap(QIconSet::Small, QIconSet::Normal);
00776     if (!miniIcon.isNull()) {
00777         button[BtnMenu]->setPixmap(miniIcon);
00778     } else {
00779         button[BtnMenu]->setPixmap(kdelogo);
00780     }
00781 
00782     // Reset the menu button ?
00783     for (int i = BtnHelp; i < BtnCount; i++) {
00784         if (button[i]) button[i]->reset();
00785     }
00786 
00787     widget()->repaint(false);
00788 }
00789 
00790 void RedmondDeco::captionChange()
00791 {
00792     widget()->repaint(titlebar->geometry(), false);
00793 }
00794 
00795 void RedmondDeco::maximizeChange()
00796 {
00797     bool m = (maximizeMode() == MaximizeFull);
00798     button[BtnMax]->setBitmap(m ? minmax_bits : maximize_bits);
00799     QToolTip::remove(button[BtnMax]);
00800     QToolTip::add(button[BtnMax], m ? i18n("Restore") : i18n("Maximize"));
00801 }
00802 
00803 void RedmondDeco::desktopChange()
00804 {
00805 }
00806 
00807 void RedmondDeco::shadeChange()
00808 {
00809 }
00810 
00811 void RedmondDeco::menuButtonPressed()
00812 {
00813     static QTime* t = NULL;
00814     static RedmondDeco* lastClient = NULL;
00815     if (t == NULL) t = new QTime;
00816     bool dbl = ( lastClient == this && t->elapsed() <= QApplication::doubleClickInterval());
00817     lastClient = this;
00818     t->start();
00819     if (!dbl) {
00820         QPoint menupoint(button[BtnMenu]->rect().bottomLeft().x()-3,
00821                          button[BtnMenu]->rect().bottomLeft().y()+4);
00822                 KDecorationFactory* f = factory();
00823         showWindowMenu(button[BtnMenu]->mapToGlobal(menupoint));
00824                 if( !f->exists( this )) // 'this' was destroyed
00825                     return;
00826         button[BtnMenu]->setDown(false);
00827     } else {
00828         closeWindow();
00829     }
00830 }
00831 
00832 bool RedmondDeco::eventFilter(QObject *o, QEvent *e)
00833 {
00834     if (o != widget()) { return false; }
00835     switch (e->type()) {
00836         case QEvent::Resize: {
00837             resizeEvent(static_cast<QResizeEvent *>(e));
00838             return true;
00839         }
00840         case QEvent::Paint: {
00841             paintEvent(static_cast<QPaintEvent *>(e));
00842             return true;
00843         }
00844         case QEvent::Show: {
00845             showEvent(static_cast<QShowEvent *>(e));
00846             return true;
00847         }
00848         case QEvent::MouseButtonDblClick: {
00849             mouseDoubleClickEvent(static_cast<QMouseEvent *>(e));
00850             return true;
00851         }
00852         case QEvent::MouseButtonPress: {
00853             processMousePressEvent(static_cast<QMouseEvent *>(e));
00854             return true;
00855         }
00856         default: {
00857             break;
00858         }
00859     }
00860 
00861     return false;
00862 }
00863 
00864 void RedmondDecoFactory::readConfig() {
00865     normalTitleHeight = QFontMetrics(options()->font(true)).height();
00866     toolTitleHeight = QFontMetrics(options()->font(true, true)).height();
00867     switch(options()->preferredBorderSize(this)) {
00868     case BorderLarge:
00869         borderWidth = 8;
00870         if (normalTitleHeight < 20) normalTitleHeight = 20;
00871         if (toolTitleHeight < 20) toolTitleHeight = 20;
00872         break;
00873     case BorderVeryLarge:
00874         borderWidth = 12;
00875         if (normalTitleHeight < 24) normalTitleHeight = 24;
00876         if (toolTitleHeight < 24) toolTitleHeight = 24;
00877         break;
00878     case BorderHuge:
00879         borderWidth = 18;
00880         if (normalTitleHeight < 28) normalTitleHeight = 28;
00881         if (toolTitleHeight < 28) toolTitleHeight = 28;
00882         break;
00883     case BorderVeryHuge:
00884         borderWidth = 27;
00885         if (normalTitleHeight < 33) normalTitleHeight = 33;
00886         if (toolTitleHeight < 33) toolTitleHeight = 33;
00887         break;
00888     case BorderOversized:
00889         borderWidth = 40;
00890         if (normalTitleHeight < 40) normalTitleHeight = 40;
00891         if (toolTitleHeight < 40) toolTitleHeight = 40;
00892         break;
00893     case BorderTiny:
00894     case BorderNormal:
00895     default:
00896         borderWidth = 4;
00897         if (normalTitleHeight < 16) normalTitleHeight = 16;
00898         if (toolTitleHeight < 16) toolTitleHeight = 16;
00899     }
00900 }
00901 
00902 RedmondDecoFactory::RedmondDecoFactory()
00903 {
00904     readConfig();
00905     create_pixmaps();
00906 }
00907 
00908 RedmondDecoFactory::~RedmondDecoFactory()
00909 {
00910     Redmond::delete_pixmaps();
00911 }
00912 
00913 KDecoration *RedmondDecoFactory::createDecoration( KDecorationBridge *b )
00914 {
00915     return new RedmondDeco(b, this);
00916 }
00917 
00918 bool RedmondDecoFactory::reset( unsigned long changed )
00919 {
00920     if ( changed & ( SettingFont | SettingBorder | SettingColors ) ) {
00921         delete_pixmaps();
00922         readConfig();
00923         create_pixmaps();
00924         resetDecorations(changed);
00925         return true;
00926     } else {
00927         resetDecorations(changed);
00928         return false;
00929     }
00930 }
00931 
00932 QValueList< RedmondDecoFactory::BorderSize > RedmondDecoFactory::borderSizes() const
00933 { // the list must be sorted
00934   return QValueList< BorderSize >() << BorderNormal << BorderLarge <<
00935       BorderVeryLarge <<  BorderHuge << BorderVeryHuge << BorderOversized;
00936 }
00937 
00938 }
00939 
00940 extern "C" KDecorationFactory *create_factory()
00941 {
00942     return new Redmond::RedmondDecoFactory();
00943 }
00944 
00945 
00946 #include "redmond.moc"
00947 // vim: ts=4
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 29 21:20:53 2004 by doxygen 1.2.15 written by Dimitri van Heesch, © 1997-2003