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