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 Tue Aug 31 00:02:12 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003