Back to index

kdeartwork  4.3.2
nextclient.cpp
Go to the documentation of this file.
00001 /********************************************************************
00002 This program is free software; you can redistribute it and/or modify
00003 it under the terms of the GNU General Public License as published by
00004 the Free Software Foundation; either version 2 of the License, or
00005 (at your option) any later version.
00006 
00007 This program is distributed in the hope that it will be useful,
00008 but WITHOUT ANY WARRANTY; without even the implied warranty of
00009 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00010 GNU General Public License for more details.
00011 
00012 You should have received a copy of the GNU General Public License
00013 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00014 *********************************************************************/
00015 
00016 #include "nextclient.h"
00017 
00018 #include <QStyle>
00019 #include <QPainter>
00020 #include <QBoxLayout>
00021 #include <QHBoxLayout>
00022 #include <QVBoxLayout>
00023 #include <QEvent>
00024 #include <QPaintEvent>
00025 #include <QShowEvent>
00026 #include <QResizeEvent>
00027 #include <QMouseEvent>
00028 
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 
00032 namespace KStep {
00033 
00034 static const unsigned char close_bits[] = {
00035   0x03, 0x03, 0x87, 0x03, 0xce, 0x01, 0xfc, 0x00, 0x78, 0x00, 0x78, 0x00,
00036   0xfc, 0x00, 0xce, 0x01, 0x87, 0x03, 0x03, 0x03};
00037 
00038 static const unsigned char iconify_bits[] = {
00039   0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03, 0x03, 0x03, 0x03, 0x03,
00040   0x03, 0x03, 0x03, 0x03, 0xff, 0x03, 0xff, 0x03};
00041 
00042 static const unsigned char question_bits[] = {
00043   0x00, 0x00, 0x78, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00,
00044   0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00};
00045 
00046 static const unsigned char sticky_bits[] = {
00047   0x00, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0xfe, 0x01, 0xfe, 0x01,
00048   0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00};
00049 
00050 static const unsigned char unsticky_bits[] = {
00051   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0xfe, 0x01,
00052   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
00053 
00054 static const unsigned char maximize_bits[] = {
00055    0x30, 0x00, 0x78, 0x00, 0xfc, 0x00, 0xfe, 0x01, 0x00, 0x00, 0xfe, 0x01,
00056    0x02, 0x01, 0x84, 0x00, 0x48, 0x00, 0x30, 0x00 };
00057 
00058 static const unsigned char shade_bits[] = {
00059     0xff,0x03,
00060     0xff,0x03,
00061     0x03,0x03,
00062     0xff,0x03,
00063     0xff,0x03,
00064     0x00,0x00,
00065     0x00,0x00,
00066     0x00,0x00,
00067     0x00,0x00,
00068     0x00,0x00
00069 };
00070 
00071 static const unsigned char unshade_bits[] = {
00072     0xff,0x03,
00073     0xff,0x03,
00074     0x03,0x03,
00075     0x03,0x03,
00076     0x03,0x03,
00077     0x03,0x03,
00078     0x03,0x03,
00079     0x03,0x03,
00080     0xff,0x03,
00081     0xff,0x03
00082 };
00083 
00084 static const unsigned char keep_above_bits[] = {
00085     0x30,0x00,
00086     0x78,0x00,
00087     0xfc,0x00,
00088     0x00,0x00,
00089     0xff,0x03,
00090     0xff,0x03,
00091     0x00,0x00,
00092     0x00,0x00,
00093     0x00,0x00,
00094     0x00,0x00
00095 };
00096 
00097 static const unsigned char from_above_bits[] = {
00098     0xff,0x03,
00099     0xff,0x03,
00100     0x00,0x00,
00101     0xfc,0x00,
00102     0x78,0x00,
00103     0x30,0x00,
00104     0x00,0x00,
00105     0x00,0x00,
00106     0x00,0x00,
00107     0x00,0x00
00108 };
00109 
00110 static const unsigned char keep_below_bits[] = {
00111     0x00,0x00,
00112     0x00,0x00,
00113     0x00,0x00,
00114     0x00,0x00,
00115     0xff,0x03,
00116     0xff,0x03,
00117     0x00,0x00,
00118     0xfc,0x00,
00119     0x78,0x00,
00120     0x30,0x00
00121 };
00122 
00123 static const unsigned char from_below_bits[] = {
00124     0x00,0x00,
00125     0x00,0x00,
00126     0x00,0x00,
00127     0x00,0x00,
00128     0x30,0x00,
00129     0x78,0x00,
00130     0xfc,0x00,
00131     0x00,0x00,
00132     0xff,0x03,
00133     0xff,0x03
00134 };
00135 
00136 static const unsigned char resize_bits[] = {
00137     0xff, 0x03,
00138     0xff, 0x03,
00139     0x33, 0x03,
00140     0x33, 0x03,
00141     0xf3, 0x03,
00142     0xf3, 0x03,
00143     0x03, 0x03,
00144     0x03, 0x03,
00145     0xff, 0x03,
00146     0xff, 0x03
00147 };
00148 
00149 
00150 // If the maximize graphic above (which I did quickly in about a
00151 // minute, just so I could have something) doesn't please, maybe one
00152 // of the following would be better.  IMO it doesn't matter, as long
00153 // as it's not offensive---people will get used to whatever you use.
00154 // True NeXT fans won't turn on the maximize button anyway.
00155 //
00156 // static const unsigned char maximize_bits[] = {
00157 //    0xcf, 0x03, 0x87, 0x03, 0xcf, 0x03, 0xfd, 0x02, 0x48, 0x00, 0x48, 0x00,
00158 //    0xfd, 0x02, 0xcf, 0x03, 0x87, 0x03, 0xcf, 0x03 };
00159 //
00160 // static const unsigned char maximize_bits[] = {
00161 //    0xcf, 0x03, 0x87, 0x03, 0x87, 0x03, 0x79, 0x02, 0x48, 0x00, 0x48, 0x00,
00162 //    0x79, 0x02, 0x87, 0x03, 0x87, 0x03, 0xcf, 0x03 };
00163 //
00164 // static const unsigned char maximize_bits[] = {
00165 //    0x87, 0x03, 0x03, 0x03, 0xfd, 0x02, 0x84, 0x00, 0x84, 0x00, 0x84, 0x00,
00166 //    0x84, 0x00, 0xfd, 0x02, 0x03, 0x03, 0x87, 0x03 };
00167 //
00168 // static const unsigned char maximize_bits[] = {
00169 //    0x30, 0x00, 0x78, 0x00, 0xcc, 0x00, 0x86, 0x01, 0x33, 0x03, 0x79, 0x02,
00170 //    0xcd, 0x02, 0x87, 0x03, 0x03, 0x03, 0x01, 0x02 };
00171 //
00172 // static const unsigned char maximize_bits[] = {
00173 //    0x30, 0x00, 0x78, 0x00, 0x78, 0x00, 0xfc, 0x00, 0xfc, 0x00, 0xfe, 0x01,
00174 //    0xfe, 0x01, 0xff, 0x03, 0xff, 0x03, 0xff, 0x03 };
00175 
00176 
00177 static QPixmap *aTitlePix;
00178 static QPixmap *iTitlePix;
00179 static QPixmap *aFramePix;
00180 static QPixmap *iFramePix;
00181 static QPixmap *aHandlePix;
00182 static QPixmap *iHandlePix;
00183 static QPixmap *aBtn;
00184 static QPixmap *aBtnDown;
00185 static QPixmap *iBtn;
00186 static QPixmap *iBtnDown;
00187 static QColor *btnForeground;
00188 static bool pixmaps_created = false;
00189 
00190 static int titleHeight = 16;
00191 
00192 // Precomputed border sizes for accessibility
00193 // The sizes are applied for tiny -> normal -> large -> very large -> huge -> very huge -> oversized
00194 static const int borderSizes[] = { 4,  6,  9, 14, 21, 32, 48 };
00195 
00196 static int handleSize = 6;  // the resize handle size in pixels
00197 
00198 static inline const KDecorationOptions* options()
00199 {
00200     return KDecoration::options();
00201 }
00202 
00203 static void verticalGradient(QPixmap *pix, QColor first, QColor second)
00204 {
00205     QLinearGradient gradient(0, 0, 0, pix->height());
00206     gradient.setColorAt(0.0, first);
00207     gradient.setColorAt(1.0, second);
00208     QBrush brush(gradient);
00209     QPainter p;
00210     p.begin(pix);
00211     p.fillRect(pix->rect(), brush);
00212     p.end();
00213 }
00214 
00215 static void diagonalGradient(QPixmap *pix, QColor first, QColor second)
00216 {
00217     QLinearGradient gradient(0, 0, pix->width(), pix->height());
00218     gradient.setColorAt(0.0, first);
00219     gradient.setColorAt(1.0, second);
00220     QBrush brush(gradient);
00221     QPainter p;
00222     p.begin(pix);
00223     p.fillRect(pix->rect(), brush);
00224     p.end();
00225 }
00226 
00227 static void create_pixmaps(NextClientFactory *f)
00228 {
00229     if(pixmaps_created)
00230         return;
00231     pixmaps_created = true;
00232 
00233     // find preferred border size
00234     int i = options()->preferredBorderSize(f);
00235     if (i >= 0 && i <= 6) handleSize = borderSizes[i];
00236 
00237     titleHeight = QFontMetrics(options()->font(true)).height() + 4;
00238     if (titleHeight < handleSize) titleHeight = handleSize;
00239     titleHeight &= ~1; // Make title height even
00240     if (titleHeight < 16) titleHeight = 16;
00241 
00242     const int gradientHeight = titleHeight - 2;
00243     aTitlePix = new QPixmap(32, gradientHeight);
00244 
00245     verticalGradient(aTitlePix,
00246            options()->color(KDecoration::ColorTitleBar, true),
00247            options()->color(KDecoration::ColorTitleBlend, true));
00248     iTitlePix = new QPixmap(32, titleHeight - 2);
00249     
00250     verticalGradient(iTitlePix,
00251            options()->color(KDecoration::ColorTitleBar, false),
00252            options()->color(KDecoration::ColorTitleBlend, false));
00253     // Bottom frame gradient
00254     aFramePix = new QPixmap(32, handleSize);
00255     verticalGradient(aFramePix,
00256            options()->color(KDecoration::ColorFrame, true).light(150),
00257            options()->color(KDecoration::ColorFrame, true).dark(120));
00258     iFramePix = new QPixmap(32, handleSize);
00259     verticalGradient(iFramePix,
00260            options()->color(KDecoration::ColorFrame, false).light(150),
00261            options()->color(KDecoration::ColorFrame, false).dark(120));
00262     // Handle gradient
00263     aHandlePix = new QPixmap(32, handleSize);
00264     verticalGradient(aHandlePix,
00265            options()->color(KDecoration::ColorHandle, true).light(150),
00266            options()->color(KDecoration::ColorHandle, true).dark(120));
00267     iHandlePix = new QPixmap(32, handleSize);
00268     verticalGradient(iHandlePix,
00269            options()->color(KDecoration::ColorHandle, true).light(150),
00270            options()->color(KDecoration::ColorHandle, true).dark(120));
00271 
00272     int btnWidth = titleHeight;
00273     iBtn = new QPixmap(btnWidth, btnWidth);
00274     iBtnDown = new QPixmap(btnWidth, btnWidth);
00275     aBtn = new QPixmap(btnWidth, btnWidth);
00276     aBtnDown = new QPixmap(btnWidth, btnWidth);
00277     int internalHeight = btnWidth - 6;
00278     QPixmap internal(internalHeight, internalHeight);
00279     QRect dest(internal.rect());
00280     dest.moveTo(3, 3);
00281 
00282     QPainter p;
00283 
00284     // inactive buttons
00285     QColor c(options()->color(KDecoration::ColorButtonBg, false));
00286     diagonalGradient(iBtn, c.light(120), c.dark(120));
00287     diagonalGradient(&internal, c.dark(120), c.light(120));
00288     p.begin(iBtn);
00289     p.drawPixmap(dest, internal, internal.rect());
00290     p.end();
00291     //bitBlt(iBtn, 3, 3, &internal, 0, 0, internalHeight, internalHeight, Qt::CopyROP, true);
00292 
00293 
00294     diagonalGradient(iBtnDown, c.dark(120), c.light(120));
00295     diagonalGradient(&internal, c.light(120), c.dark(120));
00296     //bitBlt(iBtnDown, 3, 3, &internal, 0, 0, internalHeight, internalHeight, Qt::CopyROP, true);
00297     p.begin(iBtnDown);
00298     dest.moveTo(3, 3);
00299     p.drawPixmap(dest, internal, internal.rect());
00300     p.end();
00301 
00302     // active buttons
00303     c = options()->color(KDecoration::ColorButtonBg, true);
00304     diagonalGradient(aBtn, c.light(120), c.dark(120));
00305     diagonalGradient(&internal, c.dark(120), c.light(120));
00306     //bitBlt(aBtn, 3, 3, &internal, 0, 0, internalHeight, internalHeight, Qt::CopyROP, true);
00307     p.begin(aBtn);
00308     p.drawPixmap(dest, internal, internal.rect());
00309     p.end();
00310 
00311     diagonalGradient(aBtnDown, c.dark(120), c.light(120));
00312     diagonalGradient(&internal, c.light(120), c.dark(120));
00313     //bitBlt(aBtnDown, 3, 3, &internal, 0, 0, internalHeight, internalHeight, Qt::CopyROP, true);
00314     p.begin(aBtnDown);
00315     p.drawPixmap(dest, internal, internal.rect());
00316     p.end();
00317 
00318     p.begin(aBtn);
00319     p.setPen(Qt::black);
00320     p.drawRect(0, 0, btnWidth, btnWidth);
00321     p.end();
00322     p.begin(iBtn);
00323     p.setPen(Qt::black);
00324     p.drawRect(0, 0, btnWidth, btnWidth);
00325     p.end();
00326     p.begin(aBtnDown);
00327     p.setPen(Qt::black);
00328     p.drawRect(0, 0, btnWidth, btnWidth);
00329     p.end();
00330     p.begin(iBtnDown);
00331     p.setPen(Qt::black);
00332     p.drawRect(0, 0, btnWidth, btnWidth);
00333     p.end();
00334 
00335     if(qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 128)
00336         btnForeground = new QColor(Qt::black);
00337     else
00338         btnForeground = new QColor(Qt::white);
00339 }
00340 
00341 static void delete_pixmaps()
00342 {
00343     delete aTitlePix;
00344     delete iTitlePix;
00345     delete aFramePix;
00346     delete iFramePix;
00347     delete aHandlePix;
00348     delete iHandlePix;
00349     delete aBtn;
00350     delete iBtn;
00351     delete aBtnDown;
00352     delete iBtnDown;
00353     delete btnForeground;
00354 
00355     pixmaps_created = false;
00356 }
00357 
00358 // =====================================
00359 
00360 NextButton::NextButton(NextClient *parent, 
00361                        const unsigned char *bitmap, int bw, int bh,
00362                        const QString& tip, const int realizeBtns)
00363     : QAbstractButton(parent->widget()),
00364       client(parent), last_button(Qt::NoButton)
00365 {
00366     realizeButtons = realizeBtns;
00367 
00368     setAttribute(Qt::WA_NoSystemBackground);
00369     resize(titleHeight, titleHeight);
00370     setFixedSize(titleHeight, titleHeight);
00371 
00372     if(bitmap)
00373         setBitmap(bitmap, bw, bh);
00374 
00375        this->setToolTip( tip);
00376 }
00377 
00378 void NextButton::reset()
00379 {
00380     repaint();
00381 }
00382 
00383 void NextButton::setBitmap(const unsigned char *bitmap, int w, int h)
00384 {
00385     QSize s(w, h);
00386     deco = QBitmap::fromData(s, bitmap, QImage::Format_MonoLSB);
00387     deco.setMask(deco);
00388     repaint();
00389 }
00390 
00391 void NextButton::paintEvent(QPaintEvent *)
00392 {
00393     QPainter p(this);
00394     if (client->isActive())
00395         p.drawPixmap(0, 0, isDown() ? *aBtnDown : *aBtn);
00396     else
00397         p.drawPixmap(0, 0, isDown() ? *iBtnDown : *iBtn);
00398 
00399     // If we have a decoration, draw it; otherwise, we have the menu
00400     // button (remember, we set the bitmap to NULL).
00401     int offset;
00402     if (!deco.isNull()) {
00403         offset = (titleHeight - 10) / 2 + (isDown() ? 1 : 0);
00404         p.setPen(*btnForeground);
00405         p.drawPixmap(offset, offset, deco);
00406     } else {
00407         offset = (titleHeight - 16) / 2;
00408        QPixmap btnpix = client->icon().pixmap(
00409               style()->pixelMetric(QStyle::PM_SmallIconSize),
00410               client->isActive() ? QIcon::Normal : QIcon::Disabled);
00411         p.drawPixmap(offset, offset, btnpix);
00412     }
00413 }
00414 
00415 void NextButton::mousePressEvent( QMouseEvent* e )
00416 {
00417     last_button = e->button();
00418     QMouseEvent me(e->type(), e->pos(), e->globalPos(),
00419            (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
00420            (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
00421            e->modifiers());
00422     QAbstractButton::mousePressEvent(&me);
00423 }
00424 
00425 void NextButton::mouseReleaseEvent( QMouseEvent* e )
00426 {
00427     last_button = e->button();
00428     QMouseEvent me(e->type(), e->pos(), e->globalPos(),
00429            (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
00430            (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
00431            e->modifiers());
00432     QAbstractButton::mouseReleaseEvent(&me);
00433 }
00434 
00435 // =====================================
00436 
00437 NextClient::NextClient(KDecorationBridge *b, KDecorationFactory *f)
00438     : KDecoration(b, f)
00439 {
00440 }
00441 
00442 void NextClient::init()
00443 {
00444     createMainWidget();
00445     widget()->setAttribute(Qt::WA_NoSystemBackground);
00446     widget()->installEventFilter(this);
00447 
00448     //widget()->setBackgroundMode( NoBackground );
00449 
00450     QVBoxLayout *mainLayout = new QVBoxLayout(widget());
00451     QBoxLayout *titleLayout = new QBoxLayout(QBoxLayout::LeftToRight);
00452     QHBoxLayout *windowLayout = new QHBoxLayout();
00453     mainLayout->setMargin(0);
00454     titleLayout->setMargin(0);
00455     titleLayout->setSpacing(0);
00456     mainLayout->addLayout(titleLayout);
00457     mainLayout->addLayout(windowLayout, 1);
00458     mainLayout->addSpacing(mustDrawHandle() ? handleSize : 1);
00459 
00460     windowLayout->addSpacing(1);
00461     if (isPreview())
00462         windowLayout->addWidget(new QLabel(i18n(
00463                      "<center><b>KStep preview</b></center>"), widget()));
00464     else
00465         windowLayout->addItem(new QSpacerItem( 0, 0 ));
00466 
00467     windowLayout->addSpacing(1);
00468 
00469     initializeButtonsAndTitlebar(titleLayout);
00470 }
00471 
00483 void NextClient::initializeButtonsAndTitlebar(QBoxLayout* titleLayout)
00484 {
00485     // Null the buttons to begin with (they are not guaranteed to be null).
00486     for (int i=0; i<MAX_NUM_BUTTONS; i++) {
00487         button[i] = NULL;
00488     }
00489 
00490     // The default button positions for other styles do not match the
00491     // behavior of older versions of KStep, so we have to set these
00492     // manually when customButtonPositions isn't enabled.
00493     QString left, right;
00494     if (options()->customButtonPositions()) {
00495         left = options()->titleButtonsLeft();
00496         right = options()->titleButtonsRight();
00497     } else {
00498         left = QString("I");
00499         right = QString("SX");
00500     }
00501 
00502     // Do actual creation and addition to titleLayout
00503     addButtons(titleLayout, left);
00504 
00505     titlebar = new QSpacerItem(10, titleHeight, QSizePolicy::Expanding,
00506                                QSizePolicy::Minimum );
00507     titleLayout->addItem(titlebar);
00508     addButtons(titleLayout, right);
00509 
00510     // Finally, activate all live buttons
00511     for (int i = 0; i < MAX_NUM_BUTTONS; i++) {
00512         if (button[i]) {
00513             button[i]->setMouseTracking( TRUE );
00514         }
00515     }
00516 }
00517 
00521 void NextClient::addButtons(QBoxLayout* titleLayout, const QString& spec)
00522 {
00523     for (int i = 0; i < spec.length(); i++) {
00524         switch (spec[i].toLatin1()) {
00525         case 'A':
00526             if (isMaximizable()) {
00527                 button[MAXIMIZE_IDX] =
00528                     new NextButton(this, maximize_bits, 10, 10,
00529                                    i18n("Maximize"), Qt::LeftButton|Qt::MidButton|Qt::RightButton);
00530                 titleLayout->addWidget( button[MAXIMIZE_IDX] );
00531                 connect( button[MAXIMIZE_IDX], SIGNAL(clicked()),
00532                          this, SLOT(maximizeButtonClicked()) );
00533             }
00534             break;
00535 
00536         case 'H':
00537            if (providesContextHelp()) {
00538               button[HELP_IDX] = new NextButton(this, question_bits, 10, 10, i18n("Help"));
00539               titleLayout->addWidget( button[HELP_IDX] );
00540               connect( button[HELP_IDX], SIGNAL(clicked()),
00541                       this, SLOT(showContextHelp()) );
00542            }
00543             break;
00544 
00545         case 'I':
00546             if (isMinimizable()) {
00547                 button[ICONIFY_IDX] =
00548                     new NextButton(this, iconify_bits, 10, 10,
00549                                    i18n("Minimize"));
00550                 titleLayout->addWidget( button[ICONIFY_IDX] );
00551                 connect( button[ICONIFY_IDX], SIGNAL(clicked()),
00552                          this, SLOT(minimize()) );
00553             }
00554             break;
00555 
00556         case 'M':
00557             button[MENU_IDX] =
00558                 new NextButton(this, NULL, 10, 10, i18n("Menu"), Qt::LeftButton|Qt::RightButton);
00559             titleLayout->addWidget( button[MENU_IDX] );
00560             // NOTE DIFFERENCE: capture pressed(), not clicked()
00561             connect( button[MENU_IDX], SIGNAL(pressed()),
00562                      this, SLOT(menuButtonPressed()) );
00563             break;
00564 
00565         case 'L':
00566             button[SHADE_IDX] =
00567                 new NextButton(this, NULL, 0, 0, i18n("Shade"));
00568             titleLayout->addWidget( button[SHADE_IDX] );
00569             connect( button[SHADE_IDX], SIGNAL(clicked()),
00570                      this, SLOT(shadeClicked()) );
00571             // NOTE DIFFERENCE: set the pixmap separately (2 states)
00572            shadeChange();
00573             break;
00574 
00575         case 'S':
00576             button[STICKY_IDX] =
00577                 new NextButton(this, NULL, 0, 0, i18n("On all desktops"));
00578             titleLayout->addWidget( button[STICKY_IDX] );
00579             connect( button[STICKY_IDX], SIGNAL(clicked()),
00580                      this, SLOT(toggleOnAllDesktops()) );
00581             // NOTE DIFFERENCE: set the pixmap separately (2 states)
00582            desktopChange();
00583             break;
00584 
00585        case 'F':
00586             button[ABOVE_IDX] = new NextButton(this, NULL, 0, 0, "");
00587             titleLayout->addWidget( button[ABOVE_IDX] );
00588             connect( button[ABOVE_IDX], SIGNAL(clicked()),
00589                      this, SLOT(aboveClicked()) );
00590            connect(this, SIGNAL(keepAboveChanged(bool)),
00591                   SLOT(keepAboveChange(bool)));
00592            keepAboveChange(keepAbove());
00593            break;
00594 
00595        case 'B':
00596             button[BELOW_IDX] = new NextButton(this, NULL, 0, 0, "");
00597             titleLayout->addWidget( button[BELOW_IDX] );
00598             connect( button[BELOW_IDX], SIGNAL(clicked()),
00599                      this, SLOT(belowClicked()) );
00600            connect(this, SIGNAL(keepBelowChanged(bool)),
00601                   SLOT(keepBelowChange(bool)));
00602            keepBelowChange(keepBelow());
00603            break;
00604 
00605        case 'X':
00606             if (isCloseable()) {
00607               button[CLOSE_IDX] =
00608                   new NextButton(this, close_bits, 10, 10,
00609                                i18n("Close"));
00610               titleLayout->addWidget(button[CLOSE_IDX]);
00611               connect(button[CLOSE_IDX], SIGNAL(clicked()),
00612                       this, SLOT(closeWindow()));
00613             }
00614             break;
00615 
00616        case 'R':
00617             if (mustDrawHandle()) {
00618               button[RESIZE_IDX] =
00619                   new NextButton(this, resize_bits, 10, 10,
00620                                i18n("Resize"));
00621               titleLayout->addWidget(button[RESIZE_IDX]);
00622               // NOTE DIFFERENCE: capture pressed(), not clicked()
00623               connect(button[RESIZE_IDX], SIGNAL(pressed()),
00624                       this, SLOT(resizePressed()));
00625             }
00626            break;
00627         case '_':
00628             // TODO: Add spacer handling
00629             break;
00630 
00631         default:
00632             kDebug() << " Can't happen: unknown button code "
00633                       << QString(spec[i]);
00634             break;
00635         }
00636     }
00637 }
00638 
00639 bool NextClient::mustDrawHandle() const
00640 {
00641     bool drawSmallBorders = !options()->moveResizeMaximizedWindows();
00642     if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) {
00643        return false;
00644     } else {
00645        return isResizable();
00646     }
00647 }
00648 
00649 void NextClient::iconChange()
00650 {
00651     if (button[MENU_IDX] && button[MENU_IDX]->isVisible())
00652        button[MENU_IDX]->repaint();
00653 }
00654 
00655 void NextClient::menuButtonPressed()
00656 {
00657     // Probably don't need this null check, but we might as well.
00658     if (button[MENU_IDX]) {
00659        QRect menuRect = button[MENU_IDX]->rect();
00660         QPoint menuTop = button[MENU_IDX]->mapToGlobal(menuRect.topLeft());
00661         QPoint menuBottom = button[MENU_IDX]->mapToGlobal(menuRect.bottomRight());
00662        menuTop += QPoint(1, 1);
00663        menuBottom += QPoint(1, 1);
00664         KDecorationFactory* f = factory();
00665         showWindowMenu(QRect(menuTop, menuBottom));
00666         if( !f->exists( this )) // 'this' was deleted
00667             return;
00668        button[MENU_IDX]->setDown(false);
00669     }
00670 }
00671 
00672 // Copied, with minor edits, from KDEDefaultClient::slotMaximize()
00673 void NextClient::maximizeButtonClicked()
00674 {
00675     if (button[MAXIMIZE_IDX]) {
00676         maximize(button[MAXIMIZE_IDX]->lastButton());
00677     }
00678 }
00679 
00680 void NextClient::shadeClicked()
00681 {
00682     setShade(!isSetShade());
00683 }
00684 
00685 void NextClient::aboveClicked()
00686 {
00687     setKeepAbove(!keepAbove());
00688 }
00689 
00690 void NextClient::belowClicked()
00691 {
00692     setKeepBelow(!keepBelow());
00693     keepAboveChange(keepAbove());
00694     keepBelowChange(keepBelow());
00695 }
00696 
00697 void NextClient::resizePressed()
00698 {
00699     performWindowOperation(ResizeOp);
00700 }
00701 
00702 void NextClient::resizeEvent(QResizeEvent *)
00703 {
00704     if (widget()->isVisible()) {
00705        // TODO ? update border area only?
00706         widget()->update();
00707 #if 0
00708         widget()->update(titlebar->geometry());
00709         QPainter p(widget());
00710        QRect t = titlebar->geometry();
00711        t.setTop( 0 );
00712        QRegion r = widget()->rect();
00713        r = r.subtract( t );
00714        p.setClipRegion( r );
00715        p.eraseRect(widget()->rect());
00716 #endif
00717     }
00718 }
00719 
00720 void NextClient::captionChange()
00721 {
00722     widget()->repaint(titlebar->geometry());
00723 }
00724 
00725 
00726 void NextClient::paintEvent( QPaintEvent* )
00727 {
00728     QPainter p(widget());
00729 
00730     // Draw black frame
00731     QRect fr = widget()->rect();
00732     p.setPen(Qt::black);
00733     p.drawRect(fr);
00734 
00735     // Draw title bar
00736     QRect t = titlebar->geometry();
00737     t.setTop(1);
00738     p.drawTiledPixmap(t.x()+1, t.y()+1, t.width()-2, t.height()-2,
00739                       isActive() ? *aTitlePix : *iTitlePix);
00740     qDrawShadePanel(&p, t.x(), t.y(), t.width(), t.height()-1,
00741                    options()->palette(KDecoration::ColorTitleBar, isActive()));
00742     p.drawLine(t.x(), t.bottom(), t.right(), t.bottom());
00743 
00744 #if 0
00745     // Why setting up a clipping region if it is not used? (setClipping(false))
00746     QRegion r = fr;
00747     r = r.subtract( t );
00748     p.setClipRegion( r );
00749     p.setClipping(false);
00750 #endif
00751 
00752     t.setTop( 1 );
00753     t.setHeight(t.height()-2);
00754     t.setLeft( t.left() + 4 );
00755     t.setRight( t.right() - 2 );
00756 
00757     p.setPen(options()->color(KDecoration::ColorFont, isActive()));
00758     p.setFont(options()->font(isActive()));
00759     p.drawText( t, Qt::AlignCenter | Qt::AlignVCenter, caption() );
00760 
00761     // Draw resize handle
00762     if (mustDrawHandle()) {
00763         int corner = 16 + 3*handleSize/2;
00764        qDrawShadePanel(&p,
00765               fr.x() + 1, fr.bottom() - handleSize, corner-1, handleSize,
00766               options()->palette(KDecoration::ColorHandle, isActive()),
00767               false);
00768        p.drawTiledPixmap(fr.x() + 2, fr.bottom() - handleSize + 1,
00769               corner - 3, handleSize - 2, isActive() ? *aHandlePix : *iHandlePix);
00770 
00771        qDrawShadePanel(&p,
00772               fr.x() + corner, fr.bottom() - handleSize,
00773               fr.width() - 2*corner, handleSize,
00774               options()->palette(KDecoration::ColorFrame, isActive()),
00775               false);
00776        p.drawTiledPixmap(fr.x() + corner + 1, fr.bottom() - handleSize + 1,
00777               fr.width() - 2*corner - 2, handleSize - 2,
00778               isActive() ? *aFramePix : *iFramePix);
00779 
00780        qDrawShadePanel(&p,
00781               fr.right() - corner + 1, fr.bottom() - handleSize, corner - 1, handleSize,
00782               options()->palette(KDecoration::ColorHandle, isActive()),
00783               false);
00784        p.drawTiledPixmap(fr.right() - corner + 2, fr.bottom() - handleSize + 1,
00785               corner - 3, handleSize - 2, isActive() ? *aHandlePix : *iHandlePix);
00786     }
00787 }
00788 
00789 void NextClient::mouseDoubleClickEvent( QMouseEvent * e )
00790 {
00791     if (e->button() == Qt::LeftButton && titlebar->geometry().contains( e->pos() ) )
00792        titlebarDblClickOperation();
00793 }
00794 
00795 void NextClient::showEvent(QShowEvent *)
00796 {
00797     widget()->repaint();
00798 }
00799 
00800 void NextClient::desktopChange()
00801 {
00802     bool on = isOnAllDesktops();
00803     if (NextButton * b = button[STICKY_IDX]) {
00804         b->setBitmap( on ? unsticky_bits : sticky_bits, 10, 10);
00805        //QToolTip::remove(b);
00806        b->setToolTip( on ? i18n("Not on all desktops") : i18n("On all desktops"));
00807     }
00808 }
00809 
00810 void NextClient::maximizeChange()
00811 {
00812     if (button[MAXIMIZE_IDX]) {
00813        bool m = maximizeMode() == MaximizeFull;
00814        //button[MAXIMIZE_IDX]->setBitmap(m ? minmax_bits : maximize_bits);
00815        //QToolTip::remove(button[MAXIMIZE_IDX]);
00816        button[MAXIMIZE_IDX]->setToolTip(
00817               m ? i18n("Restore") : i18n("Maximize"));
00818     }
00819     //spacer->changeSize(10, mustDrawHandle() ? handleSize : 1,
00820     //            QSizePolicy::Expanding, QSizePolicy::Minimum);
00821     //mainLayout->activate();
00822 }
00823 
00824 void NextClient::activeChange()
00825 {
00826     widget()->repaint();
00827     slotReset();
00828 }
00829 
00830 void NextClient::slotReset()
00831 {
00832     for (int i=0; i<MAX_NUM_BUTTONS; i++) {
00833         if (button[i]) {
00834             button[i]->reset();
00835         }
00836     }
00837 }
00838 
00839 KDecoration::Position
00840 NextClient::mousePosition( const QPoint& p ) const
00841 {
00842   Position m = PositionCenter;
00843 
00844   if (p.y() < (height() - handleSize))
00845     m = KDecoration::mousePosition(p);
00846 
00847   else {
00848     int corner = 16 + 3*handleSize/2;
00849     if (p.x() >= (width() - corner))
00850       m = PositionBottomRight;
00851     else if (p.x() <= corner)
00852       m = PositionBottomLeft;
00853     else
00854       m = PositionBottom;
00855   }
00856 
00857   return m;
00858 }
00859 
00860 void NextClient::borders(int &left, int &right, int &top, int &bottom) const
00861 {
00862     left = right = 1;
00863     top = titleHeight; // FRAME is this ok?
00864     bottom = mustDrawHandle() ? handleSize : 1;
00865 }
00866 
00867 void NextClient::shadeChange()
00868 {
00869     if (NextButton *b = button[SHADE_IDX]) {
00870         b->setBitmap(isSetShade() ? unshade_bits : shade_bits, 10, 10);
00871        //QToolTip::remove(b);
00872        b->setToolTip( isSetShade() ? i18n("Unshade") : i18n("Shade"));
00873     }
00874 }
00875 
00876 void NextClient::keepAboveChange(bool above)
00877 {
00878     if (NextButton *b = button[ABOVE_IDX]) {
00879         b->setBitmap(above ? from_above_bits : keep_above_bits, 10, 10);
00880        //QToolTip::remove(b);
00881        b->setToolTip( above ?
00882               i18n("Do not keep above others") : i18n("Keep above others"));
00883        b->repaint();
00884     }
00885 }
00886 
00887 void NextClient::keepBelowChange(bool below)
00888 {
00889     if (NextButton *b = button[BELOW_IDX]) {
00890         b->setBitmap(below ? from_below_bits : keep_below_bits, 10, 10);
00891        //QToolTip::remove(b);
00892        b->setToolTip( below ?
00893               i18n("Do not keep below others") : i18n("Keep below others"));
00894        b->repaint();
00895     }
00896 }
00897 
00898 QSize NextClient::minimumSize() const
00899 {
00900     return QSize(titleHeight * 6 + 2, titleHeight + handleSize + 2);
00901 }
00902 
00903 void NextClient::resize(const QSize& s)
00904 {
00905     widget()->resize(s);
00906 }
00907 
00908 void NextClient::reset(unsigned long)
00909 {
00910     for (int i = 0; i < MAX_NUM_BUTTONS; ++i) {
00911         if (button[i])
00912             button[i]->reset();
00913     }
00914     widget()->repaint();
00915 }
00916 
00917 bool NextClient::eventFilter(QObject *o, QEvent *e)
00918 {
00919     if (o != widget())
00920        return false;
00921     switch (e->type()) {
00922     case QEvent::Resize:
00923        resizeEvent(static_cast< QResizeEvent* >( e ));
00924        return true;
00925     case QEvent::Paint:
00926        paintEvent(static_cast< QPaintEvent* >( e ));
00927        return true;
00928     case QEvent::MouseButtonDblClick:
00929        mouseDoubleClickEvent(static_cast< QMouseEvent* >( e ));
00930        return true;
00931     case QEvent::MouseButtonPress:
00932        processMousePressEvent(static_cast< QMouseEvent* >( e ));
00933        return true;
00934     case QEvent::Show:
00935        showEvent(static_cast< QShowEvent* >( e ));
00936        return true;
00937     default:
00938        break;
00939     }
00940     return false;
00941 }
00942 
00943 bool NextClient::drawbound(const QRect& geom, bool /* clear */)
00944 {
00945     // Let kwin draw the bounds, for now.
00946     return false;
00947 #if 0
00948     QPainter p(workspaceWidget());
00949     p.setPen(QPen(Qt::white, 3));
00950     p.setCompositionMode(QPainter::CompositionMode_Xor);
00951     p.drawRect(geom);
00952     int leftMargin = geom.left() + 2;
00953     p.fillRect(leftMargin, geom.top() + titleHeight - 1,
00954            geom.width() - 4, 3, Qt::white);
00955     if (mustDrawHandle()) {
00956        p.fillRect(leftMargin, geom.bottom() - handleSize - 1,
00957               geom.width() - 4, 3, Qt::white);
00958     }
00959     return true;
00960 #endif
00961 }
00962 
00963 // =====================================
00964 
00965 NextClientFactory::NextClientFactory()
00966 {
00967     create_pixmaps(this);
00968 }
00969 
00970 NextClientFactory::~NextClientFactory()
00971 {
00972     delete_pixmaps();
00973 }
00974 
00975 KDecoration *NextClientFactory::createDecoration(KDecorationBridge *b)
00976 {
00977     return new NextClient(b, this);
00978 }
00979 
00980 bool NextClientFactory::reset(unsigned long /*changed*/)
00981 {
00982     // TODO Do not recreate decorations if it is not needed. Look at
00983     // ModernSystem for how to do that
00984     delete_pixmaps();
00985     create_pixmaps(this);
00986     // For now just return true.
00987     return true;
00988 }
00989 
00990 bool NextClientFactory::supports( Ability ability ) const
00991 {
00992     switch( ability )
00993     {
00994         case AbilityAnnounceButtons:
00995         case AbilityButtonMenu:
00996         case AbilityButtonOnAllDesktops:
00997         case AbilityButtonHelp:
00998         case AbilityButtonMinimize:
00999         case AbilityButtonMaximize:
01000         case AbilityButtonClose:
01001         case AbilityButtonAboveOthers:
01002         case AbilityButtonBelowOthers:
01003         case AbilityButtonShade:
01004         case AbilityButtonResize:
01005             return true;
01006         default:
01007             return false;
01008     };
01009 }
01010 
01011 QList< NextClientFactory::BorderSize >
01012 NextClientFactory::borderSizes() const
01013 {
01014     // the list must be sorted
01015     return QList< BorderSize >() << BorderTiny << BorderNormal <<
01016        BorderLarge << BorderVeryLarge <<  BorderHuge <<
01017        BorderVeryHuge << BorderOversized;
01018 }
01019 
01020 } // KStep namespace
01021 
01022 extern "C" KDE_EXPORT KDecorationFactory* create_factory()
01023 {
01024     return new KStep::NextClientFactory();
01025 }
01026 
01027 #include "nextclient.moc"
01028 
01029 // vim: sw=4