add more spacing
[personal-kdebase.git] / workspace / kwin / clients / ozone / oxygenclient.cpp
blob39e9d5262840cf2a730471c8ffed3db6c162bf96
1 //////////////////////////////////////////////////////////////////////////////
2 // oxygenclient.cpp
3 // -------------------
4 // Ozone window decoration for KDE
5 // -------------------
6 // Copyright (c) 2006, 2007 Casper Boemann <cbr@boemann.dk>
7 // Copyright (c) 2006, 2007 Riccardo Iaconelli <riccardo@kde.org>
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to
11 // deal in the Software without restriction, including without limitation the
12 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13 // sell copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 // IN THE SOFTWARE.
26 //////////////////////////////////////////////////////////////////////////////
27 // #ifndef OXYGENCLIENT_H
28 // #define OXYGENCLIENT_H
30 #include "oxygenclient.h"
32 #include <KConfig>
33 #include <KGlobal>
34 #include <KLocale>
35 #include <KDebug>
36 #include <KColorUtils>
38 #include <QBitmap>
39 #include <QLabel>
40 #include <QLayout>
41 #include <QPainter>
42 #include <QToolTip>
43 //Added by qt3to4:
44 #include <QBoxLayout>
45 #include <QGridLayout>
46 #include <QResizeEvent>
47 #include <QMouseEvent>
48 #include <QEvent>
49 #include <QShowEvent>
50 #include <QPaintEvent>
51 #include <QPainterPath>
52 #include <QTimer>
53 #include <QCache>
54 #include <QtGui/QApplication>
56 #include "math.h"
58 #include "oxygenclient.moc"
59 #include "oxygenbutton.h"
60 #include "oxygen.h"
62 namespace Ozone
64 namespace Oxygen
67 K_GLOBAL_STATIC_WITH_ARGS(OxygenHelper, globalHelper, ("OxygenDeco"))
69 OxygenHelper *oxygenHelper()
71 return globalHelper;
74 static void oxkwincleanupBefore()
76 OxygenHelper *h = globalHelper;
77 h->invalidateCaches();
80 void renderDot(QPainter *p, const QPointF &point, qreal diameter)
82 p->drawEllipse(QRectF(point.x()-diameter/2, point.y()-diameter/2, diameter, diameter));
86 OxygenClient::OxygenClient(KDecorationBridge *b, KDecorationFactory *f)
87 : KCommonDecorationUnstable(b, f)
88 , colorCacheInvalid_(true)
89 , helper_(*globalHelper)
91 qAddPostRoutine(oxkwincleanupBefore);
94 OxygenClient::~OxygenClient()
97 QString OxygenClient::visibleName() const
99 return i18n("Ozone");
102 void OxygenClient::init()
104 KCommonDecoration::init();
106 widget()->setAutoFillBackground(false);
107 widget()->setAttribute(Qt::WA_OpaquePaintEvent);
110 bool OxygenClient::decorationBehaviour(DecorationBehaviour behaviour) const
112 switch (behaviour) {
113 case DB_MenuClose:
114 return true;//Handler()->menuClose();
116 case DB_WindowMask:
117 return false;
119 default:
120 return KCommonDecoration::decorationBehaviour(behaviour);
124 int OxygenClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const
126 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
128 switch (lm) {
129 case LM_BorderLeft:
130 case LM_BorderRight:
131 case LM_BorderBottom:
133 if (respectWindowState && maximized) {
134 return 0;
135 } else {
136 return BFRAMESIZE;
140 case LM_TitleEdgeTop:
142 if (respectWindowState && maximized) {
143 return 0;
144 } else {
145 return TFRAMESIZE;
149 case LM_TitleEdgeBottom:
151 return 0;
154 case LM_TitleEdgeLeft:
155 case LM_TitleEdgeRight:
157 if (respectWindowState && maximized) {
158 return 0;
159 } else {
160 return 6;
164 case LM_TitleBorderLeft:
165 case LM_TitleBorderRight:
166 return 5;
168 case LM_ButtonWidth:
169 case LM_ButtonHeight:
170 case LM_TitleHeight:
172 if (respectWindowState && isToolWindow()) {
173 return OXYGEN_BUTTONSIZE;
174 } else {
175 return OXYGEN_BUTTONSIZE;
179 case LM_ButtonSpacing:
180 return 1;
182 case LM_ButtonMarginTop:
183 return 0;
185 default:
186 return KCommonDecoration::layoutMetric(lm, respectWindowState, btn);
191 KCommonDecorationButton *OxygenClient::createButton(::ButtonType type)
193 switch (type) {
194 case MenuButton:
195 return new OxygenButton(*this, i18n("Menu"), ButtonMenu);
197 case HelpButton:
198 return new OxygenButton(*this, i18n("Help"), ButtonHelp);
200 case MinButton:
201 return new OxygenButton(*this, i18n("Minimize"), ButtonMin);
203 case MaxButton:
204 return new OxygenButton(*this, i18n("Maximize"), ButtonMax);
206 case CloseButton:
207 return new OxygenButton(*this, i18n("Close"), ButtonClose);
209 case AboveButton:
210 return new OxygenButton(*this, i18n("Keep Above Others"), ButtonAbove);
212 case BelowButton:
213 return new OxygenButton(*this, i18n("Keep Below Others"), ButtonBelow);
215 case OnAllDesktopsButton:
216 return new OxygenButton(*this, i18n("On All Desktops"), ButtonSticky);
218 case ShadeButton:
219 return new OxygenButton(*this, i18n("Shade Button"), ButtonShade);
221 default:
222 return 0;
227 // c0 - background
228 // c1 - foreground
229 // t - target contrast ratio
230 QColor reduceContrast(const QColor &c0, const QColor &c1, double t)
232 double s = KColorUtils::contrastRatio(c0, c1);
233 if (s < t)
234 return c1;
236 double l = 0.0, h = 1.0;
237 double x = s, a;
238 QColor r = c1;
239 for (int maxiter = 16; maxiter; --maxiter) {
240 a = 0.5 * (l + h);
241 r = KColorUtils::mix(c0, c1, a);
242 x = KColorUtils::contrastRatio(c0, r);
243 if (fabs(x - t) < 0.01)
244 break;
245 if (x > t)
246 h = a;
247 else
248 l = a;
250 return r;
254 QColor OxygenClient::titlebarTextColor(const QPalette &palette)
256 if( !OxygenFactory::blendTitlebarColors())
257 return options()->color(ColorFont, isActive());
258 if (isActive())
259 return palette.color(QPalette::Active, QPalette::WindowText);
260 else {
261 if(colorCacheInvalid_) {
262 QColor ab = palette.color(QPalette::Active, QPalette::Window);
263 QColor af = palette.color(QPalette::Active, QPalette::WindowText);
264 QColor nb = palette.color(QPalette::Inactive, QPalette::Window);
265 QColor nf = palette.color(QPalette::Inactive, QPalette::WindowText);
267 colorCacheInvalid_ = false;
268 cachedTitlebarTextColor_ = reduceContrast(nb, nf, qMax(qreal(2.5), KColorUtils::contrastRatio(ab, KColorUtils::mix(ab, af, 0.4))));
270 return cachedTitlebarTextColor_;
275 void OxygenClient::paintEvent(QPaintEvent *e)
277 Q_UNUSED(e)
278 if (!OxygenFactory::initialized()) return;
280 updateButtons();
282 QPalette palette = widget()->palette();
283 QPainter painter(widget());
285 // Set palette to the right group.
286 // TODO - fix KWin to do this for us :-).
287 if (isActive())
288 palette.setCurrentColorGroup(QPalette::Active);
289 else
290 palette.setCurrentColorGroup(QPalette::Inactive);
292 int x,y,w,h;
293 QRect frame = widget()->frameGeometry();
294 QColor color = OxygenFactory::blendTitlebarColors() ? palette.window().color() : options()->color( ColorTitleBar, isActive());
295 QColor light = helper_.calcLightColor( color );
296 QColor dark = helper_.calcDarkColor( color );
298 const int titleHeight = layoutMetric(LM_TitleHeight);
299 const int titleTop = layoutMetric(LM_TitleEdgeTop) + frame.top();
300 const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft);
301 const int marginLeft = layoutMetric(LM_TitleBorderLeft);
302 const int marginRight = layoutMetric(LM_TitleBorderRight);
304 const int titleLeft = frame.left() + titleEdgeLeft + buttonsLeftWidth() + marginLeft;
305 const int titleWidth = frame.width() -
306 titleEdgeLeft - layoutMetric(LM_TitleEdgeRight) -
307 buttonsLeftWidth() - buttonsRightWidth() -
308 marginLeft - marginRight;
310 QPalette pal2( palette );
311 if( !OxygenFactory::blendTitlebarColors()) {
312 pal2.setColor( QPalette::Window, options()->color(
313 KDecorationDefines::ColorTitleBar, isActive()));
315 // draw window background
316 helper_.renderWindowBackground(&painter, frame, this->widget(), pal2, 0);
318 // draw title text
319 painter.setFont(options()->font(isActive(), false));
320 painter.setPen(titlebarTextColor(pal2));
321 painter.drawText(titleLeft, titleTop-1, titleWidth, titleHeight, // -1 is to go into top resizearea
322 OxygenFactory::titleAlignment() | Qt::AlignVCenter, caption());
324 painter.setRenderHint(QPainter::Antialiasing);
326 // Draw dividing line
327 frame = widget()->rect();
328 if (shadowsActive()) {
329 frame.adjust(-1,-1,1,1);
331 frame.getRect(&x, &y, &w, &h);
333 if(isActive()) {
334 helper_.drawSeparator(&painter, QRect(x, titleTop+titleHeight-1.5, w, 2), color, Qt::Horizontal);
337 // draw stripes as indicator for active windows
338 if (isActive() && OxygenFactory::showStripes()) {
339 Qt::Alignment align = OxygenFactory::titleAlignment();
340 if (widget()->layoutDirection() == Qt::RightToLeft)
342 if (align == Qt::AlignLeft)
343 align = Qt::AlignRight;
344 else if (align == Qt::AlignRight)
345 align = Qt::AlignLeft;
348 if (align & Qt::AlignLeft) {
349 int left = titleLeft + QFontMetrics(options()->font(isActive(), false)).width(caption()) + 4;
350 int right = titleLeft + titleWidth;
351 drawScratch(&painter, palette, left, right, titleTop+6);
353 if (align & Qt::AlignRight) {
354 int left = titleLeft;
355 int right = titleLeft + titleWidth - QFontMetrics(options()->font(isActive(), false)).width(caption()) - 4;
356 drawScratch(&painter, palette, right, left, titleTop+6);
358 if (align & Qt::AlignHCenter) {
359 int textWidth = QFontMetrics(options()->font(isActive(), false)).width(caption());
360 int left = titleLeft;
361 int centerLeft = titleLeft + titleWidth/2 - textWidth/2 - 4;
362 int centerRight = titleLeft + titleWidth/2 + textWidth/2 + 4;
363 int right = titleLeft + titleWidth;
364 drawScratch(&painter, palette, centerLeft, left, titleTop+6);
365 drawScratch(&painter, palette, centerRight, right, titleTop+6);
369 // Draw shadows of the frame
370 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
372 if(maximized)
373 return;
375 helper_.drawFloatFrame(&painter, frame, color, !shadowsActive(), isActive(),
376 KDecoration::options()->color(ColorTitleBar));
378 if(!isResizable())
379 return;
381 // Draw the 3-dots resize handles
382 qreal cenY = frame.height() / 2 + x + 0.5;
383 qreal posX = frame.width() + y - 2.5;
384 painter.setPen(Qt::NoPen);
385 painter.setBrush(QColor(0, 0, 0, 66));
386 renderDot(&painter, QPointF(posX, cenY - 3), 1.8);
387 renderDot(&painter, QPointF(posX, cenY), 1.8);
388 renderDot(&painter, QPointF(posX, cenY + 3), 1.8);
390 painter.translate(x + frame.width()-9, y + frame.height()-9);
391 renderDot(&painter, QPointF(2.5, 6.5), 1.8);
392 renderDot(&painter, QPointF(5.5, 5.5), 1.8);
393 renderDot(&painter, QPointF(6.5, 2.5), 1.8);
396 void OxygenClient::drawScratch(QPainter *p, QPalette &palette, const int start, const int end, const int topMargin)
398 QLinearGradient scratchlg(QPoint(start,0), QPoint(end,0));
399 scratchlg.setColorAt(0.0, Qt::transparent);
400 scratchlg.setColorAt(0.05, KDecoration::options()->color(ColorTitleBar));
401 scratchlg.setColorAt(1.0, Qt::transparent);
402 QPen pen1(scratchlg, 0.5);
404 QLinearGradient scratchlg2(QPoint(start,0), QPoint(end,0));
405 scratchlg2.setColorAt(0.0, Qt::transparent);
406 scratchlg2.setColorAt(0.05, helper_.calcLightColor(palette.color(QPalette::Window)));
407 scratchlg2.setColorAt(1.0, Qt::transparent);
408 QPen pen2(scratchlg2, 0.5);
410 bool antialiasing = p->testRenderHint(QPainter::Antialiasing);
411 p->setRenderHint(QPainter::Antialiasing, false);
412 for (int i = 0; i < 3; ++i)
414 p->setPen(pen1);
415 p->drawLine(QPointF(start, topMargin+4*i), QPointF(end, topMargin+4*i));
416 p->setPen(pen2);
417 p->drawLine(QPointF(start, topMargin+4*i+1), QPointF(end, topMargin+4*i+1));
419 p->setRenderHint(QPainter::Antialiasing, antialiasing);
423 void OxygenClient::updateWindowShape()
425 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
426 int w=widget()->width();
427 int h=widget()->height();
429 if(maximized) {
430 QRegion mask(0,0,w,h);
431 setMask(mask);
432 return;
435 if (!shadowsActive()) {
436 QRegion mask(4, 0, w-8, h);
437 mask += QRegion(0, 4, w, h-8);
438 mask += QRegion(2, 1, w-4, h-2);
439 mask += QRegion(1, 2, w-2, h-4);
441 setMask(mask);
443 else {
444 QRegion mask(5, 0, w-10, h-0);
445 mask += QRegion(0, 5, w-0, h-10);
446 mask += QRegion(2, 2, w-4, h-4);
447 mask += QRegion(3, 1, w-6, h-2);
448 mask += QRegion(1, 3, w-2, h-6);
450 setMask(mask);
454 QList<QRect> OxygenClient::shadowQuads( ShadowType type ) const
456 Q_UNUSED(type)
458 QSize size = widget()->size();
459 int outside=21, underlap=4, cornersize=25;
460 // These are underlap under the decoration so the corners look nicer 10px on the outside
461 QList<QRect> quads;
462 quads.append(QRect(-outside, size.height()-underlap, cornersize, cornersize));
463 quads.append(QRect(underlap, size.height()-underlap, size.width()-2*underlap, cornersize));
464 quads.append(QRect(size.width()-underlap, size.height()-underlap, cornersize, cornersize));
465 quads.append(QRect(-outside, underlap, cornersize, size.height()-2*underlap));
466 quads.append(QRect(size.width()-underlap, underlap, cornersize, size.height()-2*underlap));
467 quads.append(QRect(-outside, -outside, cornersize, cornersize));
468 quads.append(QRect(underlap, -outside, size.width()-2*underlap, cornersize));
469 quads.append(QRect(size.width()-underlap, -outside, cornersize, cornersize));
470 return quads;
473 double OxygenClient::shadowOpacity( ShadowType type ) const
475 switch( type ) {
476 case ShadowBorderedActive:
477 if( isActive() )
478 return 1.0;
479 return 0.0;
480 case ShadowBorderedInactive:
481 if( isActive() )
482 return 0.0;
483 return 1.0;
484 default:
485 abort(); // Should never be reached
487 return 0;
490 } //namespace Oxygen
491 } //namespace Ozone
493 //#include "oxygenclient.moc"
495 // #endif