not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / clients / oxygen / oxygenclient.cpp
blobbd24dc422a2c88f4375d14d2c83814c0b32ca494
1 //////////////////////////////////////////////////////////////////////////////
2 // oxygenclient.cpp
3 // -------------------
4 // Oxygen 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 Oxygen
65 K_GLOBAL_STATIC_WITH_ARGS(OxygenHelper, globalHelper, ("OxygenDeco"))
67 OxygenHelper *oxygenHelper()
69 return globalHelper;
72 static void oxkwincleanupBefore()
74 OxygenHelper *h = globalHelper;
75 h->invalidateCaches();
78 void renderDot(QPainter *p, const QPointF &point, qreal diameter)
80 p->drawEllipse(QRectF(point.x()-diameter/2, point.y()-diameter/2, diameter, diameter));
84 OxygenClient::OxygenClient(KDecorationBridge *b, KDecorationFactory *f)
85 : KCommonDecorationUnstable(b, f)
86 , colorCacheInvalid_(true)
87 , helper_(*globalHelper)
89 qAddPostRoutine(oxkwincleanupBefore);
92 OxygenClient::~OxygenClient()
95 QString OxygenClient::visibleName() const
97 return i18n("Oxygen");
100 void OxygenClient::init()
102 KCommonDecoration::init();
104 widget()->setAutoFillBackground(false);
105 widget()->setAttribute(Qt::WA_OpaquePaintEvent);
108 bool OxygenClient::decorationBehaviour(DecorationBehaviour behaviour) const
110 switch (behaviour) {
111 case DB_MenuClose:
112 return true;//Handler()->menuClose();
114 case DB_WindowMask:
115 return false;
117 default:
118 return KCommonDecoration::decorationBehaviour(behaviour);
122 int OxygenClient::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const
124 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
126 switch (lm) {
127 case LM_BorderLeft:
128 case LM_BorderRight:
129 case LM_BorderBottom:
131 if (respectWindowState && maximized) {
132 return 0;
133 } else {
134 return BFRAMESIZE;
138 case LM_TitleEdgeTop:
140 if (respectWindowState && maximized) {
141 return 0;
142 } else {
143 return TFRAMESIZE;
147 case LM_TitleEdgeBottom:
149 return 0;
152 case LM_TitleEdgeLeft:
153 case LM_TitleEdgeRight:
155 if (respectWindowState && maximized) {
156 return 0;
157 } else {
158 return 6;
162 case LM_TitleBorderLeft:
163 case LM_TitleBorderRight:
164 return 5;
166 case LM_ButtonWidth:
167 case LM_ButtonHeight:
168 case LM_TitleHeight:
170 if (respectWindowState && isToolWindow()) {
171 return OXYGEN_BUTTONSIZE;
172 } else {
173 return OXYGEN_BUTTONSIZE;
177 case LM_ButtonSpacing:
178 return 1;
180 case LM_ButtonMarginTop:
181 return 0;
183 default:
184 return KCommonDecoration::layoutMetric(lm, respectWindowState, btn);
189 KCommonDecorationButton *OxygenClient::createButton(::ButtonType type)
191 switch (type) {
192 case MenuButton:
193 return new OxygenButton(*this, i18n("Menu"), ButtonMenu);
195 case HelpButton:
196 return new OxygenButton(*this, i18n("Help"), ButtonHelp);
198 case MinButton:
199 return new OxygenButton(*this, i18n("Minimize"), ButtonMin);
201 case MaxButton:
202 return new OxygenButton(*this, i18n("Maximize"), ButtonMax);
204 case CloseButton:
205 return new OxygenButton(*this, i18n("Close"), ButtonClose);
207 case AboveButton:
208 return new OxygenButton(*this, i18n("Keep Above Others"), ButtonAbove);
210 case BelowButton:
211 return new OxygenButton(*this, i18n("Keep Below Others"), ButtonBelow);
213 case OnAllDesktopsButton:
214 return new OxygenButton(*this, i18n("On All Desktops"), ButtonSticky);
216 case ShadeButton:
217 return new OxygenButton(*this, i18n("Shade Button"), ButtonShade);
219 default:
220 return 0;
225 // c0 - background
226 // c1 - foreground
227 // t - target contrast ratio
228 QColor reduceContrast(const QColor &c0, const QColor &c1, double t)
230 double s = KColorUtils::contrastRatio(c0, c1);
231 if (s < t)
232 return c1;
234 double l = 0.0, h = 1.0;
235 double x = s, a;
236 QColor r = c1;
237 for (int maxiter = 16; maxiter; --maxiter) {
238 a = 0.5 * (l + h);
239 r = KColorUtils::mix(c0, c1, a);
240 x = KColorUtils::contrastRatio(c0, r);
241 if (fabs(x - t) < 0.01)
242 break;
243 if (x > t)
244 h = a;
245 else
246 l = a;
248 return r;
252 QColor OxygenClient::titlebarTextColor(const QPalette &palette)
254 if (isActive())
255 return palette.color(QPalette::Active, QPalette::WindowText);
256 else {
257 if(colorCacheInvalid_) {
258 QColor ab = palette.color(QPalette::Active, QPalette::Window);
259 QColor af = palette.color(QPalette::Active, QPalette::WindowText);
260 QColor nb = palette.color(QPalette::Inactive, QPalette::Window);
261 QColor nf = palette.color(QPalette::Inactive, QPalette::WindowText);
263 colorCacheInvalid_ = false;
264 cachedTitlebarTextColor_ = reduceContrast(nb, nf, qMax(qreal(2.5), KColorUtils::contrastRatio(ab, KColorUtils::mix(ab, af, 0.4))));
266 return cachedTitlebarTextColor_;
271 void OxygenClient::paintEvent(QPaintEvent *e)
273 Q_UNUSED(e)
274 if (!OxygenFactory::initialized()) return;
276 updateButtons();
278 QPalette palette = widget()->palette();
279 QPainter painter(widget());
281 // Set palette to the right group.
282 // TODO - fix KWin to do this for us :-).
283 if (isActive())
284 palette.setCurrentColorGroup(QPalette::Active);
285 else
286 palette.setCurrentColorGroup(QPalette::Inactive);
288 int x,y,w,h;
289 QRect frame = widget()->frameGeometry();
290 QColor color = palette.window().color();
291 QColor light = helper_.calcLightColor( color );
292 QColor dark = helper_.calcDarkColor( color );
294 const int titleHeight = layoutMetric(LM_TitleHeight);
295 const int titleTop = layoutMetric(LM_TitleEdgeTop) + frame.top();
296 const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft);
297 const int marginLeft = layoutMetric(LM_TitleBorderLeft);
298 const int marginRight = layoutMetric(LM_TitleBorderRight);
300 const int titleLeft = frame.left() + titleEdgeLeft + buttonsLeftWidth() + marginLeft;
301 const int titleWidth = frame.width() -
302 titleEdgeLeft - layoutMetric(LM_TitleEdgeRight) -
303 buttonsLeftWidth() - buttonsRightWidth() -
304 marginLeft - marginRight;
306 // draw window background
307 helper_.renderWindowBackground(&painter, frame, this->widget(), palette, 0);
309 // draw title text
310 painter.setFont(options()->font(isActive(), false));
311 painter.setPen(titlebarTextColor(palette));
312 painter.drawText(titleLeft, titleTop-1, titleWidth, titleHeight, // -1 is to go into top resizearea
313 OxygenFactory::titleAlignment() | Qt::AlignVCenter, caption());
315 painter.setRenderHint(QPainter::Antialiasing);
317 // Draw dividing line
318 frame = widget()->rect();
319 if (shadowsActive()) {
320 frame.adjust(-1,-1,1,1);
322 frame.getRect(&x, &y, &w, &h);
324 if(isActive()) {
325 helper_.drawSeparator(&painter, QRect(x, titleTop+titleHeight-1.5, w, 2), color, Qt::Horizontal);
328 // draw stripes as indicator for active windows
329 if (isActive() && OxygenFactory::showStripes()) {
330 Qt::Alignment align = OxygenFactory::titleAlignment();
331 if (widget()->layoutDirection() == Qt::RightToLeft)
333 if (align == Qt::AlignLeft)
334 align = Qt::AlignRight;
335 else if (align == Qt::AlignRight)
336 align = Qt::AlignLeft;
339 if (align & Qt::AlignLeft) {
340 int left = titleLeft + QFontMetrics(options()->font(isActive(), false)).width(caption()) + 4;
341 int right = titleLeft + titleWidth;
342 drawScratch(&painter, palette, left, right, titleTop+6);
344 if (align & Qt::AlignRight) {
345 int left = titleLeft;
346 int right = titleLeft + titleWidth - QFontMetrics(options()->font(isActive(), false)).width(caption()) - 4;
347 drawScratch(&painter, palette, right, left, titleTop+6);
349 if (align & Qt::AlignHCenter) {
350 int textWidth = QFontMetrics(options()->font(isActive(), false)).width(caption());
351 int left = titleLeft;
352 int centerLeft = titleLeft + titleWidth/2 - textWidth/2 - 4;
353 int centerRight = titleLeft + titleWidth/2 + textWidth/2 + 4;
354 int right = titleLeft + titleWidth;
355 drawScratch(&painter, palette, centerLeft, left, titleTop+6);
356 drawScratch(&painter, palette, centerRight, right, titleTop+6);
360 // Draw shadows of the frame
361 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
363 if(maximized)
364 return;
366 helper_.drawFloatFrame(&painter, frame, color, !shadowsActive(), isActive(),
367 KDecoration::options()->color(ColorTitleBar));
369 if(!isResizable())
370 return;
372 // Draw the 3-dots resize handles
373 qreal cenY = frame.height() / 2 + x + 0.5;
374 qreal posX = frame.width() + y - 2.5;
375 painter.setPen(Qt::NoPen);
376 painter.setBrush(QColor(0, 0, 0, 66));
377 renderDot(&painter, QPointF(posX, cenY - 3), 1.8);
378 renderDot(&painter, QPointF(posX, cenY), 1.8);
379 renderDot(&painter, QPointF(posX, cenY + 3), 1.8);
381 painter.translate(x + frame.width()-9, y + frame.height()-9);
382 renderDot(&painter, QPointF(2.5, 6.5), 1.8);
383 renderDot(&painter, QPointF(5.5, 5.5), 1.8);
384 renderDot(&painter, QPointF(6.5, 2.5), 1.8);
387 void OxygenClient::drawScratch(QPainter *p, QPalette &palette, const int start, const int end, const int topMargin)
389 QLinearGradient scratchlg(QPoint(start,0), QPoint(end,0));
390 scratchlg.setColorAt(0.0, Qt::transparent);
391 scratchlg.setColorAt(0.05, KDecoration::options()->color(ColorTitleBar));
392 scratchlg.setColorAt(1.0, Qt::transparent);
393 QPen pen1(scratchlg, 0.5);
395 QLinearGradient scratchlg2(QPoint(start,0), QPoint(end,0));
396 scratchlg2.setColorAt(0.0, Qt::transparent);
397 scratchlg2.setColorAt(0.05, helper_.calcLightColor(palette.color(QPalette::Window)));
398 scratchlg2.setColorAt(1.0, Qt::transparent);
399 QPen pen2(scratchlg2, 0.5);
401 bool antialiasing = p->testRenderHint(QPainter::Antialiasing);
402 p->setRenderHint(QPainter::Antialiasing, false);
403 for (int i = 0; i < 3; ++i)
405 p->setPen(pen1);
406 p->drawLine(QPointF(start, topMargin+4*i), QPointF(end, topMargin+4*i));
407 p->setPen(pen2);
408 p->drawLine(QPointF(start, topMargin+4*i+1), QPointF(end, topMargin+4*i+1));
410 p->setRenderHint(QPainter::Antialiasing, antialiasing);
414 void OxygenClient::updateWindowShape()
416 bool maximized = maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows();
417 int w=widget()->width();
418 int h=widget()->height();
420 if(maximized) {
421 QRegion mask(0,0,w,h);
422 setMask(mask);
423 return;
426 if (!shadowsActive()) {
427 QRegion mask(4, 0, w-8, h);
428 mask += QRegion(0, 4, w, h-8);
429 mask += QRegion(2, 1, w-4, h-2);
430 mask += QRegion(1, 2, w-2, h-4);
432 setMask(mask);
434 else {
435 QRegion mask(5, 0, w-10, h-0);
436 mask += QRegion(0, 5, w-0, h-10);
437 mask += QRegion(2, 2, w-4, h-4);
438 mask += QRegion(3, 1, w-6, h-2);
439 mask += QRegion(1, 3, w-2, h-6);
441 setMask(mask);
445 QList<QRect> OxygenClient::shadowQuads( ShadowType type ) const
447 Q_UNUSED(type)
449 QSize size = widget()->size();
450 int outside=21, underlap=4, cornersize=25;
451 // These are underlap under the decoration so the corners look nicer 10px on the outside
452 QList<QRect> quads;
453 quads.append(QRect(-outside, size.height()-underlap, cornersize, cornersize));
454 quads.append(QRect(underlap, size.height()-underlap, size.width()-2*underlap, cornersize));
455 quads.append(QRect(size.width()-underlap, size.height()-underlap, cornersize, cornersize));
456 quads.append(QRect(-outside, underlap, cornersize, size.height()-2*underlap));
457 quads.append(QRect(size.width()-underlap, underlap, cornersize, size.height()-2*underlap));
458 quads.append(QRect(-outside, -outside, cornersize, cornersize));
459 quads.append(QRect(underlap, -outside, size.width()-2*underlap, cornersize));
460 quads.append(QRect(size.width()-underlap, -outside, cornersize, cornersize));
461 return quads;
464 double OxygenClient::shadowOpacity( ShadowType type ) const
466 switch( type ) {
467 case ShadowBorderedActive:
468 if( isActive() )
469 return 1.0;
470 return 0.0;
471 case ShadowBorderedInactive:
472 if( isActive() )
473 return 0.0;
474 return 1.0;
475 default:
476 abort(); // Should never be reached
478 return 0;
481 } //namespace Oxygen
483 //#include "oxygenclient.moc"
485 // #endif