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     int left, right, top, bottom;
00743     borders(left, right, top, bottom);
00744     return QSize(left + right + 2 * buttonSize, top + bottom);
00745 }
00746 
00747 void B2Client::resize(const QSize& s)
00748 {
00749     widget()->resize(s);
00750 }
00751 
00752 void B2Client::borders(int &left, int &right, int &top, int &bottom) const
00753 {
00754     left = right = thickness;
00755     top = buttonSize + 4;
00756     bottom = thickness + (isResizable() ? 4 : 0);
00757 }
00758 
00759 void B2Client::menuButtonPressed()
00760 {
00761     QPoint menupoint = button[BtnMenu]->mapToGlobal(
00762         button[BtnMenu]->rect().bottomLeft());
00763     KDecorationFactory* f = factory();
00764     showWindowMenu(menupoint);
00765     if( !f->exists( this )) // 'this' was destroyed
00766         return;
00767     button[BtnMenu]->setDown(false);
00768 }
00769 
00770 #if 0
00771 void B2Client::slotReset()
00772 {
00773     redraw_pixmaps();
00774     QColor c = options()->colorGroup(KDecoration::ColorTitleBar, isActive()).
00775         color(QColorGroup::Button);
00776 
00777     for (int i = 0; i < BtnCount; i++)
00778         if (button[i]) {
00779             button[i]->setBg(c);
00780             button[i]->repaint(false);
00781         }
00782 
00783     widget()->repaint();
00784     titlebar->recalcBuffer();
00785     titlebar->repaint(false);
00786 }
00787 #endif
00788 
00789 void B2Client::unobscureTitlebar()
00790 {
00791     /* we just noticed, that we got obscured by other windows
00792        so we look at all windows above us (stacking_order) merging their
00793        masks, intersecting it with our titlebar area, and see if we can
00794        find a place not covered by any window */
00795     if (in_unobs) {
00796     return;
00797     }
00798     in_unobs = 1;
00799     QRegion reg(QRect(0,0,width(), buttonSize + 4));
00800     reg = unobscuredRegion( reg );
00801     if (!reg.isEmpty()) {
00802         // there is at least _one_ pixel from our title area, which is not
00803     // obscured, we use the first rect we find
00804     // for a first test, we use boundingRect(), later we may refine
00805     // to rect(), and search for the nearest, or biggest, or smthg.
00806     titleMoveAbs(reg.boundingRect().x());
00807     }
00808     in_unobs = 0;
00809 }
00810 
00811 static void redraw_pixmaps()
00812 {
00813     QColorGroup aGrp = options()->colorGroup(KDecoration::ColorButtonBg, true);
00814     QColorGroup iGrp = options()->colorGroup(KDecoration::ColorButtonBg, false);
00815 
00816     // close
00817     drawB2Rect(PIXMAP_A(P_CLOSE), aGrp.button(), false);
00818     drawB2Rect(PIXMAP_AD(P_CLOSE), aGrp.button(), true);
00819 
00820     drawB2Rect(PIXMAP_I(P_CLOSE), iGrp.button(), false);
00821     drawB2Rect(PIXMAP_ID(P_CLOSE), iGrp.button(), true);
00822 
00823     // maximize
00824     int i;
00825     for (i = 0; i < 4; i++) {
00826     *pixmap[P_MAX*4 + i] = *pixmap[P_CLOSE*4 + i];
00827     pixmap[P_MAX*4 + i]->detach();
00828     }
00829 
00830     // normalize + iconify
00831     KPixmap smallBox;
00832     smallBox.resize(10, 10);
00833     KPixmap largeBox;
00834     largeBox.resize(12, 12);
00835 
00836     for (i = 0; i < 4; i++) {
00837     bool is_act = (i < 2);
00838     bool is_down = ((i & 1) == 1);
00839     KPixmap *pix = pixmap[P_NORMALIZE*4 + i];
00840     drawB2Rect(&smallBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00841     drawB2Rect(&largeBox, is_act ? aGrp.button() : iGrp.button(), is_down);
00842     pix->fill(options()->color(KDecoration::ColorTitleBar, is_act));
00843     bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox,
00844            0, 0, 12, 12, Qt::CopyROP, true);
00845     bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00846 
00847     bitBlt(pixmap[P_ICONIFY * 4 + i], 0, 0,
00848            &smallBox, 0, 0, 10, 10, Qt::CopyROP, true);
00849     }
00850 
00851     QPainter p;
00852     // x for close + menu + help
00853     int pix = P_CLOSE;
00854     const unsigned char *light = close_white_bits;
00855     const unsigned char *dark = close_dgray_bits;
00856     int off = (pixmap[pix * 4]->width() - 16) / 2;
00857     for (i = 0; i < 4; i++) {
00858     p.begin(pixmap[pix * 4 + i]);
00859     kColorBitmaps(&p, (i < 2) ? aGrp : iGrp, off, off, 16, 16, true,
00860               light, NULL, NULL, dark, NULL, NULL);
00861     p.end();
00862     }
00863     // x for close + menu + help
00864     for (int j = 0; j < 2; j++) {
00865         switch (j) {
00866     case 1:
00867         pix = P_MENU; light = menu_white_bits; dark = menu_dgray_bits;
00868         break;
00869         default:
00870         pix = P_HELP; light = help_light_bits; dark = help_dark_bits;
00871         break;
00872         }
00873         for (i = 0; i < 4; i++) {
00874             p.begin(pixmap[pix * 4 + i]);
00875             kColorBitmaps(&p, (i < 2) ? aGrp : iGrp, 0, 0, 16, 16, true,
00876                           light, NULL, NULL, dark, NULL, NULL);
00877             p.end();
00878         }
00879     }
00880 
00881     // pin
00882     for (i = 0; i < 4; i++) {
00883         const unsigned char *white = (i&1) ? pindown_white_bits : pinup_white_bits;
00884         const unsigned char *gray = (i&1) ? pindown_gray_bits : pinup_gray_bits;
00885         const unsigned char *dgray = (i&1) ? pindown_dgray_bits : pinup_dgray_bits;
00886         p.begin(pixmap[P_PINUP*4 + i]);
00887         kColorBitmaps(&p, (i<2)?aGrp:iGrp, 0, 0, 16, 16, true, white,
00888                       gray, NULL, dgray, NULL, NULL);
00889         p.end();
00890     }
00891 
00892     // Create the titlebar gradients
00893     if (QPixmap::defaultDepth() > 8) {
00894     QColor titleColor[4] = {
00895         options()->color(KDecoration::ColorTitleBar, true),
00896             options()->color(KDecoration::ColorFrame, true),
00897 
00898         options()->color(KDecoration::ColorTitleBlend, false),
00899         options()->color(KDecoration::ColorTitleBar, false)
00900     };
00901 
00902     if (colored_frame) {
00903         titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true);
00904         titleColor[1] = options()->color(KDecoration::ColorTitleBar, true);
00905     }
00906 
00907     for (i = 0; i < 2; i++) {
00908         if (titleColor[2 * i] != titleColor[2 * i + 1]) {
00909         if (!titleGradient[i]) {
00910             titleGradient[i] = new KPixmap;
00911         }
00912         titleGradient[i]->resize(64, buttonSize + 3);
00913         KPixmapEffect::gradient(*titleGradient[i],
00914             titleColor[2 * i], titleColor[2 * i + 1],
00915             KPixmapEffect::VerticalGradient);
00916         } else {
00917            delete titleGradient[i];
00918            titleGradient[i] = 0;
00919         }
00920     }
00921     }
00922 }
00923 
00924 void B2Client::positionButtons()
00925 {
00926     QFontMetrics fm(options()->font(isActive()));
00927     QString cap = caption();
00928     if (cap.length() < 5) // make sure the titlebar has sufficiently wide
00929         cap = "XXXXX";    // area for dragging the window
00930     int textLen = fm.width( cap );
00931 
00932     QRect t = titlebar->captionSpacer->geometry();
00933     int titleWidth = titlebar->width() - t.width() + textLen+2;
00934     if (titleWidth > width()) titleWidth=width();
00935 
00936     titlebar->resize(titleWidth, buttonSize + 4);
00937     titlebar->move(bar_x_ofs, 0);
00938 }
00939 
00940 // Transparent bound stuff.
00941 
00942 static QRect *visible_bound;
00943 static QPointArray bound_shape;
00944 
00945 bool B2Client::drawbound(const QRect& geom, bool clear)
00946 {
00947     if (clear) {
00948     if (!visible_bound) return true;
00949     }
00950 
00951     if (!visible_bound) {
00952     visible_bound = new QRect(geom);
00953     QRect t = titlebar->geometry();
00954     int frameTop = geom.top() + t.bottom();
00955     int barLeft = geom.left() + bar_x_ofs;
00956     int barRight = barLeft + t.width() - 1;
00957     if (barRight > geom.right()) barRight = geom.right();
00958 
00959     bound_shape.putPoints(0, 8,
00960         geom.left(), frameTop,
00961         barLeft, frameTop,
00962         barLeft, geom.top(),
00963         barRight, geom.top(),
00964         barRight, frameTop,
00965         geom.right(), frameTop,
00966         geom.right(), geom.bottom(),
00967         geom.left(), geom.bottom());
00968     } else {
00969     *visible_bound = geom;
00970     }
00971     QPainter p(workspaceWidget());
00972     p.setPen(QPen(Qt::white, 5));
00973     p.setRasterOp(Qt::XorROP);
00974     p.drawPolygon(bound_shape);
00975 
00976     if (clear) {
00977     delete visible_bound;
00978     visible_bound = 0;
00979     }
00980     return true;
00981 }
00982 
00983 bool B2Client::eventFilter(QObject *o, QEvent *e)
00984 {
00985     if (o != widget())
00986     return false;
00987     switch (e->type()) {
00988     case QEvent::Resize:
00989     resizeEvent(static_cast< QResizeEvent* >(e));
00990     return true;
00991     case QEvent::Paint:
00992     paintEvent(static_cast< QPaintEvent* >(e));
00993     return true;
00994     case QEvent::MouseButtonDblClick:
00995     titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e));
00996     return true;
00997     case QEvent::MouseButtonPress:
00998     processMousePressEvent(static_cast< QMouseEvent* >(e));
00999     return true;
01000     case QEvent::Show:
01001     showEvent(static_cast< QShowEvent* >(e));
01002     return true;
01003     default:
01004     break;
01005     }
01006     return false;
01007 }
01008 
01009 // =====================================
01010 
01011 B2Button::B2Button(B2Client *_client, QWidget *parent, const QString& tip, const int realizeBtns)
01012    : QButton(parent, 0)
01013 {
01014     setBackgroundMode(NoBackground);
01015     realizeButtons = realizeBtns;
01016     client = _client;
01017     useMiniIcon = false;
01018     setFixedSize(buttonSize, buttonSize);
01019     QToolTip::add(this, tip);
01020 }
01021 
01022 
01023 QSize B2Button::sizeHint() const
01024 {
01025     return QSize(buttonSize, buttonSize);
01026 }
01027 
01028 QSizePolicy B2Button::sizePolicy() const
01029 {
01030     return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
01031 }
01032 
01033 void B2Button::drawButton(QPainter *p)
01034 {
01035     KPixmap* gradient = titleGradient[client->isActive() ? 0 : 1];
01036     if (gradient) {
01037     p->drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2);
01038     } else {
01039     p->fillRect(rect(), bg);
01040     }
01041     if (useMiniIcon) {
01042         QPixmap miniIcon = client->icon().pixmap(QIconSet::Small,
01043         client->isActive() ? QIconSet::Normal : QIconSet::Disabled);
01044         p->drawPixmap((width()-miniIcon.width())/2,
01045                       (height()-miniIcon.height())/2, miniIcon);
01046     }
01047     else{
01048         if(client->isActive()){
01049             if (isDown())
01050                 p->drawPixmap((width()-pDown->width())/2,
01051                               (height()-pDown->height())/2, *pDown);
01052             else
01053                 p->drawPixmap((width()-pNorm->width())/2,
01054                               (height()-pNorm->height())/2, *pNorm);
01055         }
01056         else{
01057             if (isDown())
01058                 p->drawPixmap((width()-pDown->width())/2,
01059                               (height()-pDown->height())/2, *iDown);
01060             else
01061                 p->drawPixmap((width()-pNorm->width())/2,
01062                               (height()-pNorm->height())/2, *iNorm);
01063         }
01064     }
01065 }
01066 
01067 void B2Button::setPixmaps(KPixmap *pix, KPixmap *pixDown, KPixmap *iPix,
01068                           KPixmap *iPixDown)
01069 {
01070     pNorm = pix;
01071     pDown = pixDown;
01072     iNorm = iPix;
01073     iDown = iPixDown;
01074     repaint(false);
01075 }
01076 
01077 void B2Button::setPixmaps(int button_id)
01078 {
01079   button_id *= 4;
01080   setPixmaps(B2::pixmap[button_id], B2::pixmap[button_id+1],
01081          B2::pixmap[button_id+2], B2::pixmap[button_id+3]);
01082 }
01083 
01084 void B2Button::mousePressEvent( QMouseEvent* e )
01085 {
01086     last_button = e->button();
01087     QMouseEvent me(e->type(), e->pos(), e->globalPos(),
01088                     (e->button()&realizeButtons)?LeftButton:NoButton, e->state());
01089     QButton::mousePressEvent(&me);
01090 }
01091 
01092 void B2Button::mouseReleaseEvent( QMouseEvent* e )
01093 {
01094     last_button = e->button();
01095     QMouseEvent me(e->type(), e->pos(), e->globalPos(),
01096                    (e->button()&realizeButtons)?LeftButton:NoButton, e->state());
01097     QButton::mouseReleaseEvent(&me);
01098 }
01099 
01100 // =====================================
01101 
01102 B2Titlebar::B2Titlebar(B2Client *parent)
01103     : QWidget(parent->widget(), 0, WStyle_Customize | WRepaintNoErase),
01104       client(parent),
01105       set_x11mask(false), isfullyobscured(false), shift_move(false)
01106 {
01107     setBackgroundMode(NoBackground);
01108     captionSpacer = new QSpacerItem(10, buttonSize + 4,
01109         QSizePolicy::Expanding, QSizePolicy::Fixed);
01110 }
01111 
01112 bool B2Titlebar::x11Event(XEvent *e)
01113 {
01114     if (!set_x11mask) {
01115     set_x11mask = true;
01116     XSelectInput(qt_xdisplay(), winId(),
01117         KeyPressMask | KeyReleaseMask |
01118         ButtonPressMask | ButtonReleaseMask |
01119         KeymapStateMask |
01120         ButtonMotionMask |
01121         EnterWindowMask | LeaveWindowMask |
01122         FocusChangeMask |
01123         ExposureMask |
01124         PropertyChangeMask |
01125         StructureNotifyMask | SubstructureRedirectMask |
01126         VisibilityChangeMask);
01127     }
01128     switch ( e->type ) {
01129     case VisibilityNotify:
01130     isfullyobscured = false;
01131     if (e->xvisibility.state == VisibilityFullyObscured) {
01132         isfullyobscured = true;
01133         client->unobscureTitlebar();
01134     }
01135     break;
01136     default:
01137     break;
01138     }
01139     return QWidget::x11Event(e);
01140 }
01141 
01142 void B2Titlebar::drawTitlebar(QPainter &p, bool state)
01143 {
01144     KPixmap* gradient = titleGradient[state ? 0 : 1];
01145 
01146     QRect t = rect();
01147     // black titlebar frame
01148     p.setPen(Qt::black);
01149     p.drawLine(0, 0, 0, t.bottom());
01150     p.drawLine(0, 0, t.right(), 0);
01151     p.drawLine(t.right(), 0, t.right(), t.bottom());
01152 
01153     // titlebar fill
01154     const QColorGroup cg =
01155     options()->colorGroup(KDecoration::ColorTitleBar, state);
01156     QBrush brush(cg.background());
01157     if (gradient) brush.setPixmap(*gradient);
01158     qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1,
01159            cg, false, 1, 0, &brush);
01160 
01161     // and the caption
01162     p.setPen(options()->color(KDecoration::ColorFont, state));
01163     p.setFont(options()->font(state));
01164     t = captionSpacer->geometry();
01165     p.drawText(t, AlignLeft | AlignVCenter, client->caption());
01166 }
01167 
01168 void B2Titlebar::recalcBuffer()
01169 {
01170     QFontMetrics fm(options()->font(true));
01171     titleBuffer.resize(width(), height());
01172 
01173     QPainter p(&titleBuffer);
01174     drawTitlebar(p, true);
01175     oldTitle = caption();
01176 }
01177 
01178 void B2Titlebar::resizeEvent(QResizeEvent *)
01179 {
01180     recalcBuffer();
01181     repaint(false);
01182 }
01183 
01184 
01185 void B2Titlebar::paintEvent(QPaintEvent *)
01186 {
01187     if(client->isActive())
01188         bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(),
01189                titleBuffer.height(), Qt::CopyROP, true);
01190     else {
01191         QPainter p(this);
01192     drawTitlebar(p, false);
01193     }
01194 }
01195 
01196 void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e)
01197 {
01198     if (e->y() < height()) {
01199     client->titlebarDblClickOperation();
01200     }
01201 }
01202 
01203 void B2Titlebar::mousePressEvent( QMouseEvent * e )
01204 {
01205     shift_move = e->state() & ShiftButton;
01206     if (shift_move) {
01207         moveOffset = e->globalPos();
01208     } else {
01209     e->ignore();
01210     }
01211 }
01212 
01213 void B2Titlebar::mouseReleaseEvent( QMouseEvent * e )
01214 {
01215     if (shift_move) shift_move = false;
01216     else e->ignore();
01217 }
01218 
01219 void B2Titlebar::mouseMoveEvent( QMouseEvent * e )
01220 {
01221     if (shift_move) {
01222     int oldx = mapFromGlobal(moveOffset).x();
01223         int xdiff = e->globalPos().x() - moveOffset.x();
01224         moveOffset = e->globalPos();
01225     if (oldx >= 0 && oldx <= rect().right()) {
01226             client->titleMoveRel(xdiff);
01227     }
01228     } else {
01229         e->ignore();
01230     }
01231 }
01232 
01233 } // namespace B2
01234 
01235 #include "b2client.moc"
01236 
01237 // vim: sw=4
01238 
KDE Logo
This file is part of the documentation for kwin Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 16 14:44:21 2004 by doxygen 1.3.6 written by Dimitri van Heesch, © 1997-2003