////////////////////////////////////////////////////////////////////////////// // Nitrogenbutton.h // ------------------- // Nitrogen window decoration for KDE. // ------------------- // Copyright (c) 2006, 2007 Riccardo Iaconelli // Copyright (c) 2009, 2010 Hugo Pereira // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. ////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include "nitrogen.h" #include "nitrogen.moc" #include "nitrogenclient.h" extern "C" { KDE_EXPORT KDecorationFactory* create_factory() { return new Nitrogen::NitrogenFactory(); } } namespace Nitrogen { // referenced from definition in Nitrogendclient.cpp NitrogenHelper *nitrogenHelper(); // initialize static members bool NitrogenFactory::initialized_ = false; NitrogenConfiguration NitrogenFactory::defaultConfiguration_; NitrogenExceptionList NitrogenFactory::exceptions_; //___________________________________________________ NitrogenFactory::NitrogenFactory() { readConfig(); setInitialized( true ); } //___________________________________________________ NitrogenFactory::~NitrogenFactory() { setInitialized( false ); } //___________________________________________________ KDecoration* NitrogenFactory::createDecoration(KDecorationBridge* bridge ) { NitrogenClient* client( new NitrogenClient( bridge, this ) ); connect( this, SIGNAL( configurationChanged() ), client, SLOT( resetConfiguration() ) ); return client->decoration(); } //___________________________________________________ bool NitrogenFactory::reset(unsigned long changed) { QTextStream( stdout ) << "NitrogenConfig::reset." << endl; // read in the configuration setInitialized( false ); bool configuration_changed = readConfig(); setInitialized( true ); if( configuration_changed || (changed & (SettingDecoration | SettingButtons | SettingBorder)) ) { emit configurationChanged(); return true; } else { emit configurationChanged(); resetDecorations(changed); return false; } } //___________________________________________________ bool NitrogenFactory::readConfig() { QTextStream( stdout ) << "NitrogenConfig::readConfig." << endl; // create a config object KConfig config("nitrogenrc"); KConfigGroup group( config.group("Windeco") ); NitrogenConfiguration configuration( group ); bool changed = !( configuration == defaultConfiguration() ); // check configurations changed |= (NitrogenConfiguration::useCompiz() == NitrogenConfiguration::checkUseCompiz()); // read exceptionsreadConfig NitrogenExceptionList exceptions( config ); if( !( exceptions == exceptions_ ) ) { exceptions_ = exceptions; changed = true; } if( changed ) { nitrogenHelper()->invalidateCaches(); setDefaultConfiguration( configuration ); return true; } else return false; } //_________________________________________________________________ bool NitrogenFactory::supports( Ability ability ) const { switch( ability ) { // announce case AbilityAnnounceButtons: case AbilityAnnounceColors: // buttons case AbilityButtonMenu: case AbilityButtonHelp: case AbilityButtonMinimize: case AbilityButtonMaximize: case AbilityButtonClose: case AbilityButtonOnAllDesktops: case AbilityButtonAboveOthers: case AbilityButtonBelowOthers: case AbilityButtonSpacer: case AbilityButtonShade: // // colors // case AbilityColorTitleBack: // case AbilityColorTitleFore: // case AbilityColorFrame: // compositing #if KDE_IS_VERSION(4,2,92) case AbilityProvidesShadow: // TODO: UI option to use default shadows instead case AbilityUsesAlphaChannel: return !NitrogenConfiguration::useCompiz(); #else case AbilityCompositingShadow: return !NitrogenConfiguration::useCompiz(); #endif // no colors supported at this time default: return false; }; } #if !KDE_IS_VERSION(4,2,92) ////////////////////////////////////////////////////////////////////////////// // Shadows (kde4.2 version) //_________________________________________________________________ QList< QList > NitrogenFactory::shadowTextures() { QList< QList > textureLists; // in compiz mode, no textures are used if( NitrogenConfiguration::useCompiz() ) { return textureLists; } textureLists.append( makeTextures( defaultConfiguration().useOxygenShadows() ? Active:Inactive ) ); textureLists.append( makeTextures( Inactive ) ); return textureLists; } //_________________________________________________________________ QList NitrogenFactory::makeTextures( NitrogenFactory::Activity state ) const { #define shadowFuzzyness 10 #define shadowSize 10 // palette QPalette palette = qApp->palette(); palette.setCurrentColorGroup(QPalette::Active); // shadow size qreal size = 25.5; // prepare pixmap and painter QPixmap shadow( size*2, size*2 ); shadow.fill( Qt::transparent ); QPainter p( &shadow ); p.setRenderHint( QPainter::Antialiasing ); p.setPen( Qt::NoPen ); // prepare colors QColor color = palette.window().color(); QColor light = nitrogenHelper()->calcLightColor(nitrogenHelper()->backgroundTopColor(color)); QColor dark = nitrogenHelper()->calcDarkColor(nitrogenHelper()->backgroundBottomColor(color)); QLinearGradient lg = QLinearGradient(0.0, size-4.5, 0.0, size+4.5); if( defaultConfiguration().frameBorder() <= NitrogenConfiguration::BorderTiny ) { lg.setColorAt(0, nitrogenHelper()->backgroundTopColor(color) ); lg.setColorAt(0.52, nitrogenHelper()->backgroundTopColor(color)); lg.setColorAt(1.0, nitrogenHelper()->backgroundBottomColor(color) ); } else { lg.setColorAt(0.52, light); lg.setColorAt(1.0, dark); } switch( state ) { case Active: { QColor glow = KDecoration::options()->color(ColorFrame); QColor glow2 = KDecoration::options()->color(ColorTitleBar); QRadialGradient rg( size, size, size ); QColor c = color; c.setAlpha( 255 ); rg.setColorAt( 4.4/size, c ); c = glow; c.setAlpha( 220 ); rg.setColorAt( 4.5/size, c ); c.setAlpha( 180 ); rg.setColorAt( 5/size, c ); c.setAlpha( 25 ); rg.setColorAt( 5.5/size, c ); c.setAlpha( 0 ); rg.setColorAt( 6.5/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); rg = QRadialGradient( size, size, size ); c = color; c.setAlpha( 255 ); rg.setColorAt( 4.4/size, c ); c = glow2; c.setAlpha( 0.58*255 ); rg.setColorAt( 4.5/size, c ); c.setAlpha( 0.43*255 ); rg.setColorAt( 5.5/size, c ); c.setAlpha( 0.30*255 ); rg.setColorAt( 6.5/size, c ); c.setAlpha( 0.22*255 ); rg.setColorAt( 7.5/size, c ); c.setAlpha( 0.15*255 ); rg.setColorAt( 8.5/size, c ); c.setAlpha( 0.08*255 ); rg.setColorAt( 11.5/size, c ); c.setAlpha( 0); rg.setColorAt( 14.5/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); // draw the corner of the window - actually all 4 corners as one circle p.setBrush( Qt::NoBrush ); p.setPen(QPen(lg, 0.8)); p.drawEllipse(QRectF(size-4, size-4, 8, 8)); // cut out the part of the texture that is under the window p.setCompositionMode( QPainter::CompositionMode_DestinationOut ); p.setBrush( QColor( 0, 0, 0, 255 )); p.setPen( Qt::NoPen ); p.drawEllipse(QRectF(size-3, size-3, 6, 6)); p.drawRect(QRectF(size-3, size-1, 6, 2)); p.drawRect(QRectF(size-1, size-3, 2, 6)); p.end(); return makeTextures( shadow ); } case Inactive: { //--------------------------------------------------------------- // Inactive shadow texture QRadialGradient rg( size, size+4, size ); QColor c( Qt::black ); c.setAlpha( 0.12*255 ); rg.setColorAt( 4.5/size, c ); c.setAlpha( 0.11*255 ); rg.setColorAt( 6.6/size, c ); c.setAlpha( 0.075*255 ); rg.setColorAt( 8.5/size, c ); c.setAlpha( 0.06*255 ); rg.setColorAt( 11.5/size, c ); c.setAlpha( 0.035*255 ); rg.setColorAt( 14.5/size, c ); c.setAlpha( 0.025*255 ); rg.setColorAt( 17.5/size, c ); c.setAlpha( 0.01*255 ); rg.setColorAt( 21.5/size, c ); c.setAlpha( 0.0*255 ); rg.setColorAt( 25.5/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); rg = QRadialGradient( size, size+2, size ); c = QColor( Qt::black ); c.setAlpha( 0.25*255 ); rg.setColorAt( 4.5/size, c ); c.setAlpha( 0.20*255 ); rg.setColorAt( 5.5/size, c ); c.setAlpha( 0.13*255 ); rg.setColorAt( 7.5/size, c ); c.setAlpha( 0.06*255 ); rg.setColorAt( 8.5/size, c ); c.setAlpha( 0.015*255 ); rg.setColorAt( 11.5/size, c ); c.setAlpha( 0.0*255 ); rg.setColorAt( 14.5/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); rg = QRadialGradient( size, size+0.2, size ); c = QColor( Qt::black ); c.setAlpha( 0.35*255 ); rg.setColorAt( 0/size, c ); c.setAlpha( 0.32*255 ); rg.setColorAt( 4.5/size, c ); c.setAlpha( 0.22*255 ); rg.setColorAt( 5.0/size, c ); c.setAlpha( 0.03*255 ); rg.setColorAt( 5.5/size, c ); c.setAlpha( 0.0*255 ); rg.setColorAt( 6.5/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); rg = QRadialGradient( size, size, size ); c = color; c.setAlpha( 255 ); rg.setColorAt( 4.0/size, c ); c.setAlpha( 0 ); rg.setColorAt( 4.01/size, c ); p.setBrush( rg ); p.drawRect( shadow.rect() ); // draw the corner of the window - actually all 4 corners as one circle p.setBrush( Qt::NoBrush ); p.setPen(QPen(lg, 0.8)); p.drawEllipse(QRectF(size-4, size-4, 8, 8)); // cut out the part of the texture that is under the window p.setCompositionMode( QPainter::CompositionMode_DestinationOut ); p.setBrush( QColor( 0, 0, 0, 255 )); p.setPen( Qt::NoPen ); p.drawEllipse(QRectF(size-3, size-3, 6, 6)); p.drawRect(QRectF(size-3, size-1, 6, 2)); p.drawRect(QRectF(size-1, size-3, 2, 6)); p.end(); return makeTextures( shadow ); } default: return QList(); } } //______________________________________________________ QImage NitrogenFactory::makeTexture( const QPixmap& pixmap, const QRect& rect ) const { QPixmap dump( rect.size() ); dump.fill( Qt::transparent ); QPainter painter( &dump ); painter.drawPixmap( QPoint(0, 0), pixmap, rect ); painter.end(); return dump.toImage(); } //___________________________________________________________ QList NitrogenFactory::makeTextures( const QPixmap& shadow ) const { QList textures; int w = shadow.width() / 2; int h = shadow.height() / 2; textures.append( makeTexture( shadow, QRect( 0, h+1, w, h ) ) ); textures.append( makeTexture( shadow, QRect( w, h+1, 1, h ) ) ); textures.append( makeTexture( shadow, QRect( w+1, h+1, w, h ) ) ); textures.append( makeTexture( shadow, QRect( 0, h, w, 1 ) ) ); textures.append( makeTexture( shadow, QRect( w+1, h, w, 1 ) ) ); textures.append( makeTexture( shadow, QRect( 0, 0, w, h ) ) ); textures.append( makeTexture( shadow, QRect( w, 0, 1, h ) ) ); textures.append( makeTexture( shadow, QRect( w+1, 0, w, h ) ) ); return textures; } //_________________________________________________________________ int NitrogenFactory::shadowTextureList( ShadowType type ) const { switch( type ) { case ShadowBorderedInactive: return 1; default: return 0; } abort(); // Should never be reached } //_________________________________________________________________ QList NitrogenFactory::shadowQuads( ShadowType, QSize ) const { return QList(); } //____________________________________________________________________ double NitrogenFactory::shadowOpacity( ShadowType ) const { return 1.0; } #endif //____________________________________________________________________ NitrogenConfiguration NitrogenFactory::configuration( const NitrogenClient& client ) { QString window_title; QString class_name; for( NitrogenExceptionList::const_iterator iter = exceptions_.begin(); iter != exceptions_.end(); iter++ ) { // discard disabled exceptions if( !iter->enabled() ) continue; /* decide which value is to be compared to the regular expression, based on exception type */ QString value; switch( iter->type() ) { case NitrogenException::WindowTitle: { value = window_title.isEmpty() ? (window_title = client.caption()):window_title; break; } case NitrogenException::WindowClassName: { if( class_name.isEmpty() ) { // retrieve class name KWindowInfo info( client.windowId(), 0, NET::WM2WindowClass ); QString window_class_name( info.windowClassName() ); QString window_class( info.windowClassClass() ); class_name = window_class_name + " " + window_class; } value = class_name; break; } default: assert( false ); } if( iter->regExp().indexIn( value ) < 0 ) continue; NitrogenConfiguration configuration( defaultConfiguration() ); // propagate all features found in mask to the output configuration if( iter->mask() & NitrogenException::FrameBorder ) configuration.setFrameBorder( iter->frameBorder() ); if( iter->mask() & NitrogenException::BlendColor ) configuration.setBlendColor( iter->blendColor() ); if( iter->mask() & NitrogenException::DrawSeparator ) configuration.setDrawSeparator( iter->drawSeparator() ); if( iter->mask() & NitrogenException::ShowStripes ) configuration.setShowStripes( iter->showStripes() ); if( iter->mask() & NitrogenException::OverwriteColors ) configuration.setOverwriteColors( iter->overwriteColors() ); if( iter->mask() & NitrogenException::DrawSizeGrip ) configuration.setDrawSizeGrip( iter->drawSizeGrip() ); return configuration; } return defaultConfiguration(); } } //namespace Nitrogen