add more spacing
[personal-kdebase.git] / workspace / kwin / clients / b2 / b2client.cpp
blob90f425f4747c3961879035e4e3e3770dc1952df6
1 /*********************************************************************
2 B-II KWin Client
4 Changes:
5 Customizable button positions by Karol Szwed <gallium@kde.org>
7 Thin frame in fixed size windows, titlebar gradient support, accessibility
8 improvements, customizable menu double click action and button hover
9 effects are
10 Copyright (c) 2003, 2004, 2006 Luciano Montanaro <mikelima@cirulla.net>
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 *********************************************************************/
26 #ifndef CLIENTS_B2_B2CLIENT
27 #define CLIENTS_B2_B2CLIENT
29 #include "b2client.h"
31 #include <QApplication>
32 #include <QLayout>
33 #include <QPixmap>
34 #include <QPaintEvent>
35 #include <QPolygon>
36 #include <QGridLayout>
37 #include <QEvent>
38 #include <QBoxLayout>
39 #include <QShowEvent>
40 #include <QStyle>
41 #include <QResizeEvent>
42 #include <QMouseEvent>
43 #include <QPainter>
44 #include <QUrl>
45 #include <QTextStream>
46 #include <kicontheme.h>
47 #include <kiconeffect.h>
48 #include <klocale.h>
49 #include <kconfig.h>
50 #include <QBitmap>
51 #include <QLabel>
53 #include <X11/Xlib.h>
54 #include <QX11Info>
55 #include <KUrl>
56 #include <KConfigGroup>
57 #include <KDebug>
59 namespace B2 {
61 #include "bitmaps.h"
63 enum {
64 Norm = 0,
65 Hover, Down, INorm, IHover, IDown,
66 NumStates
69 enum {
70 P_CLOSE = 0,
71 P_MAX = NumStates,
72 P_NORMALIZE = P_MAX + NumStates,
73 P_ICONIFY = P_NORMALIZE + NumStates,
74 P_PINUP = P_ICONIFY + NumStates,
75 P_MENU = P_PINUP + NumStates,
76 P_HELP = P_MENU + NumStates,
77 P_SHADE = P_HELP + NumStates,
78 P_RESIZE = P_SHADE + NumStates,
79 P_NUM_PIXMAPS = P_RESIZE + NumStates
82 static QPixmap *pixmap[P_NUM_PIXMAPS];
84 // active
85 #define PIXMAP_A(i) (pixmap[(i) + Norm])
86 // active, hover
87 #define PIXMAP_AH(i) (pixmap[(i) + Hover])
88 // active, down
89 #define PIXMAP_AD(i) (pixmap[(i) + Down])
90 // inactive
91 #define PIXMAP_I(i) (pixmap[(i) + INorm])
92 // inactive, hover
93 #define PIXMAP_IH(i) (pixmap[(i) + IHover])
94 // inactive, down
95 #define PIXMAP_ID(i) (pixmap[(i) + IDown])
97 static QPixmap* titleGradient[2] = {0, 0};
99 static int thickness = 4; // Frame thickness
100 static int buttonSize = 16;
102 enum DblClickOperation {
103 NoOp = 0,
104 MinimizeOp,
105 ShadeOp,
106 CloseOp
109 static DblClickOperation menu_dbl_click_op = NoOp;
111 static bool pixmaps_created = false;
112 static bool colored_frame = false;
113 static bool do_draw_handle = true;
114 static bool drawSmallBorders = false;
116 // =====================================
118 extern "C" KDE_EXPORT KDecorationFactory* create_factory()
120 return new B2::B2ClientFactory();
123 // =====================================
125 static inline const KDecorationOptions *options()
127 return KDecoration::options();
130 static void redraw_pixmaps();
132 static void read_config(B2ClientFactory *f)
134 // Force button size to be in a reasonable range.
135 // If the frame width is large, the button size must be large too.
136 buttonSize = (QFontMetrics(options()->font(true)).height() - 1) & 0x3e;
137 if (buttonSize < 16) buttonSize = 16;
139 KConfig _conf("kwinb2rc");
140 KConfigGroup conf(&_conf, "General");
141 colored_frame = conf.readEntry("UseTitleBarBorderColors", false);
142 do_draw_handle = conf.readEntry("DrawGrabHandle", true);
143 drawSmallBorders = !options()->moveResizeMaximizedWindows();
145 QString opString = conf.readEntry("MenuButtonDoubleClickOperation", "NoOp");
146 if (opString == "Close") {
147 menu_dbl_click_op = B2::CloseOp;
148 } else if (opString == "Minimize") {
149 menu_dbl_click_op = B2::MinimizeOp;
150 } else if (opString == "Shade") {
151 menu_dbl_click_op = B2::ShadeOp;
152 } else {
153 menu_dbl_click_op = B2::NoOp;
156 switch (options()->preferredBorderSize(f)) {
157 case KDecoration::BorderTiny:
158 thickness = 2;
159 break;
160 case KDecoration::BorderLarge:
161 thickness = 5;
162 break;
163 case KDecoration::BorderVeryLarge:
164 thickness = 7;
165 break;
166 case KDecoration::BorderHuge:
167 thickness = 9;
168 break;
169 case KDecoration::BorderVeryHuge:
170 thickness = 11;
171 break;
172 case KDecoration::BorderOversized:
173 thickness = 14;
174 break;
175 case KDecoration::BorderNormal:
176 default:
177 thickness = 4;
181 static void drawB2Rect(QPixmap *pix, const QColor &primary, bool down)
183 QPainter p(pix);
184 QColor hColor = primary.light(150);
185 QColor lColor = primary.dark(150);
187 if (down) qSwap(hColor, lColor);
189 if (QPixmap::defaultDepth() > 8) {
190 QLinearGradient gradient(0, 0, pix->width(), pix->height());
191 gradient.setColorAt(0.0, hColor);
192 gradient.setColorAt(1.0, lColor);
193 QBrush brush(gradient);
194 p.fillRect(pix->rect(), brush);
196 else
197 pix->fill(primary);
198 const int x2 = pix->width() - 1;
199 const int y2 = pix->height() - 1;
200 p.setPen(lColor);
201 p.drawLine(0, 0, x2, 0);
202 p.drawLine(0, 0, 0, y2);
203 p.drawLine(1, x2 - 1, x2 - 1, y2 - 1);
204 p.drawLine(x2 - 1, 1, x2 - 1, y2 - 1);
205 p.setPen(hColor);
206 p.drawRect(1, 1, x2 - 1, y2 - 1);
210 QPixmap* kwin_get_menu_pix_hack()
212 //return menu_pix; FIXME
213 return PIXMAP_A(P_MENU);
216 static void create_pixmaps()
218 if (pixmaps_created)
219 return;
220 pixmaps_created = true;
222 int i;
223 int bsize = buttonSize - 2;
224 if (bsize < 16) bsize = 16;
226 for (i = 0; i < P_NUM_PIXMAPS; i++) {
227 switch (NumStates *(i / NumStates)) {
228 case P_CLOSE: // will be initialized by copying P_MAX
229 case P_RESIZE:
230 pixmap[i] = new QPixmap();
231 break;
232 case P_ICONIFY:
233 pixmap[i] = new QPixmap(10, 10);
234 break;
235 case P_SHADE:
236 case P_MAX:
237 case P_HELP:
238 pixmap[i] = new QPixmap(bsize, bsize);
239 break;
240 default:
241 pixmap[i] = new QPixmap(16, 16);
242 break;
247 // This should stay here, before the mask creation, because of a bug with
248 // drawing a bitmap with drawPixmap() with the mask set (present at least
249 // in Qt 4.2.3).
250 titleGradient[0] = 0;
251 titleGradient[1] = 0;
252 redraw_pixmaps();
254 // there seems to be no way to load X bitmaps from data properly, so
255 // we need to create new ones for each mask :P
256 QBitmap pinupMask = QBitmap::fromData(QSize(16, 16), pinup_mask_bits);
257 QBitmap pindownMask = QBitmap::fromData(QSize(16, 16), pindown_mask_bits);
258 QBitmap menuMask = QBitmap::fromData(QSize(16, 16), menu_mask_bits);
259 for (i = 0; i < NumStates; i++) {
260 bool isDown = (i == Down) || (i == IDown);
261 pixmap[P_MENU + i]->setMask(menuMask);
262 pixmap[P_PINUP + i]->setMask(isDown ? pindownMask: pinupMask);
265 QBitmap normalizeMask(16, 16);
266 normalizeMask.clear();
268 // draw normalize icon mask
269 QPainter mask;
270 mask.begin(&normalizeMask);
272 QBrush one(Qt::color1);
273 mask.fillRect(normalizeMask.width() - 12, normalizeMask.height() - 12,
274 12, 12, one);
275 mask.fillRect(0, 0, 10, 10, one);
276 mask.end();
278 for (i = 0; i < NumStates; i++)
279 pixmap[P_NORMALIZE + i]->setMask(normalizeMask);
281 QBitmap shadeMask(bsize, bsize);
282 shadeMask.clear();
283 mask.begin(&shadeMask);
284 mask.fillRect(0, 0, bsize, 6, one);
285 mask.end();
286 for (i = 0; i < NumStates; i++)
287 pixmap[P_SHADE + i]->setMask(shadeMask);
290 static void delete_pixmaps()
292 for (int i = 0; i < P_NUM_PIXMAPS; i++) {
293 delete pixmap[i];
294 pixmap[i] = 0;
296 for (int i = 0; i < 2; i++) {
297 delete titleGradient[i];
298 titleGradient[i] = 0;
300 pixmaps_created = false;
303 // =====================================
305 B2ClientFactory::B2ClientFactory()
307 read_config(this);
308 create_pixmaps();
311 B2ClientFactory::~B2ClientFactory()
313 delete_pixmaps();
316 KDecoration *B2ClientFactory::createDecoration(KDecorationBridge *b)
318 return new B2::B2Client(b, this);
321 bool B2ClientFactory::reset(unsigned long changed)
323 bool needsReset = SettingColors ? true : false;
324 // TODO Do not recreate decorations if it is not needed. Look at
325 // ModernSystem for how to do that
326 read_config(this);
327 if (changed & SettingFont) {
328 delete_pixmaps();
329 create_pixmaps();
330 needsReset = true;
332 redraw_pixmaps();
333 // For now just return true.
334 return needsReset;
337 bool B2ClientFactory::supports(Ability ability) const
339 switch (ability) {
340 // announce
341 case AbilityAnnounceButtons:
342 case AbilityAnnounceColors:
343 // buttons
344 case AbilityButtonMenu:
345 case AbilityButtonOnAllDesktops:
346 case AbilityButtonSpacer:
347 case AbilityButtonHelp:
348 case AbilityButtonMinimize:
349 case AbilityButtonMaximize:
350 case AbilityButtonClose:
351 case AbilityButtonShade:
352 case AbilityButtonResize:
353 // colors
354 case AbilityColorTitleBack:
355 case AbilityColorTitleBlend:
356 case AbilityColorTitleFore:
357 case AbilityColorFrame:
358 case AbilityColorButtonBack:
359 return true;
360 // These are not (yet) supported.
361 case AbilityButtonAboveOthers:
362 case AbilityButtonBelowOthers:
363 default:
364 return false;
368 QList< B2ClientFactory::BorderSize > B2ClientFactory::borderSizes() const
370 // the list must be sorted
371 return QList< BorderSize >() << BorderTiny << BorderNormal <<
372 BorderLarge << BorderVeryLarge <<
373 BorderHuge << BorderVeryHuge << BorderOversized;
376 // =====================================
378 void B2Client::maxButtonClicked()
380 maximize(button[BtnMax]->last_button);
383 void B2Client::shadeButtonClicked()
385 setShade(!isSetShade());
388 void B2Client::resizeButtonPressed()
390 performWindowOperation(ResizeOp);
393 B2Client::B2Client(KDecorationBridge *b, KDecorationFactory *f)
394 : KDecoration(b, f), bar_x_ofs(0), in_unobs(0)
398 void B2Client::init()
400 const QString tips[] = {
401 i18n("Menu"),
402 isOnAllDesktops() ?
403 i18n("Not on all desktops") : i18n("On all desktops"),
404 i18n("Minimize"), i18n("Maximize"),
405 i18n("Close"), i18n("Help"),
406 isSetShade() ? i18n("Unshade") : i18n("Shade"),
407 i18n("Resize")
410 // Check this early, otherwise the preview will be rendered badly.
411 resizable = isResizable();
413 createMainWidget();
414 widget()->setAttribute(Qt::WA_NoSystemBackground);
415 widget()->installEventFilter(this);
417 // Set button pointers to NULL so we know what has been created
418 for (int i = 0; i < BtnCount; i++)
419 button[i] = NULL;
421 g = new QGridLayout(widget());
422 // Left and right border width
424 leftSpacer = new QSpacerItem(thickness, 16,
425 QSizePolicy::Fixed, QSizePolicy::Expanding);
426 rightSpacer = new QSpacerItem(thickness, 16,
427 QSizePolicy::Fixed, QSizePolicy::Expanding);
429 g->addItem(leftSpacer, 1, 0);
430 g->addItem(rightSpacer, 1, 2);
432 // Top border height
433 topSpacer = new QSpacerItem(10, buttonSize + 4,
434 QSizePolicy::Expanding, QSizePolicy::Fixed);
435 g->addItem(topSpacer, 0, 1);
437 // Bottom border height.
438 bottomSpacer = new QSpacerItem(10,
439 thickness + (mustDrawHandle() ? 4 : 0),
440 QSizePolicy::Expanding, QSizePolicy::Fixed);
441 g->addItem(bottomSpacer, 2, 1);
442 if (isPreview()) {
443 QLabel *previewLabel = new QLabel(
444 i18n("<b><center>B II preview</center></b>"),
445 widget());
446 previewLabel->setAutoFillBackground(true);
447 g->addWidget(previewLabel, 1, 1);
448 } else {
449 g->addItem(new QSpacerItem(0, 0), 1, 1);
452 // titlebar
453 g->addItem(new QSpacerItem(0, buttonSize + 4), 0, 0);
455 titlebar = new B2Titlebar(this);
456 titlebar->setMinimumWidth(buttonSize + 4);
457 titlebar->setFixedHeight(buttonSize + 4);
459 QBoxLayout *titleLayout = new QBoxLayout(QBoxLayout::LeftToRight, titlebar);
460 titleLayout->setMargin(2);
461 titleLayout->setSpacing(1);
463 if (options()->customButtonPositions()) {
464 addButtons(options()->titleButtonsLeft(), tips, titlebar, titleLayout);
465 titleLayout->addItem(titlebar->captionSpacer);
466 addButtons(options()->titleButtonsRight(), tips, titlebar, titleLayout);
467 } else {
468 addButtons("MSH", tips, titlebar, titleLayout);
469 titleLayout->addItem(titlebar->captionSpacer);
470 addButtons("IAX", tips, titlebar, titleLayout);
473 titleLayout->addSpacing(2);
475 QColor c = options()->palette(KDecoration::ColorTitleBar, isActive()).color(QPalette::Active, QPalette::Button);
477 for (int i = 0; i < BtnCount; i++) {
478 if (button[i])
479 button[i]->setBg(c);
482 titlebar->updateGeometry();
483 positionButtons();
484 titlebar->recalcBuffer();
485 titlebar->installEventFilter(this);
488 void B2Client::addButtons(const QString& s, const QString tips[],
489 B2Titlebar* tb, QBoxLayout* titleLayout)
491 if (s.length() <= 0)
492 return;
494 for (int i = 0; i < s.length(); i++) {
495 switch (s[i].toLatin1()) {
496 case 'M': // Menu button
497 if (!button[BtnMenu]) {
498 button[BtnMenu] = new B2Button(this, tb, tips[BtnMenu],
499 Qt::LeftButton | Qt::RightButton);
500 button[BtnMenu]->setPixmaps(P_MENU);
501 button[BtnMenu]->setUseMiniIcon();
502 connect(button[BtnMenu], SIGNAL(pressed()),
503 this, SLOT(menuButtonPressed()));
504 titleLayout->addWidget(button[BtnMenu]);
506 break;
507 case 'S': // Sticky button
508 if (!button[BtnSticky]) {
509 button[BtnSticky] = new B2Button(this, tb, tips[BtnSticky]);
510 button[BtnSticky]->setPixmaps(P_PINUP);
511 button[BtnSticky]->setToggle();
512 button[BtnSticky]->setDown(isOnAllDesktops());
513 connect(button[BtnSticky], SIGNAL(clicked()),
514 this, SLOT(toggleOnAllDesktops()));
515 titleLayout->addWidget(button[BtnSticky]);
517 break;
518 case 'H': // Help button
519 if (providesContextHelp() && (!button[BtnHelp])) {
520 button[BtnHelp] = new B2Button(this, tb, tips[BtnHelp]);
521 button[BtnHelp]->setPixmaps(P_HELP);
522 connect(button[BtnHelp], SIGNAL(clicked()),
523 this, SLOT(showContextHelp()));
524 titleLayout->addWidget(button[BtnHelp]);
526 break;
527 case 'I': // Minimize button
528 if (isMinimizable() && (!button[BtnIconify])) {
529 button[BtnIconify] = new B2Button(this, tb,tips[BtnIconify]);
530 button[BtnIconify]->setPixmaps(P_ICONIFY);
531 connect(button[BtnIconify], SIGNAL(clicked()),
532 this, SLOT(minimize()));
533 titleLayout->addWidget(button[BtnIconify]);
535 break;
536 case 'A': // Maximize button
537 if (isMaximizable() && (!button[BtnMax])) {
538 button[BtnMax] = new B2Button(this, tb, tips[BtnMax],
539 Qt::LeftButton | Qt::MidButton | Qt::RightButton);
540 button[BtnMax]->setPixmaps(maximizeMode() == MaximizeFull ?
541 P_NORMALIZE : P_MAX);
542 connect(button[BtnMax], SIGNAL(clicked()),
543 this, SLOT(maxButtonClicked()));
544 titleLayout->addWidget(button[BtnMax]);
546 break;
547 case 'X': // Close button
548 if (isCloseable() && !button[BtnClose]) {
549 button[BtnClose] = new B2Button(this, tb, tips[BtnClose]);
550 button[BtnClose]->setPixmaps(P_CLOSE);
551 connect(button[BtnClose], SIGNAL(clicked()),
552 this, SLOT(closeWindow()));
553 titleLayout->addWidget(button[BtnClose]);
555 break;
556 case 'L': // Shade button
557 if (isShadeable() && !button[BtnShade]) {
558 button[BtnShade] = new B2Button(this, tb, tips[BtnShade]);
559 button[BtnShade]->setPixmaps(P_SHADE);
560 connect(button[BtnShade], SIGNAL(clicked()),
561 this, SLOT(shadeButtonClicked()));
562 titleLayout->addWidget(button[BtnShade]);
564 break;
565 case 'R': // Resize button
566 if (resizable && !button[BtnResize]) {
567 button[BtnResize] = new B2Button(this, tb, tips[BtnResize]);
568 button[BtnResize]->setPixmaps(P_RESIZE);
569 connect(button[BtnResize], SIGNAL(pressed()),
570 this, SLOT(resizeButtonPressed()));
571 titleLayout->addWidget(button[BtnResize]);
573 break;
574 case '_': // Additional spacing
575 titleLayout->addSpacing(4);
576 break;
581 bool B2Client::mustDrawHandle() const
583 if (drawSmallBorders && (maximizeMode() & MaximizeVertical)) {
584 return false;
585 } else {
586 return do_draw_handle && resizable;
590 void B2Client::iconChange()
592 if (button[BtnMenu])
593 button[BtnMenu]->repaint();
596 // Gallium: New button show/hide magic for customizable
597 // button positions.
598 void B2Client::calcHiddenButtons()
600 // Hide buttons in this order:
601 // Shade, Sticky, Help, Resize, Maximize, Minimize, Close, Menu
602 B2Button* btnArray[] = {
603 button[BtnShade], button[BtnSticky], button[BtnHelp], button[BtnResize],
604 button[BtnMax], button[BtnIconify], button[BtnClose], button[BtnMenu]
606 const int minWidth = 120;
607 int currentWidth = width();
608 int count = 0;
610 // Determine how many buttons we need to hide
611 while (currentWidth < minWidth) {
612 currentWidth += buttonSize + 1; // Allow for spacer (extra 1pix)
613 count++;
615 // Bound the number of buttons to hide
616 if (count > BtnCount) count = BtnCount;
618 // Hide the required buttons
619 for (int i = 0; i < count; i++) {
620 if (btnArray[i] && btnArray[i]->isVisible())
621 btnArray[i]->hide();
623 // Show the rest of the buttons
624 for (int i = count; i < BtnCount; i++) {
625 if (btnArray[i] && (!btnArray[i]->isVisible()))
626 btnArray[i]->show();
630 void B2Client::resizeEvent(QResizeEvent * /*e*/)
632 calcHiddenButtons();
633 titlebar->layout()->activate();
634 positionButtons();
636 // may be the resize cut off some space occupied by titlebar, which
637 // was moved, so instead of reducing it, we first try to move it
638 titleMoveAbs(bar_x_ofs);
640 doShape();
641 widget()->repaint(); // the frame is misrendered without this
644 void B2Client::captionChange()
646 // XXX This function and resizeEvent are quite similar.
647 // Factor out common code.
648 calcHiddenButtons();
649 titlebar->layout()->activate();
650 positionButtons();
652 titleMoveAbs(bar_x_ofs);
654 doShape();
655 titlebar->recalcBuffer();
656 titlebar->repaint();
659 void B2Client::paintEvent(QPaintEvent* e)
661 QPainter p(widget());
663 KDecoration::ColorType frameColorGroup = colored_frame ?
664 KDecoration::ColorTitleBar : KDecoration::ColorFrame;
666 QRect t = titlebar->geometry();
668 // Frame height, this is used a lot of times
669 const int fHeight = height() - t.height() - 1;
670 const int fWidth = width() - 1;
672 // distance from the bottom border - it is different if window is resizable
673 const int bb = mustDrawHandle() ? 4 : 0;
674 const int bDepth = thickness + bb;
676 QPalette fillColor = options()->palette(frameColorGroup, isActive());
677 QBrush fillBrush(options()->color(frameColorGroup, isActive()));
679 // outer frame rect
680 p.drawRect(0, t.bottom() - thickness + 1,
681 fWidth, fHeight - bb + thickness);
683 if (thickness >= 2) {
684 // inner window rect
685 p.drawRect(thickness - 1, t.bottom(),
686 fWidth - 2 * (thickness - 1), fHeight - bDepth + 2);
688 if (thickness >= 3) {
689 // frame shade panel
690 qDrawShadePanel(&p, 1, t.bottom() - thickness + 2,
691 width() - 2, fHeight - 1 - bb + thickness, fillColor, false);
692 if (thickness == 4) {
693 p.setPen(fillColor.color(QPalette::Background));
694 p.drawRect(thickness - 2, t.bottom() - 1,
695 width() - 2 * thickness + 3, fHeight + 4 - bDepth);
696 } else if (thickness > 4) {
697 qDrawShadePanel(&p, thickness - 2,
698 t.bottom() - 1, width() - 2 * (thickness - 2),
699 fHeight + 5 - bDepth, fillColor, true);
700 if (thickness >= 5) {
701 // draw frame interior
702 p.fillRect(2, t.bottom() - thickness + 3,
703 width() - 4, thickness - 4, fillBrush);
704 p.fillRect(2, height() - bDepth + 2,
705 width() - 4, thickness - 4, fillBrush);
706 p.fillRect(2, t.bottom() - 1,
707 thickness - 4, fHeight - bDepth + 5, fillBrush);
708 p.fillRect(width() - thickness + 2, t.bottom() - 1,
709 thickness - 4, fHeight - bDepth + 5, fillBrush);
715 // bottom handle rect
716 if (mustDrawHandle()) {
717 p.setPen(Qt::black);
718 const int hx = width() - 40;
719 const int hw = 40;
721 p.drawLine(width() - 1, height() - thickness - 4,
722 width() - 1, height() - 1);
723 p.drawLine(hx, height() - 1, width() - 1, height() - 1);
724 p.drawLine(hx, height() - 4, hx, height() - 1);
726 p.fillRect(hx + 1, height() - thickness - 3,
727 hw - 2, thickness + 2, fillBrush);
729 p.setPen(fillColor.color(QPalette::Dark));
730 p.drawLine(width() - 2, height() - thickness - 4,
731 width() - 2, height() - 2);
732 p.drawLine(hx + 1, height() - 2, width() - 2, height() - 2);
734 p.setPen(fillColor.color(QPalette::Light));
735 p.drawLine(hx + 1, height() - thickness - 2,
736 hx + 1, height() - 3);
737 p.drawLine(hx + 1, height() - thickness - 3,
738 width() - 3, height() - thickness - 3);
741 /* OK, we got a paint event, which means parts of us are now visible
742 which were not before. We try the titlebar if it is currently fully
743 obscured, and if yes, try to unobscure it, in the hope that some
744 of the parts which we just painted were in the titlebar area.
745 It can happen, that the titlebar, as it got the FullyObscured event
746 had no chance of becoming partly visible. The problem is, that
747 we now might have the space available, but the titlebar gets no
748 visibilitinotify events until its state changes, so we just try
750 if (titlebar->isFullyObscured()) {
751 /* We first see, if our repaint contained the titlebar area */
752 QRegion reg(QRect(0, 0, width(), buttonSize + 4));
753 reg = reg.intersect(e->region());
754 if (!reg.isEmpty())
755 unobscureTitlebar();
759 void B2Client::doShape()
761 const QRect t = titlebar->geometry();
762 const int w = width();
763 const int h = height();
764 QRegion mask(widget()->rect());
765 // top to the tilebar right
766 if (bar_x_ofs) {
767 // left from bar
768 mask -= QRect(0, 0, bar_x_ofs, t.height() - thickness);
769 // top left point
770 mask -= QRect(0, t.height() - thickness, 1, 1);
772 if (t.right() < w - 1) {
773 mask -= QRect(w - 1, t.height() - thickness, 1, 1); // top right point
774 mask -= QRect(t.right() + 1, 0,
775 w - t.right() - 1, t.height() - thickness);
777 // bottom right point
778 mask -= QRect(w - 1, h - 1, 1, 1);
779 if (mustDrawHandle()) {
780 // bottom left point
781 mask -= QRect(0, h - 5, 1, 1);
782 // handle left point
783 mask -= QRect(w - 40, h - 1, 1, 1);
784 // bottom left
785 mask -= QRect(0, h - 4, w - 40, 4);
786 } else {
787 // bottom left point
788 mask -= QRect(0, h - 1, 1, 1);
791 setMask(mask);
794 void B2Client::showEvent(QShowEvent *)
796 calcHiddenButtons();
797 positionButtons();
798 // TODO check if setting a flag and doing this during the paintEvent is a
799 // better approach.
800 doShape();
803 KDecoration::Position B2Client::mousePosition(const QPoint& p) const
805 const int range = 16;
806 QRect t = titlebar->geometry();
807 t.setHeight(buttonSize + 4 - thickness);
808 const int ly = t.bottom();
809 const int lx = t.right();
810 const int bb = mustDrawHandle() ? 0 : 5;
812 if (p.x() > lx) {
813 if (p.y() <= ly + range && p.x() >= width() - range)
814 return PositionTopRight;
815 else if (p.y() <= ly + thickness)
816 return PositionTop;
817 } else if (p.x() < bar_x_ofs) {
818 if (p.y() <= ly + range && p.x() <= range)
819 return PositionTopLeft;
820 else if (p.y() <= ly + thickness)
821 return PositionTop;
822 } else if (p.y() < ly) {
823 if (p.x() > bar_x_ofs + thickness &&
824 p.x() < lx - thickness && p.y() > thickness)
825 return KDecoration::mousePosition(p);
826 if (p.x() > bar_x_ofs + range && p.x() < lx - range)
827 return PositionTop;
828 if (p.y() <= range) {
829 if (p.x() <= bar_x_ofs + range)
830 return PositionTopLeft;
831 else return PositionTopRight;
832 } else {
833 if (p.x() <= bar_x_ofs + range)
834 return PositionLeft;
835 else return PositionRight;
839 if (p.y() >= height() - 8 + bb) {
840 /* the normal Client:: only wants border of 4 pixels */
841 if (p.x() <= range) return PositionBottomLeft;
842 if (p.x() >= width() - range) return PositionBottomRight;
843 return PositionBottom;
846 return KDecoration::mousePosition(p);
849 void B2Client::titleMoveAbs(int new_ofs)
851 if (new_ofs < 0) new_ofs = 0;
852 if (new_ofs > width() - titlebar->width()) {
853 new_ofs = width() - titlebar->width();
855 if (bar_x_ofs != new_ofs) {
856 bar_x_ofs = new_ofs;
857 positionButtons();
858 doShape();
859 widget()->repaint(0, 0, width(), buttonSize + 4);
860 titlebar->repaint();
864 void B2Client::titleMoveRel(int xdiff)
866 titleMoveAbs(bar_x_ofs + xdiff);
869 void B2Client::desktopChange()
871 bool on = isOnAllDesktops();
872 if (B2Button *b = button[BtnSticky]) {
873 b->setDown(on);
874 b->setToolTip(
875 on ? i18n("Not on all desktops") : i18n("On all desktops"));
879 void B2Client::maximizeChange()
881 bool m = maximizeMode() == MaximizeFull;
882 if (button[BtnMax]) {
883 button[BtnMax]->setPixmaps(m ? P_NORMALIZE : P_MAX);
884 button[BtnMax]->repaint();
885 button[BtnMax]->setToolTip(
886 m ? i18n("Restore") : i18n("Maximize"));
888 bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
889 QSizePolicy::Expanding, QSizePolicy::Minimum);
891 g->activate();
892 doShape();
893 widget()->repaint();
896 void B2Client::activeChange()
898 widget()->repaint();
899 titlebar->repaint();
901 QColor c = options()->palette(
902 KDecoration::ColorTitleBar, isActive()).color(QPalette::Active, QPalette::Button);
904 for (int i = 0; i < BtnCount; i++)
905 if (button[i]) {
906 button[i]->setBg(c);
907 button[i]->repaint();
911 void B2Client::shadeChange()
913 bottomSpacer->changeSize(10, thickness + (mustDrawHandle() ? 4 : 0),
914 QSizePolicy::Expanding, QSizePolicy::Minimum);
915 g->activate();
916 doShape();
917 if (B2Button *b = button[BtnShade]) {
918 b->setToolTip(isSetShade() ? i18n("Unshade") : i18n("Shade"));
922 QSize B2Client::minimumSize() const
924 int left, right, top, bottom;
925 borders(left, right, top, bottom);
926 return QSize(left + right + 2 * buttonSize, top + bottom);
929 void B2Client::resize(const QSize& s)
931 widget()->resize(s);
934 void B2Client::borders(int &left, int &right, int &top, int &bottom) const
936 left = right = thickness;
937 top = buttonSize + 4;
938 bottom = thickness + (mustDrawHandle() ? 4 : 0);
941 void B2Client::menuButtonPressed()
943 static B2Client *lastClient = NULL;
945 const bool dbl = (lastClient == this &&
946 time.elapsed() <= QApplication::doubleClickInterval());
947 lastClient = this;
948 time.start();
949 if (!dbl) {
950 KDecorationFactory* f = factory();
951 QRect menuRect = button[BtnMenu]->rect();
952 QPoint menuTop = button[BtnMenu]->mapToGlobal(menuRect.topLeft());
953 QPoint menuBottom = button[BtnMenu]->mapToGlobal(menuRect.bottomRight());
954 showWindowMenu(QRect(menuTop, menuBottom));
955 if (!f->exists(this)) // 'this' was destroyed
956 return;
957 button[BtnMenu]->setDown(false);
958 } else {
959 switch (menu_dbl_click_op) {
960 case B2::MinimizeOp:
961 minimize();
962 break;
963 case B2::ShadeOp:
964 setShade(!isSetShade());
965 break;
966 case B2::CloseOp:
967 closeWindow();
968 break;
969 case B2::NoOp:
970 default:
971 break;
976 void B2Client::unobscureTitlebar()
978 /* we just noticed, that we got obscured by other windows
979 so we look at all windows above us (stacking_order) merging their
980 masks, intersecting it with our titlebar area, and see if we can
981 find a place not covered by any window */
982 if (in_unobs) {
983 return;
985 in_unobs = 1;
986 QRegion reg(QRect(0, 0, width(), buttonSize + 4));
987 reg = unobscuredRegion(reg);
988 if (!reg.isEmpty()) {
989 // there is at least _one_ pixel from our title area, which is not
990 // obscured, we use the first rect we find
991 // for a first test, we use boundingRect(), later we may refine
992 // to rect(), and search for the nearest, or biggest, or smthg.
993 titleMoveAbs(reg.boundingRect().x());
995 in_unobs = 0;
998 static void redraw_pixmaps()
1000 QPalette aPal = options()->palette(KDecoration::ColorButtonBg, true);
1001 QPalette iPal = options()->palette(KDecoration::ColorButtonBg, false);
1003 QColor inactiveColor = iPal.color(QPalette::Button);
1004 QColor activeColor = aPal.color(QPalette::Button);
1006 // maximize
1007 for (int i = 0; i < NumStates; i++) {
1008 bool is_act = (i < 2);
1009 bool is_down = ((i & 1) == 1);
1010 QPixmap *pix = pixmap[P_MAX + i];
1011 QColor color = is_act ? activeColor : inactiveColor;
1012 drawB2Rect(pix, color, is_down);
1015 // shade
1016 QPixmap thinBox(buttonSize - 2, 6);
1017 for (int i = 0; i < NumStates; i++) {
1018 bool is_act = (i < 2);
1019 bool is_down = ((i & 1) == 1);
1020 QPixmap *pix = pixmap[P_SHADE + i];
1021 QColor color = is_act ? activeColor : inactiveColor;
1022 drawB2Rect(&thinBox, color, is_down);
1023 pix->fill(Qt::black);
1024 bitBlt(pix, 0, 0, &thinBox,
1025 0, 0, thinBox.width(), thinBox.height());
1028 // normalize + iconify
1029 QPixmap smallBox(10, 10);
1030 QPixmap largeBox(12, 12);
1032 for (int i = 0; i < NumStates; i++) {
1033 bool is_act = (i < 3);
1034 bool is_down = (i == Down || i == IDown);
1035 QPixmap *pix = pixmap[P_NORMALIZE + i];
1036 QColor color = is_act ? activeColor : inactiveColor;
1037 drawB2Rect(&smallBox, color, is_down);
1038 drawB2Rect(&largeBox, color, is_down);
1039 pix->fill(options()->color(KDecoration::ColorTitleBar, is_act));
1040 bitBlt(pix, pix->width() - 12, pix->width() - 12, &largeBox,
1041 0, 0, 12, 12);
1042 bitBlt(pix, 0, 0, &smallBox, 0, 0, 10, 10);
1044 bitBlt(pixmap[P_ICONIFY + i], 0, 0,
1045 &smallBox, 0, 0, 10, 10);
1048 // resize
1049 for (int i = 0; i < NumStates; i++) {
1050 bool is_act = (i < 3);
1051 bool is_down = (i == Down || i == IDown);
1052 *pixmap[P_RESIZE + i] = *pixmap[P_MAX + i];
1053 pixmap[P_RESIZE + i]->detach();
1054 drawB2Rect(&smallBox, is_act ? activeColor : inactiveColor, is_down);
1055 bitBlt(pixmap[P_RESIZE + i], 0, 0, &smallBox, 0, 0, 10, 10);
1058 QPainter p;
1059 // close: copy the maximize image, then add the X
1060 for (int i = 0; i < NumStates; i++) {
1061 *pixmap[P_CLOSE + i] = *pixmap[P_MAX + i];
1062 pixmap[P_CLOSE + i]->detach();
1065 for (int i = 0; i < NumStates; i++) {
1066 bool isAct = (i < 3);
1067 QPixmap *pixm = pixmap[P_CLOSE + i];
1068 p.begin(pixm);
1069 QColor color = isAct ? activeColor : inactiveColor;
1070 QRect r = QRect(3, 3, pixm->width() - 6, pixm->height() - 6);
1071 for (int j = 0; j < 2; j++) {
1072 r.moveTo(j + 3, 3);
1073 p.setPen(j == 0 ? color.light(150) : color.dark(150));
1074 p.drawLine(r.left(), r.top(), r.right() - 1, r.bottom() - 1);
1075 p.drawLine(r.left(), r.top() + 1, r.right() - 1, r.bottom());
1076 p.drawLine(r.right() - 1, r.top(), r.left(), r.bottom() - 1);
1077 p.drawLine(r.right() - 1, r.top() + 1, r.left(), r.bottom());
1079 p.end();
1081 for (int i = 0; i < 2; i++) {
1085 // menu
1087 int off = (pixmap[P_MENU]->width() - 16) / 2;
1088 QSize bSize(16, 16);
1089 QBitmap lightBitmap = QBitmap::fromData(bSize,
1090 menu_white_bits, QImage::Format_MonoLSB);
1091 //lightBitmap.setMask(lightBitmap);
1092 QBitmap darkBitmap = QBitmap::fromData(bSize,
1093 menu_dgray_bits, QImage::Format_MonoLSB);
1094 //darkBitmap.setMask(darkBitmap);
1096 for (int i = 0; i < NumStates; i++) {
1097 bool isAct = (i < 3);
1098 QPixmap *pixm = pixmap[P_MENU + i];
1099 p.begin(pixm);
1100 QColor color = isAct ? activeColor : inactiveColor;
1101 p.setPen(color.light(150));
1102 p.drawPixmap(off, off, lightBitmap);
1103 p.setPen(color.dark(150));
1104 p.drawPixmap(off, off, darkBitmap);
1105 p.end();
1109 // Help button: a question mark.
1111 QFont font = options()->font(true);
1112 font.setWeight(QFont::Black);
1113 font.setStretch(110);
1114 font.setPointSizeF(font.pointSizeF() * 1.1);
1115 for (int i = 0; i < NumStates; i++) {
1116 bool isAct = (i < 3);
1117 QPixmap *pixm = pixmap[P_HELP + i];
1118 pixm->fill(QColor(qRgba(0, 0, 0, 0)));
1119 pixm->setAlphaChannel(QPixmap(*pixm));
1120 p.begin(pixm);
1121 QColor color = isAct ? activeColor : inactiveColor;
1122 QRect r = QRect(0, 0, pixm->width(), pixm->height());
1123 p.setFont(font);
1124 QString label = i18nc("Help button label, one character", "?");
1125 r.moveTo(1, 2);
1126 p.setPen(color.light(150));
1127 p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, label);
1128 r.moveTo(0, 1);
1129 p.setPen(color.dark(150));
1130 p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, label);
1131 p.end();
1135 // Help button: a question mark.
1136 // pin
1137 for (int i = 0; i < NumStates; i++) {
1138 const bool isDown = (i == Down || i == IDown);
1139 bool isAct = (i < 3);
1141 QSize pinSize(16, 16);
1142 QBitmap white = QBitmap::fromData(pinSize,
1143 isDown ? pindown_white_bits : pinup_white_bits,
1144 QImage::Format_MonoLSB);
1145 QBitmap gray = QBitmap::fromData(pinSize,
1146 isDown ? pindown_gray_bits : pinup_gray_bits,
1147 QImage::Format_MonoLSB);
1148 QBitmap dgray = QBitmap::fromData(pinSize,
1149 isDown ? pindown_dgray_bits : pinup_dgray_bits,
1150 QImage::Format_MonoLSB);
1152 QPixmap *pix = pixmap[P_PINUP + i];
1153 QColor color = isAct ? activeColor : inactiveColor;
1154 QPoint origin(0, 0);
1155 QImage pin(16, 16, QImage::Format_ARGB32_Premultiplied);
1156 p.begin(&pin);
1157 p.setPen(color.light(150));
1158 p.drawPixmap(origin, white);
1159 p.setPen(color);
1160 p.drawPixmap(origin, gray);
1161 p.setPen(color.dark(150));
1162 p.drawPixmap(origin, dgray);
1163 p.end();
1164 *pix = QPixmap::fromImage(pin);
1167 // Apply the hilight effect to the 'Hover' icons
1168 KIconEffect ie;
1169 QPixmap hilighted;
1170 for (int i = 0; i < P_NUM_PIXMAPS; i += NumStates) {
1171 hilighted = ie.apply(*pixmap[i + Norm],
1172 KIconLoader::Small, KIconLoader::ActiveState);
1173 *pixmap[i + Hover] = hilighted;
1175 hilighted = ie.apply(*pixmap[i + INorm],
1176 KIconLoader::Small, KIconLoader::ActiveState);
1177 *pixmap[i + IHover] = hilighted;
1180 // Create the titlebar gradients
1181 if (QPixmap::defaultDepth() > 8) {
1182 QColor titleColor[4] = {
1183 options()->color(KDecoration::ColorTitleBar, true),
1184 options()->color(KDecoration::ColorFrame, true),
1186 options()->color(KDecoration::ColorTitleBlend, false),
1187 options()->color(KDecoration::ColorTitleBar, false)
1190 if (colored_frame) {
1191 titleColor[0] = options()->color(KDecoration::ColorTitleBlend, true);
1192 titleColor[1] = options()->color(KDecoration::ColorTitleBar, true);
1195 for (int i = 0; i < 2; i++) {
1196 if (titleColor[2 * i] != titleColor[2 * i + 1]) {
1197 if (!titleGradient[i]) {
1198 titleGradient[i] = new QPixmap;
1200 const int titleHeight = buttonSize + 3;
1201 *titleGradient[i] = QPixmap(64, titleHeight);
1203 QPainter p(titleGradient[i]);
1204 QLinearGradient gradient(0, 0, 0, titleHeight);
1205 gradient.setColorAt(0.0, titleColor[2 * i]);
1206 gradient.setColorAt(1.0, titleColor[2 * i + 1]);
1207 QBrush brush(gradient);
1208 p.fillRect(0, 0, 64, titleHeight, brush);
1209 } else {
1210 delete titleGradient[i];
1211 titleGradient[i] = 0;
1217 void B2Client::positionButtons()
1219 QFontMetrics fm(options()->font(isActive()));
1220 QString cap = caption();
1221 if (cap.length() < 5) // make sure the titlebar has sufficiently wide
1222 cap = "XXXXX"; // area for dragging the window
1223 int textLen = fm.width(cap);
1225 QRect t = titlebar->captionSpacer->geometry();
1226 int titleWidth = titlebar->width() - t.width() + textLen + 2;
1227 if (titleWidth > width()) titleWidth = width();
1229 titlebar->resize(titleWidth, buttonSize + 4);
1230 titlebar->move(bar_x_ofs, 0);
1233 // Transparent bound stuff.
1234 static QRect *visible_bound;
1235 static QPolygon bound_shape;
1238 bool B2Client::drawbound(const QRect& geom, bool clear)
1240 // Let kwin draw the bounds, for now.
1241 return false;
1242 #if 0
1243 if (clear) {
1244 if (!visible_bound) return true;
1247 if (!visible_bound) {
1248 visible_bound = new QRect(geom);
1249 QRect t = titlebar->geometry();
1250 QRect g = geom;
1251 // line width is 5 pixels, so compensate for the 2 outer pixels (#88657)
1252 g.adjust(2, 2, -2, -2);
1253 int frameTop = geom.top() + t.bottom() + 2;
1254 int barLeft = geom.left() + bar_x_ofs + 2;
1255 int barRight = barLeft + t.width() - 1;
1256 if (barRight > geom.right()) barRight = geom.right();
1257 barRight -= 2;
1259 bound_shape.putPoints(0, 8,
1260 g.left(), frameTop,
1261 barLeft, frameTop,
1262 barLeft, g.top(),
1263 barRight, g.top(),
1264 barRight, frameTop,
1265 g.right(), frameTop,
1266 g.right(), g.bottom(),
1267 g.left(), g.bottom());
1268 } else {
1269 *visible_bound = geom;
1271 QPainter p;
1272 if (p.begin(workspaceWidget())) {
1273 p.setPen(QPen(Qt::white, 5));
1274 p.setCompositionMode(QPainter::CompositionMode_Xor);
1275 p.drawPolygon(bound_shape);
1276 if (clear) {
1277 delete visible_bound;
1278 visible_bound = 0;
1280 p.end();
1282 #endif
1283 return true;
1286 bool B2Client::eventFilter(QObject *o, QEvent *e)
1288 if (o != widget())
1289 return false;
1290 switch (e->type()) {
1291 case QEvent::Resize:
1292 resizeEvent(static_cast< QResizeEvent* >(e));
1293 return true;
1294 case QEvent::Paint:
1295 paintEvent(static_cast< QPaintEvent* >(e));
1296 return true;
1297 case QEvent::MouseButtonDblClick:
1298 titlebar->mouseDoubleClickEvent(static_cast< QMouseEvent* >(e));
1299 return true;
1300 case QEvent::Wheel:
1301 titlebar->wheelEvent(static_cast< QWheelEvent* >(e));
1302 return true;
1303 case QEvent::MouseButtonPress:
1304 processMousePressEvent(static_cast< QMouseEvent* >(e));
1305 return true;
1306 case QEvent::Show:
1307 showEvent(static_cast< QShowEvent* >(e));
1308 return true;
1309 default:
1310 break;
1312 return false;
1315 // =====================================
1317 B2Button::B2Button(B2Client *_client, QWidget *parent,
1318 const QString& tip, const int realizeBtns)
1319 : QAbstractButton(parent), hover(false)
1321 setAttribute(Qt::WA_NoSystemBackground);
1322 setCursor(Qt::ArrowCursor);
1323 realizeButtons = realizeBtns;
1324 client = _client;
1325 useMiniIcon = false;
1326 setFixedSize(buttonSize, buttonSize);
1327 this->setToolTip(tip);
1331 QSize B2Button::sizeHint() const
1333 return QSize(buttonSize, buttonSize);
1336 QSizePolicy B2Button::sizePolicy() const
1338 return(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
1341 void B2Button::paintEvent(QPaintEvent *)
1343 QPainter p(this);
1344 QPixmap* gradient = titleGradient[client->isActive() ? 0 : 1];
1345 if (gradient) {
1346 p.drawTiledPixmap(0, 0, buttonSize, buttonSize, *gradient, 0, 2);
1347 } else {
1348 p.fillRect(rect(), bg);
1350 if (useMiniIcon) {
1351 QPixmap miniIcon = client->icon().pixmap(
1352 style()->pixelMetric(QStyle::PM_SmallIconSize),
1353 client->isActive() ? QIcon::Normal : QIcon::Disabled);
1354 p.drawPixmap(1 + (width() - miniIcon.width()) / 2,
1355 (height() - miniIcon.height()) / 2, miniIcon);
1356 } else {
1357 int type;
1358 if (client->isActive()) {
1359 if (isChecked() || isDown())
1360 type = Down;
1361 else if (hover)
1362 type = Hover;
1363 else
1364 type = Norm;
1365 } else {
1366 if (isChecked() || isDown())
1367 type = IDown;
1368 else if (hover)
1369 type = IHover;
1370 else
1371 type = INorm;
1373 p.drawPixmap(1 + (width() - icon[type]->width()) / 2,
1374 (height() - icon[type]->height()) / 2, *icon[type]);
1378 void B2Button::setPixmaps(int button_id)
1380 for (int i = 0; i < NumStates; i++) {
1381 icon[i] = B2::pixmap[button_id + i];
1383 repaint();
1386 void B2Button::mousePressEvent(QMouseEvent * e)
1388 last_button = e->button();
1389 QMouseEvent me(e->type(), e->pos(), e->globalPos(),
1390 (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
1391 (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
1392 e->modifiers());
1393 QAbstractButton::mousePressEvent(&me);
1396 void B2Button::mouseReleaseEvent(QMouseEvent * e)
1398 last_button = e->button();
1399 QMouseEvent me(e->type(), e->pos(), e->globalPos(),
1400 (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
1401 (e->button() & realizeButtons) ? Qt::LeftButton : Qt::NoButton,
1402 e->modifiers());
1403 QAbstractButton::mouseReleaseEvent(&me);
1406 void B2Button::enterEvent(QEvent *e)
1408 hover = true;
1409 repaint();
1410 QAbstractButton::enterEvent(e);
1413 void B2Button::leaveEvent(QEvent *e)
1415 hover = false;
1416 repaint();
1417 QAbstractButton::leaveEvent(e);
1420 // =====================================
1422 B2Titlebar::B2Titlebar(B2Client *parent)
1423 : QWidget(parent->widget(), Qt::WStyle_Customize | Qt::WNoAutoErase),
1424 client(parent),
1425 set_x11mask(false), isfullyobscured(false), shift_move(false)
1427 setAttribute(Qt::WA_NoSystemBackground);
1428 captionSpacer = new QSpacerItem(buttonSize, buttonSize + 3,
1429 QSizePolicy::Expanding, QSizePolicy::Fixed);
1432 bool B2Titlebar::x11Event(XEvent *e)
1434 if (!set_x11mask) {
1435 set_x11mask = true;
1436 XSelectInput(QX11Info::display(), winId(),
1437 KeyPressMask | KeyReleaseMask |
1438 ButtonPressMask | ButtonReleaseMask |
1439 KeymapStateMask |
1440 ButtonMotionMask |
1441 EnterWindowMask | LeaveWindowMask |
1442 FocusChangeMask |
1443 ExposureMask |
1444 PropertyChangeMask |
1445 StructureNotifyMask | SubstructureRedirectMask |
1446 VisibilityChangeMask);
1448 switch (e->type) {
1449 case VisibilityNotify:
1450 isfullyobscured = false;
1451 if (e->xvisibility.state == VisibilityFullyObscured) {
1452 isfullyobscured = true;
1453 client->unobscureTitlebar();
1455 break;
1456 default:
1457 break;
1459 return QWidget::x11Event(e);
1462 void B2Titlebar::drawTitlebar(QPainter &p, bool state)
1464 QPixmap* gradient = titleGradient[state ? 0 : 1];
1466 QRect t = rect();
1467 // black titlebar frame
1468 p.setPen(Qt::black);
1469 p.drawLine(0, 0, 0, t.bottom());
1470 p.drawLine(0, 0, t.right(), 0);
1471 p.drawLine(t.right(), 0, t.right(), t.bottom());
1473 // titlebar fill
1474 const QPalette cg = options()->palette(KDecoration::ColorTitleBar, state);
1475 QBrush brush(cg.background());
1476 if (gradient) brush.setTexture(*gradient);
1477 qDrawShadeRect(&p, 1, 1, t.right() - 1, t.height() - 1,
1478 cg, false, 1, 0, &brush);
1480 // and the caption
1481 p.setPen(options()->color(KDecoration::ColorFont, state));
1482 p.setFont(options()->font(state));
1483 t = captionSpacer->geometry();
1484 p.drawText(t.translated(0, 1), Qt::AlignCenter | Qt::AlignVCenter, client->caption());
1487 void B2Titlebar::recalcBuffer()
1489 titleBuffer = QPixmap(width(), height());
1491 QPainter p(&titleBuffer);
1492 drawTitlebar(p, true);
1493 oldTitle = windowTitle();
1496 void B2Titlebar::resizeEvent(QResizeEvent *)
1498 recalcBuffer();
1499 repaint();
1503 void B2Titlebar::paintEvent(QPaintEvent *)
1505 if (client->isActive())
1506 bitBlt(this, 0, 0, &titleBuffer, 0, 0, titleBuffer.width(),
1507 titleBuffer.height());
1508 else {
1509 QPainter p(this);
1510 drawTitlebar(p, false);
1514 void B2Titlebar::mouseDoubleClickEvent(QMouseEvent *e)
1516 if (e->button() == Qt::LeftButton && e->y() < height()) {
1517 client->titlebarDblClickOperation();
1521 void B2Titlebar::wheelEvent(QWheelEvent *e)
1523 if (client->isSetShade() || rect().contains(e->pos()))
1524 client->titlebarMouseWheelOperation(e->delta());
1527 void B2Titlebar::mousePressEvent(QMouseEvent * e)
1529 shift_move = e->modifiers() & Qt::ShiftModifier;
1530 if (shift_move) {
1531 moveOffset = e->globalPos();
1532 } else {
1533 e->ignore();
1537 void B2Titlebar::mouseReleaseEvent(QMouseEvent * e)
1539 if (shift_move) shift_move = false;
1540 else e->ignore();
1543 void B2Titlebar::mouseMoveEvent(QMouseEvent * e)
1545 if (shift_move) {
1546 int oldx = mapFromGlobal(moveOffset).x();
1547 int xdiff = e->globalPos().x() - moveOffset.x();
1548 moveOffset = e->globalPos();
1549 if (oldx >= 0 && oldx <= rect().right()) {
1550 client->titleMoveRel(xdiff);
1552 } else {
1553 e->ignore();
1557 } // namespace B2
1559 #include "b2client.moc"
1561 // vim: sw=4 ts=8
1563 #endif // CLIENTS/B2/B2CLIENT.CPP