kwin Library API Documentation

b2client.cpp

00001 /*
00002  * B-II KWin Client
00003  *
00004  * Changes:
00005  *   Customizable button positions by Karol Szwed <gallium@kde.org>
00006  *
00007  *   Thin frame in fixed size windows, titlebar gradient support, accessibility
00008  *   improvements, customizable menu double click action and button hover
00009  *   effects are
00010  *   Copyright (c) 2003,2004 Luciano Montanaro <mikelima@cirulla.net>
00011  */
00012 
00013 #include "b2client.h"
00014 #include <qapplication.h>
00015 #include <qlayout.h>
00016 #include <qdrawutil.h>
00017 #include <kpixmapeffect.h>
00018 #include <kimageeffect.h>
00019 #include <kicontheme.h>
00020 #include <kiconeffect.h>
00021 #include <kdrawutil.h>
00022 #include <klocale.h>
00023 #include <kconfig.h>
00024 #include <qbitmap.h>
00025 #include <qlabel.h>
00026 #include <qtooltip.h>
00027 
00028 #include <X11/Xlib.h>
00029 
00030 namespace B2 {
00031 
00032 #include "bitmaps.h"
00033 
00034 enum { 
00035     Norm = 0, 
00036     Hover, Down, INorm, IHover, IDown, 
00037     NumStates 
00038 };
00039 
00040 enum {
00041     P_CLOSE = 0, 
00042     P_MAX, P_NORMALIZE, P_ICONIFY, P_PINUP, P_MENU, P_HELP, P_SHADE, P_RESIZE,
00043     P_NUM_BUTTON_TYPES
00044 };
00045 
00046 #define NUM_PIXMAPS (P_NUM_BUTTON_TYPES * NumStates)
00047 
00048 static KPixmap *pixmap[NUM_PIXMAPS];
00049 
00050 // active
00051 #define PIXMAP_A(i)  (pixmap[(i) * NumStates + Norm])
00052 // active, hover
00053 #define PIXMAP_AH(i) (pixmap[(i) * NumStates + Hover])
00054 // active, down
00055 #define PIXMAP_AD(i) (pixmap[(i) * NumStates + Down])
00056 // inactive
00057 #define PIXMAP_I(i)  (pixmap[(i) * NumStates + INorm])
00058 // inactive, hover
00059 #define PIXMAP_IH(i) (pixmap[(i) * NumStates + IHover])
00060 // inactive, down
00061 #define PIXMAP_ID(i) (pixmap[(i) * NumStates + IDown])
00062 
00063 static KPixmap* titleGradient[2] = {0, 0};
00064 
00065 static int thickness = 4; // Frame thickness
00066 static int buttonSize = 16;
00067 
00068 enum DblClickOperation {
00069     NoOp = 0,
00070     MinimizeOp,
00071     ShadeOp,
00072     CloseOp
00073 };
00074 
00075 static DblClickOperation menu_dbl_click_op = NoOp;
00076 
00077 static bool pixmaps_created = false;
00078 static bool colored_frame = false;
00079 static bool do_draw_handle = true;
00080 static bool drawSmallBorders = false;
00081 
00082 // =====================================
00083 
00084 extern "C" KDE_EXPORT KDecorationFactory* create_factory()
00085 {
00086     return new B2::B2ClientFactory();
00087 }
00088 
00089 // =====================================
00090 
00091 static inline const KDecorationOptions *options()
00092 {
00093     return KDecoration::options();
00094 }
00095 
00096 static void redraw_pixmaps();
00097 
00098 static void read_config(B2ClientFactory *f)
00099 {
00100     // Force button size to be in a reasonable range.
00101     // If the frame width is large, the button size must be large too.
00102     buttonSize = (QFontMetrics(options()->font(true)).height() + 1) & 0x3e;
00103     if (buttonSize < 16) buttonSize = 16;
00104 
00105     KConfig conf("kwinb2rc");
00106     conf.setGroup("General");
00107     colored_frame = conf.readBoolEntry("UseTitleBarBorderColors", false);
00108     do_draw_handle = conf.readBoolEntry("DrawGrabHandle", true);
00109     drawSmallBorders = !options()->moveResizeMaximizedWindows();
00110 
00111     QString opString = conf.readEntry("MenuButtonDoubleClickOperation", "NoOp");
00112     if (opString == "Close") {
00113         menu_dbl_click_op = B2::CloseOp;
00114     } else if (opString == "Minimize") {
00115         menu_dbl_click_op = B2::MinimizeOp;
00116     } else if (opString == "Shade") {
00117         menu_dbl_click_op = B2::ShadeOp;
00118     } else {
00119         menu_dbl_click_op = B2::NoOp;
00120     }
00121 
00122     switch (options()->preferredBorderSize(f)) {
00123     case KDecoration::BorderTiny:
00124     thickness = 2;
00125     break;
00126     case KDecoration::BorderLarge:
00127     thickness = 5;
00128     break;
00129     case KDecoration::BorderVeryLarge:
00130     thickness = 8;
00131     break;
00132     case KDecoration::BorderHuge:
00133     thickness = 12;
00134     break;
00135     case KDecoration::BorderVeryHuge:
00136     case KDecoration::BorderOversized:
00137     case KDecoration::BorderNormal:
00138     default:
00139     thickness = 4;
00140     }
00141 }
00142 
00143 static void drawB2Rect(KPixmap *pix, const QColor &primary, bool down)
00144 {
00145     QPainter p(pix);
00146     QColor hColor = primary.light(150);
00147     QColor lColor = primary.dark(150);
00148 
00149     if (QPixmap::defaultDepth() > 8) {
00150         if (down)
00151             KPixmapEffect::gradient(*pix, lColor, hColor,
00152                                     KPixmapEffect::DiagonalGradient);
00153         else
00154             KPixmapEffect::gradient(*pix, hColor, lColor,
00155                                     KPixmapEffect::DiagonalGradient);
00156     }
00157     else
00158         pix->fill(primary);
00159     int x2 = pix->width() - 1;
00160     int y2 = pix->height() - 1;
00161     p.setPen(down ? hColor : lColor);
00162     p.drawLine(0, 0, x2, 0);
00163     p.drawLine(0, 0, 0, y2);
00164     p.drawLine(1, x2 - 1, x2 - 1, y2 - 1);
00165     p.drawLine(x2 - 1, 1, x2 - 1, y2 - 1);
00166     p.setPen(down ? lColor : hColor);
00167     p.drawRect(1, 1, x2, y2);
00168 
00169 }
00170 
00171 QPixmap* kwin_get_menu_pix_hack()
00172 {
00173     //return menu_pix;  FIXME
00174     return PIXMAP_A(P_MENU);
00175 }
00176 
00177 static void create_pixmaps()
00178 {
00179     if (pixmaps_created)
00180         return;
00181     pixmaps_created = true;
00182 
00183     int i;
00184     int bsize = buttonSize - 2;
00185     if (bsize < 16) bsize = 16;
00186 
00187     for (i = 0; i < NUM_PIXMAPS; i++) {
00188         pixmap[i] = new KPixmap;
00189     switch (i / NumStates) {
00190     case P_MAX: // will be initialized by copying P_CLOSE
00191     case P_RESIZE: 
00192         break;
00193     case P_ICONIFY:
00194         pixmap[i]->resize(10, 10); break;
00195     case P_SHADE:
00196     case P_CLOSE:
00197         pixmap[i]->resize(bsize, bsize); break;
00198     default:
00199         pixmap[i]->resize(16, 16); break;
00200     }
00201     }
00202 
00203     // there seems to be no way to load X bitmaps from data properly, so
00204     // we need to create new ones for each mask :P
00205     QBitmap pinupMask(16, 16, pinup_mask_bits, true);
00206     PIXMAP_A(P_PINUP)->setMask(pinupMask);
00207     PIXMAP_I(P_PINUP)->setMask(pinupMask);
00208     QBitmap pindownMask(16, 16, pindown_mask_bits, true);
00209     PIXMAP_AD(P_PINUP)->setMask(pindownMask);
00210     PIXMAP_ID(P_PINUP)->setMask(pindownMask);
00211 
00212     QBitmap menuMask(16, 16, menu_mask_bits, true);
00213     for (i = 0; i < NumStates; i++) 
00214     pixmap[P_MENU * NumStates + i]->setMask(menuMask);
00215 
00216     QBitmap helpMask(16, 16, help_mask_bits, true);
00217     for (i = 0; i < NumStates; i++) 
00218     pixmap[P_HELP * NumStates + i]->setMask(helpMask);
00219 
00220     QBitmap normalizeMask(16, 16, true);
00221     // draw normalize icon mask
00222     QPainter mask;
00223     mask.begin(&normalizeMask);
00224 
00225     QBrush one(Qt::color1);
00226     mask.fillRect(normalizeMask.width() - 12, normalizeMask.height() - 12, 
00227           12, 12, one);
00228     mask.fillRect(0, 0, 10, 10, one);
00229     mask.end();
00230 
00231     for (i = 0; i < NumStates; i++) 
00232     pixmap[P_NORMALIZE * NumStates + i]->setMask(normalizeMask);
00233     
00234     QBitmap shadeMask(bsize, bsize, true);
00235     mask.begin(&shadeMask);
00236     mask.fillRect(0, 0, bsize, 6, one);
00237     mask.end();
00238     for (i = 0; i < NumStates; i++) 
00239     pixmap[P_SHADE * NumStates + i]->setMask(shadeMask);
00240 
00241     titleGradient[0] = 0;
00242     titleGradient[1] = 0;
00243 
00244     redraw_pixmaps();
00245 }
00246 
00247 static void delete_pixmaps()
00248 {
00249     for (int i = 0; i < NUM_PIXMAPS; i++) {
00250         delete pixmap[i];
00251     pixmap[i] = 0;
00252     }
00253     for (int i = 0; i < 2; i++) {
00254         delete titleGradient[i];
00255     titleGradient[i] = 0;
00256     }
00257     pixmaps_created = false;
00258 }
00259 
00260 // =====================================
00261 
00262 B2ClientFactory::B2ClientFactory()
00263 {
00264     read_config(this);
00265     create_pixmaps();
00266 }
00267 
00268 B2ClientFactory::~B2ClientFactory()
00269 {
00270     delete_pixmaps();
00271 }
00272 
00273 KDecoration *B2ClientFactory::createDecoration(KDecorationBridge *b)
00274 {
00275     return new B2::B2Client(b, this);
00276 }
00277 
00278 bool B2ClientFactory::reset(unsigned long changed)
00279 {
00280     bool needsReset = SettingColors ? true : false;
00281     // TODO Do not recreate decorations if it is not needed. Look at
00282     // ModernSystem for how to do that
00283     read_config(this);
00284     if (changed & SettingFont) {
00285         delete_pixmaps();
00286         create_pixmaps();
00287     needsReset = true;
00288     }
00289     redraw_pixmaps();
00290     // For now just return true.
00291     return needsReset;
00292 }
00293 
00294 bool B2ClientFactory::supports( Ability ability )
00295 {
00296     switch( ability )
00297     {
00298         case AbilityAnnounceButtons:
00299         case AbilityButtonMenu:
00300         case AbilityButtonOnAllDesktops:
00301         case AbilityButtonSpacer:
00302         case AbilityButtonHelp:
00303         case AbilityButtonMinimize:
00304         case AbilityButtonMaximize:
00305         case AbilityButtonClose:
00306         case AbilityButtonAboveOthers:
00307         case AbilityButtonBelowOthers:
00308         case AbilityButtonShade:
00309         case AbilityButtonResize:
00310             return true;
00311         default:
00312             return false;
00313     };
00314 }
00315 
00316 QValueList< B2ClientFactory::BorderSize > B2ClientFactory::borderSizes() const
00317 {
00318     // the list must be sorted
00319     return QValueList< BorderSize >() << BorderTiny << BorderNormal <<
00320     BorderLarge << BorderVeryLarge << BorderHuge;
00321 }
00322 
00323 // =====================================
00324 
00325 void B2Client::maxButtonClicked()
00326 {
00327     maximize(button[BtnMax]->last_button);
00328 }
00329 
00330 void B2Client::shadeButtonClicked()
00331 {
00332     setShade(!isSetShade());
00333 }
00334 
00335 void B2Client::resizeButtonPressed()
00336 {
00337     performWindowOperation(ResizeOp);
00338 }
00339 
00340 B2Client::B2Client(KDecorationBridge *b, KDecorationFactory *f)
00341     : KDecoration(b, f), bar_x_ofs(0), in_unobs(0)
00342 {
00343 }
00344 
00345 void B2Client::init()
00346 {
00347     const QString tips[] = {
00348     i18n("Menu"), 
00349     isOnAllDesktops() ? 
00350         i18n("Not on all desktops") : i18n("On all desktops"), 
00351     i18n("Minimize"), i18n("Maximize"), 
00352     i18n("Close"), i18n("Help"),
00353     isSetShade() ? i18n("Unshade") : i18n("Shade"),
00354     i18n("Resize") 
00355     };
00356 
00357     // This check has to be made early - to make the preview widget work.
00358     resizable = isResizable();
00359     
00360     createMainWidget(WResizeNoErase | WRepaintNoErase);
00361     widget()->installEventFilter(this);
00362 
00363     widget()->setBackgroundMode(NoBackground);
00364 
00365     // Set button pointers to NULL so we know what has been created
00366     for (int i = 0; i < BtnCount; i++)
00367         button[i] = NULL;
00368 
00369     g = new QGridLayout(widget(), 0, 0);
00370     if (isPreview()) {
00371         g->addMultiCellWidget(
00372         new QLabel(i18n("<b><center>B II preview</center></b>"),
00373             widget()),
00374         1, 1, 1, 2);
00375     } else {
00376     g->addMultiCell(new QSpacerItem(0, 0), 1, 1, 1, 2);
00377     }
00378 
00379     // Left and right border width
00380     leftSpacer = new QSpacerItem(thickness, 16,
00381         QSizePolicy::Fixed, QSizePolicy::Expanding);
00382     rightSpacer = new QSpacerItem(thickness, 16,
00383         QSizePolicy::Fixed, QSizePolicy::Expanding);
00384 
00385     g->addItem(leftSpacer, 1, 0);
00386     g->addColSpacing(1, 16);
00387     g->setColStretch(2, 1);
00388     g->setRowStretch(1, 1);
00389     g->addItem(rightSpacer, 1, 3);
00390 
00391     // Bottom border height
00392     spacer = new QSpacerItem(10, thickness + (mustDrawHandle() ? 4 : 0),
00393         QSizePolicy::Expanding, QSizePolicy::Fixed);
00394     g->addItem(spacer, 3, 1);
00395 
00396     // titlebar
00397     g->addRowSpacing(0, buttonSize + 4);
00398 
00399     titlebar = new B2Titlebar(this);
00400     titlebar->setMinimumWidth(buttonSize + 4);
00401     titlebar->setFixedHeight(buttonSize + 4);
00402 
00403     QBoxLayout *titleLayout = new QBoxLayout(titlebar, 
00404         QBoxLayout::LeftToRight, 0, 1, 0);
00405     titleLayout->addSpacing(3);
00406 
00407     if (options()->customButtonPositions()) {
00408         addButtons(options()->titleButtonsLeft(), tips, titlebar, titleLayout);
00409         titleLayout->addItem(titlebar->captionSpacer);
00410         addButtons(options()->titleButtonsRight(), tips, titlebar, titleLayout);
00411     } else {
00412         addButtons("MSH", tips, titlebar, titleLayout);
00413         titleLayout->addItem(titlebar->captionSpacer);
00414         addButtons("IAX", tips, titlebar, titleLayout);
00415     }
00416 
00417     titleLayout->addSpacing(3);
00418 
00419     QColor c = options()->colorGroup(KDecoration::ColorTitleBar, isActive()).
00420         color(QColorGroup::Button);
00421 
00422     for (int i = 0; i < BtnCount; i++) {
00423         if (button[i])
00424             button[i]->setBg(c);
00425     }
00426 
00427     titlebar->updateGeometry();
00428     positionButtons();
00429     titlebar->recalcBuffer();
00430     titlebar->installEventFilter(this);
00431 }
00432 
00433 void B2Client::addButtons(const QString& s, const QString tips[],
00434                           B2Titlebar* tb, QBoxLayout* titleLayout)
00435 {
00436     if (s.length() <= 0)
00437     return;
00438 
00439     for (unsigned int i = 0; i < s.length(); i++) {
00440         switch (s[i].latin1()) {
00441     case 'M':  // Menu button
00442         if (!button[BtnMenu]) {
00443         button[BtnMenu] = new B2Button(this, tb, tips[BtnMenu], 
00444             LeftButton | RightButton);
00445         button[BtnMenu]->setPixmaps(P_MENU);
00446         button[BtnMenu]->setUseMiniIcon();
00447         connect(button[BtnMenu], SIGNAL(pressed()),
00448             this, SLOT(menuButtonPressed()));
00449         titleLayout->addWidget(button[BtnMenu]);
00450         }
00451         break;
00452     case 'S':  // Sticky button
00453         if (!button[BtnSticky]) {
00454         button[BtnSticky] = new B2Button(this, tb, tips[BtnSticky]);
00455         button[BtnSticky]->setPixmaps(P_PINUP);
00456         button[BtnSticky]->setToggle();
00457         button[BtnSticky]->setDown(isOnAllDesktops());
00458         connect(button[BtnSticky], SIGNAL(clicked()),
00459             this, SLOT(toggleOnAllDesktops()));
00460         titleLayout->addWidget(button[BtnSticky]);
00461         }
00462         break;
00463     case 'H':  // Help button
00464         if (providesContextHelp() && (!button[BtnHelp])) {
00465         button[BtnHelp] = new B2Button(this, tb, tips[BtnHelp]);
00466         button[BtnHelp]->setPixmaps(P_HELP);
00467         connect(button[BtnHelp], SIGNAL(clicked()),
00468             this, SLOT(showContextHelp()));
00469         titleLayout->addWidget(button[BtnHelp]);
00470         }
00471         break;
00472     case 'I':  // Minimize button
00473         if (isMinimizable() && (!button[BtnIconify])) {
00474         button[BtnIconify] = new B2Button(this, tb,tips[BtnIconify]);
00475         button[BtnIconify]->setPixmaps(P_ICONIFY);
00476         connect(button[BtnIconify], SIGNAL(clicked()),
00477             this, SLOT(minimize()));
00478         titleLayout->addWidget(button[BtnIconify]);
00479         }
00480         break;
00481     case 'A':  // Maximize button
00482         if (isMaximizable() && (!button[BtnMax])) {
00483         button[BtnMax] = new B2Button(this, tb, tips[BtnMax], 
00484             LeftButton | MidButton | RightButton);
00485         button[BtnMax]->setPixmaps(maximizeMode() == MaximizeFull ?
00486             P_NORMALIZE : P_MAX);
00487         connect(button[BtnMax], SIGNAL(clicked()),
00488             this, SLOT(maxButtonClicked()));
00489         titleLayout->addWidget(button[BtnMax]);
00490         }
00491         break;
00492     case 'X':  // Close button
00493         if (isCloseable() && !button[BtnClose]) {
00494         button[BtnClose] = new B2Button(this, tb, tips[BtnClose]);
00495         button[BtnClose]->setPixmaps(P_CLOSE);
00496         connect(button[BtnClose], SIGNAL(clicked()),
00497             this, SLOT(closeWindow()));
00498         titleLayout->addWidget(button[BtnClose]);
00499         }
00500         break;
00501     case 'L': // Shade button
00502         if (isShadeable() && !button[BtnShade]) {
00503         button[BtnShade] = new B2Button(this, tb, tips[BtnShade]);
00504         button[BtnShade]->setPixmaps(P_SHADE);
00505         connect(button[BtnShade], SIGNAL(clicked()),
00506             this, SLOT(shadeButtonClicked()));
00507         titleLayout->addWidget(button[BtnShade]);
00508         }
00509         break;
00510     case 'R': // Resize button
00511         if (resizable && !button[BtnResize]) {
00512         button[BtnResize] = new B2Button(this, tb, tips[BtnResize]);
00513         button[BtnResize]->setPixmaps(P_RESIZE);
00514         connect(button[BtnResize], SIGNAL(pressed()),
00515             this, SLOT(resizeButtonPressed()));
00516         titleLayout->addWidget(button[BtnResize]);
00517         }
00518         break;
00519     case '_': // Additional spacing
00520         titleLayout->addSpacing(4);
00521         break;
00522     }
00523     }
00524 }
00525 
00526 bool B2Client::mustDrawHandle() const 
00527 { 
00528     if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) {
00529     return false;
00530     } else {
00531     return do_draw_handle & resizable;
00532     }
00533 }
00534 
00535 void B2Client::iconChange()
00536 {
00537     if (button[BtnMenu])
00538         button[BtnMenu]->repaint(false);
00539 }
00540 
00541 // Gallium: New button show/hide magic for customizable
00542 //          button positions.
00543 void B2Client::calcHiddenButtons()
00544 {
00545     // Hide buttons in this order:
00546     // Shade, Sticky, Help, Resize, Maximize, Minimize, Close, Menu
00547     B2Button* btnArray[] = { 
00548     button[BtnShade], button[BtnSticky], button[BtnHelp], button[BtnResize],
00549     button[BtnMax], button[BtnIconify], button[BtnClose], button[BtnMenu] 
00550     };
00551     int minWidth = 120;
00552     int currentWidth = width();
00553     int count = 0;
00554     int i;
00555 
00556     // Determine how many buttons we need to hide
00557     while (currentWidth < minWidth) {
00558         currentWidth += buttonSize + 1; // Allow for spacer (extra 1pix)
00559         count++;
00560     }
00561     // Bound the number of buttons to hide
00562     if (count > BtnCount) count = BtnCount;
00563 
00564     // Hide the required buttons
00565     for (i = 0; i < count; i++) {
00566         if (btnArray[i] && btnArray[i]->isVisible())
00567             btnArray[i]->hide();
00568     }
00569     // Show the rest of the buttons
00570     for (i = count; i < BtnCount; i++) {
00571         if (btnArray[i] && (!btnArray[i]->isVisible()))
00572             btnArray[i]->show();
00573     }
00574 }
00575 
00576 void B2Client::resizeEvent(QResizeEvent * /*e*/)
00577 {
00578     calcHiddenButtons();
00579     titlebar->layout()->activate();
00580     positionButtons();
00581 
00582     /* may be the resize cut off some space occupied by titlebar, which
00583        was moved, so instead of reducing it, we first try to move it */
00584     titleMoveAbs(bar_x_ofs);
00585 
00586     doShape();
00587     widget()->repaint(); // the frame is misrendered without this
00588 }
00589 
00590 void B2Client::captionChange()
00591 {
00592     positionButtons();
00593     titleMoveAbs(bar_x_ofs);
00594     doShape();
00595     titlebar->recalcBuffer();
00596     titlebar->repaint(false);
00597 }
00598 
00599 void B2Client::paintEvent(QPaintEvent* e)
00600 {
00601     QPainter p(widget());
00602 
00603     KDecoration::ColorType frameColorGroup = colored_frame ?
00604     KDecoration::ColorTitleBar : KDecoration::ColorFrame;
00605 
00606     QRect t = titlebar->geometry();
00607 
00608     // Frame height, this is used a lot of times
00609     int fHeight = height() - t.height();
00610 
00611     // distance from the bottom border - it is different if window is resizable
00612     int bb = mustDrawHandle() ? 4 : 0;
00613     int bDepth = thickness + bb;
00614 
00615     QColorGroup fillColor = options()->colorGroup(frameColorGroup, isActive());
00616     QBrush fillBrush(options()->color(frameColorGroup, isActive()));
00617 
00618     // outer frame rect
00619     p.drawRect(0, t.bottom() - thickness + 1, 
00620         width(), fHeight - bb + thickness);
00621 
00622     if (thickness >= 2) {
00623     // inner window rect
00624     p.drawRect(thickness - 1, t.bottom(), 
00625         width() - 2 * (thickness - 1), fHeight - bDepth + 2);
00626 
00627     if (thickness >= 3) {
00628         // frame shade panel
00629         qDrawShadePanel(&p, 1, t.bottom() - thickness + 2,
00630             width() - 2, fHeight - 2 - bb + thickness, fillColor, false);
00631         if (thickness == 4) {
00632         p.setPen(fillColor.background());
00633         p.drawRect(thickness - 2, t.bottom() - 1,
00634             width() - 2 * (thickness - 2), fHeight + 4 - bDepth);
00635         } else if (thickness > 4) {
00636         qDrawShadePanel(&p, thickness - 2,
00637                 t.bottom() - 1, width() - 2 * (thickness - 2),
00638                 fHeight + 4 - bDepth, fillColor, true);
00639         if (thickness >= 5) {
00640             // draw frame interior
00641             p.fillRect(2, t.bottom() - thickness + 3,
00642                 width() - 4, thickness - 4, fillBrush);
00643             p.fillRect(2, height() - bDepth + 2,
00644                 width() - 4, thickness - 4, fillBrush);
00645             p.fillRect(2, t.bottom() - 1,
00646                 thickness - 4, fHeight - bDepth + 4, fillBrush);
00647             p.fillRect(width() - thickness + 2, t.bottom() - 1,
00648                 thickness - 4, fHeight - bDepth + 4, fillBrush);
00649         }
00650         }
00651     }
00652     }
00653 
00654     // bottom handle rect
00655     if (mustDrawHandle()) {
00656         p.setPen(Qt::black);
00657     int hx = width() - 40;
00658     int hw = 40;
00659 
00660     p.drawLine(width() - 1, height() - thickness - 4,
00661         width() - 1, height() - 1);
00662     p.drawLine(hx, height() - 1, width() - 1, height() - 1);
00663     p.drawLine(hx, height() - 4, hx, height() - 1);
00664 
00665     p.fillRect(hx + 1, height() - thickness - 3,
00666         hw - 2, thickness + 2, fillBrush);
00667 
00668     p.setPen(fillColor.dark());
00669     p.drawLine(width() - 2, height() - thickness - 4,
00670         width() - 2, height() - 2);
00671     p.drawLine(hx + 1, height() - 2, width() - 2, height() - 2);
00672 
00673     p.setPen(fillColor.light());
00674     p.drawLine(hx + 1, height() - thickness - 2,
00675         hx + 1, height() - 3);
00676     p.drawLine(hx + 1, height() - thickness - 3,
00677         width() - 3, height() - thickness - 3);
00678     }
00679 
00680     /* OK, we got a paint event, which means parts of us are now visible
00681        which were not before. We try the titlebar if it is currently fully
00682        obscured, and if yes, try to unobscure it, in the hope that some
00683        of the parts which we just painted were in the titlebar area.
00684        It can happen, that the titlebar, as it got the FullyObscured event
00685        had no chance of becoming partly visible. The problem is, that
00686        we now might have the space available, but the titlebar gets no
00687        visibilitinotify events until its state changes, so we just try
00688      */
00689     if (titlebar->isFullyObscured()) {
00690         /* We first see, if our repaint contained the titlebar area */
00691     QRegion reg(QRect(0, 0, width(), buttonSize + 4));
00692     reg = reg.intersect(e->region());
00693     if (!reg.isEmpty())
00694         unobscureTitlebar();
00695     }
00696 }
00697 
00698 void B2Client::doShape()
00699 {
00700     QRect t = titlebar->geometry();
00701     QRegion mask(widget()->rect());
00702     // top to the tilebar right
00703     if (bar_x_ofs) {
00704     mask -= QRect(0, 0, bar_x_ofs, t.height() - thickness); //left from bar
00705     mask -= QRect(0, t.height() - thickness, 1, 1); //top left point
00706     }
00707     if (t.right() < width() - 1) {
00708     mask -= QRect(width() - 1,
00709         t.height() - thickness, 1, 1); //top right point
00710     mask -= QRect(t.right() + 1, 0,
00711         width() - t.right() - 1, t.height() - thickness);
00712     }
00713     mask -= QRect(width() - 1, height() - 1, 1, 1); //bottom right point
00714     if (mustDrawHandle()) {
00715     mask -= QRect(0, height() - 5, 1, 1); //bottom left point
00716     mask -= QRect(width() - 40, height() - 1, 1, 1); //handle left point
00717     mask -= QRect(0, height() - 4, width() - 40, 4); //bottom left
00718     } else {
00719     mask -= QRect(0, height() - 1, 1, 1); // bottom left point
00720     }
00721 
00722     setMask(mask);
00723 }
00724 
00725 void B2Client::showEvent(QShowEvent *)
00726 {
00727     calcHiddenButtons();
00728     positionButtons();
00729     doShape();
00730 }
00731 
00732 KDecoration::Position B2Client::mousePosition(const QPoint& p) const
00733 {
00734     const int range = 16;
00735     QRect t = titlebar->geometry();
00736     t.setHeight(buttonSize + 4 - thickness);
00737     int ly = t.bottom();
00738     int lx = t.right();
00739     int bb = mustDrawHandle() ? 0 : 5;
00740 
00741     if (p.x() > t.right()) {
00742         if (p.y() <= ly + range && p.x() >= width() - range)
00743             return PositionTopRight;
00744         else if (p.y() <= ly + thickness)
00745             return PositionTop;
00746     } else if (p.x() < bar_x_ofs) {
00747         if (p.y() <= ly + range && p.x() <= range)
00748             return PositionTopLeft;
00749         else if (p.y() <= ly + thickness)
00750             return PositionTop;
00751     } else if (p.y() < ly) {
00752         if (p.x() > bar_x_ofs + thickness &&
00753         p.x() < lx - thickness && p.y() > thickness)
00754             return KDecoration::mousePosition(p);
00755         if (p.x() > bar_x_ofs + range && p.x() < lx - range)
00756             return PositionTop;
00757         if (p.y() <= range) {
00758             if (p.x() <= bar_x_ofs + range)
00759                 return PositionTopLeft;
00760             else return PositionTopRight;
00761         } else {
00762             if (p.x() <= bar_x_ofs + range)
00763                 return PositionLeft;
00764             else return PositionRight;
00765         }
00766     }
00767 
00768     if (p.y() >= height() - 8 + bb) {
00769         /* the normal Client:: only wants border of 4 pixels */
00770     if (p.x() <= range) return PositionBottomLeft;
00771     if (p.x() >= width() - range) return PositionBottomRight;
00772     return PositionBottom;
00773     }
00774 
00775     return KDecoration::mousePosition(p);
00776 }
00777 
00778 void B2Client::titleMoveAbs(int new_ofs)
00779 {
00780     if (new_ofs < 0) new_ofs = 0;
00781     if (new_ofs + titlebar->width() > width()) {
00782         new_ofs = width() - titlebar->width();
00783     }
00784     if (bar_x_ofs != new_ofs) {
00785         bar_x_ofs = new_ofs;
00786     positionButtons();
00787     doShape();
00788     widget()->repaint(0, 0, width(), buttonSize + 4, false);
00789     titlebar->repaint(false);
00790     }
00791 }
00792 
00793 void B2Client::titleMoveRel(int xdiff)
00794 {
00795     titleMoveAbs(bar_x_ofs + xdiff);
00796 }
00797 
00798 void B2Client::desktopChange()
00799 {
00800     bool on = isOnAllDesktops();
00801     if (B2Button *b = button[BtnSticky]) {
00802         b->setDown(on);
00803     QToolTip::remove(b);
00804     QToolTip::add(b, 
00805         on ? i18n("Not on all desktops") : i18n("On all desktops"));
00806     }
00807 }
00808 
00809 void B2Client::maximizeChange()
00810 {
00811     bool m = maximizeMode() == MaximizeFull;
00812     if (button[BtnMax]) {
00813         button[BtnMax]->setPixmaps(m ? P_NORMALIZE : P_MAX);
00814         button[BtnMax]->repaint();
00815     QToolTip::remove(button[BtnMax]);
00816     QToolTip::add(button[BtnMax],
00817         m ? i18n("Restore") : i18n("Maximize"));
00818     }
00819     spacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
00820         QSizePolicy::Expanding, QSizePolicy::Minimum);
00821 
00822     g->activate();
00823     doShape();
00824     widget()->repaint(false);
00825 }
00826 
00827 void B2Client::activeChange()
00828 {
00829     widget()->repaint(false);
00830     titlebar->repaint(false);
00831 
00832     QColor c = options()->colorGroup(
00833         KDecoration::ColorTitleBar, isActive()).color(QColorGroup::Button);
00834 
00835     for (int i = 0; i < BtnCount; i++)
00836         if (button[i]) {
00837            button[i]->setBg(c);
00838            button[i]->repaint(false);
00839         }
00840 }
00841 
00842 void B2Client::shadeChange()
00843 {
00844     spacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
00845         QSizePolicy::Expanding, QSizePolicy::Minimum);
00846     g->activate();
00847     doShape();
00848     if (B2Button *b = button[BtnShade]) {
00849     QToolTip::remove(b);
00850     QToolTip::add(b, isSetShade() ? i18n("Unshade") : i18n("Shade"));
00851     }
00852 }
00853 
00854 QSize B2Client::minimumSize() const
00855 {
00856     int left, right, top, bottom;
00857     borders(left, right, top, bottom);
00858     return QSize(left + right + 2 * buttonSize, top + bottom);
00859 }
00860 
00861 void B2Client::resize(const QSize& s)
00862 {
00863     widget()->resize(s);
00864 }
00865 
00866 void B2Client::borders(int &left, int &right, int &top, int &bottom) const
00867 {
00868     left = right = thickness;
00869     top = buttonSize + 4;
00870     bottom = thickness + (mustDrawHandle() ? 4 : 0);
00871 }
00872 
00873 void B2Client::menuButtonPressed()
00874 {
00875     static B2Client *lastClient = NULL;
00876     
00877     bool dbl = (lastClient == this && 
00878             time.elapsed() <= QApplication::doubleClickInterval());
00879     lastClient = this;
00880     time.start();
00881     if (!dbl) {
00882     KDecorationFactory* f = factory();
00883     QRect menuRect = button[BtnMenu]->rect();
00884     QPoint menuTop = button[BtnMenu]->mapToGlobal(menuRect.topLeft());
00885     QPoint menuBottom = button[BtnMenu]->mapToGlobal(menuRect.bottomRight());
00886     showWindowMenu(QRect(menuTop, menuBottom));
00887     if (!f->exists(this)) // 'this' was destroyed
00888         return;
00889     button[BtnMenu]->setDown(false);
00890     } else {
00891     switch (menu_dbl_click_op) {
00892     case B2::MinimizeOp:
00893         minimize();
00894         break;
00895     case B2::ShadeOp:
00896         setShade(!isSetShade());
00897         break;
00898     case B2::CloseOp:
00899         closeWindow();
00900         break;
00901     case B2::NoOp:
00902     default:
00903         break;
00904     }
00905     }
00906 }
00907 
00908 void B2Client::unobscureTitlebar()
00909 {
00910     /* we just noticed, that we got obscured by other windows
00911        so we look at all windows above us (stacking_order) merging their
00912        masks, intersecting it with our titlebar area, and see if we can
00913        find a place not covered by any window */
00914     if (in_unobs) {
00915     return;
00916     }
00917     in_unobs = 1;
00918     QRegion reg(QRect(0,0,width(), buttonSize + 4));
00919     reg = unobscuredRegion(reg);
00920     if (!reg.isEmpty()) {
00921         // there is at least _one_ pixel from our title area, which is not
00922     // obscured, we use the first rect we find
00923     // for a first test, we use boundingRect(), later we may refine
00924     // to rect(), and search for the nearest, or biggest, or smthg.
00925     titleMoveAbs(reg.boundingRect().x());
00926     }
00927     in_unobs = 0;
00928 }
00929 
00930 static void redraw_pixmaps()
00931 {
00932     int i;
00933     QColorGroup aGrp = options()->colorGroup(KDecoration::ColorButtonBg, true);
00934     QColorGroup iGrp = options()->colorGroup(KDecoration::ColorButtonBg, false);
00935 
00936     // close
00937     drawB2Rect(PIXMAP_A(P_CLOSE), aGrp.button(), false);
00938     drawB2Rect(PIXMAP_AH(P_CLOSE), aGrp.button(), true);
00939     drawB2Rect(PIXMAP_AD(P_CLOSE), aGrp.button(), true);
00940 
00941     drawB2Rect(PIXMAP_I(P_CLOSE), iGrp.button(), false);
00942     drawB2Rect(PIXMAP_IH(P_CLOSE), iGrp.button(), true);
00943     drawB2Rect(PIXMAP_ID(P_CLOSE), iGrp.button(), true);
00944 
00945     // shade
00946     KPixmap thinBox;
00947     thinBox.resize(buttonSize - 2, 6);
00948     for (i = 0; i < NumStates; i++) {
00949     bool is_act = (i < 2);
00950     bool is_down = ((i & 1) == 1);
00951     KPixmap *pix = pixmap[P_SHADE * NumStates + i];
00952     QColor color = is_act ? aGrp.button() : iGrp.button();
00953     drawB2Rect(&thinBox, color, is_down);
00954     pix->fill(Qt::black);
00955     bitBlt(pix, 0, 0, &thinBox, 
00956         0, 0, thinBox.width(), thinBox.height(), Qt::CopyROP, true);
00957     }
00958 
00959     // maximize
00960     for (i = 0; i < NumStates; i++) {
00961     *pixmap[P_MAX * NumStates + i] = *pixmap[P_CLOSE * NumStates + i];
00962     pixmap[P_MAX * NumStates + i]->detach();
00963     }
00964     
00965     // normalize + iconify
00966     KPixmap smallBox;
00967     smallBox.resize(10, 10);
00968     KPixmap largeBox;
00969     largeBox.resize(12, 12);
00970 
00971     for (i = 0; i < NumStates; i++) {
00972     bool is_act = (i < 3);
00973     bool is_down = (i == Down || i == IDown);
00974     KPixmap *pix = pixmap[P_NORMALIZE * NumStates + i];
00975     drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00976     drawB2Rect(&largeBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00977     pix->fill(options()->color(KDecoration::ColorTitleBar, is_act));
00978     bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox,
00979            0, 0, 12, 12, Qt::CopyROP, true);
00980     bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00981 
00982     bitBlt(pixmap[P_ICONIFY * NumStates + i], 0, 0,
00983            &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00984     }
00985     
00986     // resize
00987     for (i = 0; i < NumStates; i++) {
00988     bool is_act = (i < 3);
00989     bool is_down = (i == Down || i == IDown);
00990     *pixmap[P_RESIZE * NumStates + i] = *pixmap[P_CLOSE * NumStates + i];
00991     pixmap[P_RESIZE * NumStates + i]->detach();
00992     drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00993     bitBlt(pixmap[P_RESIZE * NumStates + i], 
00994         0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00995     }
00996 
00997 
00998     QPainter p;
00999     // x for close + menu + help
01000     for (int j = 0; j < 3; j++) {
01001         int pix;
01002         unsigned const char *light, *dark;
01003         switch (j) {
01004         case 0:
01005             pix = P_CLOSE; light = close_white_bits; dark = close_dgray_bits;
01006             break;
01007         case 1:
01008             pix = P_MENU; light = menu_white_bits; dark = menu_dgray_bits;
01009             break;
01010         default:
01011             pix = P_HELP; light = help_light_bits; dark = help_dark_bits;
01012             break;
01013         }
01014     int off = (pixmap[pix * NumStates]->width() - 16) / 2;
01015         for (i = 0; i < NumStates; i++) {
01016             p.begin(pixmap[pix * NumStates + i]);
01017             kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, off, off, 16, 16, true,
01018                           light, NULL, NULL, dark, NULL, NULL);
01019             p.end();
01020         }
01021     }
01022 
01023     // pin
01024     for (i = 0; i < NumStates; i++) {
01025     bool isDown = (i == Down || i == IDown);
01026         unsigned const char *white = isDown ? pindown_white_bits : pinup_white_bits;
01027         unsigned const char *gray = isDown ? pindown_gray_bits : pinup_gray_bits;
01028         unsigned const char *dgray =isDown ? pindown_dgray_bits : pinup_dgray_bits;
01029         p.begin(pixmap[P_PINUP * NumStates + i]);
01030         kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, 0, 0, 16, 16, true, white,
01031                       gray, NULL, dgray, NULL, NULL);
01032         p.end();
01033     }
01034 
01035     // Apply the hilight effect to the 'Hover' icons
01036     KIconEffect ie;
01037     QPixmap hilighted;
01038     for (i = 0; i < P_NUM_BUTTON_TYPES; i++) {
01039     int offset = i * NumStates;
01040     hilighted = ie.apply(*pixmap[offset + Norm], 
01041         KIcon::Small, KIcon::ActiveState);
01042     *pixmap[offset + Hover] = hilighted;    
01043 
01044     hilighted = ie.apply(*pixmap[offset + INorm], 
01045         KIcon::Small, KIcon::ActiveState);
01046     *pixmap[offset + IHover] = hilighted;    
01047     }
01048 
01049     
01050     // Create the titlebar gradients
01051     if (QPixmap::defaultDepth() > 8) {
01052     QColor titleColor[4] = {
01053         options()->color(KDecoration::ColorTitleBar, true),
01054             options()->color(KDecoration::ColorFrame, true),
01055 
01056         options()->color(KDecoration::ColorTitleBlend, false),
01057         options()->color(KDecoration::ColorTitleBar, false)
01058     };
01059 
01060     if (colored_frame) {
01061         titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true);
01062         titleColor[1] = options()->color(KDecoration::ColorTitleBar, true);
01063     }
01064 
01065     for (i = 0; i < 2; i++) {
01066         if (titleColor[2 * i] != titleColor[2 * i + 1]) {
01067         if (!titleGradient[i]) {
01068             titleGradient[i] = new KPixmap;
01069         }
01070         titleGradient[i]->resize(64, buttonSize + 3);
01071         KPixmapEffect::gradient(*titleGradient[i],
01072             titleColor[2 * i], titleColor[2 * i + 1],
01073             KPixmapEffect::VerticalGradient);
01074         } else {
01075            delete titleGradient[i];
01076            titleGradient[i] = 0;
01077         }
01078     }
01079     }
01080 }
01081 
01082 void B2Client::positionButtons()
01083 {
01084     QFontMetrics fm(options()->font(isActive()));
01085     QString cap = caption();
01086     if (cap.length() < 5) // make sure the titlebar has sufficiently wide
01087         cap = "XXXXX";    // area for dragging the window
01088     int textLen = fm.width(cap);
01089 
01090     QRect t = titlebar->captionSpacer->geometry();
01091     int titleWidth = titlebar->width() - t.width() + textLen + 2;
01092     if (titleWidth > width()) titleWidth = width();
01093 
01094     titlebar->resize(titleWidth, buttonSize + 4);
01095     titlebar->move(bar_x_ofs, 0);
01096 }
01097 
01098 // Transparent bound stuff.
01099 
01100 static QRect *visible_bound;
01101 static QPointArray bound_shape;
01102 
01103 bool B2Client::drawbound(const QRect& geom, bool clear)
01104 {
01105     if (clear) {
01106     if (!visible_bound) return true;
01107     }
01108 
01109     if (!visible_bound) {
01110     visible_bound = new QRect(geom);
01111     QRect t = titlebar->geometry();
01112     int frameTop = geom.top() + t.bottom();
01113     int barLeft = geom.left() + bar_x_ofs;
01114     int barRight = barLeft + t.width() - 1;
01115     if (barRight > geom.right()) barRight = geom.right();
01116         // line width is 5 pixels, so compensate for the 2 outer pixels (#88657)
01117         QRect g = geom;
01118         g.setLeft( g.left() + 2 );
01119         g.setTop( g.top() + 2 );
01120         g.setRight( g.right() - 2 );
01121         g.setBottom( g.bottom() - 2 );
01122         frameTop += 2;
01123         barLeft += 2;
01124         barRight -= 2;
01125 
01126     bound_shape.putPoints(0, 8,
01127         g.left(), frameTop,
01128         barLeft, frameTop,
01129         barLeft, g.top(),
01130         barRight, g.top(),
01131         barRight, frameTop,
01132         g.right(), frameTop,
01133         g.right(), g.bottom(),
01134         g.left(), g.bottom());
01135     } else {
01136     *visible_bound = geom;
01137     }
01138     QPainter p(workspaceWidget());
01139     p.setPen(QPen(Qt::white, 5));
01140     p.setRasterOp(Qt::XorROP);
01141     p.drawPolygon(bound_shape);
01142 
01143     if (clear) {
01144     delete visible_bound;
01145     visible_bound = 0;
01146     }
01147     return true;
01148 }
01149 
01150 bool B2Client::eventFilter(QObject *o, QEvent *e)
01151 {
01152     if (o != widget())
01153     return false;
01154     switch (e->type()) {
01155     case QEvent::Resize:
01156     resizeEvent(static_cast< QResizeEvent* >(e));
01157     return true;
01158     case QEvent::Paint:
01159     paintEvent(static_cast< QPaintEvent* >(e));
01160     return true;
01161     case QEvent::MouseButtonDblClick:
01162     titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e));
01163     return true;
01164     case QEvent::MouseButtonPress:
01165     processMousePressEvent(static_cast< QMouseEvent* >(e));
01166     return true;
01167     case QEvent::Show:
01168     showEvent(static_cast< QShowEvent* >(e));
01169     return true;
01170     default:
01171     break;
01172     }
01173     return false;
01174 }
01175 
01176 // =====================================
01177 
01178 B2Button::B2Button(B2Client *_client, QWidget *parent, 
01179     const QString& tip, const int realizeBtns)
01180    : QButton(parent, 0), hover(false)
01181 {
01182     setBackgroundMode(NoBackground);
01183     setCursor(arrowCursor);
01184     realizeButtons = realizeBtns;
01185     client = _client;
01186     useMiniIcon = false;
01187     setFixedSize(buttonSize, buttonSize);
01188     QToolTip::add(this, tip);
01189 }
01190 
01191 
01192 QSize B2Button::sizeHint() const
01193 {
01194     return QSize(buttonSize, buttonSize);
01195 }
01196 
01197 QSizePolicy B2Button::sizePolicy() const
01198 {
01199     return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
01200 }
01201 
01202 void B2Button::drawButton(QPainter *p)
01203 {
01204     KPixmap* gradient = titleGradient[client->isActive() ? 0 : 1];
01205     if (gradient) {
01206     p->drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2);
01207     } else {
01208     p->fillRect(rect(), bg);
01209     }
01210     if (useMiniIcon) {
01211         QPixmap miniIcon = client->icon().pixmap(QIconSet::Small,
01212         client->isActive() ? QIconSet::Normal : QIconSet::Disabled);
01213         p->drawPixmap((width() - miniIcon.width()) / 2,
01214                       (height() - miniIcon.height()) / 2, miniIcon);
01215     } else {
01216     int type;
01217         if (client->isActive()) {
01218             if (isOn() || isDown())
01219         type = Down;
01220             else if (hover)
01221         type = Hover;
01222         else
01223         type = Norm;
01224         } else {
01225             if (isOn() || isDown())
01226         type = IDown;
01227         else if (hover)
01228         type = IHover;
01229             else
01230         type = INorm;
01231         }
01232     p->drawPixmap((width() - icon[type]->width()) / 2, 
01233         (height() - icon[type]->height()) / 2, *icon[type]);
01234     }
01235 }
01236 
01237 void B2Button::setPixmaps(int button_id)
01238 {
01239     button_id *= NumStates;
01240     for (int i = 0; i < NumStates; i++) {
01241     icon[i] = B2::pixmap[button_id + i];
01242     }
01243     repaint(false);
01244 }
01245 
01246 void B2Button::mousePressEvent(QMouseEvent * e)
01247 {
01248     last_button = e->button();
01249     QMouseEvent me(e->type(), e->pos(), e->globalPos(), 
01250         (e->button() & realizeButtons) ? LeftButton : NoButton, 
01251         e->state());
01252     QButton::mousePressEvent(&me);
01253 }
01254 
01255 void B2Button::mouseReleaseEvent(QMouseEvent * e)
01256 {
01257     last_button = e->button();
01258     QMouseEvent me(e->type(), e->pos(), e->globalPos(), 
01259         (e->button() & realizeButtons) ? LeftButton : NoButton, 
01260         e->state());
01261     QButton::mouseReleaseEvent(&me);
01262 }
01263 
01264 void B2Button::enterEvent(QEvent *e)
01265 {
01266     hover = true;
01267     repaint(false);
01268     QButton::enterEvent(e);
01269 }
01270 
01271 void B2Button::leaveEvent(QEvent *e)
01272 {
01273     hover = false;
01274     repaint(false);
01275     QButton::leaveEvent(e);
01276 }
01277 
01278 // =====================================
01279 
01280 B2Titlebar::B2Titlebar(B2Client *parent)
01281     : QWidget(parent->widget(), 0, WStyle_Customize | WRepaintNoErase),
01282       client(parent),
01283       set_x11mask(false), isfullyobscured(false), shift_move(false)
01284 {
01285     setBackgroundMode(NoBackground);
01286     captionSpacer = new QSpacerItem(buttonSize, buttonSize + 4,
01287         QSizePolicy::Expanding, QSizePolicy::Fixed);
01288 }
01289 
01290 bool B2Titlebar::x11Event(XEvent *e)
01291 {
01292     if (!set_x11mask) {
01293     set_x11mask = true;
01294     XSelectInput(qt_xdisplay(), winId(),
01295         KeyPressMask | KeyReleaseMask |
01296         ButtonPressMask | ButtonReleaseMask |
01297         KeymapStateMask |
01298         ButtonMotionMask |
01299         EnterWindowMask | LeaveWindowMask |
01300         FocusChangeMask |
01301         ExposureMask |
01302         PropertyChangeMask |
01303         StructureNotifyMask | SubstructureRedirectMask |
01304         VisibilityChangeMask);
01305     }
01306     switch (e->type) {
01307     case VisibilityNotify:
01308     isfullyobscured = false;
01309     if (e->xvisibility.state == VisibilityFullyObscured) {
01310         isfullyobscured = true;
01311         client->unobscureTitlebar();
01312     }
01313     break;
01314     default:
01315     break;
01316     }
01317     return QWidget::x11Event(e);
01318 }
01319 
01320 void B2Titlebar::drawTitlebar(QPainter &p, bool state)
01321 {
01322     KPixmap* gradient = titleGradient[state ? 0 : 1];
01323 
01324     QRect t = rect();
01325     // black titlebar frame
01326     p.setPen(Qt::black);
01327     p.drawLine(0, 0, 0, t.bottom());
01328     p.drawLine(0, 0, t.right(), 0);
01329     p.drawLine(t.right(), 0, t.right(), t.bottom());
01330 
01331     // titlebar fill
01332     const QColorGroup cg =
01333     options()->colorGroup(KDecoration::ColorTitleBar, state);
01334     QBrush brush(cg.background());
01335     if (gradient) brush.setPixmap(*gradient);
01336     qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1,
01337            cg, false, 1, 0, &brush);
01338 
01339     // and the caption
01340     p.setPen(options()->color(KDecoration::ColorFont, state));
01341     p.setFont(options()->font(state));
01342     t = captionSpacer->geometry();
01343     p.drawText(t, AlignLeft | AlignVCenter, client->caption());
01344 }
01345 
01346 void B2Titlebar::recalcBuffer()
01347 {
01348     titleBuffer.resize(width(), height());
01349 
01350     QPainter p(&titleBuffer);
01351     drawTitlebar(p, true);
01352     oldTitle = caption();
01353 }
01354 
01355 void B2Titlebar::resizeEvent(QResizeEvent *)
01356 {
01357     recalcBuffer();
01358     repaint(false);
01359 }
01360 
01361 
01362 void B2Titlebar::paintEvent(QPaintEvent *)
01363 {
01364     if(client->isActive())
01365         bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(),
01366                titleBuffer.height(), Qt::CopyROP, true);
01367     else {
01368         QPainter p(this);
01369     drawTitlebar(p, false);
01370     }
01371 }
01372 
01373 void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e)
01374 {
01375     if (e->button() == LeftButton && e->y() < height()) {
01376     client->titlebarDblClickOperation();
01377     }
01378 }
01379 
01380 void B2Titlebar::mousePressEvent(QMouseEvent * e)
01381 {
01382     shift_move = e->state() & ShiftButton;
01383     if (shift_move) {
01384         moveOffset = e->globalPos();
01385     } else {
01386     e->ignore();
01387     }
01388 }
01389 
01390 void B2Titlebar::mouseReleaseEvent(QMouseEvent * e)
01391 {
01392     if (shift_move) shift_move = false;
01393     else e->ignore();
01394 }
01395 
01396 void B2Titlebar::mouseMoveEvent(QMouseEvent * e)
01397 {
01398     if (shift_move) {
01399     int oldx = mapFromGlobal(moveOffset).x();
01400         int xdiff = e->globalPos().x() - moveOffset.x();
01401         moveOffset = e->globalPos();
01402     if (oldx >= 0 && oldx <= rect().right()) {
01403             client->titleMoveRel(xdiff);
01404     }
01405     } else {
01406         e->ignore();
01407     }
01408 }
01409 
01410 } // namespace B2
01411 
01412 #include "b2client.moc"
01413 
01414 // vim: sw=4
01415 
KDE Logo
This file is part of the documentation for kwin Library Version 3.4.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jun 14 01:54:12 2006 by doxygen 1.4.4 written by Dimitri van Heesch, © 1997-2003