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