add more spacing
[personal-kdebase.git] / workspace / kwin / clients / redmond / redmond.cpp
blob7ac5dad37ce22dfbcfd11867a7f608a9a5f4e513
1 /********************************************************************
3 Redmond KWin client
5 Copyright 2001
6 Karol Szwed <gallium@kde.org>
7 http://gallium.n3.net/
9 Based on the default KWin client.
11 Updated to support toolwindows 3/2001 (KS)
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *********************************************************************/
27 #include "redmond.h"
29 #include <QtGui/qdrawutil.h>
30 #include <QDateTime>
31 //Added by qt3to4:
32 #include <QPixmap>
33 #include <QPaintEvent>
34 #include <klocale.h>
36 #include <QBitmap>
37 #include <QImage>
38 #include <QApplication>
39 #include <QPainter>
41 namespace Redmond {
43 static const char *kdelogo[] = {
44 /* columns rows colors chars-per-pixel */
45 "16 16 8 1",
46 " c None",
47 ". c #000000",
48 "+ c #A0A0A4",
49 "@ c #FFFFFF",
50 "# c #585858",
51 "$ c #C0C0C0",
52 "% c #808080",
53 "& c #DCDCDC",
54 " ",
55 " .. .. ",
56 " .+@. .@#. ",
57 " .@@@. .@@@# ",
58 " .@@@..$@@$. ",
59 " .@@@.@@@$. ",
60 " .@@@%@@$. ",
61 " .@@@&@@. ",
62 " .@@@@@@. ",
63 " .@@@$@@&. ",
64 " .@@@.@@@. ",
65 " .@@@.+@@@. ",
66 " .@@@..$@@&. ",
67 " .@@%. .@@@. ",
68 " .... ... ",
69 " "};
71 static unsigned char iconify_bits[] = {
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00};
75 static unsigned char close_bits[] = {
76 0x00, 0x00, 0x86, 0x01, 0xcc, 0x00, 0x78, 0x00, 0x30, 0x00, 0x78, 0x00,
77 0xcc, 0x00, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00};
79 static unsigned char maximize_bits[] = {
80 0xff, 0x01, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
81 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x00, 0x00};
83 static unsigned char minmax_bits[] = {
84 0xfc, 0x00, 0xfc, 0x00, 0x84, 0x00, 0xbf, 0x00, 0xbf, 0x00, 0xe1, 0x00,
85 0x21, 0x00, 0x21, 0x00, 0x3f, 0x00, 0x00, 0x00};
87 static unsigned char question_bits[] = {
88 0x00, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00,
89 0x00, 0x00, 0x18, 0x00, 0x18, 0x00, 0x00, 0x00};
92 // Up / Down titlebar button images
93 static QPixmap *btnPix1;
94 static QPixmap *iBtnPix1;
95 static QPixmap *btnDownPix1;
96 static QPixmap *iBtnDownPix1;
98 static QPixmap *miniBtnPix1;
99 static QPixmap *iMiniBtnPix1;
100 static QPixmap *miniBtnDownPix1;
101 static QPixmap *iMiniBtnDownPix1;
103 static QPixmap *defaultMenuPix;
104 static QColor *btnForeground;
105 static bool pixmaps_created = false;
107 static int toolTitleHeight;
108 static int normalTitleHeight;
109 static int borderWidth;
111 static inline const KDecorationOptions *options()
113 return KDecoration::options();
116 static void drawButtonFrame( QPixmap *pix, const QPalette &g, bool sunken )
118 QPainter p;
119 int x2 = pix->width() - 1;
120 int y2 = pix->height() - 1;
121 p.begin(pix);
123 // titlebar button frame
124 p.setPen( sunken ? g.color(QPalette::Dark).dark(155) : g.color(QPalette::Light));
125 p.drawLine(0, 0, x2-1, 0);
126 p.drawLine(0, 0, 0, y2-1);
128 if (sunken)
130 p.setPen( g.color(QPalette::Mid).dark(135) );
131 p.drawLine(1, 1, x2-2, 1);
132 p.drawLine(1, 1, 1, y2-2);
135 p.setPen( sunken ? g.color(QPalette::Light) : g.color(QPalette::Mid).dark(135));
136 p.drawLine(1, y2-1, x2-1, y2-1);
137 p.drawLine(x2-1, 1, x2-1, y2-1);
139 p.setPen( sunken ? g.color(QPalette::Light) : g.color(QPalette::Dark).dark(155));
140 p.drawLine(0, y2, x2, y2);
141 p.drawLine(x2, 0, x2, y2);
144 static void gradientFill(QPixmap *pixmap,
145 const QColor &color1, const QColor &color2, bool horizontal = false)
147 QPainter p(pixmap);
148 QLinearGradient gradient(0, 0,
149 horizontal ? pixmap->width() : 0,
150 horizontal ? 0 : pixmap->height());
151 gradient.setColorAt(0.0, color1);
152 gradient.setColorAt(1.0, color2);
153 QBrush brush(gradient);
154 p.fillRect(pixmap->rect(), brush);
157 static void create_pixmaps ()
159 if (pixmaps_created)
160 return;
162 pixmaps_created = true;
164 bool highcolor = QPixmap::defaultDepth() > 8;
166 btnPix1 = new QPixmap;
167 btnDownPix1 = new QPixmap;
168 iBtnPix1 = new QPixmap;
169 iBtnDownPix1 = new QPixmap;
170 miniBtnPix1 = new QPixmap;
171 miniBtnDownPix1 = new QPixmap;
172 iMiniBtnPix1 = new QPixmap;
173 iMiniBtnDownPix1 = new QPixmap;
174 defaultMenuPix = new QPixmap(kdelogo);
176 // buttons (active/inactive, sunken/unsunken)
177 QColorGroup g = options()->palette(KDecoration::ColorButtonBg, true).active();
178 QColor c = g.background();
179 *btnPix1 = QPixmap(normalTitleHeight, normalTitleHeight-2);
180 *btnDownPix1 = QPixmap(normalTitleHeight, normalTitleHeight-2);
181 *iBtnPix1 = QPixmap(normalTitleHeight, normalTitleHeight-2);
182 *iBtnDownPix1 = QPixmap(normalTitleHeight, normalTitleHeight-2);
184 *miniBtnPix1 = QPixmap(toolTitleHeight, toolTitleHeight);
185 *miniBtnDownPix1 = QPixmap(toolTitleHeight, toolTitleHeight);
186 *iMiniBtnPix1 = QPixmap(toolTitleHeight, toolTitleHeight);
187 *iMiniBtnDownPix1 = QPixmap(toolTitleHeight, toolTitleHeight);
189 if (highcolor && false) {
190 gradientFill(btnPix1, c.light(130), c.dark(130));
191 gradientFill(btnDownPix1, c.dark(130), c.light(130));
192 gradientFill(miniBtnPix1, c.light(130), c.dark(130));
193 gradientFill(miniBtnDownPix1, c.dark(130), c.light(130));
195 g = options()->palette(KDecoration::ColorButtonBg, false);
196 g.setCurrentColorGroup( QPalette::Active );
197 c = g.background();
198 gradientFill(iBtnPix1, c.light(130), c.dark(130));
199 gradientFill(iBtnDownPix1, c.dark(130), c.light(130));
200 gradientFill(iMiniBtnPix1, c.light(130), c.dark(130));
201 gradientFill(iMiniBtnDownPix1, c.dark(130), c.light(130));
202 } else {
203 btnPix1->fill(c.rgb());
204 btnDownPix1->fill(c.rgb());
205 miniBtnPix1->fill(c.rgb());
206 miniBtnDownPix1->fill(c.rgb());
208 g = options()->palette(KDecoration::ColorButtonBg, false);
209 g.setCurrentColorGroup( QPalette::Active );
210 c = g.background();
211 iBtnPix1->fill(c.rgb());
212 iBtnDownPix1->fill(c.rgb());
213 iMiniBtnPix1->fill(c.rgb());
214 iMiniBtnDownPix1->fill(c.rgb());
217 g = options()->palette(KDecoration::ColorButtonBg, true);
218 g.setCurrentColorGroup( QPalette::Active );
219 drawButtonFrame(btnPix1, g, false);
220 drawButtonFrame(btnDownPix1, g, true);
221 drawButtonFrame(miniBtnPix1, g, false);
222 drawButtonFrame(miniBtnDownPix1, g, true);
224 g = options()->palette(KDecoration::ColorButtonBg, false);
225 g.setCurrentColorGroup( QPalette::Active );
226 drawButtonFrame(iBtnPix1, g, false);
227 drawButtonFrame(iBtnDownPix1, g, true);
228 drawButtonFrame(iMiniBtnPix1, g, false);
229 drawButtonFrame(iMiniBtnDownPix1, g, true);
231 // Make sure button pixmaps contrast with the current colour scheme.
232 if (qGray(options()->color(KDecoration::ColorButtonBg, true).rgb()) > 127)
233 btnForeground = new QColor(Qt::black);
234 else
235 btnForeground = new QColor(Qt::white);
238 void delete_pixmaps()
240 delete btnPix1;
241 delete btnDownPix1;
242 delete iBtnPix1;
243 delete iBtnDownPix1;
244 delete miniBtnPix1;
245 delete miniBtnDownPix1;
246 delete iMiniBtnPix1;
247 delete iMiniBtnDownPix1;
248 delete defaultMenuPix;
249 delete btnForeground;
250 pixmaps_created = false;
253 RedmondButton::RedmondButton(ButtonType type, RedmondDeco *parent)
254 : KCommonDecorationButton(type, parent)
256 // Eliminate background flicker
257 setAttribute(Qt::WA_NoSystemBackground, true);
259 miniBtn = decoration()->isToolWindow();
262 void RedmondButton::reset(unsigned long changed)
264 if (changed&DecorationReset || changed&ManualReset || changed&SizeChange || changed&StateChange) {
265 switch (type() ) {
266 case CloseButton:
267 setBitmap(close_bits);
268 break;
269 case HelpButton:
270 setBitmap(question_bits);
271 break;
272 case MinButton:
273 setBitmap(iconify_bits);
274 break;
275 case MaxButton:
276 setBitmap( isChecked() ? minmax_bits : maximize_bits );
277 break;
278 case MenuButton:
280 QPixmap miniIcon = decoration()->icon().pixmap(QIcon::Small, QIcon::Normal);
281 if (!miniIcon.isNull()) {
282 setPixmap(miniIcon);
283 } else {
284 setPixmap(*defaultMenuPix);
286 break;
288 default:
289 setBitmap(0);
290 break;
293 this->update();
298 void RedmondButton::setBitmap(const unsigned char *bitmap)
300 pix = QPixmap();
302 if (bitmap)
303 deco = QBitmap::fromData( QSize(10, 10), bitmap);
304 else {
305 deco = QBitmap(10,10);
306 deco.fill(Qt::color0);
308 deco.setMask(deco);
312 void RedmondButton::setPixmap( const QPixmap &p )
314 deco = QPixmap();
315 pix = p;
317 repaint();
320 void RedmondButton::paintEvent(QPaintEvent *)
322 QPainter p(this);
323 drawButton(&p);
326 void RedmondButton::drawButton(QPainter *p)
328 if ( pix.isNull() ) {
329 if ( decoration()->isActive() ) {
330 if ( isDown() )
331 p->drawPixmap(0, 0, miniBtn ? *miniBtnDownPix1 : *btnDownPix1);
332 else
333 p->drawPixmap(0, 0, miniBtn ? *miniBtnPix1 : *btnPix1);
334 } else {
335 if ( isDown() )
336 p->drawPixmap(0, 0, miniBtn ? *iMiniBtnDownPix1 : *iBtnDownPix1);
337 else
338 p->drawPixmap(0, 0, miniBtn ? *iMiniBtnPix1 : *iBtnPix1);
341 p->setPen( *btnForeground );
342 int xOff = (width()-10)/2;
343 int yOff = (height()-10)/2;
344 p->drawPixmap(isDown() ? xOff+1: xOff, isDown() ? yOff+1 : yOff, deco);
345 } else {
346 if (isLeft() ) {
347 p->fillRect(0, 0, width(), height(),
348 options()->color(KDecoration::ColorTitleBar, decoration()->isActive()));
349 } else {
350 p->fillRect(0, 0, width(), height(),
351 options()->color(KDecoration::ColorTitleBlend, decoration()->isActive()));
354 if ( type()==MenuButton && height() < 16) {
356 // Smooth scale the menu button pixmap
357 QPixmap tmpPix = QPixmap::fromImage(
358 pix.toImage().scaled( height(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation ));
360 p->drawPixmap( 0, 0, tmpPix );
361 } else {
362 int xOff = (width() -pix.width() )/2;
363 int yOff = (height()-pix.height())/2;
364 p->drawPixmap(xOff, yOff, pix );
370 RedmondDeco::RedmondDeco(KDecorationBridge *b, KDecorationFactory *f)
371 : KCommonDecoration(b, f)
375 QString RedmondDeco::visibleName() const
377 return i18n("Redmond");
380 QString RedmondDeco::defaultButtonsLeft() const
382 return "M";
385 QString RedmondDeco::defaultButtonsRight() const
387 return "HIA_X";
390 bool RedmondDeco::decorationBehaviour(DecorationBehaviour behaviour) const
392 switch (behaviour) {
393 case DB_MenuClose:
394 return true;
396 case DB_WindowMask:
397 return false;
399 case DB_ButtonHide:
400 return true;
402 default:
403 return KCommonDecoration::decorationBehaviour(behaviour);
407 int RedmondDeco::layoutMetric(LayoutMetric lm, bool respectWindowState, const KCommonDecorationButton *btn) const
409 bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows());
411 switch (lm) {
412 case LM_BorderLeft:
413 case LM_BorderRight:
414 case LM_BorderBottom:
415 return border ? borderWidth : 0;
417 case LM_TitleEdgeLeft:
418 case LM_TitleEdgeRight:
419 return border ? borderWidth+2 : 2;
421 case LM_TitleEdgeTop:
422 return border ? borderWidth+2 : 2;
424 case LM_TitleEdgeBottom:
425 return border ? 1 : 0;
427 case LM_TitleBorderLeft:
428 case LM_TitleBorderRight:
429 return border ? 1 : 0;
431 case LM_TitleHeight:
432 return titleHeight-2;
434 case LM_ButtonWidth:
435 return titleHeight-2;
436 case LM_ButtonHeight:
437 if (isToolWindow() || (btn && btn->type()==MenuButton) ) {
438 return titleHeight-2;
439 } else {
440 return titleHeight-2-2;
443 case LM_ButtonSpacing:
444 return 0;
446 default:
447 return KCommonDecoration::layoutMetric(lm, respectWindowState, btn);
451 KCommonDecorationButton *RedmondDeco::createButton(ButtonType type)
453 switch (type) {
454 case MenuButton:
455 return new RedmondButton(MenuButton, this);
456 case HelpButton:
457 return new RedmondButton(HelpButton, this);
458 case MinButton:
459 return new RedmondButton(MinButton, this);
460 case MaxButton:
461 return new RedmondButton(MaxButton, this);
462 case CloseButton:
463 return new RedmondButton(CloseButton, this);
465 default:
466 return 0;
470 void RedmondDeco::init()
472 // Finally, toolwindows look small
473 if ( isToolWindow() ) {
474 titleHeight = toolTitleHeight;
475 } else {
476 titleHeight = normalTitleHeight;
479 KCommonDecoration::init();
482 void RedmondDeco::reset( unsigned long changed )
484 KCommonDecoration::reset(changed);
487 void RedmondDeco::paintEvent( QPaintEvent* )
489 bool hicolor = QPixmap::defaultDepth() > 8;
490 int fontoffset = 1;
492 // Modify borderWith used by titlebar to 0, when maximized and not move or resize able
493 bool border = !(maximizeMode()==MaximizeFull && !options()->moveResizeMaximizedWindows());
494 int modBorderWidth = border ? borderWidth : 0;
496 QPainter p(widget());
498 // Obtain widget bounds.
499 QRect r(widget()->rect());
500 int x = r.x();
501 int y = r.y();
502 int x2 = r.width()-1;
503 int y2 = r.height()-1;
504 int w = r.width();
505 int h = r.height();
507 // Draw part of the frame that is the frame color
508 // ==============================================
509 QPalette g = options()->palette(KDecoration::ColorFrame, isActive());
510 g.setCurrentColorGroup( QPalette::Active );
511 p.setPen( g.background().color() );
512 p.drawLine( x, y, x2-1, y );
513 p.drawLine( x, y, x, y2-1 );
515 // Draw line under title bar
516 p.drawLine( x+borderWidth, y+titleHeight+borderWidth, x2-borderWidth, y+titleHeight+borderWidth );
517 // Draw a hidden line that appears during shading
518 p.drawLine( x+borderWidth, y2-borderWidth, x2-borderWidth, y2-borderWidth );
520 // Fill out the border edges
521 for (int i = 1; i < borderWidth; i++)
522 p.drawRect( x+i, y+i, x2-2*i, y2-2*i );
524 // Draw highlights and lowlights
525 p.setPen(g.color( QPalette::Light ));
526 for (int i = 1; i <= borderWidth/3; i++) {
527 p.drawLine( x+i, y+i, x2-i-1, y+i);
528 p.drawLine( x+i, y+i, x+i, y2-i-1);
531 p.setPen(g.color(QPalette::Dark).dark(135));
532 for (int i = 1; i <= borderWidth/3; i++) {
533 p.drawLine( x2-i, y+i+1, x2-i, y2-i);
534 p.drawLine( x+i+1, y2-i, x2-i, y2-i);
537 // Draw black edges
538 p.setPen( g.color(QPalette::Dark).dark(155) );
539 p.drawLine(x2, y, x2, y2);
540 p.drawLine(x, y2, x2, y2);
542 // Draw the title bar.
543 // ===================
544 r = titleRect();
545 // QFontMetrics fm(options()->font(true));
547 // Obtain blend colours.
548 QColor c1 = options()->color(KDecoration::ColorTitleBar, isActive() );
549 QColor c2 = options()->color(KDecoration::ColorTitleBlend, isActive() );
551 QFont fnt = options()->font(true, isToolWindow() );
552 if (isToolWindow() ) {
553 fnt.setWeight( QFont::Normal );
554 fontoffset = 0;
557 // Paint without a buffer if the colours are the same to
558 // improve performance, and only draw gradients on hicolor displays.
559 if ((c1 != c2) && hicolor) {
560 // KS - Add gradient caching if needed at a later stage.
562 // Create a disposable pixmap buffer for the title blend
563 QPixmap* titleBuffer = new QPixmap;
564 *titleBuffer = QPixmap(w-2*modBorderWidth, titleHeight);
566 if (titleBuffer->depth() > 16) {
567 gradientFill(titleBuffer, c1, c2, true);
570 QPainter p2( titleBuffer );
572 // Since drawing the gradient is (relatively) slow, it is best
573 // to draw the title text on the pixmap.
575 p2.setFont( fnt );
576 p2.setPen( options()->color(KDecoration::ColorFont, isActive() ));
577 p2.drawText( r.x(), fontoffset, r.width()-3, r.height()-1,
578 Qt::AlignLeft | Qt::AlignVCenter, caption() );
579 p2.end();
581 p.drawPixmap( modBorderWidth, modBorderWidth, *titleBuffer );
583 delete titleBuffer;
585 } else {
586 // Assume lower ended hardware, so don't use buffers.
587 // Don't draw a gradient either.
588 p.fillRect( modBorderWidth, modBorderWidth, w-2*modBorderWidth, titleHeight, c1 );
590 // Draw the title text.
591 p.setFont( fnt );
592 p.setPen(options()->color(KDecoration::ColorFont, isActive() ));
593 p.drawText(r.x()+4, r.y()+fontoffset-2, r.width()-3, r.height()-1,
594 Qt::AlignLeft | Qt::AlignVCenter, caption() );
599 void RedmondDecoFactory::readConfig() {
600 normalTitleHeight = QFontMetrics(options()->font(true)).height();
601 QFont toolFont = options()->font(true, true);
602 toolFont.setWeight(QFont::Normal);
603 toolTitleHeight = QFontMetrics(toolFont).height();
604 switch(options()->preferredBorderSize(this)) {
605 case BorderLarge:
606 borderWidth = 8;
607 if (normalTitleHeight < 20) normalTitleHeight = 20;
608 if (toolTitleHeight < 20) toolTitleHeight = 20;
609 break;
610 case BorderVeryLarge:
611 borderWidth = 12;
612 if (normalTitleHeight < 24) normalTitleHeight = 24;
613 if (toolTitleHeight < 24) toolTitleHeight = 24;
614 break;
615 case BorderHuge:
616 borderWidth = 18;
617 if (normalTitleHeight < 28) normalTitleHeight = 28;
618 if (toolTitleHeight < 28) toolTitleHeight = 28;
619 break;
620 case BorderVeryHuge:
621 borderWidth = 27;
622 if (normalTitleHeight < 33) normalTitleHeight = 33;
623 if (toolTitleHeight < 33) toolTitleHeight = 33;
624 break;
625 case BorderOversized:
626 borderWidth = 40;
627 if (normalTitleHeight < 40) normalTitleHeight = 40;
628 if (toolTitleHeight < 40) toolTitleHeight = 40;
629 break;
630 case BorderTiny:
631 case BorderNormal:
632 default:
633 borderWidth = 4;
634 if (normalTitleHeight < 16) normalTitleHeight = 16;
635 if (toolTitleHeight < 16) toolTitleHeight = 16;
639 RedmondDecoFactory::RedmondDecoFactory()
641 readConfig();
642 create_pixmaps();
645 RedmondDecoFactory::~RedmondDecoFactory()
647 Redmond::delete_pixmaps();
650 KDecoration *RedmondDecoFactory::createDecoration( KDecorationBridge *b )
652 return (new RedmondDeco(b, this))->decoration();
655 bool RedmondDecoFactory::reset( unsigned long changed )
657 // SettingButtons is handled by KCommonDecoration
658 if ( changed & ( SettingFont | SettingBorder | SettingColors | SettingButtons ) ) {
659 delete_pixmaps();
660 readConfig();
661 create_pixmaps();
662 resetDecorations(changed);
663 return true;
664 } else {
665 resetDecorations(changed);
666 return false;
670 bool RedmondDecoFactory::supports( Ability ability ) const
672 switch( ability )
674 // announce
675 case AbilityAnnounceButtons:
676 case AbilityAnnounceColors:
677 // buttons
678 case AbilityButtonMenu:
679 case AbilityButtonHelp:
680 case AbilityButtonMinimize:
681 case AbilityButtonMaximize:
682 case AbilityButtonClose:
683 case AbilityButtonSpacer:
684 // colors
685 case AbilityColorTitleBack:
686 case AbilityColorTitleBlend:
687 case AbilityColorTitleFore:
688 return true;
689 default:
690 return false;
694 QList< RedmondDecoFactory::BorderSize > RedmondDecoFactory::borderSizes() const
695 { // the list must be sorted
696 return QList< BorderSize >() << BorderNormal << BorderLarge <<
697 BorderVeryLarge << BorderHuge << BorderVeryHuge << BorderOversized;
702 extern "C" KDE_EXPORT KDecorationFactory *create_factory()
704 return new Redmond::RedmondDecoFactory();
708 #include "redmond.moc"
709 // vim: ts=4 sw=4
710 // kate: space-indent off; tab-width 4;