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     createMainWidget(WResizeNoErase | WRepaintNoErase);
00358     widget()->installEventFilter(this);
00359 
00360     widget()->setBackgroundMode(NoBackground);
00361 
00362     // Set button pointers to NULL so we know what has been created
00363     for (int i = 0; i < BtnCount; i++)
00364         button[i] = NULL;
00365 
00366     g = new QGridLayout(widget(), 0, 0);
00367     if (isPreview()) {
00368         g->addMultiCellWidget(
00369         new QLabel(i18n("<b><center>B II preview</center></b>"),
00370             widget()),
00371         1, 1, 1, 2);
00372     } else {
00373     g->addMultiCell(new QSpacerItem(0, 0), 1, 1, 1, 2);
00374     }
00375 
00376     // Left and right border width
00377     leftSpacer = new QSpacerItem(thickness, 16,
00378         QSizePolicy::Fixed, QSizePolicy::Expanding);
00379     rightSpacer = new QSpacerItem(thickness, 16,
00380         QSizePolicy::Fixed, QSizePolicy::Expanding);
00381 
00382     g->addItem(leftSpacer, 1, 0);
00383     g->addColSpacing(1, 16);
00384     g->setColStretch(2, 1);
00385     g->setRowStretch(1, 1);
00386     g->addItem(rightSpacer, 1, 3);
00387 
00388     // Bottom border height
00389     spacer = new QSpacerItem(10, thickness + (mustDrawHandle() ? 4 : 0),
00390         QSizePolicy::Expanding, QSizePolicy::Fixed);
00391     g->addItem(spacer, 3, 1);
00392 
00393     // titlebar
00394     g->addRowSpacing(0, buttonSize + 4);
00395 
00396     titlebar = new B2Titlebar(this);
00397     titlebar->setMinimumWidth(buttonSize + 4);
00398     titlebar->setFixedHeight(buttonSize + 4);
00399 
00400     QBoxLayout *titleLayout = new QBoxLayout(titlebar, 
00401         QBoxLayout::LeftToRight, 0, 1, 0);
00402     titleLayout->addSpacing(3);
00403 
00404     if (options()->customButtonPositions()) {
00405         addButtons(options()->titleButtonsLeft(), tips, titlebar, titleLayout);
00406         titleLayout->addItem(titlebar->captionSpacer);
00407         addButtons(options()->titleButtonsRight(), tips, titlebar, titleLayout);
00408     } else {
00409         addButtons("MSH", tips, titlebar, titleLayout);
00410         titleLayout->addItem(titlebar->captionSpacer);
00411         addButtons("IAX", tips, titlebar, titleLayout);
00412     }
00413 
00414     titleLayout->addSpacing(3);
00415 
00416     QColor c = options()->colorGroup(KDecoration::ColorTitleBar, isActive()).
00417         color(QColorGroup::Button);
00418 
00419     for (int i = 0; i < BtnCount; i++) {
00420         if (button[i])
00421             button[i]->setBg(c);
00422     }
00423 
00424     titlebar->updateGeometry();
00425     positionButtons();
00426     titlebar->recalcBuffer();
00427     titlebar->installEventFilter(this);
00428     resizable = isResizable();
00429 }
00430 
00431 void B2Client::addButtons(const QString& s, const QString tips[],
00432                           B2Titlebar* tb, QBoxLayout* titleLayout)
00433 {
00434     if (s.length() <= 0)
00435     return;
00436 
00437     for (unsigned int i = 0; i < s.length(); i++) {
00438         switch (s[i].latin1()) {
00439     case 'M':  // Menu button
00440         if (!button[BtnMenu]) {
00441         button[BtnMenu] = new B2Button(this, tb, tips[BtnMenu], 
00442             LeftButton | RightButton);
00443         button[BtnMenu]->setPixmaps(P_MENU);
00444         button[BtnMenu]->setUseMiniIcon();
00445         connect(button[BtnMenu], SIGNAL(pressed()),
00446             this, SLOT(menuButtonPressed()));
00447         titleLayout->addWidget(button[BtnMenu]);
00448         }
00449         break;
00450     case 'S':  // Sticky button
00451         if (!button[BtnSticky]) {
00452         button[BtnSticky] = new B2Button(this, tb, tips[BtnSticky]);
00453         button[BtnSticky]->setPixmaps(P_PINUP);
00454         button[BtnSticky]->setToggle();
00455         button[BtnSticky]->setDown(isOnAllDesktops());
00456         connect(button[BtnSticky], SIGNAL(clicked()),
00457             this, SLOT(toggleOnAllDesktops()));
00458         titleLayout->addWidget(button[BtnSticky]);
00459         }
00460         break;
00461     case 'H':  // Help button
00462         if (providesContextHelp() && (!button[BtnHelp])) {
00463         button[BtnHelp] = new B2Button(this, tb, tips[BtnHelp]);
00464         button[BtnHelp]->setPixmaps(P_HELP);
00465         connect(button[BtnHelp], SIGNAL(clicked()),
00466             this, SLOT(showContextHelp()));
00467         titleLayout->addWidget(button[BtnHelp]);
00468         }
00469         break;
00470     case 'I':  // Minimize button
00471         if (isMinimizable() && (!button[BtnIconify])) {
00472         button[BtnIconify] = new B2Button(this, tb,tips[BtnIconify]);
00473         button[BtnIconify]->setPixmaps(P_ICONIFY);
00474         connect(button[BtnIconify], SIGNAL(clicked()),
00475             this, SLOT(minimize()));
00476         titleLayout->addWidget(button[BtnIconify]);
00477         }
00478         break;
00479     case 'A':  // Maximize button
00480         if (isMaximizable() && (!button[BtnMax])) {
00481         button[BtnMax] = new B2Button(this, tb, tips[BtnMax], 
00482             LeftButton | MidButton | RightButton);
00483         button[BtnMax]->setPixmaps(maximizeMode() == MaximizeFull ?
00484             P_NORMALIZE : P_MAX);
00485         connect(button[BtnMax], SIGNAL(clicked()),
00486             this, SLOT(maxButtonClicked()));
00487         titleLayout->addWidget(button[BtnMax]);
00488         }
00489         break;
00490     case 'X':  // Close button
00491         if (isCloseable() && !button[BtnClose]) {
00492         button[BtnClose] = new B2Button(this, tb, tips[BtnClose]);
00493         button[BtnClose]->setPixmaps(P_CLOSE);
00494         connect(button[BtnClose], SIGNAL(clicked()),
00495             this, SLOT(closeWindow()));
00496         titleLayout->addWidget(button[BtnClose]);
00497         }
00498         break;
00499     case 'L': // Shade button
00500         if (isShadeable() && !button[BtnShade]) {
00501         button[BtnShade] = new B2Button(this, tb, tips[BtnShade]);
00502         button[BtnShade]->setPixmaps(P_SHADE);
00503         connect(button[BtnShade], SIGNAL(clicked()),
00504             this, SLOT(shadeButtonClicked()));
00505         titleLayout->addWidget(button[BtnShade]);
00506         }
00507         break;
00508     case 'R': // Resize button
00509         if (resizable && !button[BtnResize]) {
00510         button[BtnResize] = new B2Button(this, tb, tips[BtnResize]);
00511         button[BtnResize]->setPixmaps(P_RESIZE);
00512         connect(button[BtnResize], SIGNAL(pressed()),
00513             this, SLOT(resizeButtonPressed()));
00514         titleLayout->addWidget(button[BtnResize]);
00515         }
00516         break;
00517     case '_': // Additional spacing
00518         titleLayout->addSpacing(4);
00519         break;
00520     }
00521     }
00522 }
00523 
00524 bool B2Client::mustDrawHandle() const 
00525 { 
00526     if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) {
00527     return false;
00528     } else {
00529     return do_draw_handle & resizable;
00530     }
00531 }
00532 
00533 void B2Client::iconChange()
00534 {
00535     if (button[BtnMenu])
00536         button[BtnMenu]->repaint(false);
00537 }
00538 
00539 // Gallium: New button show/hide magic for customizable
00540 //          button positions.
00541 void B2Client::calcHiddenButtons()
00542 {
00543     // Hide buttons in this order:
00544     // Shade, Sticky, Help, Resize, Maximize, Minimize, Close, Menu
00545     B2Button* btnArray[] = { 
00546     button[BtnShade], button[BtnSticky], button[BtnHelp], button[BtnResize],
00547     button[BtnMax], button[BtnIconify], button[BtnClose], button[BtnMenu] 
00548     };
00549     int minWidth = 120;
00550     int currentWidth = width();
00551     int count = 0;
00552     int i;
00553 
00554     // Determine how many buttons we need to hide
00555     while (currentWidth < minWidth) {
00556         currentWidth += buttonSize + 1; // Allow for spacer (extra 1pix)
00557         count++;
00558     }
00559     // Bound the number of buttons to hide
00560     if (count > BtnCount) count = BtnCount;
00561 
00562     // Hide the required buttons
00563     for (i = 0; i < count; i++) {
00564         if (btnArray[i] && btnArray[i]->isVisible())
00565             btnArray[i]->hide();
00566     }
00567     // Show the rest of the buttons
00568     for (i = count; i < BtnCount; i++) {
00569         if (btnArray[i] && (!btnArray[i]->isVisible()))
00570             btnArray[i]->show();
00571     }
00572 }
00573 
00574 void B2Client::resizeEvent(QResizeEvent * /*e*/)
00575 {
00576     calcHiddenButtons();
00577     titlebar->layout()->activate();
00578     positionButtons();
00579 
00580     /* may be the resize cut off some space occupied by titlebar, which
00581        was moved, so instead of reducing it, we first try to move it */
00582     titleMoveAbs(bar_x_ofs);
00583 
00584     doShape();
00585     widget()->repaint(); // the frame is misrendered without this
00586 }
00587 
00588 void B2Client::captionChange()
00589 {
00590     positionButtons();
00591     titleMoveAbs(bar_x_ofs);
00592     doShape();
00593     titlebar->recalcBuffer();
00594     titlebar->repaint(false);
00595 }
00596 
00597 void B2Client::paintEvent(QPaintEvent* e)
00598 {
00599     QPainter p(widget());
00600 
00601     KDecoration::ColorType frameColorGroup = colored_frame ?
00602     KDecoration::ColorTitleBar : KDecoration::ColorFrame;
00603 
00604     QRect t = titlebar->geometry();
00605 
00606     // Frame height, this is used a lot of times
00607     int fHeight = height() - t.height();
00608 
00609     // distance from the bottom border - it is different if window is resizable
00610     int bb = mustDrawHandle() ? 4 : 0;
00611     int bDepth = thickness + bb;
00612 
00613     QColorGroup fillColor = options()->colorGroup(frameColorGroup, isActive());
00614     QBrush fillBrush(options()->color(frameColorGroup, isActive()));
00615 
00616     // outer frame rect
00617     p.drawRect(0, t.bottom() - thickness + 1, 
00618         width(), fHeight - bb + thickness);
00619 
00620     if (thickness >= 2) {
00621     // inner window rect
00622     p.drawRect(thickness - 1, t.bottom(), 
00623         width() - 2 * (thickness - 1), fHeight - bDepth + 2);
00624 
00625     if (thickness >= 3) {
00626         // frame shade panel
00627         qDrawShadePanel(&p, 1, t.bottom() - thickness + 2,
00628             width() - 2, fHeight - 2 - bb + thickness, fillColor, false);
00629         if (thickness == 4) {
00630         p.setPen(fillColor.background());
00631         p.drawRect(thickness - 2, t.bottom() - 1,
00632             width() - 2 * (thickness - 2), fHeight + 4 - bDepth);
00633         } else if (thickness > 4) {
00634         qDrawShadePanel(&p, thickness - 2,
00635                 t.bottom() - 1, width() - 2 * (thickness - 2),
00636                 fHeight + 4 - bDepth, fillColor, true);
00637         if (thickness >= 5) {
00638             // draw frame interior
00639             p.fillRect(2, t.bottom() - thickness + 3,
00640                 width() - 4, thickness - 4, fillBrush);
00641             p.fillRect(2, height() - bDepth + 2,
00642                 width() - 4, thickness - 4, fillBrush);
00643             p.fillRect(2, t.bottom() - 1,
00644                 thickness - 4, fHeight - bDepth + 4, fillBrush);
00645             p.fillRect(width() - thickness + 2, t.bottom() - 1,
00646                 thickness - 4, fHeight - bDepth + 4, fillBrush);
00647         }
00648         }
00649     }
00650     }
00651 
00652     // bottom handle rect
00653     if (mustDrawHandle()) {
00654         p.setPen(Qt::black);
00655     int hx = width() - 40;
00656     int hw = 40;
00657 
00658     p.drawLine(width() - 1, height() - thickness - 4,
00659         width() - 1, height() - 1);
00660     p.drawLine(hx, height() - 1, width() - 1, height() - 1);
00661     p.drawLine(hx, height() - 4, hx, height() - 1);
00662 
00663     p.fillRect(hx + 1, height() - thickness - 3,
00664         hw - 2, thickness + 2, fillBrush);
00665 
00666     p.setPen(fillColor.dark());
00667     p.drawLine(width() - 2, height() - thickness - 4,
00668         width() - 2, height() - 2);
00669     p.drawLine(hx + 1, height() - 2, width() - 2, height() - 2);
00670 
00671     p.setPen(fillColor.light());
00672     p.drawLine(hx + 1, height() - thickness - 2,
00673         hx + 1, height() - 3);
00674     p.drawLine(hx + 1, height() - thickness - 3,
00675         width() - 3, height() - thickness - 3);
00676     }
00677 
00678     /* OK, we got a paint event, which means parts of us are now visible
00679        which were not before. We try the titlebar if it is currently fully
00680        obscured, and if yes, try to unobscure it, in the hope that some
00681        of the parts which we just painted were in the titlebar area.
00682        It can happen, that the titlebar, as it got the FullyObscured event
00683        had no chance of becoming partly visible. The problem is, that
00684        we now might have the space available, but the titlebar gets no
00685        visibilitinotify events until its state changes, so we just try
00686      */
00687     if (titlebar->isFullyObscured()) {
00688         /* We first see, if our repaint contained the titlebar area */
00689     QRegion reg(QRect(0, 0, width(), buttonSize + 4));
00690     reg = reg.intersect(e->region());
00691     if (!reg.isEmpty())
00692         unobscureTitlebar();
00693     }
00694 }
00695 
00696 void B2Client::doShape()
00697 {
00698     QRect t = titlebar->geometry();
00699     QRegion mask(widget()->rect());
00700     // top to the tilebar right
00701     if (bar_x_ofs) {
00702     mask -= QRect(0, 0, bar_x_ofs, t.height() - thickness); //left from bar
00703     mask -= QRect(0, t.height() - thickness, 1, 1); //top left point
00704     }
00705     if (t.right() < width() - 1) {
00706     mask -= QRect(width() - 1,
00707         t.height() - thickness, 1, 1); //top right point
00708     mask -= QRect(t.right() + 1, 0,
00709         width() - t.right() - 1, t.height() - thickness);
00710     }
00711     mask -= QRect(width() - 1, height() - 1, 1, 1); //bottom right point
00712     if (mustDrawHandle()) {
00713     mask -= QRect(0, height() - 5, 1, 1); //bottom left point
00714     mask -= QRect(width() - 40, height() - 1, 1, 1); //handle left point
00715     mask -= QRect(0, height() - 4, width() - 40, 4); //bottom left
00716     } else {
00717     mask -= QRect(0, height() - 1, 1, 1); // bottom left point
00718     }
00719 
00720     setMask(mask);
00721 }
00722 
00723 void B2Client::showEvent(QShowEvent *)
00724 {
00725     calcHiddenButtons();
00726     positionButtons();
00727     doShape();
00728 }
00729 
00730 KDecoration::Position B2Client::mousePosition(const QPoint& p) const
00731 {
00732     const int range = 16;
00733     QRect t = titlebar->geometry();
00734     t.setHeight(buttonSize + 4 - thickness);
00735     int ly = t.bottom();
00736     int lx = t.right();
00737     int bb = mustDrawHandle() ? 0 : 5;
00738 
00739     if (p.x() > t.right()) {
00740         if (p.y() <= ly + range && p.x() >= width() - range)
00741             return PositionTopRight;
00742         else if (p.y() <= ly + thickness)
00743             return PositionTop;
00744     } else if (p.x() < bar_x_ofs) {
00745         if (p.y() <= ly + range && p.x() <= range)
00746             return PositionTopLeft;
00747         else if (p.y() <= ly + thickness)
00748             return PositionTop;
00749     } else if (p.y() < ly) {
00750         if (p.x() > bar_x_ofs + thickness &&
00751         p.x() < lx - thickness && p.y() > thickness)
00752             return KDecoration::mousePosition(p);
00753         if (p.x() > bar_x_ofs + range && p.x() < lx - range)
00754             return PositionTop;
00755         if (p.y() <= range) {
00756             if (p.x() <= bar_x_ofs + range)
00757                 return PositionTopLeft;
00758             else return PositionTopRight;
00759         } else {
00760             if (p.x() <= bar_x_ofs + range)
00761                 return PositionLeft;
00762             else return PositionRight;
00763         }
00764     }
00765 
00766     if (p.y() >= height() - 8 + bb) {
00767         /* the normal Client:: only wants border of 4 pixels */
00768     if (p.x() <= range) return PositionBottomLeft;
00769     if (p.x() >= width() - range) return PositionBottomRight;
00770     return PositionBottom;
00771     }
00772 
00773     return KDecoration::mousePosition(p);
00774 }
00775 
00776 void B2Client::titleMoveAbs(int new_ofs)
00777 {
00778     if (new_ofs < 0) new_ofs = 0;
00779     if (new_ofs + titlebar->width() > width()) {
00780         new_ofs = width() - titlebar->width();
00781     }
00782     if (bar_x_ofs != new_ofs) {
00783         bar_x_ofs = new_ofs;
00784     positionButtons();
00785     doShape();
00786     widget()->repaint(0, 0, width(), buttonSize + 4, false);
00787     titlebar->repaint(false);
00788     }
00789 }
00790 
00791 void B2Client::titleMoveRel(int xdiff)
00792 {
00793     titleMoveAbs(bar_x_ofs + xdiff);
00794 }
00795 
00796 void B2Client::desktopChange()
00797 {
00798     bool on = isOnAllDesktops();
00799     if (B2Button *b = button[BtnSticky]) {
00800         b->setDown(on);
00801     QToolTip::remove(b);
00802     QToolTip::add(b, 
00803         on ? i18n("Not on all desktops") : i18n("On all desktops"));
00804     }
00805 }
00806 
00807 void B2Client::maximizeChange()
00808 {
00809     bool m = maximizeMode() == MaximizeFull;
00810     if (button[BtnMax]) {
00811         button[BtnMax]->setPixmaps(m ? P_NORMALIZE : P_MAX);
00812         button[BtnMax]->repaint();
00813     QToolTip::remove(button[BtnMax]);
00814     QToolTip::add(button[BtnMax],
00815         m ? i18n("Restore") : i18n("Maximize"));
00816     }
00817     spacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
00818         QSizePolicy::Expanding, QSizePolicy::Minimum);
00819 
00820     g->activate();
00821     doShape();
00822     widget()->repaint(false);
00823 }
00824 
00825 void B2Client::activeChange()
00826 {
00827     widget()->repaint(false);
00828     titlebar->repaint(false);
00829 
00830     QColor c = options()->colorGroup(
00831         KDecoration::ColorTitleBar, isActive()).color(QColorGroup::Button);
00832 
00833     for (int i = 0; i < BtnCount; i++)
00834         if (button[i]) {
00835            button[i]->setBg(c);
00836            button[i]->repaint(false);
00837         }
00838 }
00839 
00840 void B2Client::shadeChange()
00841 {
00842     spacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
00843         QSizePolicy::Expanding, QSizePolicy::Minimum);
00844     g->activate();
00845     doShape();
00846     if (B2Button *b = button[BtnShade]) {
00847     QToolTip::remove(b);
00848     QToolTip::add(b, isSetShade() ? i18n("Unshade") : i18n("Shade"));
00849     }
00850 }
00851 
00852 QSize B2Client::minimumSize() const
00853 {
00854     int left, right, top, bottom;
00855     borders(left, right, top, bottom);
00856     return QSize(left + right + 2 * buttonSize, top + bottom);
00857 }
00858 
00859 void B2Client::resize(const QSize& s)
00860 {
00861     widget()->resize(s);
00862 }
00863 
00864 void B2Client::borders(int &left, int &right, int &top, int &bottom) const
00865 {
00866     left = right = thickness;
00867     top = buttonSize + 4;
00868     bottom = thickness + (mustDrawHandle() ? 4 : 0);
00869 }
00870 
00871 void B2Client::menuButtonPressed()
00872 {
00873     static B2Client *lastClient = NULL;
00874     
00875     bool dbl = (lastClient == this && 
00876             time.elapsed() <= QApplication::doubleClickInterval());
00877     lastClient = this;
00878     time.start();
00879     if (!dbl) {
00880     KDecorationFactory* f = factory();
00881     QRect menuRect = button[BtnMenu]->rect();
00882     QPoint menuTop = button[BtnMenu]->mapToGlobal(menuRect.topLeft());
00883     QPoint menuBottom = button[BtnMenu]->mapToGlobal(menuRect.bottomRight());
00884     showWindowMenu(QRect(menuTop, menuBottom));
00885     if (!f->exists(this)) // 'this' was destroyed
00886         return;
00887     button[BtnMenu]->setDown(false);
00888     } else {
00889     switch (menu_dbl_click_op) {
00890     case B2::MinimizeOp:
00891         minimize();
00892         break;
00893     case B2::ShadeOp:
00894         setShade(!isSetShade());
00895         break;
00896     case B2::CloseOp:
00897         closeWindow();
00898         break;
00899     case B2::NoOp:
00900     default:
00901         break;
00902     }
00903     }
00904 }
00905 
00906 void B2Client::unobscureTitlebar()
00907 {
00908     /* we just noticed, that we got obscured by other windows
00909        so we look at all windows above us (stacking_order) merging their
00910        masks, intersecting it with our titlebar area, and see if we can
00911        find a place not covered by any window */
00912     if (in_unobs) {
00913     return;
00914     }
00915     in_unobs = 1;
00916     QRegion reg(QRect(0,0,width(), buttonSize + 4));
00917     reg = unobscuredRegion(reg);
00918     if (!reg.isEmpty()) {
00919         // there is at least _one_ pixel from our title area, which is not
00920     // obscured, we use the first rect we find
00921     // for a first test, we use boundingRect(), later we may refine
00922     // to rect(), and search for the nearest, or biggest, or smthg.
00923     titleMoveAbs(reg.boundingRect().x());
00924     }
00925     in_unobs = 0;
00926 }
00927 
00928 static void redraw_pixmaps()
00929 {
00930     int i;
00931     QColorGroup aGrp = options()->colorGroup(KDecoration::ColorButtonBg, true);
00932     QColorGroup iGrp = options()->colorGroup(KDecoration::ColorButtonBg, false);
00933 
00934     // close
00935     drawB2Rect(PIXMAP_A(P_CLOSE), aGrp.button(), false);
00936     drawB2Rect(PIXMAP_AH(P_CLOSE), aGrp.button(), true);
00937     drawB2Rect(PIXMAP_AD(P_CLOSE), aGrp.button(), true);
00938 
00939     drawB2Rect(PIXMAP_I(P_CLOSE), iGrp.button(), false);
00940     drawB2Rect(PIXMAP_IH(P_CLOSE), iGrp.button(), true);
00941     drawB2Rect(PIXMAP_ID(P_CLOSE), iGrp.button(), true);
00942 
00943     // shade
00944     KPixmap thinBox;
00945     thinBox.resize(buttonSize - 2, 6);
00946     for (i = 0; i < NumStates; i++) {
00947     bool is_act = (i < 2);
00948     bool is_down = ((i & 1) == 1);
00949     KPixmap *pix = pixmap[P_SHADE * NumStates + i];
00950     QColor color = is_act ? aGrp.button() : iGrp.button();
00951     drawB2Rect(&thinBox, color, is_down);
00952     pix->fill(Qt::black);
00953     bitBlt(pix, 0, 0, &thinBox, 
00954         0, 0, thinBox.width(), thinBox.height(), Qt::CopyROP, true);
00955     }
00956 
00957     // maximize
00958     for (i = 0; i < NumStates; i++) {
00959     *pixmap[P_MAX * NumStates + i] = *pixmap[P_CLOSE * NumStates + i];
00960     pixmap[P_MAX * NumStates + i]->detach();
00961     }
00962     
00963     // normalize + iconify
00964     KPixmap smallBox;
00965     smallBox.resize(10, 10);
00966     KPixmap largeBox;
00967     largeBox.resize(12, 12);
00968 
00969     for (i = 0; i < NumStates; i++) {
00970     bool is_act = (i < 3);
00971     bool is_down = (i == Down || i == IDown);
00972     KPixmap *pix = pixmap[P_NORMALIZE * NumStates + i];
00973     drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00974     drawB2Rect(&largeBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00975     pix->fill(options()->color(KDecoration::ColorTitleBar, is_act));
00976     bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox,
00977            0, 0, 12, 12, Qt::CopyROP, true);
00978     bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00979 
00980     bitBlt(pixmap[P_ICONIFY * NumStates + i], 0, 0,
00981            &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00982     }
00983     
00984     // resize
00985     for (i = 0; i < NumStates; i++) {
00986     bool is_act = (i < 3);
00987     bool is_down = (i == Down || i == IDown);
00988     *pixmap[P_RESIZE * NumStates + i] = *pixmap[P_CLOSE * NumStates + i];
00989     pixmap[P_RESIZE * NumStates + i]->detach();
00990     drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00991     bitBlt(pixmap[P_RESIZE * NumStates + i], 
00992         0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00993     }
00994 
00995 
00996     QPainter p;
00997     // x for close + menu + help
00998     for (int j = 0; j < 3; j++) {
00999         int pix;
01000         unsigned const char *light, *dark;
01001         switch (j) {
01002         case 0:
01003             pix = P_CLOSE; light = close_white_bits; dark = close_dgray_bits;
01004             break;
01005         case 1:
01006             pix = P_MENU; light = menu_white_bits; dark = menu_dgray_bits;
01007             break;
01008         default:
01009             pix = P_HELP; light = help_light_bits; dark = help_dark_bits;
01010             break;
01011         }
01012     int off = (pixmap[pix * NumStates]->width() - 16) / 2;
01013         for (i = 0; i < NumStates; i++) {
01014             p.begin(pixmap[pix * NumStates + i]);
01015             kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, off, off, 16, 16, true,
01016                           light, NULL, NULL, dark, NULL, NULL);
01017             p.end();
01018         }
01019     }
01020 
01021     // pin
01022     for (i = 0; i < NumStates; i++) {
01023     bool isDown = (i == Down || i == IDown);
01024         unsigned const char *white = isDown ? pindown_white_bits : pinup_white_bits;
01025         unsigned const char *gray = isDown ? pindown_gray_bits : pinup_gray_bits;
01026         unsigned const char *dgray =isDown ? pindown_dgray_bits : pinup_dgray_bits;
01027         p.begin(pixmap[P_PINUP * NumStates + i]);
01028         kColorBitmaps(&p, (i < 3) ? aGrp : iGrp, 0, 0, 16, 16, true, white,
01029                       gray, NULL, dgray, NULL, NULL);
01030         p.end();
01031     }
01032 
01033     // Apply the hilight effect to the 'Hover' icons
01034     KIconEffect ie;
01035     QPixmap hilighted;
01036     for (i = 0; i < P_NUM_BUTTON_TYPES; i++) {
01037     int offset = i * NumStates;
01038     hilighted = ie.apply(*pixmap[offset + Norm], 
01039         KIcon::Small, KIcon::ActiveState);
01040     *pixmap[offset + Hover] = hilighted;    
01041 
01042     hilighted = ie.apply(*pixmap[offset + INorm], 
01043         KIcon::Small, KIcon::ActiveState);
01044     *pixmap[offset + IHover] = hilighted;    
01045     }
01046 
01047     
01048     // Create the titlebar gradients
01049     if (QPixmap::defaultDepth() > 8) {
01050     QColor titleColor[4] = {
01051         options()->color(KDecoration::ColorTitleBar, true),
01052             options()->color(KDecoration::ColorFrame, true),
01053 
01054         options()->color(KDecoration::ColorTitleBlend, false),
01055         options()->color(KDecoration::ColorTitleBar, false)
01056     };
01057 
01058     if (colored_frame) {
01059         titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true);
01060         titleColor[1] = options()->color(KDecoration::ColorTitleBar, true);
01061     }
01062 
01063     for (i = 0; i < 2; i++) {
01064         if (titleColor[2 * i] != titleColor[2 * i + 1]) {
01065         if (!titleGradient[i]) {
01066             titleGradient[i] = new KPixmap;
01067         }
01068         titleGradient[i]->resize(64, buttonSize + 3);
01069         KPixmapEffect::gradient(*titleGradient[i],
01070             titleColor[2 * i], titleColor[2 * i + 1],
01071             KPixmapEffect::VerticalGradient);
01072         } else {
01073            delete titleGradient[i];
01074            titleGradient[i] = 0;
01075         }
01076     }
01077     }
01078 }
01079 
01080 void B2Client::positionButtons()
01081 {
01082     QFontMetrics fm(options()->font(isActive()));
01083     QString cap = caption();
01084     if (cap.length() < 5) // make sure the titlebar has sufficiently wide
01085         cap = "XXXXX";    // area for dragging the window
01086     int textLen = fm.width(cap);
01087 
01088     QRect t = titlebar->captionSpacer->geometry();
01089     int titleWidth = titlebar->width() - t.width() + textLen + 2;
01090     if (titleWidth > width()) titleWidth = width();
01091 
01092     titlebar->resize(titleWidth, buttonSize + 4);
01093     titlebar->move(bar_x_ofs, 0);
01094 }
01095 
01096 // Transparent bound stuff.
01097 
01098 static QRect *visible_bound;
01099 static QPointArray bound_shape;
01100 
01101 bool B2Client::drawbound(const QRect& geom, bool clear)
01102 {
01103     if (clear) {
01104     if (!visible_bound) return true;
01105     }
01106 
01107     if (!visible_bound) {
01108     visible_bound = new QRect(geom);
01109     QRect t = titlebar->geometry();
01110     int frameTop = geom.top() + t.bottom();
01111     int barLeft = geom.left() + bar_x_ofs;
01112     int barRight = barLeft + t.width() - 1;
01113     if (barRight > geom.right()) barRight = geom.right();
01114         // line width is 5 pixels, so compensate for the 2 outer pixels (#88657)
01115         QRect g = geom;
01116         g.setLeft( g.left() + 2 );
01117         g.setTop( g.top() + 2 );
01118         g.setRight( g.right() - 2 );
01119         g.setBottom( g.bottom() - 2 );
01120         frameTop += 2;
01121         barLeft += 2;
01122         barRight -= 2;
01123 
01124     bound_shape.putPoints(0, 8,
01125         g.left(), frameTop,
01126         barLeft, frameTop,
01127         barLeft, g.top(),
01128         barRight, g.top(),
01129         barRight, frameTop,
01130         g.right(), frameTop,
01131         g.right(), g.bottom(),
01132         g.left(), g.bottom());
01133     } else {
01134     *visible_bound = geom;
01135     }
01136     QPainter p(workspaceWidget());
01137     p.setPen(QPen(Qt::white, 5));
01138     p.setRasterOp(Qt::XorROP);
01139     p.drawPolygon(bound_shape);
01140 
01141     if (clear) {
01142     delete visible_bound;
01143     visible_bound = 0;
01144     }
01145     return true;
01146 }
01147 
01148 bool B2Client::eventFilter(QObject *o, QEvent *e)
01149 {
01150     if (o != widget())
01151     return false;
01152     switch (e->type()) {
01153     case QEvent::Resize:
01154     resizeEvent(static_cast< QResizeEvent* >(e));
01155     return true;
01156     case QEvent::Paint:
01157     paintEvent(static_cast< QPaintEvent* >(e));
01158     return true;
01159     case QEvent::MouseButtonDblClick:
01160     titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e));
01161     return true;
01162     case QEvent::MouseButtonPress:
01163     processMousePressEvent(static_cast< QMouseEvent* >(e));
01164     return true;
01165     case QEvent::Show:
01166     showEvent(static_cast< QShowEvent* >(e));
01167     return true;
01168     default:
01169     break;
01170     }
01171     return false;
01172 }
01173 
01174 // =====================================
01175 
01176 B2Button::B2Button(B2Client *_client, QWidget *parent, 
01177     const QString& tip, const int realizeBtns)
01178    : QButton(parent, 0), hover(false)
01179 {
01180     setBackgroundMode(NoBackground);
01181     setCursor(arrowCursor);
01182     realizeButtons = realizeBtns;
01183     client = _client;
01184     useMiniIcon = false;
01185     setFixedSize(buttonSize, buttonSize);
01186     QToolTip::add(this, tip);
01187 }
01188 
01189 
01190 QSize B2Button::sizeHint() const
01191 {
01192     return QSize(buttonSize, buttonSize);
01193 }
01194 
01195 QSizePolicy B2Button::sizePolicy() const
01196 {
01197     return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
01198 }
01199 
01200 void B2Button::drawButton(QPainter *p)
01201 {
01202     KPixmap* gradient = titleGradient[client->isActive() ? 0 : 1];
01203     if (gradient) {
01204     p->drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2);
01205     } else {
01206     p->fillRect(rect(), bg);
01207     }
01208     if (useMiniIcon) {
01209         QPixmap miniIcon = client->icon().pixmap(QIconSet::Small,
01210         client->isActive() ? QIconSet::Normal : QIconSet::Disabled);
01211         p->drawPixmap((width() - miniIcon.width()) / 2,
01212                       (height() - miniIcon.height()) / 2, miniIcon);
01213     } else {
01214     int type;
01215         if (client->isActive()) {
01216             if (isOn() || isDown())
01217         type = Down;
01218             else if (hover)
01219         type = Hover;
01220         else
01221         type = Norm;
01222         } else {
01223             if (isOn() || isDown())
01224         type = IDown;
01225         else if (hover)
01226         type = IHover;
01227             else
01228         type = INorm;
01229         }
01230     p->drawPixmap((width() - icon[type]->width()) / 2, 
01231         (height() - icon[type]->height()) / 2, *icon[type]);
01232     }
01233 }
01234 
01235 void B2Button::setPixmaps(int button_id)
01236 {
01237     button_id *= NumStates;
01238     for (int i = 0; i < NumStates; i++) {
01239     icon[i] = B2::pixmap[button_id + i];
01240     }
01241     repaint(false);
01242 }
01243 
01244 void B2Button::mousePressEvent(QMouseEvent * e)
01245 {
01246     last_button = e->button();
01247     QMouseEvent me(e->type(), e->pos(), e->globalPos(), 
01248         (e->button() & realizeButtons) ? LeftButton : NoButton, 
01249         e->state());
01250     QButton::mousePressEvent(&me);
01251 }
01252 
01253 void B2Button::mouseReleaseEvent(QMouseEvent * e)
01254 {
01255     last_button = e->button();
01256     QMouseEvent me(e->type(), e->pos(), e->globalPos(), 
01257         (e->button() & realizeButtons) ? LeftButton : NoButton, 
01258         e->state());
01259     QButton::mouseReleaseEvent(&me);
01260 }
01261 
01262 void B2Button::enterEvent(QEvent *e)
01263 {
01264     hover = true;
01265     repaint(false);
01266     QButton::enterEvent(e);
01267 }
01268 
01269 void B2Button::leaveEvent(QEvent *e)
01270 {
01271     hover = false;
01272     repaint(false);
01273     QButton::leaveEvent(e);
01274 }
01275 
01276 // =====================================
01277 
01278 B2Titlebar::B2Titlebar(B2Client *parent)
01279     : QWidget(parent->widget(), 0, WStyle_Customize | WRepaintNoErase),
01280       client(parent),
01281       set_x11mask(false), isfullyobscured(false), shift_move(false)
01282 {
01283     setBackgroundMode(NoBackground);
01284     captionSpacer = new QSpacerItem(buttonSize, buttonSize + 4,
01285         QSizePolicy::Expanding, QSizePolicy::Fixed);
01286 }
01287 
01288 bool B2Titlebar::x11Event(XEvent *e)
01289 {
01290     if (!set_x11mask) {
01291     set_x11mask = true;
01292     XSelectInput(qt_xdisplay(), winId(),
01293         KeyPressMask | KeyReleaseMask |
01294         ButtonPressMask | ButtonReleaseMask |
01295         KeymapStateMask |
01296         ButtonMotionMask |
01297         EnterWindowMask | LeaveWindowMask |
01298         FocusChangeMask |
01299         ExposureMask |
01300         PropertyChangeMask |
01301         StructureNotifyMask | SubstructureRedirectMask |
01302         VisibilityChangeMask);
01303     }
01304     switch (e->type) {
01305     case VisibilityNotify:
01306     isfullyobscured = false;
01307     if (e->xvisibility.state == VisibilityFullyObscured) {
01308         isfullyobscured = true;
01309         client->unobscureTitlebar();
01310     }
01311     break;
01312     default:
01313     break;
01314     }
01315     return QWidget::x11Event(e);
01316 }
01317 
01318 void B2Titlebar::drawTitlebar(QPainter &p, bool state)
01319 {
01320     KPixmap* gradient = titleGradient[state ? 0 : 1];
01321 
01322     QRect t = rect();
01323     // black titlebar frame
01324     p.setPen(Qt::black);
01325     p.drawLine(0, 0, 0, t.bottom());
01326     p.drawLine(0, 0, t.right(), 0);
01327     p.drawLine(t.right(), 0, t.right(), t.bottom());
01328 
01329     // titlebar fill
01330     const QColorGroup cg =
01331     options()->colorGroup(KDecoration::ColorTitleBar, state);
01332     QBrush brush(cg.background());
01333     if (gradient) brush.setPixmap(*gradient);
01334     qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1,
01335            cg, false, 1, 0, &brush);
01336 
01337     // and the caption
01338     p.setPen(options()->color(KDecoration::ColorFont, state));
01339     p.setFont(options()->font(state));
01340     t = captionSpacer->geometry();
01341     p.drawText(t, AlignLeft | AlignVCenter, client->caption());
01342 }
01343 
01344 void B2Titlebar::recalcBuffer()
01345 {
01346     titleBuffer.resize(width(), height());
01347 
01348     QPainter p(&titleBuffer);
01349     drawTitlebar(p, true);
01350     oldTitle = caption();
01351 }
01352 
01353 void B2Titlebar::resizeEvent(QResizeEvent *)
01354 {
01355     recalcBuffer();
01356     repaint(false);
01357 }
01358 
01359 
01360 void B2Titlebar::paintEvent(QPaintEvent *)
01361 {
01362     if(client->isActive())
01363         bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(),
01364                titleBuffer.height(), Qt::CopyROP, true);
01365     else {
01366         QPainter p(this);
01367     drawTitlebar(p, false);
01368     }
01369 }
01370 
01371 void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e)
01372 {
01373     if (e->y() < height()) {
01374     client->titlebarDblClickOperation();
01375     }
01376 }
01377 
01378 void B2Titlebar::mousePressEvent(QMouseEvent * e)
01379 {
01380     shift_move = e->state() & ShiftButton;
01381     if (shift_move) {
01382         moveOffset = e->globalPos();
01383     } else {
01384     e->ignore();
01385     }
01386 }
01387 
01388 void B2Titlebar::mouseReleaseEvent(QMouseEvent * e)
01389 {
01390     if (shift_move) shift_move = false;
01391     else e->ignore();
01392 }
01393 
01394 void B2Titlebar::mouseMoveEvent(QMouseEvent * e)
01395 {
01396     if (shift_move) {
01397     int oldx = mapFromGlobal(moveOffset).x();
01398         int xdiff = e->globalPos().x() - moveOffset.x();
01399         moveOffset = e->globalPos();
01400     if (oldx >= 0 && oldx <= rect().right()) {
01401             client->titleMoveRel(xdiff);
01402     }
01403     } else {
01404         e->ignore();
01405     }
01406 }
01407 
01408 } // namespace B2
01409 
01410 #include "b2client.moc"
01411 
01412 // vim: sw=4
01413 
KDE Logo
This file is part of the documentation for kwin Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jun 14 01:19:56 2006 by doxygen 1.4.0 written by Dimitri van Heesch, © 1997-2003