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 Thu Dec 16 19:08:40 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003