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