File indexing completed on 2024-04-28 05:32:16

0001 /*
0002     this file is part of the oxygen gtk engine
0003     SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
0004     SPDX-FileCopyrightText: 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "oxygenstyle.h"
0010 #include "oxygencairocontext.h"
0011 #include "oxygencairoutils.h"
0012 #include "oxygencolorutils.h"
0013 #include "oxygenfontinfo.h"
0014 #include "oxygengtkutils.h"
0015 #include "oxygenmetrics.h"
0016 #include "oxygenwindecobutton.h"
0017 #include "oxygenwindowshadow.h"
0018 
0019 #include "oxygengtktypenames.h"
0020 
0021 #include <algorithm>
0022 #include <cmath>
0023 
0024 #ifdef GDK_WINDOWING_X11
0025 #include <X11/Xatom.h>
0026 #endif
0027 
0028 namespace Oxygen
0029 {
0030 
0031     //__________________________________________________________________
0032     Style* Style::_instance = 0;
0033     Style& Style::instance( void )
0034     {
0035         if( !_instance )
0036         {
0037             #if OXYGEN_DEBUG
0038             std::cerr << "Oxygen::Style::instance - creating new style." << std::endl;
0039             #endif
0040 
0041             _instance = new Style();
0042         }
0043 
0044         return *_instance;
0045     }
0046 
0047     //__________________________________________________________________
0048     Style::Style( void )
0049     {
0050         #ifdef GDK_WINDOWING_X11
0051         _blurAtom = None;
0052         #endif
0053     }
0054 
0055     //__________________________________________________________________
0056     bool Style::initialize( unsigned int flags )
0057     {
0058 
0059         // initialize ref surface
0060         _helper.initializeRefSurface();
0061 
0062         // reinitialize settings
0063         if( !_settings.initialize( flags ) ) return false;
0064 
0065         // reset caches if colors have changed
0066         if( flags&QtSettings::Colors )
0067         {
0068             _helper.clearCaches();
0069             ColorUtils::clearCaches();
0070         }
0071 
0072         // connect files
0073         QtSettings::FileMap& monitoredFiles( _settings.monitoredFiles() );
0074         for( QtSettings::FileMap::iterator iter = monitoredFiles.begin(); iter != monitoredFiles.end(); ++iter )
0075         {
0076             if( !iter->second.signal.isConnected() )
0077             { iter->second.signal.connect( G_OBJECT( iter->second.monitor ), "changed", G_CALLBACK(fileChanged), this ); }
0078         }
0079 
0080         // reinitialize animations
0081         _animations.initialize( _settings );
0082 
0083         if( flags&QtSettings::Oxygen )
0084         {
0085 
0086             // widget explorer
0087             _widgetExplorer.setEnabled( _settings.widgetExplorerEnabled() );
0088 
0089             // pass window drag mode to window manager
0090             if( !_settings.windowDragEnabled() ) _windowManager.setDragMode( WindowManager::Disabled );
0091             else if( _settings.windowDragMode() == QtSettings::WD_MINIMAL ) _windowManager.setDragMode( WindowManager::Minimal );
0092             else _windowManager.setDragMode( WindowManager::Full );
0093 
0094             // use window manager to handle window drag
0095             _windowManager.setUseWMMoveResize( _settings.useWMMoveResize() );
0096 
0097         }
0098 
0099         if( flags&QtSettings::KdeGlobals )
0100         {
0101             // pass drag distance and delay to window manager
0102             _windowManager.setDragDistance( _settings.startDragDist() );
0103             _windowManager.setDragDelay( _settings.startDragTime() );
0104         }
0105 
0106         // background surface
0107         if( !_settings.backgroundPixmap().empty() ) setBackgroundSurface( _settings.backgroundPixmap() );
0108 
0109         // create window shadow
0110         WindowShadow shadow( _settings, _helper );
0111         _shadowHelper.setSupported( _settings.isWMShadowsSupported() );
0112         _shadowHelper.setApplicationName( _settings.applicationName() );
0113         _shadowHelper.initialize( _settings.palette().color(Palette::Window), shadow );
0114 
0115         #ifdef GDK_WINDOWING_X11
0116         if( _blurAtom == None )
0117         {
0118 
0119             GdkDisplay *display( gdk_display_get_default() );
0120             if( display )
0121             { _blurAtom = XInternAtom(GDK_DISPLAY_XDISPLAY( display ),"_KDE_NET_WM_BLUR_BEHIND_REGION",False); }
0122 
0123         }
0124 
0125         #endif
0126 
0127         return true;
0128 
0129     }
0130 
0131     //__________________________________________________________________
0132     Cairo::Surface Style::tabCloseButton( const StyleOptions& options )
0133     {
0134 
0135         // active tab
0136         if( options&Focus )
0137         {
0138             // create button
0139             if( !_tabCloseButtons.active )
0140             {
0141                 const std::string filename( std::string(GTK_THEME_DIR)+ "/special-icons/standardbutton-closetab-down-16.png" );
0142                 _tabCloseButtons.active = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
0143             }
0144 
0145             return _tabCloseButtons.active;
0146         }
0147 
0148         // prelight
0149         if( options&Hover )
0150         {
0151             // create button
0152             if( !_tabCloseButtons.prelight )
0153             {
0154                 const std::string filename( std::string(GTK_THEME_DIR) + "/special-icons/standardbutton-closetab-hover-16.png" );
0155                 _tabCloseButtons.prelight = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
0156             }
0157 
0158             return _tabCloseButtons.prelight;
0159 
0160         }
0161 
0162         // normal or inactive
0163         if( !_tabCloseButtons.normal )
0164         {
0165             const std::string filename( std::string(GTK_THEME_DIR) + "/special-icons/standardbutton-closetab-16.png" );
0166             _tabCloseButtons.normal = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
0167         }
0168 
0169         // inactive
0170         if( (options&Disabled) && _tabCloseButtons.normal )
0171         {
0172 
0173             if( !_tabCloseButtons.inactive )
0174             {
0175 
0176                 // make deep copy of the normal image
0177                 _tabCloseButtons.inactive = Cairo::Surface( cairo_surface_copy( _tabCloseButtons.normal ) );
0178                 cairo_surface_add_alpha(  _tabCloseButtons.inactive, 0.5 );
0179                 cairo_image_surface_saturate( _tabCloseButtons.inactive, 0.1 );
0180 
0181             }
0182 
0183             return _tabCloseButtons.inactive;
0184 
0185         }
0186 
0187         // fallback to normal
0188         return _tabCloseButtons.normal;
0189 
0190     }
0191 
0192     //____________________________________________________________________________________
0193     bool Style::hasBackgroundSurface( void ) const
0194     {
0195         if( !_backgroundSurface.isValid() ) return false;
0196         const cairo_status_t status( cairo_surface_status( _backgroundSurface ) );
0197         return
0198             status != CAIRO_STATUS_NO_MEMORY &&
0199             status != CAIRO_STATUS_FILE_NOT_FOUND &&
0200             status != CAIRO_STATUS_READ_ERROR;
0201     }
0202 
0203     //__________________________________________________________________
0204     void Style::fill( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color ) const
0205     {
0206 
0207         // define colors
0208         Cairo::Context context( window, clipRect );
0209         cairo_rectangle( context, x, y, w, h );
0210         cairo_set_source( context, color );
0211         cairo_fill( context );
0212 
0213     }
0214 
0215     //__________________________________________________________________
0216     void Style::outline( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color ) const
0217     {
0218 
0219         // define colors
0220         Cairo::Context context( window, clipRect );
0221         cairo_rectangle( context, 0.5+x, 0.5+y, w-1, h-1 );
0222         cairo_set_line_width( context, 1 );
0223         cairo_set_source( context, color );
0224         cairo_stroke( context );
0225 
0226     }
0227 
0228     //__________________________________________________________________
0229     void Style::drawSeparator( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const StyleOptions& options )
0230     {
0231 
0232         // define colors
0233         ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
0234         if( options & Blend )
0235         {
0236 
0237             gint wh, wy;
0238             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
0239             if( wh > 0 )
0240             {
0241                 if( options & Menu ) base = ColorUtils::menuBackgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
0242                 else base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
0243             }
0244 
0245         }
0246 
0247         Cairo::Context context( window, clipRect );
0248         _helper.drawSeparator( context, base, x, y, w, h, (options&Vertical) );
0249 
0250     }
0251 
0252     //__________________________________________________________________
0253     bool Style::renderWindowBackground(
0254         cairo_t* context, GdkWindow* window, GtkWidget* widget,
0255         GdkRectangle* clipRect, gint x, gint y, gint w, gint h,
0256         const StyleOptions& options,
0257         bool isMaximized )
0258     {
0259         if( _settings.useBackgroundGradient() )
0260         {
0261 
0262             if( !renderBackgroundGradient( context, window, widget, clipRect, x, y, w, h, options, isMaximized ) )
0263             { return false; }
0264 
0265         } else {
0266 
0267             ColorUtils::Rgba base( color( Palette::Window, options ) );
0268             const bool needToDestroyContext( !context );
0269             if( !context )
0270             {
0271 
0272                 // create context
0273                 context = gdk_cairo_create(window);
0274 
0275                 // set clip rect
0276                 if( clipRect )
0277                 {
0278                     cairo_rectangle(context,clipRect->x,clipRect->y,clipRect->width,clipRect->height);
0279                     cairo_clip(context);
0280                 }
0281 
0282             } else cairo_save( context );
0283 
0284             cairo_set_source(context,base);
0285             cairo_rectangle(context,x,y,w,h);
0286             cairo_fill(context);
0287 
0288             if( needToDestroyContext )
0289             {
0290                 cairo_destroy(context);
0291                 context = 0;
0292             }
0293             else cairo_restore(context);
0294 
0295         }
0296 
0297         // background pixmap
0298         if( hasBackgroundSurface() )
0299         { renderBackgroundPixmap( context, window, widget, clipRect, x, y, w, h, isMaximized ); }
0300 
0301         return true;
0302 
0303     }
0304 
0305     //__________________________________________________________________
0306     bool Style::renderBackgroundGradient(
0307         cairo_t* context, GdkWindow* window, GtkWidget* widget,
0308         GdkRectangle* clipRect, gint x, gint y, gint w, gint h,
0309         const StyleOptions& options,
0310         bool isMaximized )
0311     {
0312 
0313         // define colors
0314         ColorUtils::Rgba base( color( Palette::Window, options ) );
0315         bool renderingWindeco(context && !window);
0316 
0317         // the hard-coded metrics are copied for
0318         // kdebase/workspace/libs/oxygen/oxygenhelper.cpp
0319         // vertical shift to account for window decoration
0320         const int yShift = 23;
0321 
0322         // toplevel window information and relative positioning
0323         gint ww(0), wh(0);
0324         gint wx(0), wy(0);
0325 
0326         // if we aren't going to draw window decorations...
0327         bool needToDestroyContext( false );
0328         if( renderingWindeco )
0329         {
0330             // drawing window decorations, so logic is simplified
0331             ww=w;
0332             wh=h;
0333             cairo_save(context);
0334             cairo_translate(context,x,y);
0335             x=0;
0336             y=0;
0337 
0338         } else {
0339 
0340 
0341             if( !context )
0342             {
0343               // create context and translate to toplevel coordinates
0344               // make it the old good way since context is cairo_t* instead Cairo::Context
0345               context = gdk_cairo_create(window);
0346               needToDestroyContext=true;
0347 
0348               if( clipRect )
0349               {
0350                   cairo_rectangle(context,clipRect->x,clipRect->y,clipRect->width,clipRect->height);
0351                   cairo_clip(context);
0352               }
0353 
0354             } else cairo_save( context );
0355 
0356             // get window dimension and position
0357             // paint flat background when mapping failed
0358             if( !Gtk::gdk_map_to_toplevel( window, widget, &wx, &wy, &ww, &wh, true ) )
0359             {
0360 
0361                 #if OXYGEN_DEBUG
0362                 std::cerr << "Oxygen::Style::renderBackgroundGradient - map_to_toplevel failed" << std::endl;
0363                 std::cerr << "original xywh: ("<<x<<","<<y<<","<<w<<","<<h<<")\n";
0364                 #endif
0365 
0366                 if( _settings.applicationName().isOpenOffice() && GTK_IS_WINDOW(widget))
0367                 {
0368                     gtk_window_get_size(GTK_WINDOW(widget),&ww,&wh);
0369                     wx=0;
0370                     wy=0;
0371                     cairo_translate(context,x,y);
0372                     if(clipRect)
0373                     {
0374                         #if OXYGEN_DEBUG
0375                         std::cerr << "original clipRect: " << *clipRect << std::endl;
0376                         #endif
0377                         clipRect->x-=x;
0378                         clipRect->y-=y;
0379                         clipRect->width-=x;
0380                         clipRect->height-=y;
0381                     }
0382                     x=y=0;
0383                     #if OXYGEN_DEBUG
0384                     std::cerr <<"Oxygen::Style::renderBackgroundGradient - setting openoffice-specific coords:"<<wx<<","<<wy<<","<<ww<<","<<wh<<"\n\n";
0385                     #endif
0386                 }
0387                 else
0388                 {
0389                     // flat painting for all other apps
0390                     cairo_set_source(context,base);
0391                     cairo_rectangle(context,x,y,w,h);
0392                     cairo_fill(context);
0393                     if( needToDestroyContext ) cairo_destroy(context);
0394                     else cairo_restore(context);
0395                     return false;
0396                 }
0397 
0398             }
0399 
0400             // translate to toplevel coordinates
0401             wy += yShift;
0402             x+=wx;
0403             y+=wy;
0404 
0405             // no sense in context saving since it will be destroyed
0406             cairo_translate( context, -wx, -wy );
0407 
0408         }
0409 
0410         if(options&DrawAlphaChannel)
0411         {
0412             base.setAlpha(_settings.backgroundOpacity()/255.);
0413             cairo_set_operator(context,CAIRO_OPERATOR_SOURCE);
0414         }
0415 
0416         // split
0417         const int splitY( std::min(300, 3*wh/4 ) );
0418 
0419         // store rectangle
0420         GdkRectangle rect = { x, y, w, h };
0421 
0422         /*
0423         if there is a valid clipRect,
0424         intersects it with painting Rect, for performances
0425         */
0426         if( clipRect )
0427         {
0428 
0429             GdkRectangle localClip( *clipRect );
0430             localClip.x += wx;
0431             localClip.y += wy;
0432             gdk_rectangle_intersect( &rect, &localClip, &rect );
0433 
0434         }
0435 
0436         // upper rect
0437         GdkRectangle upperRect = { 0, 0, ww, splitY };
0438         if( gdk_rectangle_intersect( &rect, &upperRect, &upperRect ) )
0439         {
0440 
0441             const Cairo::Surface& surface( _helper.verticalGradient( base, splitY ) );
0442             cairo_set_source_surface( context, surface, 0, 0 );
0443             cairo_pattern_set_extend( cairo_get_source( context ), CAIRO_EXTEND_REPEAT );
0444             gdk_cairo_rectangle( context, &upperRect );
0445             cairo_fill( context );
0446 
0447         }
0448 
0449         // fill lower rect
0450         GdkRectangle lowerRect = { 0, splitY, ww, wh - splitY + yShift };
0451         if( gdk_rectangle_intersect( &rect, &lowerRect, &lowerRect ) )
0452         {
0453 
0454             ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
0455             gdk_cairo_rectangle( context, &lowerRect );
0456             cairo_set_source( context, bottom );
0457             cairo_fill( context );
0458 
0459         }
0460 
0461         // gradient should be rendered with full opacity
0462         base.setAlpha(1);
0463         cairo_set_operator(context,CAIRO_OPERATOR_OVER);
0464 
0465         // radial pattern
0466         const int patternHeight = 64;
0467         const int radialW( std::min(600, ww ) );
0468 
0469         GdkRectangle radialRect = {  (ww - radialW)/2, 0, radialW, patternHeight };
0470         if( gdk_rectangle_intersect( &rect, &radialRect, &radialRect ) )
0471         {
0472 
0473             const Cairo::Surface& surface( _helper.radialGradient( base, 64 ) );
0474             cairo_set_source_surface( context, surface, 0, 0 );
0475 
0476             // add matrix transformation
0477             cairo_matrix_t transformation;
0478             cairo_matrix_init_identity( &transformation );
0479             cairo_matrix_scale( &transformation, 128.0/radialW, 1.0 );
0480             cairo_matrix_translate( &transformation, -(ww - radialW)/2, 0 );
0481             cairo_pattern_set_matrix( cairo_get_source( context ), &transformation );
0482 
0483             gdk_cairo_rectangle( context, &radialRect );
0484             cairo_fill( context );
0485 
0486         }
0487 
0488         if(needToDestroyContext) cairo_destroy(context);
0489         else cairo_restore(context);
0490 
0491         return true;
0492 
0493     }
0494 
0495     //__________________________________________________________________
0496     bool Style::renderBackgroundPixmap(
0497         cairo_t* context, GdkWindow* window, GtkWidget* widget,
0498         GdkRectangle* clipRect, gint x, gint y, gint w, gint h,
0499         bool isMaximized )
0500     {
0501 
0502         // define colors
0503         bool renderingWindeco(context && !window);
0504 
0505         // the hard-coded metrics are copied for
0506         // kdebase/workspace/libs/oxygen/oxygenhelper.cpp
0507         // vertical shift to account for window decoration
0508         const int yShift = 23;
0509 
0510         // toplevel window information and relative positioning
0511         gint ww(0), wh(0);
0512         gint wx(0), wy(0);
0513 
0514         // if we aren't going to draw window decorations...
0515         bool needToDestroyContext( false );
0516         if( renderingWindeco )
0517         {
0518             // drawing window decorations, so logic is simplified
0519             ww=w;
0520             wh=h;
0521             cairo_save(context);
0522             cairo_translate(context,x,y);
0523             x=0;
0524             y=0;
0525 
0526         } else {
0527 
0528             if( !context )
0529             {
0530               // create context and translate to toplevel coordinates
0531               // make it the old good way since context is cairo_t* instead Cairo::Context
0532               context = gdk_cairo_create(window);
0533               needToDestroyContext=true;
0534 
0535               if( clipRect )
0536               {
0537                   cairo_rectangle(context,clipRect->x,clipRect->y,clipRect->width,clipRect->height);
0538                   cairo_clip(context);
0539               }
0540 
0541             } else cairo_save( context );
0542 
0543             // get window dimension and position
0544             // paint flat background when mapping failed
0545             if( !Gtk::gdk_map_to_toplevel( window, widget, &wx, &wy, &ww, &wh, true ) )
0546             {
0547                 if( needToDestroyContext ) cairo_destroy(context);
0548                 else cairo_restore(context);
0549                 return false;
0550             }
0551 
0552             // translate to toplevel coordinates
0553             wy += yShift;
0554             x+=wx;
0555             y+=wy;
0556 
0557             // no sense in context saving since it will be destroyed
0558             cairo_translate( context, -wx, -wy );
0559 
0560         }
0561 
0562         // store rectangle
0563         GdkRectangle rect = { x, y, w, h };
0564 
0565         /*
0566         if there is a valid clipRect,
0567         intersects it with painting Rect, for performances
0568         */
0569         if( clipRect )
0570         {
0571 
0572             GdkRectangle localClip( *clipRect );
0573             localClip.x += wx;
0574             localClip.y += wy;
0575             gdk_rectangle_intersect( &rect, &localClip, &rect );
0576 
0577         }
0578 
0579         // Additional clip constraint so that no extra space is filled (important for LibreOffice)
0580         cairo_rectangle(context,x,y,w,h);
0581         cairo_clip(context);
0582 
0583         if(renderingWindeco)
0584         {
0585             // Take border sizes into account
0586             int bgShiftX=isMaximized?0:WinDeco::getMetric(WinDeco::BorderLeft);
0587             int bgShiftY=WinDeco::getMetric(WinDeco::BorderTop)-yShift;
0588             cairo_translate(context,bgShiftX,bgShiftY);
0589         }
0590 
0591         // no sense in context saving since it will be either destroyed or restored to earlier state
0592         cairo_translate( context, -40, -(48-20) );
0593         cairo_set_source_surface( context, _backgroundSurface, 0, 0 );
0594         cairo_rectangle( context, 0, 0, ww + wx + 40, wh + wy + 48 - 20 );
0595         cairo_fill( context );
0596 
0597         if(needToDestroyContext) cairo_destroy(context);
0598         else cairo_restore(context);
0599 
0600         return true;
0601 
0602     }
0603 
0604     //__________________________________________________________________
0605     bool Style::renderGroupBoxBackground(
0606         cairo_t* context,
0607         GdkWindow* window, GtkWidget* widget,
0608         GdkRectangle* clipRect, gint x, gint y, gint w, gint h,
0609         const StyleOptions& options,
0610         TileSet::Tiles tiles )
0611     {
0612 
0613         // find groupbox parent
0614         GtkWidget* parent( Gtk::gtk_parent_groupbox( widget ) );
0615         if( !( parent && _animations.groupBoxEngine().contains( parent ) ) ) return false;
0616 
0617         // toplevel window information and relative positioning
0618         gint ww(0), wh(0);
0619         gint wx(0), wy(0);
0620 
0621         // map to parent
0622         if( !Gtk::gtk_widget_map_to_parent( widget, parent, &wx, &wy, &ww, &wh ) )
0623         { return false; }
0624 
0625         // create context and translate
0626         bool needToDestroyContext( false );
0627         if( !context )
0628         {
0629             // create context and translate to toplevel coordinates
0630             // make it the old good way since context is cairo_t* instead Cairo::Context
0631             context = gdk_cairo_create(window);
0632             needToDestroyContext=true;
0633 
0634             if( clipRect )
0635             {
0636               cairo_rectangle(context,clipRect->x,clipRect->y,clipRect->width,clipRect->height);
0637               cairo_clip(context);
0638             }
0639 
0640         } else cairo_save( context );
0641 
0642         const int margin( 1 );
0643         wh += 2*margin;
0644         ww += 2*margin;
0645         x+=wx;
0646         y+=wy;
0647         cairo_translate( context, -wx, -wy );
0648 
0649         // define colors
0650         ColorUtils::Rgba base;
0651         if( options&Blend )
0652         {
0653 
0654             gint wwh, wwy;
0655             Gtk::gtk_widget_map_to_toplevel( parent, 0L, &wwy, 0L, &wwh );
0656             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wwh, wwy-1+wh/2 );
0657 
0658         } else {
0659 
0660             base = _settings.palette().color( Palette::Window );
0661 
0662         }
0663 
0664         const int xGroupBox = x - wx - margin;
0665         const int yGroupBox = y - wy - margin;
0666         renderGroupBox( context, base, xGroupBox, yGroupBox, ww, wh, options );
0667 
0668         if(needToDestroyContext) cairo_destroy(context);
0669         else cairo_restore(context);
0670 
0671         return true;
0672 
0673     }
0674 
0675     //__________________________________________________________________
0676     bool Style::renderMenuBackground( GdkWindow* window, Cairo::Context& context, gint x, gint y, gint w, gint h, const StyleOptions& options ) const
0677     {
0678         // define colors
0679         ColorUtils::Rgba base( color( Palette::Window, options ) );
0680         ColorUtils::Rgba top( ColorUtils::backgroundTopColor( base ) );
0681         ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
0682 
0683         // get window dimension and position
0684         gint ww, wh;
0685         gint wx, wy;
0686         if( !Gtk::gdk_map_to_toplevel( window, 0L, &wx, &wy, &ww, &wh, true ) )
0687         { return false; }
0688 
0689         // translate to toplevel coordinates
0690         x+=wx;
0691         y+=wy;
0692 
0693         cairo_translate( context, -wx, -wy );
0694         const bool hasAlpha( options&Alpha );
0695         const bool isMenu( options&Menu );
0696         const bool round( options&Round );
0697 
0698         GdkRectangle rect = { x, y, w, h };
0699 
0700         // paint translucent first
0701         if( hasAlpha )
0702         {
0703             cairo_rectangle( context, 0, 0, ww, wh );
0704             cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
0705             cairo_set_source( context, ColorUtils::alphaColor( base, 0 ) );
0706             cairo_fill( context );
0707 
0708             if(_settings.backgroundOpacity()<255)
0709             {
0710                 double opacity(_settings.backgroundOpacity()/255.);
0711                 top.setAlpha(opacity);
0712                 bottom.setAlpha(opacity);
0713             }
0714         }
0715 
0716         const int splitY( std::min(200, 3*wh/4 ) );
0717         const int verticalOffset( (isMenu && round) ? Menu_VerticalOffset:0 );
0718 
0719         GdkRectangle upperRect = { 0, verticalOffset, ww, splitY - verticalOffset };
0720         if( gdk_rectangle_intersect( &rect, &upperRect, &upperRect ) )
0721         {
0722             // upper rect
0723             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, splitY ) );
0724             cairo_pattern_add_color_stop( pattern, 0, top );
0725             cairo_pattern_add_color_stop( pattern, 1, bottom );
0726 
0727             gdk_cairo_rounded_rectangle( context, &upperRect, 3.5, round ? CornersTop:CornersNone );
0728             cairo_set_source( context, pattern );
0729             cairo_fill( context );
0730 
0731         }
0732 
0733         GdkRectangle lowerRect = { 0, splitY, ww, wh-splitY - verticalOffset };
0734         if( gdk_rectangle_intersect( &rect, &lowerRect, &lowerRect ) )
0735         {
0736 
0737             // lower part
0738             gdk_cairo_rounded_rectangle( context, &lowerRect, 3.5, round ? CornersBottom:CornersNone );
0739             cairo_set_source( context, bottom );
0740             cairo_fill( context );
0741 
0742         }
0743 
0744         return true;
0745 
0746     }
0747 
0748     //__________________________________________________________________
0749     void Style::renderTooltipBackground( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const StyleOptions& options ) const
0750     {
0751 
0752         // define colors
0753         ColorUtils::Rgba base(_settings.palette().color( Palette::Tooltip ) );
0754         ColorUtils::Rgba top( ColorUtils::backgroundTopColor( base ) );
0755         ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
0756 
0757         // create context and translate
0758         Cairo::Context context( window, clipRect );
0759         cairo_translate( context, x, y );
0760 
0761         // paint translucent first
0762         const bool hasAlpha( (options&Alpha) );
0763         bool round( GDK_IS_WINDOW( window ) && (options&Round) );
0764 
0765         if( hasAlpha )
0766         {
0767             if( _settings.tooltipTransparent() )
0768             {
0769                 top.setAlpha( 0.86 );
0770                 bottom.setAlpha( 0.86 );
0771             }
0772 
0773             cairo_rectangle( context, 0, 0, w, h );
0774             cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
0775             cairo_set_source( context, ColorUtils::alphaColor( base, 0 ) );
0776             cairo_fill( context );
0777         }
0778 
0779         // fill
0780         {
0781             GdkRectangle rect = { 0, 0, w, h };
0782             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, h ) );
0783             cairo_pattern_add_color_stop( pattern, 0, top );
0784             cairo_pattern_add_color_stop( pattern, 1, bottom );
0785 
0786             gdk_cairo_rounded_rectangle( context, &rect, 3.5, round ? CornersAll:CornersNone );
0787             cairo_set_source( context, pattern );
0788             cairo_fill( context );
0789 
0790         }
0791 
0792         // contrast pixel
0793         {
0794             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, h ) );
0795             cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::lightColor( bottom ) );
0796             cairo_pattern_add_color_stop( pattern, 0.9, bottom );
0797 
0798             cairo_rounded_rectangle( context, 0.5, 0.5, w-1, h-1, 3.5, round ? CornersAll:CornersNone );
0799             cairo_set_line_width( context, 1.0 );
0800             cairo_set_source( context, pattern );
0801             cairo_stroke( context );
0802         }
0803 
0804         return;
0805 
0806     }
0807 
0808     //__________________________________________________________________
0809     void Style::renderHeaderBackground( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h )
0810     {
0811 
0812         // load color
0813         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
0814 
0815         // render normal window background
0816         renderWindowBackground( window, clipRect, x, y, w, h );
0817 
0818         // render lines
0819         renderHeaderLines( window, clipRect, x, y, w, h );
0820 
0821         // render side dots
0822         Cairo::Context context( window, clipRect );
0823         int yCenter( y + h/2 );
0824         int xDots( x + w - 1 );
0825         _helper.renderDot( context, base, xDots, yCenter - 3 );
0826         _helper.renderDot( context, base, xDots, yCenter );
0827         _helper.renderDot( context, base, xDots, yCenter + 3 );
0828 
0829     }
0830 
0831 
0832     //__________________________________________________________________
0833     void Style::renderHeaderLines( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h ) const
0834     {
0835 
0836         // add horizontal lines
0837         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
0838         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
0839         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
0840 
0841         Cairo::Context context( window, clipRect );
0842         cairo_set_line_width( context, 1.0 );
0843 
0844         // dark line
0845         cairo_set_source( context, dark );
0846         cairo_move_to( context, x, y+h-0.5 );
0847         cairo_line_to( context, x+w, y+h-0.5 );
0848         cairo_stroke( context );
0849 
0850         // light line
0851         cairo_set_source( context, light );
0852         cairo_move_to( context, x, y+h-1.5 );
0853         cairo_line_to( context, x+w, y+h-1.5 );
0854         cairo_stroke( context );
0855 
0856     }
0857 
0858     //____________________________________________________________________________________
0859     void Style::renderTreeLines( GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const Gtk::CellInfoFlags& cellFlags, const StyleOptions& options ) const
0860     {
0861 
0862         // define pen color
0863         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
0864         const ColorUtils::Rgba base( ColorUtils::mix(
0865             _settings.palette().color( group, Palette::Text ),
0866             _settings.palette().color( group, Palette::Window ),
0867             0.8 ) );
0868 
0869         Cairo::Context context( window, clipRect );
0870         cairo_set_source( context, base );
0871         cairo_set_line_width( context, 1.0 );
0872 
0873         const bool reversed( cellFlags._flags & Gtk::CellInfoFlags::Reversed );
0874 
0875         int cellIndent( cellFlags._levelIndent + cellFlags._expanderSize + 4 );
0876         int xStart( x + cellIndent/2 );
0877 
0878         if( reversed ) {
0879 
0880             xStart += w - cellIndent;
0881             cellIndent *= -1;
0882 
0883         }
0884 
0885         for( unsigned int i=0; i< cellFlags._depth; ++i  )
0886         {
0887 
0888             const bool isLastCell( cellFlags._isLast[i] );
0889             const bool last( i == cellFlags._depth -1 );
0890             double xCenter = xStart;
0891 
0892             if( last )
0893             {
0894 
0895                 double yCenter = int(y+h/2);
0896                 const bool hasChildren( cellFlags._flags & Gtk::CellInfoFlags::HasChildren );
0897 
0898                 if( hasChildren )
0899                 {
0900                     // first vertical line
0901                     cairo_move_to( context, xCenter + 0.5 , y );
0902                     cairo_line_to( context, xCenter + 0.5, yCenter - int(cellFlags._expanderSize/3 )-1 );
0903 
0904                     // second vertical line
0905                     if( !isLastCell )
0906                     {
0907 
0908                         cairo_move_to( context, xCenter + 0.5, y+h );
0909                         cairo_line_to( context, xCenter + 0.5, yCenter + int( cellFlags._expanderSize/3 )+2 );
0910                     }
0911 
0912                     // horizontal line
0913                     if( reversed )
0914                     {
0915 
0916                         cairo_move_to( context, xCenter - 1 - int( cellFlags._expanderSize/3 ), yCenter + 0.5 );
0917                         cairo_line_to( context, xCenter + 1  - cellFlags._expanderSize*2/3, yCenter + 0.5 );
0918 
0919                     } else {
0920 
0921                         cairo_move_to( context, xCenter + 2 + int( cellFlags._expanderSize/3 ), yCenter + 0.5 );
0922                         cairo_line_to( context, xCenter + cellFlags._expanderSize*2/3, yCenter + 0.5 );
0923 
0924                     }
0925 
0926                 } else {
0927 
0928                     // vertical line
0929                     cairo_move_to( context, xCenter + 0.5, y );
0930                     if( isLastCell ) cairo_line_to( context, xCenter + 0.5, yCenter );
0931                     else cairo_line_to( context, xCenter + 0.5, y+h );
0932 
0933                     // horizontal line
0934                     if( reversed )
0935                     {
0936 
0937                         cairo_move_to( context, xCenter + 1 , yCenter + 0.5 );
0938                         cairo_line_to( context, xCenter + 1  - cellFlags._expanderSize*2/3, yCenter + 0.5 );
0939 
0940                     } else {
0941 
0942                         cairo_move_to( context, xCenter, yCenter + 0.5 );
0943                         cairo_line_to( context, xCenter + cellFlags._expanderSize*2/3, yCenter + 0.5 );
0944 
0945                     }
0946 
0947                 }
0948 
0949             } else if( !isLastCell ) {
0950 
0951 
0952                 // vertical line
0953                 cairo_move_to( context, xCenter + 0.5, y );
0954                 cairo_line_to( context, xCenter + 0.5, y + h );
0955 
0956             }
0957 
0958             // render
0959             cairo_stroke( context );
0960 
0961             // increment
0962             xStart += cellIndent;
0963 
0964         }
0965 
0966         return;
0967     }
0968 
0969     //____________________________________________________________________________________
0970     void Style::renderHoleBackground(
0971         GdkWindow* window,
0972         GtkWidget* widget,
0973         GdkRectangle* clipRect,
0974         gint x, gint y, gint w, gint h, const StyleOptions& options, TileSet::Tiles tiles,
0975         gint sideMargin )
0976     {
0977 
0978         // do nothing if not enough room
0979         if( w < 14 || h < 14 )  return;
0980 
0981         // add hole mask
0982         Cairo::Context context( window, clipRect );
0983         renderHoleMask( context, x, y, w, h, tiles, sideMargin );
0984 
0985         if( (options&Flat) || _settings.applicationName().useFlatBackground( widget ) )
0986         {
0987 
0988             // create a rounded-rect antimask for renderHoleBackground
0989             cairo_set_source( context, _settings.palette().color( Palette::Window ) );
0990             cairo_rectangle( context, x, y, w, h );
0991             cairo_fill( context );
0992 
0993         } else if( GtkWidget* parent = _animations.flatWidgetEngine().flatParent( widget ) ) {
0994 
0995             // get background color and fill
0996             if( Gtk::gtk_widget_style_is_modified( parent, GTK_STATE_NORMAL, GTK_RC_BG ) )
0997             {
0998 
0999                 cairo_set_source( context, Gtk::gdk_get_color( gtk_widget_get_modifier_style( parent )->bg[GTK_STATE_NORMAL] ) );
1000 
1001             } else {
1002 
1003                 cairo_set_source( context, _settings.palette().color( Palette::Window ) );
1004 
1005             }
1006 
1007             cairo_rectangle( context, x, y, w, h );
1008             cairo_fill( context );
1009 
1010         } else {
1011 
1012             // normal window background
1013             renderWindowBackground( context, window, 0L, clipRect, x, y, w, h, options, tiles);
1014 
1015             // possible groupbox background
1016             // Pass NoFill option in order not to render the surrounding frame
1017             if( widget )
1018             { renderGroupBoxBackground( context, window, widget, clipRect, x, y, w, h, options | Blend | NoFill, tiles ); }
1019 
1020         }
1021 
1022     }
1023 
1024     //__________________________________________________________________
1025     void Style::renderSplitter(
1026         GdkWindow* window, GdkRectangle* clipRect,
1027         gint x, gint y, gint w, gint h,
1028         const StyleOptions& options,
1029         const AnimationData& data ) const
1030     {
1031 
1032         // orientation
1033         const bool vertical( options&Vertical );
1034 
1035         // colors
1036         const ColorUtils::Rgba& base( _settings.palette().color( Palette::Window ) );
1037 
1038         // context
1039         Cairo::Context context( window, clipRect );
1040 
1041         // hover color
1042         ColorUtils::Rgba highlight;
1043         if( data._mode == AnimationHover )
1044         {
1045 
1046             highlight = ColorUtils::alphaColor( ColorUtils::lightColor( base ), 0.5*data._opacity );
1047 
1048         } else if( options&Hover ) {
1049 
1050             highlight = ColorUtils::alphaColor( ColorUtils::lightColor( base ), 0.5 );
1051 
1052         }
1053 
1054         // render hover rect
1055         if( highlight.isValid() )
1056         {
1057 
1058             Cairo::Context context( window, clipRect );
1059             Cairo::Pattern pattern;
1060             double a(0.1);
1061             if( vertical )
1062             {
1063 
1064                 if( w > 30 ) a = 10.0/w;
1065                 pattern.set( cairo_pattern_create_linear( x, 0, x+w, 0 ) );
1066 
1067             } else {
1068 
1069                 if( h>30 ) a = 10.0/h;
1070                 pattern.set( cairo_pattern_create_linear( 0, y, 0, y+h ) );
1071 
1072             }
1073 
1074             cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( highlight, 0 ) );
1075             cairo_pattern_add_color_stop( pattern, a, highlight );
1076             cairo_pattern_add_color_stop( pattern, 1.0-a, highlight );
1077             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( highlight, 0 ) );
1078 
1079             cairo_set_source( context, pattern );
1080             cairo_rectangle( context, x, y, w, h );
1081             cairo_fill( context );
1082         }
1083 
1084         // dots
1085         if( vertical )
1086         {
1087 
1088             y += h/2;
1089             const int ngroups( std::max( 1, w/250 ) );
1090             int center = ( w-( ngroups-1 )*250 )/2 + x;
1091             for( int k = 0; k < ngroups; k++, center += 250 )
1092             {
1093                 _helper.renderDot( context, base, center-3, y );
1094                 _helper.renderDot( context, base, center, y );
1095                 _helper.renderDot( context, base, center+3, y );
1096             }
1097 
1098         } else {
1099 
1100             x += w/2;
1101             const int ngroups( std::max( 1, h/250 ) );
1102             int center = ( h-( ngroups-1 )*250 )/2 + y;
1103             for( int k = 0; k < ngroups; k++, center += 250 )
1104             {
1105                 _helper.renderDot( context, base, x, center-3 );
1106                 _helper.renderDot( context, base, x, center );
1107                 _helper.renderDot( context, base, x, center+3 );
1108             }
1109 
1110         }
1111 
1112     }
1113 
1114     //____________________________________________________________________________________
1115     void Style::renderProgressBarHole(
1116         GdkWindow* window,
1117         GdkRectangle* clipRect,
1118         gint x, gint y, gint w, gint h, const StyleOptions& options )
1119     {
1120 
1121         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1122         const ColorUtils::Rgba base(_settings.palette().color( group, Palette::Window ) );
1123 
1124         Cairo::Context context( window, clipRect );
1125         renderScrollBarHole( context, x, y, w, h, base, (options&Vertical) );
1126 
1127     }
1128 
1129     //____________________________________________________________________________________
1130     void Style::renderProgressBarHandle(
1131         GdkWindow* window,
1132         GdkRectangle* clipRect,
1133         gint x, gint y, gint w, gint h, const StyleOptions& options )
1134     {
1135 
1136         // colors
1137         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1138         const ColorUtils::Rgba base( _settings.palette().color( Palette::Active, Palette::Window ) );
1139         const ColorUtils::Rgba glow( _settings.palette().color( group, Palette::Selected ) );
1140 
1141         // context
1142         Cairo::Context context( window, clipRect );
1143 
1144         // validate rect
1145         if(w<0 || h<0) return;
1146 
1147         // make sure that width is large enough
1148         const int indicatorSize( (options&Vertical ? h:w ) );
1149 
1150         if( indicatorSize >= 3 && w > 0 && h > 1 )
1151         {
1152             // get surface
1153             const Cairo::Surface& surface( _helper.progressBarIndicator( base, glow, w, h-1 ) );
1154             cairo_translate( context, x, y );
1155             cairo_rectangle( context, 0, 0, w, h-1 );
1156             cairo_set_source_surface( context, surface, 0, 0 );
1157             cairo_fill( context );
1158         }
1159 
1160     }
1161 
1162     //____________________________________________________________________________________
1163     void Style::renderScrollBarHole(
1164         GdkWindow* window,
1165         GdkRectangle* clipRect,
1166         gint x, gint y, gint w, gint h, const StyleOptions& options )
1167     {
1168 
1169         // colors
1170         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1171         const ColorUtils::Rgba base(_settings.palette().color( group, Palette::Window ) );
1172 
1173         // context
1174         Cairo::Context context( window, clipRect );
1175         renderScrollBarHole( context, x, y, w, h, base, options&Vertical );
1176 
1177     }
1178 
1179     //____________________________________________________________________________________
1180     void Style::renderHoleMask( cairo_t* context, gint x, gint y, gint w, gint h, TileSet::Tiles tiles, gint sideMargin )
1181     {
1182 
1183         GdkRectangle mask = {x+2, y+1, w-4, h-3 };
1184         const double maskRadius = 3.5;
1185         Corners corners( CornersNone );
1186         if( tiles & TileSet::Left )
1187         {
1188             mask.x += sideMargin;
1189             mask.width -= sideMargin;
1190             if( tiles & TileSet::Top ) corners |= CornersTopLeft;
1191             if( tiles & TileSet::Bottom ) corners |= CornersBottomLeft;
1192         }
1193 
1194         if( tiles & TileSet::Right )
1195         {
1196             mask.width -= sideMargin;
1197             if( tiles & TileSet::Top ) corners |= CornersTopRight;
1198             if( tiles & TileSet::Bottom ) corners |= CornersBottomRight;
1199         }
1200 
1201         // set clipping mask
1202         gdk_cairo_rounded_rectangle_negative(context,&mask,maskRadius,CornersAll);
1203         cairo_rectangle(context,x,y,w,h);
1204         cairo_clip(context);
1205 
1206         return;
1207 
1208     }
1209 
1210     //____________________________________________________________________________________
1211     void Style::renderScrollBarHandle(
1212         GdkWindow* window,
1213         GdkRectangle* clipRect,
1214         gint x, gint y, gint w, gint h,
1215         const StyleOptions& options,
1216         const AnimationData& data )
1217     {
1218 
1219         // vertical
1220         const bool vertical( options&Vertical );
1221 
1222         const double xf( vertical ? x+3 : x+4 );
1223         const double yf( vertical ? y+3 : y+2 );
1224         const double wf( vertical ? w-6 : w-8 );
1225         const double hf( vertical ? h-6 : h-5 );
1226 
1227 //         const double xf( vertical ? x+2 : x+3 );
1228 //         const double yf( vertical ? y+2 : y+1 );
1229 //         const double wf( vertical ? w-4 : w-6 );
1230 //         const double hf( vertical ? h-4 : h-3 );
1231 
1232         if( wf <= 0 || hf <= 0 ) return;
1233 
1234         // context
1235         Cairo::Context context( window, clipRect );
1236 
1237         // store colors
1238         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1239         const ColorUtils::Rgba color( _settings.palette().color( group, Palette::Button ) );
1240 
1241         const double radius( 3.5 );
1242 
1243         // glow color
1244         ColorUtils::Rgba glow;
1245         const ColorUtils::Rgba shadow( ColorUtils::alphaColor( ColorUtils::shadowColor( color ), 0.4 ) );
1246         const ColorUtils::Rgba hovered( _settings.palette().color( Palette::Hover ) );
1247         if( data._mode == AnimationHover ) glow = ColorUtils::mix( shadow, hovered, data._opacity );
1248         else if( options&Hover ) glow = hovered;
1249         else glow = shadow;
1250 
1251         _helper.scrollHandle( color, glow ).render( context, xf-3, yf-3, wf+6, hf+6, TileSet::Full );
1252 
1253         // contents
1254         const ColorUtils::Rgba mid( ColorUtils::midColor( color ) );
1255         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, yf, 0, yf+hf ) );
1256         cairo_pattern_add_color_stop( pattern, 0, color );
1257         cairo_pattern_add_color_stop( pattern, 1, mid );
1258         cairo_set_source( context, pattern );
1259         cairo_rounded_rectangle( context, xf+1, yf+1, wf-2, hf-2, radius - 2 );
1260         cairo_fill( context );
1261 
1262         // bevel pattern
1263         {
1264             const ColorUtils::Rgba light( ColorUtils::lightColor( color ) );
1265             Cairo::Pattern pattern;
1266             if( vertical ) pattern.set( cairo_pattern_create_linear( 0, 0, 0, 30 ) );
1267             else pattern.set( cairo_pattern_create_linear( 0, 0, 30, 0 ) );
1268             cairo_pattern_set_extend( pattern, CAIRO_EXTEND_REFLECT );
1269 
1270             cairo_pattern_add_color_stop( pattern, 0, ColorUtils::Rgba::transparent() );
1271             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( light, 0.1 ) );
1272 
1273             cairo_set_source( context, pattern );
1274             if( vertical ) cairo_rectangle( context, xf+3, yf, wf-6, hf );
1275             else cairo_rectangle( context, xf, yf+3, wf, hf-6 );
1276             cairo_fill( context );
1277         }
1278 
1279     }
1280 
1281     //____________________________________________________________________________________
1282     void Style::renderToolBarHandle(
1283         GdkWindow* window,
1284         GdkRectangle* clipRect,
1285         gint x, gint y, gint w, gint h, const StyleOptions& options ) const
1286     {
1287 
1288         const bool vertical( options & Vertical );
1289         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
1290 
1291         Cairo::Context context( window, clipRect );
1292         int counter(0);
1293         if( vertical )
1294         {
1295 
1296             const int xcenter( x+w/2 );
1297             for( int ycenter = y+2; ycenter <= y+h-3; ycenter+=3, ++counter )
1298             {
1299                 if( counter%2 == 0 ) _helper.renderDot( context, base, xcenter+1, ycenter );
1300                 else _helper.renderDot( context, base, xcenter-2, ycenter );
1301             }
1302 
1303         } else {
1304 
1305             const int ycenter( y + h/2 );
1306             for( int xcenter = x+2; xcenter < x+w-3; xcenter+=3, ++counter )
1307             {
1308                 if( counter%2 == 0 ) _helper.renderDot( context, base, xcenter, ycenter+1 );
1309                 else _helper.renderDot( context, base, xcenter, ycenter-2 );
1310             }
1311 
1312         }
1313 
1314         return;
1315 
1316     }
1317 
1318     //__________________________________________________________________
1319     void Style::drawFloatFrame( cairo_t* context, GdkWindow* window, GdkRectangle* clipRect, gint x, gint y, gint w, gint h, const StyleOptions& options, Palette::Role role ) const
1320     {
1321 
1322         // define colors
1323         ColorUtils::Rgba base(_settings.palette().color( role ) );
1324         ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1325         ColorUtils::Rgba dark( ColorUtils::darkColor( ColorUtils::backgroundBottomColor( base ) ) );
1326 
1327         const bool hasAlpha( options&Alpha );
1328         const bool isMenu( options&Menu );
1329         const bool drawUglyShadow( !hasAlpha );
1330         const bool rounded( options&Round );
1331 
1332         // if we aren't drawing window decoration
1333         if( !context )
1334         {
1335             // create context
1336             // make it the old good way since context is cairo_t* instead Cairo::Context
1337             context=gdk_cairo_create(window);
1338             if(clipRect)
1339             {
1340                 cairo_rectangle(context,clipRect->x,clipRect->y,clipRect->width,clipRect->height);
1341                 cairo_clip(context);
1342             }
1343         }
1344 
1345         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, double(y)+0.5, 0, y+h-1 ) );
1346 
1347         // add vertical offset
1348         if( isMenu && rounded )
1349         {
1350             y += Menu_VerticalOffset;
1351             h -= 2*Menu_VerticalOffset;
1352         }
1353 
1354         if( drawUglyShadow )
1355         {
1356 
1357             // adjust rectangle
1358             x++;
1359             y++;
1360             w-=2;
1361             h-=2;
1362 
1363             cairo_set_line_width(context,1);
1364 
1365             if( options&Focus )
1366             {
1367 
1368                 // window is active - it's a glow, not a shadow
1369                 const ColorUtils::Rgba frameColor( _settings.palette().color( Palette::ActiveWindowBackground ) );
1370                 const ColorUtils::Rgba glow = ColorUtils::mix(ColorUtils::Rgba(0.5,0.5,0.5),frameColor,0.7);
1371                 cairo_set_source(context,glow);
1372 
1373                 const double radius( 11*0.5 );
1374                 cairo_move_to( context, x+4, y-0.5 ); cairo_line_to( context, x+w-4, y-0.5 );
1375                 cairo_stroke( context );
1376 
1377                 cairo_arc_negative( context, x-0.5+radius, y-0.5+radius, radius, -0.5*M_PI, -M_PI );
1378                 cairo_stroke( context );
1379                 cairo_arc_negative( context, x+w-11+0.5+radius, y-0.5+radius, radius, 0, -0.5*M_PI );
1380                 cairo_stroke( context );
1381 
1382                 cairo_move_to( context, x-0.5, y+4 ); cairo_line_to( context, x-0.5, y+h-4 );
1383                 cairo_move_to( context, x+w+0.5, y+4 ); cairo_line_to( context, x+w+0.5, y+h-4 );
1384                 cairo_stroke( context );
1385 
1386                 cairo_arc_negative( context, x-0.5+radius, y+h-11+0.5+radius, radius, -M_PI, -1.5*M_PI );
1387                 cairo_stroke( context );
1388                 cairo_arc_negative( context, x+w-11+0.5+radius, y+h-11+0.5+radius, radius, 0.5*M_PI, 0 );
1389                 cairo_stroke( context );
1390 
1391                 cairo_move_to( context, x+4, y+h+0.5 ); cairo_line_to( context, x+w-4, y+h+0.5 );
1392                 cairo_stroke( context );
1393 
1394 
1395                 light=ColorUtils::mix(light, frameColor);
1396                 dark=ColorUtils::mix(dark,frameColor);
1397 
1398             } else {
1399 
1400                 // window inactive - draw something resembling shadow
1401                 // fully desaturate
1402                 ColorUtils::Rgba shadow( ColorUtils::darken( base, 0., 0. ) );
1403 
1404                 if(rounded)
1405                 {
1406                     const double radius( 11*0.5 );
1407                     cairo_set_source( context, ColorUtils::darken( shadow, 0.2 ));
1408                     cairo_move_to( context, x+4, y-0.5 ); cairo_line_to( context, x+w-4, y-0.5 );
1409                     cairo_stroke( context );
1410 
1411                     cairo_arc_negative( context, x-0.5+radius, y-0.5+radius, radius, -0.5*M_PI, -M_PI );
1412                     cairo_stroke( context );
1413                     cairo_arc_negative( context, x+w-11+0.5+radius, y-0.5+radius, radius, 0, -0.5*M_PI );
1414                     cairo_stroke( context );
1415 
1416                     cairo_set_source( context, ColorUtils::darken( shadow, 0.35 ));
1417                     cairo_move_to( context, x-0.5, y+4 ); cairo_line_to( context, x-0.5, y+h-4 );
1418                     cairo_move_to( context, x+w+0.5, y+4 ); cairo_line_to( context, x+w+0.5, y+h-4 );
1419                     cairo_stroke( context );
1420 
1421                     cairo_set_source( context, ColorUtils::darken( shadow, 0.45 ));
1422                     cairo_arc_negative( context, x-0.5+radius, y+h-11+0.5+radius, radius, -M_PI, -1.5*M_PI );
1423                     cairo_stroke( context );
1424                     cairo_arc_negative( context, x+w-11+0.5+radius, y+h-11+0.5+radius, radius, 0.5*M_PI, 0 );
1425                     cairo_stroke( context );
1426 
1427                     cairo_set_source( context, ColorUtils::darken( shadow, 0.6 ));
1428                     cairo_move_to( context, x+4, y+h+0.5 ); cairo_line_to( context, x+w-4, y+h+0.5 );
1429                     cairo_stroke( context );
1430 
1431                 } else {
1432 
1433                     cairo_set_source( context, ColorUtils::darken( shadow, 0.2 ));
1434                     cairo_move_to( context, x-0.5, y-0.5 ); cairo_line_to( context, x+w+0.5, y-0.5 );
1435                     cairo_stroke( context );
1436 
1437                     cairo_set_source( context, ColorUtils::darken( shadow, 0.35 ));
1438                     cairo_move_to( context, x-0.5, y-0.5 ); cairo_line_to( context, x-0.5, y+h+0.5 );
1439                     cairo_move_to( context, x+w+0.5, y-0.5 ); cairo_line_to( context, x+w+0.5, y+h+0.5 );
1440                     cairo_stroke( context );
1441 
1442                     cairo_set_source( context, ColorUtils::darken( shadow, 0.6 ));
1443                     cairo_move_to( context, x-0.5, y+h+0.5 ); cairo_line_to( context, x+w+0.5, y+h+0.5 );
1444                     cairo_stroke( context );
1445 
1446                 }
1447             }
1448         }
1449 
1450         cairo_pattern_add_color_stop( pattern, 0, light );
1451 
1452         if( !rounded )
1453         {
1454 
1455             cairo_pattern_add_color_stop( pattern, 1, ColorUtils::alphaColor( dark, 0 ) );
1456 
1457         } else {
1458 
1459             if( h > 20.5 ) cairo_pattern_add_color_stop( pattern, std::max( 0.0, 1.0 - 12.0/( double(h)-5.5 ) ), ColorUtils::alphaColor( light, 0.5 ) );
1460             else if( h > 8.5 ) cairo_pattern_add_color_stop( pattern, std::max( 0.0, 3.0/( double(h)-5.5 ) ), ColorUtils::alphaColor( light, 0.5 ) );
1461             cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( light ) );
1462 
1463         }
1464 
1465         cairo_rounded_rectangle( context, x+0.5, y+0.5, w-1, h-1, 3.5, rounded ? CornersAll : CornersNone );
1466 
1467         cairo_set_source( context, pattern );
1468         cairo_set_line_width( context, 0.8 );
1469         cairo_stroke( context );
1470     }
1471 
1472     //__________________________________________________________________
1473     void Style::renderButtonSlab(
1474         GdkWindow* window,
1475         GdkRectangle* clipRect,
1476         gint x, gint y, gint w, gint h,
1477         const StyleOptions& options,
1478         const AnimationData& animationData,
1479         TileSet::Tiles tiles
1480         )
1481     {
1482 
1483         // flat buttons are only rendered with a simple Rect, and only when either focused or sunken
1484         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1485 
1486         // glow color (depending on hover/glow
1487         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
1488 
1489         if( options & Flat )
1490         {
1491             if( options & Sunken )
1492             {
1493 
1494                 Cairo::Context context( window, clipRect );
1495                 const ColorUtils::Rgba base( color( group, Palette::Window, options ) );
1496 
1497                 const double bias( 0.75 );
1498                 double opacity( 1.0 );
1499                 if( glow.isValid() ) opacity -= bias*glow.alpha();
1500 
1501                 // fill hole
1502                 ColorUtils::Rgba color( ColorUtils::midColor( base ) );
1503                 color = ColorUtils::alphaColor( color, opacity );
1504                 cairo_save( context );
1505                 cairo_set_source( context, color );
1506                 cairo_rounded_rectangle( context, x+1, y+1, w-2, h-2, 3.5 );
1507                 cairo_fill( context );
1508                 cairo_restore( context );
1509 
1510                 if( glow.isValid() ) _helper.holeFocused( base, glow, 7, true ).render( context, x, y, w, h );
1511                 else _helper.hole( base, 7, true ).render( context, x, y, w, h );
1512 
1513             } else if( glow.isValid() ) {
1514 
1515                 Cairo::Context context( window, clipRect );
1516                 _helper.slitFocused( glow ).render( context, x, y, w, h, tiles );
1517 
1518             }
1519 
1520             return;
1521 
1522         }
1523 
1524         // forces minimum size to 14x14
1525         if( w < 14 || h < 14 )
1526         {
1527             GdkRectangle parent( Gtk::gdk_rectangle( x, y, w, h ) );
1528             GdkRectangle child( Gtk::gdk_rectangle( x, y, std::max( w, 14), std::max( h, 14 ) ) );
1529             centerRect( &parent, &child );
1530             x = child.x;
1531             y = child.y;
1532             w = child.width;
1533             h = child.height;
1534             clipRect = 0L;
1535         }
1536 
1537 
1538         // define colors
1539         ColorUtils::Rgba base( color( group, Palette::Button, options ) );
1540         if( options&Blend )
1541         {
1542 
1543             gint wh, wy;
1544             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1545             base = ColorUtils::backgroundColor( base, wh, y+wy+h/2 );
1546 
1547         }
1548 
1549         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1550 
1551         // create context
1552         Cairo::Context context( window, clipRect );
1553         cairo_save( context );
1554 
1555         // fill with appropriate pattern
1556         Cairo::Pattern pattern;
1557         if( options&Sunken )
1558         {
1559 
1560             pattern.set( cairo_pattern_create_linear( 0, y-h, 0, y+h ) );
1561             cairo_pattern_add_color_stop( pattern, 0, light );
1562             cairo_pattern_add_color_stop( pattern, 1.0, base );
1563 
1564         } else {
1565 
1566             pattern.set( cairo_pattern_create_linear( 0, double(y)-0.2*h, 0, double(y) + h + 0.4*h ) );
1567             cairo_pattern_add_color_stop( pattern, 0, light );
1568             cairo_pattern_add_color_stop( pattern, 0.6, base );
1569 
1570         }
1571 
1572         cairo_set_source( context, pattern );
1573         _helper.fillSlab( context, x, y, w, h+1, tiles );
1574         cairo_restore( context );
1575 
1576         if( options&Sunken )
1577         {
1578 
1579             _helper.slabSunken( base ).render( context, x, y, w, h, tiles );
1580 
1581         } else {
1582 
1583             _helper.slab( base, glow, 0 ).render( context, x, y, w, h, tiles );
1584 
1585         }
1586 
1587     }
1588 
1589     //__________________________________________________________________
1590     void Style::renderSlab(
1591         GdkWindow* window,
1592         GdkRectangle* clipRect,
1593         gint x, gint y, gint w, gint h, const Gtk::Gap& gap,
1594         const StyleOptions& options,
1595         const AnimationData& animationData )
1596     {
1597 
1598         // define colors
1599         ColorUtils::Rgba base;
1600         if( options&Blend )
1601         {
1602 
1603             gint wh, wy;
1604             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1605             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
1606 
1607         } else {
1608 
1609             base = _settings.palette().color( Palette::Window );
1610 
1611         }
1612 
1613         // create context
1614         Cairo::Context context( window, clipRect );
1615         generateGapMask( context, x, y, w, h, gap );
1616         renderSlab( context, x, y, w, h, base, options, animationData, TileSet::Ring );
1617 
1618     }
1619 
1620     //__________________________________________________________________
1621     void Style::renderInfoBar(
1622         GdkWindow* window,
1623         GdkRectangle* clipRect,
1624         gint x, gint y, gint w, gint h,
1625         const ColorUtils::Rgba& glow )
1626     {
1627 
1628         // create context
1629         Cairo::Context context( window, clipRect );
1630 
1631         // content
1632         cairo_rounded_rectangle( context, x+1, y+1, w-2, h-2, 5 );
1633         cairo_set_source( context, glow );
1634         cairo_fill( context );
1635 
1636         // border
1637         cairo_set_line_width( context, 1.0 );
1638         cairo_rounded_rectangle( context, 1.5+x, 1.5+y, w-3, h-3, 4.5 );
1639         cairo_set_source( context, ColorUtils::darken( glow ) );
1640         cairo_stroke( context );
1641 
1642     }
1643 
1644     //__________________________________________________________________
1645     void Style::renderCheckBox(
1646         GdkWindow* window,
1647         GdkRectangle* clipRect,
1648         gint x, gint y, gint w, gint h, GtkShadowType shadow,
1649         const StyleOptions& options,
1650         const AnimationData& animationData )
1651     {
1652 
1653         // define checkbox rect
1654         gint cbw = CheckBox_Size;
1655         if( options & Flat ) cbw -= 5;
1656         GdkRectangle parent = {x, y, w, h };
1657         GdkRectangle child = {0, 0, cbw, cbw };
1658         centerRect( &parent, &child );
1659 
1660         // define colors
1661         ColorUtils::Rgba base;
1662         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1663         const Palette::Role role( options&Flat ? Palette::Window : Palette::Button );
1664         if( options&Blend )
1665         {
1666 
1667             gint wh, wy;
1668             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1669             base = ColorUtils::backgroundColor( _settings.palette().color( group, role ), wh, y+wy+h/2 );
1670 
1671         } else {
1672 
1673             base = _settings.palette().color( group, role );
1674 
1675         }
1676 
1677         // draw slab
1678         Cairo::Context context( window, clipRect );
1679 
1680         if( options & Flat )
1681         {
1682 
1683             _helper.holeFlat( base, 0, false ).render( context, child.x+1, child.y-1, child.width, child.height, TileSet::Full );
1684             cairo_translate( context, 0, -2 );
1685 
1686         } else {
1687 
1688             StyleOptions localOptions( options );
1689             localOptions &= ~Sunken;
1690             renderSlab( context, child.x, child.y, child.width, child.height, base, localOptions, animationData );
1691 
1692         }
1693 
1694         // draw mark
1695         x = int( double(child.x + child.width/2) - 3.5 );
1696         y = int( double(child.y + child.height/2) - 2.5 );
1697 
1698         if( shadow == GTK_SHADOW_IN || shadow == GTK_SHADOW_ETCHED_IN || options&Active )
1699         {
1700 
1701             cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
1702             cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
1703             if( shadow == GTK_SHADOW_IN ) cairo_set_line_width( context, 2.0 );
1704 
1705             Palette::Group group( (options&Disabled) ? Palette::Disabled : Palette::Active );
1706             const ColorUtils::Rgba& color( _settings.palette().color( group, ( options&Flat ) ? Palette::WindowText : Palette::ButtonText ) );
1707             const ColorUtils::Rgba& background( _settings.palette().color( ( options&Flat ) ? Palette::Window : Palette::Button ) );
1708 
1709             ColorUtils::Rgba base( ColorUtils::decoColor( background, color ) );
1710             ColorUtils::Rgba contrast( ColorUtils::lightColor( background ) );
1711 
1712             // We don't want to change color on active state for menu checkboxes (it's never passed by GTK)
1713             // Also, if we ignore active state, we get correct render for LibreOffice
1714             if( options&Active && !(options&Flat) )
1715             {
1716                 base = ColorUtils::alphaColor( base, 0.3 );
1717                 contrast = ColorUtils::alphaColor( contrast, 0.3 );
1718             }
1719 
1720             cairo_translate( context, 0.5, 0.5 );
1721 
1722             const double offset( 1.0 );
1723             if( _settings.checkBoxStyle() == QtSettings::CS_CHECK )
1724             {
1725 
1726                 // dask pattern for tristate buttons
1727                 if( shadow == GTK_SHADOW_ETCHED_IN )
1728                 {
1729                     cairo_set_line_width( context, 1.3 );
1730                     double dashes[2] = { 1.3, 2.6 };
1731                     cairo_set_dash( context, &dashes[0], 2, 0 );
1732                 }
1733 
1734                 cairo_save( context );
1735                 cairo_translate( context, 0, offset );
1736                 cairo_move_to( context, x+9, y );
1737                 cairo_line_to( context, x+3, y+7 );
1738                 cairo_line_to( context, x, y+4 );
1739                 cairo_restore( context );
1740                 cairo_set_source( context, contrast );
1741                 cairo_stroke( context );
1742 
1743                 cairo_move_to( context, x+9, y );
1744                 cairo_line_to( context, x+3, y+7 );
1745                 cairo_line_to( context, x, y+4 );
1746                 cairo_set_source( context, base );
1747                 cairo_stroke( context );
1748 
1749             } else {
1750 
1751                 // dask pattern for tristate buttons
1752                 if( shadow == GTK_SHADOW_ETCHED_IN )
1753                 {
1754                     double dashes[2] = { 0.8, 4.0 };
1755                     cairo_set_dash( context, &dashes[0], 2, 0 );
1756                 }
1757 
1758                 if( options&Flat )
1759                 {
1760 
1761                     cairo_save( context );
1762                     cairo_translate( context, 0, offset );
1763                     cairo_move_to( context, x+8, y ); cairo_line_to( context, x+1, y+7 );
1764                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x+1, y );
1765                     cairo_restore( context );
1766                     cairo_set_source( context, contrast );
1767                     cairo_stroke( context );
1768 
1769                     cairo_move_to( context, x+8, y ); cairo_line_to( context, x+1, y+7 );
1770                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x+1, y );
1771                     cairo_set_source( context, base );
1772                     cairo_stroke( context );
1773 
1774                 } else {
1775 
1776                     cairo_save( context );
1777                     cairo_translate( context, 0, offset );
1778                     cairo_move_to( context, x+8, y-1 ); cairo_line_to( context, x, y+7 );
1779                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x, y-1 );
1780                     cairo_restore( context );
1781                     cairo_set_source( context, contrast );
1782                     cairo_stroke( context );
1783 
1784                     cairo_move_to( context, x+8, y-1 ); cairo_line_to( context, x, y+7 );
1785                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x, y-1 );
1786                     cairo_set_source( context, base );
1787                     cairo_stroke( context );
1788 
1789                 }
1790 
1791             }
1792 
1793         }
1794 
1795     }
1796 
1797     //___________________________________________________________________
1798     void Style::renderRadioButton(
1799         GdkWindow* window,
1800         GdkRectangle* clipRect,
1801         gint x, gint y, gint w, gint h, GtkShadowType shadow,
1802         const StyleOptions& options,
1803         const AnimationData& animationData )
1804     {
1805 
1806         // define checkbox rect
1807         gint cbw( CheckBox_Size );
1808         gint tileSize( CheckBox_Size/3 );
1809         double scale( 1.0 );
1810 
1811         if( _settings.applicationName().isOpenOffice() )
1812         {
1813             const gint dimension = std::min( w, h );
1814             cbw = std::min( 3*( 1 + dimension/3 ), (int)CheckBox_Size );
1815             scale = double(cbw)/CheckBox_Size;
1816             tileSize = cbw/3;
1817         }
1818 
1819         GdkRectangle parent = {x, y, w, h };
1820         GdkRectangle child = {0, 0, cbw, cbw };
1821         centerRect( &parent, &child );
1822         x = child.x;
1823         y = child.y;
1824 
1825         // define colors
1826         ColorUtils::Rgba base;
1827         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1828         if( options&Blend )
1829         {
1830 
1831             gint wh, wy;
1832             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1833 
1834             if( options & Menu ) base = ColorUtils::menuBackgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
1835             else base = ColorUtils::backgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
1836 
1837         } else {
1838 
1839             base = _settings.palette().color( group, Palette::Button );
1840 
1841         }
1842 
1843         // glow
1844         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
1845 
1846         // get the pixmap
1847         const Cairo::Surface& surface( _helper.roundSlab( base, glow, 0, tileSize ) );
1848 
1849         // create context
1850         Cairo::Context context( window, clipRect );
1851         cairo_save( context );
1852         cairo_translate( context, x, y );
1853         if(options&NoFill)
1854         {
1855             // Only render the glow, leave the bullet clipped out
1856             const int border(4);
1857             cairo_ellipse_negative(context,border,border,child.width-border*2,child.height-border*2);
1858             cairo_rectangle(context,0,0,child.width,child.height);
1859             cairo_clip(context);
1860         }
1861         cairo_rectangle( context, 0, 0, child.width, child.height );
1862         cairo_set_source_surface( context, surface, 0, 0 );
1863         cairo_fill( context );
1864         cairo_restore( context );
1865 
1866         if( shadow == GTK_SHADOW_IN || shadow == GTK_SHADOW_ETCHED_IN || options&Active )
1867         {
1868             double radius( shadow == GTK_SHADOW_ETCHED_IN ? 1.4:2.6 );
1869             radius *= scale;
1870 
1871             const double dx( 0.5*child.width - radius );
1872             const double dy( 0.5*child.height - radius );
1873 
1874             const ColorUtils::Rgba& background( _settings.palette().color( Palette::Button ) );
1875             const ColorUtils::Rgba& color( _settings.palette().color( group, Palette::ButtonText ) );
1876 
1877             ColorUtils::Rgba base( ColorUtils::decoColor( background, color ) );
1878             ColorUtils::Rgba contrast( ColorUtils::lightColor( background ) );
1879 
1880             // We don't want to change color on active state for menu radiobuttons (it's never passed by GTK)
1881             // Also, if we ignore active state, we get correct render for LibreOffice
1882             if( options&Active && !(options&Menu ) )
1883             {
1884                 base = ColorUtils::alphaColor( base, 0.3 );
1885                 contrast = ColorUtils::alphaColor( contrast, 0.3 );
1886             }
1887 
1888 
1889             cairo_save( context );
1890             cairo_translate( context, 0, radius/2 );
1891             cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1892             cairo_restore( context );
1893             if( shadow == GTK_SHADOW_ETCHED_IN )
1894             {
1895 
1896                 cairo_set_line_width( context, 1.3 );
1897                 cairo_set_source( context, contrast );
1898                 cairo_stroke( context );
1899 
1900                 cairo_set_source( context, base );
1901                 cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1902                 cairo_stroke( context );
1903 
1904             } else {
1905 
1906                 cairo_set_source( context, contrast );
1907                 cairo_fill( context );
1908 
1909                 cairo_set_source( context, base );
1910                 cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1911                 cairo_fill( context );
1912 
1913             }
1914 
1915         }
1916 
1917         return;
1918     }
1919 
1920     //____________________________________________________________________________________
1921     void Style::renderHole(
1922         GdkWindow* window,
1923         GdkRectangle* clipRect,
1924         gint x, gint y, gint w, gint h, const Gtk::Gap& gap,
1925         const StyleOptions& options,
1926         const AnimationData& animationData,
1927         TileSet::Tiles tiles )
1928     {
1929 
1930         // do nothing if not enough room
1931         if( w < 14 || h < 14 )  return;
1932 
1933         // enable state
1934         const bool enabled( !(options&Disabled ) );
1935 
1936         // load color
1937         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
1938         ColorUtils::Rgba fill;
1939         if( !( options&NoFill ) )
1940         {
1941             const Palette::Group group( enabled ? Palette::Active : Palette::Disabled );
1942             fill = _settings.palette().color( group, Palette::Base );
1943         }
1944 
1945         // create context, add mask, and render hole
1946         Cairo::Context context( window, clipRect );
1947         generateGapMask( context, x, y, w, h, gap );
1948 
1949         if( fill.isValid() ) tiles |= TileSet::Center;
1950 
1951         const ColorUtils::Rgba glow( holeShadowColor( options, animationData ) );
1952         if( glow.isValid() ) _helper.holeFocused( base, fill, glow ).render( context, x, y, w, h, tiles );
1953         else _helper.hole( base, fill ).render( context, x, y, w, h, tiles );
1954 
1955     }
1956 
1957     //____________________________________________________________________________________
1958     void Style::renderDockFrame(
1959         GdkWindow* window,
1960         GdkRectangle* clipRect,
1961         gint x, gint y, gint w, gint h, const Gtk::Gap& gap, const StyleOptions& options )
1962     {
1963 
1964         // do nothing if not enough room
1965         if( w < 9 || h < 9 )  return;
1966 
1967         // define colors
1968         ColorUtils::Rgba top;
1969         ColorUtils::Rgba bottom;
1970         if( options&Blend )
1971         {
1972 
1973             gint wh, wy;
1974             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1975             top = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy );
1976             bottom = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+h+wy );
1977 
1978         } else {
1979 
1980             top = _settings.palette().color( Palette::Window );
1981             bottom = _settings.palette().color( Palette::Window );
1982 
1983         }
1984 
1985         // create context, add mask, and render
1986         Cairo::Context context( window, clipRect );
1987         generateGapMask( context, x, y, w, h, gap );
1988         _helper.dockFrame( top, bottom ).render( context, x, y, w, h );
1989     }
1990 
1991     //____________________________________________________________________________________
1992     void Style::renderGroupBoxFrame(
1993         GdkWindow* window,
1994         GtkWidget* widget,
1995         GdkRectangle* clipRect,
1996         gint x, gint y, gint w, gint h, const StyleOptions& options )
1997     {
1998 
1999         // register
2000         if( widget )
2001         { _animations.groupBoxEngine().registerWidget( widget ); }
2002 
2003         // define colors
2004         ColorUtils::Rgba base;
2005         if( options&Blend )
2006         {
2007 
2008             gint wh, wy;
2009             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
2010             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
2011 
2012         } else {
2013 
2014             base = _settings.palette().color( Palette::Window );
2015 
2016         }
2017 
2018         Cairo::Context context( window, clipRect );
2019         renderGroupBox( context, base, x, y, w, h, options );
2020 
2021     }
2022 
2023     //____________________________________________________________________________________
2024     void Style::renderMenuItemRect(
2025         GdkWindow* window,
2026         GdkRectangle* clipRect,
2027         GtkWidget* widget,
2028         gint x, gint y, gint w, gint h,
2029         const StyleOptions& options,
2030         const AnimationData& data )
2031     {
2032         ColorUtils::Rgba base;
2033 
2034         gint wh, wy;
2035         Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
2036         const bool isInMenu( Gtk::gtk_parent_menu( widget ) );
2037         const bool isInMenuBar( Gtk::gtk_parent_menubar( widget ) );
2038 
2039         if( wh > 0 )
2040         {
2041             if( isInMenu ) base = ColorUtils::menuBackgroundColor( ColorUtils::midColor( _settings.palette().color( Palette::Window ) ), wh, y+wy+h/2 );
2042             else if( options&Blend ) base = ColorUtils::backgroundColor( ColorUtils::midColor( _settings.palette().color( Palette::Window ) ), wh, y+wy+h/2 );
2043             else base = ColorUtils::midColor( _settings.palette().color( Palette::Window ) );
2044 
2045         } else {
2046 
2047             base = ColorUtils::midColor( _settings.palette().color( Palette::Window ) );
2048 
2049         }
2050 
2051         // more color customization, based on menuHighlight mode
2052         ColorUtils::Rgba color( base );
2053         if( _settings.menuHighlightMode() == QtSettings::MM_STRONG )
2054         {
2055 
2056             if( (options & Sunken) || isInMenu ) color = _settings.palette().color( Palette::Selected );
2057             else color = ColorUtils::tint( color, _settings.palette().color( Palette::Hover ) );
2058 
2059         } else if( _settings.menuHighlightMode() == QtSettings::MM_SUBTLE ) {
2060 
2061             if( (options & Sunken) || isInMenu ) color = ColorUtils::mix( color, ColorUtils::tint( color, _settings.palette().color( Palette::Selected ), 0.6 ) );
2062             else color = ColorUtils::mix( color, ColorUtils::tint( color, _settings.palette().color( Palette::Hover ), 0.6 ) );
2063 
2064         }
2065 
2066         // apply animation data
2067         if( data._mode == AnimationHover )
2068         {
2069             if( data._opacity > 0 ) color = ColorUtils::alphaColor( color, data._opacity );
2070             else return;
2071         }
2072 
2073 
2074         if( isInMenuBar )
2075         {
2076 
2077             // we force ytickness in gtkrc to emulate Qt menubar/menu space separation
2078             // so adjust vertical extent of the rect in menubar
2079             if( _settings.applicationName().isXul() )
2080             {
2081 
2082                 y+=3;
2083                 h-=6;
2084 
2085             } else {
2086 
2087                 x+=1;
2088                 w-=2;
2089 
2090                 y+=1;
2091                 h-=2;
2092 
2093             }
2094         } else if ( isInMenu ) {
2095 
2096             // we force ytickness to 5 in gtkrc to emulate Qt menuitems space separation
2097             // so adjust vertical extent of the rect in menus to 21 (size with standard ytickness)
2098             if( h > 27 )
2099             {
2100 
2101                 y+=4;
2102                 h-=7;
2103 
2104             } else {
2105 
2106                 y+=(h-21)/2;
2107                 h=21;
2108 
2109             }
2110 
2111         }
2112 
2113         bool hasSubMenu( isInMenu && GTK_IS_MENU_ITEM( widget ) && gtk_menu_item_get_submenu( GTK_MENU_ITEM( widget ) ) );
2114         if( hasSubMenu )
2115         {
2116             Cairo::Context context( window, clipRect );
2117             cairo_translate( context, x, y );
2118 
2119             // draw item rect in a group
2120             cairo_push_group( context );
2121             _helper.holeFlat( color, 0 ).render( context, 0, 0, w, h, TileSet::Full );
2122             cairo_pop_group_to_source( context );
2123 
2124             // generate linear gradient for masking
2125             if( Gtk::gtk_widget_layout_is_reversed( widget ) )
2126             {
2127 
2128                 Cairo::Pattern mask( cairo_pattern_create_linear( 4, 0, 40, 0 ) );
2129                 cairo_pattern_add_color_stop( mask, 0,  ColorUtils::Rgba::transparent() );
2130                 cairo_pattern_add_color_stop( mask, 1,  ColorUtils::Rgba::black() );
2131                 cairo_mask( context, mask );
2132 
2133             } else {
2134 
2135                 Cairo::Pattern mask( cairo_pattern_create_linear( w-40, 0, w-4, 0 ) );
2136                 cairo_pattern_add_color_stop( mask, 0,  ColorUtils::Rgba::black() );
2137                 cairo_pattern_add_color_stop( mask, 1,  ColorUtils::Rgba::transparent() );
2138                 cairo_mask( context, mask );
2139 
2140             }
2141 
2142         } else {
2143 
2144             Cairo::Context context( window, clipRect );
2145             _helper.holeFlat( color, 0 ).render( context, x, y, w, h, TileSet::Full  );
2146 
2147         }
2148     }
2149 
2150     //____________________________________________________________________________________
2151     void Style::renderSelection(
2152         GdkWindow* window,
2153         GdkRectangle* clipRect,
2154         gint x, gint y, gint w, gint h,
2155         TileSet::Tiles tiles,
2156         const StyleOptions& options
2157         )
2158     {
2159 
2160         // do nothing if not selected nor hovered
2161         if( !(options & (Hover|Selected )) ) return;
2162 
2163         Palette::Group group( (options & Focus) ? Palette::Active : Palette::Inactive );
2164         ColorUtils::Rgba base( _settings.palette().color( group, Palette::Selected ) );
2165         if( options & Hover  )
2166         {
2167             if( !( options & Selected ) ) base.setAlpha( 0.2 );
2168             else base = base.light( 110 );
2169         }
2170 
2171         // create context
2172         Cairo::Context context( window, clipRect );
2173         if( !(tiles&TileSet::Left) ) { x -= 8; w+=8; }
2174         if( !(tiles&TileSet::Right) ) { w += 8; }
2175         _helper.selection( base, h, false ).render( context, x, y, w, h, tiles );
2176 
2177     }
2178 
2179     //____________________________________________________________________________________
2180     void Style::renderArrow(
2181         GdkWindow* window,
2182         GdkRectangle* clipRect,
2183         GtkArrowType orientation,
2184         gint x, gint y, gint w, gint h,
2185         QtSettings::ArrowSize arrowSize,
2186         const StyleOptions& options,
2187         const AnimationData& data,
2188         Palette::Role role ) const
2189     {
2190 
2191         // get polygon
2192         const Polygon arrow( genericArrow( orientation, arrowSize ) );
2193 
2194         // retrieve colors
2195         ColorUtils::Rgba base;
2196         if( options&Disabled ) base = _settings.palette().color( Palette::Disabled, role );
2197         else if( data._mode == AnimationHover ) base = ColorUtils::mix(
2198             _settings.palette().color( Palette::Active, role ),
2199             _settings.palette().color( Palette::Hover ),
2200             data._opacity );
2201         else if( options&Hover ) base = _settings.palette().color( Palette::Hover );
2202         else base = _settings.palette().color( Palette::Active, role );
2203 
2204         // merge base color with relevant background, if needed
2205         const Palette::Group group( (options&Disabled) ? Palette::Disabled : Palette::Active );
2206         switch( role )
2207         {
2208 
2209             case Palette::WindowText:
2210             base = ColorUtils::decoColor( _settings.palette().color( group, Palette::Window ), base );
2211             break;
2212 
2213             case Palette::ButtonText:
2214             base = ColorUtils::decoColor( _settings.palette().color( group, Palette::Button ), base );
2215             break;
2216 
2217             default: break;
2218 
2219         }
2220 
2221         // need odd width and height
2222         if( !(w%2) ) w--;
2223         if( !(h%2) ) h--;
2224         const int xcenter = x + w/2;
2225         const int ycenter = y + h/2;
2226 
2227         // create context and translate to center
2228         Cairo::Context context( window, clipRect );
2229         cairo_translate( context, xcenter, ycenter );
2230 
2231         switch( orientation )
2232         {
2233             case GTK_ARROW_UP:
2234             break;
2235 
2236             case GTK_ARROW_DOWN:
2237             if( arrowSize == QtSettings::ArrowSmall ) cairo_translate( context, 0, 0.5 );
2238             else cairo_translate( context, 0, 1 );
2239             break;
2240 
2241             case GTK_ARROW_LEFT:
2242             case GTK_ARROW_RIGHT:
2243             if( arrowSize == QtSettings::ArrowSmall )
2244             { cairo_translate( context, 0.5, 0 ); }
2245             break;
2246 
2247             default:
2248             break;
2249         }
2250 
2251         switch( arrowSize )
2252         {
2253             case QtSettings::ArrowTiny:
2254             case QtSettings::ArrowSmall:
2255             cairo_set_line_width( context, 1.2 );
2256             break;
2257 
2258             default:
2259             case QtSettings::ArrowNormal:
2260             cairo_set_line_width( context, 1.6 );
2261             break;
2262         }
2263 
2264         cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
2265         cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
2266 
2267         // contrast
2268         if( options&Contrast )
2269         {
2270 
2271             // contrast color
2272             const ColorUtils::Rgba contrast( ColorUtils::lightColor( _settings.palette().color( Palette::Window ) ) );
2273 
2274             cairo_save( context );
2275             cairo_translate( context, 0, 1 );
2276             cairo_polygon( context, arrow );
2277             cairo_restore( context );
2278 
2279             cairo_set_source( context, contrast );
2280             cairo_stroke( context );
2281         }
2282 
2283         // base
2284         cairo_polygon( context, arrow );
2285         cairo_set_source( context, base );
2286         cairo_stroke( context );
2287 
2288     }
2289 
2290     //____________________________________________________________________________________
2291     void Style::renderSliderGroove(
2292         GdkWindow* window,
2293         GdkRectangle* clipRect,
2294         gint x, gint y, gint w, gint h, const StyleOptions& options )
2295     {
2296 
2297         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2298 
2299         const bool vertical( options & Vertical );
2300         GdkRectangle parent = { x, y, w, h };
2301 
2302         GdkRectangle child;
2303         if( vertical ) child = Gtk::gdk_rectangle( 0, 0, Slider_GrooveWidth, h );
2304         else child = Gtk::gdk_rectangle( 0, 0, w, Slider_GrooveWidth );
2305         centerRect( &parent, &child );
2306 
2307         if( !vertical )
2308         {
2309             // more adjustment needed due to contrast pixel
2310             child.y += 1;
2311             child.height -= 1;
2312         }
2313 
2314         Cairo::Context context( window, clipRect );
2315         _helper.scrollHole( base, vertical, true ).render( context, child.x, child.y, child.width, child.height, TileSet::Full );
2316 
2317     }
2318 
2319     //____________________________________________________________________________________
2320     void Style::renderSliderHandle(
2321         GdkWindow* window,
2322         GdkRectangle* clipRect,
2323         gint x, gint y, gint w, gint h,
2324         const StyleOptions& options,
2325         const AnimationData& animationData )
2326     {
2327 
2328         // define colors
2329         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
2330         ColorUtils::Rgba base;
2331         if( options&Blend )
2332         {
2333 
2334             gint wh, wy;
2335             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
2336             base = ColorUtils::backgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
2337 
2338         } else {
2339 
2340             base = _settings.palette().color( group, Palette::Button );
2341 
2342         }
2343 
2344         // render slab
2345         Cairo::Context context( window, clipRect );
2346 
2347         GdkRectangle parent = { x, y, w, h };
2348         GdkRectangle child = {0, 0, 21, 21 };
2349         centerRect( &parent, &child );
2350 
2351         x = child.x;
2352         y = child.y;
2353 
2354         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
2355         const Cairo::Surface& surface( _helper.sliderSlab( base, glow, (options&Sunken), 0 ) );
2356         cairo_translate( context, x, y );
2357         cairo_rectangle( context, 0, 0, child.width, child.height );
2358         cairo_set_source_surface( context, surface, 0, 0 );
2359         cairo_fill( context );
2360 
2361     }
2362 
2363     //____________________________________________________________________________________
2364     void Style::renderSizeGrip(
2365         GdkWindow* window,
2366         GdkRectangle* clipRect,
2367         GdkWindowEdge edge,
2368         gint x, gint y, gint w, gint h ) const
2369     {
2370 
2371         gint dimension = std::min( w, h );
2372 
2373         // edges
2374         Polygon a;
2375         switch( edge )
2376         {
2377 
2378             case GDK_WINDOW_EDGE_NORTH_WEST:
2379             w = dimension;
2380             h = dimension;
2381             a << Point( double(x)+0.5, double(y)+0.5 ) << Point( double(x+w)-0.5, double(y)+0.5 ) << Point( double(x)+0.5, double(y+h)-0.5);
2382             break;
2383 
2384             case GDK_WINDOW_EDGE_NORTH_EAST:
2385             x+= w-dimension;
2386             w = dimension;
2387             h = dimension;
2388             a << Point( double(x)+0.5, double(y)+0.5 ) << Point( double(x+w)-0.5, double(y)+0.5 ) << Point( double(x+w)-0.5, double(y+h)-0.5 );
2389             break;
2390 
2391             case GDK_WINDOW_EDGE_SOUTH_WEST:
2392             y+= h-dimension;
2393             w = dimension;
2394             h = dimension;
2395             a << Point( double(x)+0.5, double(y)+0.5 ) << Point( double(x+w)-0.5, double(y+h)-0.5 ) << Point( double(x)+0.5, double(y+h)-0.5 );
2396             break;
2397 
2398             case GDK_WINDOW_EDGE_SOUTH_EAST:
2399             x+= w-dimension;
2400             y+= h-dimension;
2401             w = dimension;
2402             h = dimension;
2403             a << Point( double(x)+0.5, double(y+h)-0.5 ) << Point( double(x+w)-0.5, double(y)+0.5 ) << Point( double(x+w)-0.5, double(y+h)-0.5 );
2404             break;
2405 
2406             default: return;
2407 
2408         }
2409 
2410 
2411         // colors
2412         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2413         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
2414         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
2415 
2416         // context
2417         Cairo::Context context( window, clipRect );
2418         cairo_set_line_width( context, 1.0 );
2419 
2420         // fill
2421         cairo_polygon( context, a );
2422         cairo_set_source( context, base );
2423         cairo_fill( context );
2424 
2425         // draw edges
2426         cairo_move_to( context, a[0].x(), a[0].y() );
2427         cairo_line_to( context, a[1].x(), a[1].y() );
2428         cairo_set_source( context, dark );
2429         cairo_stroke( context );
2430 
2431         cairo_move_to( context, a[1].x(), a[1].y() );
2432         cairo_line_to( context, a[2].x(), a[2].y() );
2433         cairo_line_to( context, a[0].x(), a[0].y() );
2434         cairo_set_source( context, light );
2435         cairo_stroke( context );
2436 
2437     }
2438 
2439     //____________________________________________________________________________________
2440     void Style::renderTab(
2441         GdkWindow* window,
2442         GdkRectangle* clipRect,
2443         gint x, gint y, gint w, gint h,
2444         GtkPositionType side,
2445         const StyleOptions& options,
2446         const TabOptions& tabOptions,
2447         const AnimationData& data
2448         )
2449     {
2450 
2451         if( tabOptions & CurrentTab )
2452         {
2453 
2454             return renderActiveTab( window, clipRect, x, y, w, h, side, options, tabOptions );
2455 
2456         } else {
2457 
2458             switch( _settings.tabStyle() )
2459             {
2460                 case QtSettings::TS_SINGLE: return renderInactiveTab_Single( window, clipRect, x, y, w, h, side, options, tabOptions, data );
2461                 case QtSettings::TS_PLAIN: return renderInactiveTab_Plain( window, clipRect, x, y, w, h, side, options, tabOptions, data );
2462                 default: return;
2463             }
2464 
2465         }
2466 
2467     }
2468 
2469     //____________________________________________________________________________________
2470     void Style::renderTabBarBase(
2471         GdkWindow* window,
2472         GdkRectangle* clipRect,
2473         gint x, gint y, gint w, gint h,
2474         GtkPositionType side,
2475         Gtk::Gap gap,
2476         const StyleOptions& options,
2477         const TabOptions& tabOptions
2478         )
2479     {
2480 
2481         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2482 
2483         // adjust gap
2484         if( tabOptions & FirstTabAligned ) { gap.setX( gap.x()-3 ); gap.setWidth( gap.width()+3 ); }
2485         if( tabOptions & LastTabAligned ) { gap.setWidth( gap.width()+3 ); }
2486 
2487         // create context
2488         Cairo::Context context( window, 0L );
2489 
2490         // generate mask and main slab
2491         SlabRect tabSlab;
2492         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
2493         switch( side )
2494         {
2495             case GTK_POS_BOTTOM:
2496             tabSlab = SlabRect( x, y+h-4, w, 15, tabTiles );
2497             generateGapMask( context, x-1, y-4, w+2, h+8, gap );
2498             break;
2499 
2500             case GTK_POS_TOP:
2501             tabSlab = SlabRect( x, y-11, w, 15, tabTiles );
2502             generateGapMask( context, x-1, y-4, w+2, h+8, gap );
2503             break;
2504 
2505             case GTK_POS_RIGHT:
2506             tabSlab = SlabRect( x+w-4, y, 15, h, tabTiles );
2507             generateGapMask( context, x-4, y-1, w+8, h+2, gap );
2508             break;
2509 
2510 
2511             case GTK_POS_LEFT:
2512             tabSlab = SlabRect( x-11, y, 15, h, tabTiles );
2513             generateGapMask( context, x-4, y-1, w+8, h+2, gap );
2514             break;
2515 
2516             default: break;
2517 
2518         }
2519 
2520         // render
2521         _helper.slab( base, 0 ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
2522         return;
2523 
2524     }
2525 
2526     //__________________________________________________________________
2527     void Style::renderTabBarFrame(
2528         GdkWindow* window,
2529         GdkRectangle* clipRect,
2530         gint x, gint y, gint w, gint h,
2531         const Gtk::Gap& gap,
2532         const StyleOptions& options )
2533     {
2534 
2535         // define colors
2536         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2537 
2538         // create context
2539         Cairo::Context context( window, clipRect );
2540         generateGapMask( context, x, y, w, h, gap );
2541         renderSlab( context, x, y, w, h, base, options );
2542 
2543     }
2544 
2545     //____________________________________________________________________________________
2546     void Style::renderTreeExpander(
2547         GdkWindow* window,
2548         GdkRectangle* clipRect,
2549         gint x, gint y, gint w, gint h,
2550         GtkExpanderStyle style,
2551         const StyleOptions& options,
2552         const AnimationData& data,
2553         Palette::Role role
2554         ) const
2555     {
2556 
2557         // retrieve colors
2558         ColorUtils::Rgba base;
2559         if( options&Disabled ) base = _settings.palette().color( Palette::Disabled, role );
2560         else if( data._mode == AnimationHover ) base = ColorUtils::mix(
2561             _settings.palette().color( Palette::Active, role ),
2562             _settings.palette().color( Palette::Hover ),
2563             data._opacity );
2564         else if( options&Hover ) base = _settings.palette().color( Palette::Hover );
2565         else base = _settings.palette().color( Palette::Active, role );
2566 
2567         const int xcenter( x + w/2 );
2568         const int ycenter( y + h/2 );
2569 
2570         // expander 'radius' (copied from oxygen-qt)
2571         const int radius( ( 9 - 4 ) / 2 );
2572 
2573         // create context and translate to center
2574         Cairo::Context context( window, clipRect );
2575         cairo_set_line_width( context, 1.0 );
2576         cairo_set_source( context, base );
2577 
2578         cairo_translate( context, -0.5+xcenter, -0.5+ycenter );
2579 
2580         // horizontal line
2581         cairo_move_to( context, -radius, 0 );
2582         cairo_line_to( context, radius, 0 );
2583 
2584         // vertical line
2585         if( style == GTK_EXPANDER_COLLAPSED || style == GTK_EXPANDER_SEMI_COLLAPSED )
2586         {
2587             cairo_move_to( context, 0, -radius );
2588             cairo_line_to( context, 0, radius );
2589         }
2590 
2591         cairo_stroke( context );
2592 
2593     }
2594 
2595     //__________________________________________________________________
2596     void Style::renderWindowDecoration( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h, const gchar** windowStrings, gint titleIndentLeft, gint titleIndentRight, bool gradient )
2597     {
2598         bool hasAlpha( wopt & WinDeco::Alpha );
2599         bool drawResizeHandle( !(wopt & WinDeco::Shaded) && (wopt & WinDeco::Resizable) );
2600         bool isMaximized( wopt & WinDeco::Maximized );
2601         bool drawAlphaChannel( wopt & WinDeco::DrawAlphaChannel );
2602         StyleOptions options( hasAlpha ? Alpha : Blend );
2603 
2604         if( hasAlpha && !isMaximized )
2605         {
2606             // cut round corners using alpha
2607             cairo_rounded_rectangle(context,x,y,w,h,3.5);
2608             cairo_clip(context);
2609         }
2610 
2611         if(drawAlphaChannel)
2612             options|=DrawAlphaChannel;
2613 
2614         if( gradient ) renderWindowBackground( context, x, y, w, h, options, isMaximized );
2615         else
2616         {
2617             cairo_set_source( context, _settings.palette().color( Palette::Active, Palette::Window ) );
2618             cairo_paint( context );
2619         }
2620 
2621         options|=Round;
2622 
2623         // focus
2624         if(wopt & WinDeco::Active) options|=Focus;
2625 
2626         if( !isMaximized )
2627         { drawFloatFrame( context, 0L, 0L, x, y, w, h, options ); }
2628 
2629         if( drawResizeHandle )
2630         {
2631             ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2632             renderWindowDots( context, x, y, w, h, base, wopt);
2633         }
2634 
2635         if(windowStrings)
2636         {
2637             // caption is drawn in drawWindowDecoration
2638             if( windowStrings[1] )
2639             {
2640                 // TODO: use WMCLASS and caption to enable per-window style exceptions
2641             }
2642         }
2643     }
2644 
2645     //__________________________________________________________________
2646     void Style::drawWindowDecoration( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h, const gchar** windowStrings, gint titleIndentLeft, gint titleIndentRight )
2647     {
2648 
2649         #ifdef GDK_WINDOWING_X11
2650         /*
2651            (any element of windowStrings[] may be NULL - will be understood as "")
2652            windowStrings may also be NULL
2653 
2654            elements:
2655             windowStrings[0]: caption
2656             windowStrings[1]: WMCLASS
2657             windowStrings[2]: (gpointer)XID
2658         */
2659         /*
2660            caches layout:
2661                left&right border height: h
2662                top&bottom border width: w-BorderLeft-BorderRight
2663         */
2664 
2665         // enable gradient if XID is not passed
2666         bool gradient=true;
2667 
2668         const int buttonSpacing(WinDeco::getMetric(WinDeco::ButtonSpacing));
2669         titleIndentLeft+=2*buttonSpacing;
2670         titleIndentRight+=2*buttonSpacing;
2671 
2672         QtSettings::WindecoBlendType blendType(_settings.windecoBlendType());
2673         if( blendType==QtSettings::SolidColor )
2674         {
2675 
2676             gradient=false;
2677 
2678         } else if( blendType==QtSettings::FollowStyleHint && windowStrings && windowStrings[2] ) {
2679 
2680             Window window((Window)windowStrings[2]);
2681             Display* display( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) );
2682             if( _animations.backgroundHintEngine().backgroundGradientAtom() != None )
2683             {
2684                 Atom typeRet;
2685                 int formatRet;
2686                 unsigned long itemsRet;
2687                 unsigned long afterRet;
2688                 unsigned char *data = 0;
2689 
2690                 if( !( XGetWindowProperty(display, window, _animations.backgroundHintEngine().backgroundGradientAtom(), 0, G_MAXLONG, False,
2691                     XA_CARDINAL, &typeRet, &formatRet, &itemsRet, &afterRet, &data) == Success
2692                     && itemsRet == 1
2693                     && formatRet == 32) )
2694                 {
2695                     // if the window doesn't have this property set, it's likely
2696                     // non-oxygenized, thus shouldn't have windeco bg gradient
2697                     gradient=false;
2698                 }
2699             }
2700         }
2701 
2702         WindecoBorderKey key(wopt,w,h,gradient);
2703 
2704         {
2705             // draw left border with cache
2706             Cairo::Surface left( _helper.windecoLeftBorderCache().value(key) );
2707             int sw=WinDeco::getMetric(WinDeco::BorderLeft);
2708             if(sw)
2709             {
2710 
2711                 if( !left )
2712                 {
2713 
2714                     #if OXYGEN_DEBUG
2715                     std::cerr<<"drawWindowDecoration: drawing left border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2716                     #endif
2717                     left=_helper.createSurface(sw,h);
2718 
2719                     Cairo::Context context(left);
2720                     renderWindowDecoration( context, wopt, 0, 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient);
2721 
2722                     _helper.windecoLeftBorderCache().insert(key,left);
2723 
2724                 } else {
2725 
2726                     #if OXYGEN_DEBUG
2727                     std::cerr << "drawWindowDecoration: using saved left border" << std::endl;
2728                     #endif
2729 
2730                 }
2731 
2732                 cairo_set_source_surface(context, left, x, y);
2733                 cairo_rectangle(context,x,y,sw,h);
2734                 cairo_fill(context);
2735             }
2736         }
2737 
2738         {
2739             // draw right border with cache
2740             Cairo::Surface right( _helper.windecoRightBorderCache().value(key) );
2741             int sw=WinDeco::getMetric(WinDeco::BorderRight);
2742             if(sw)
2743             {
2744 
2745                 if( !right )
2746                 {
2747 
2748                     #if OXYGEN_DEBUG
2749                     std::cerr<<"drawWindowDecoration: drawing right border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2750                     #endif
2751 
2752                     right=_helper.createSurface(sw,h);
2753 
2754                     Cairo::Context context(right);
2755                     renderWindowDecoration( context, wopt, -(w-sw), 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2756 
2757                     _helper.windecoRightBorderCache().insert(key,right);
2758 
2759                 } else {
2760 
2761                     #if OXYGEN_DEBUG
2762                     std::cerr << "drawWindowDecoration: using saved right border" << std::endl;
2763                     #endif
2764 
2765                 }
2766 
2767                 cairo_set_source_surface(context, right, x+w-sw, y);
2768                 cairo_rectangle(context,x+w-sw,y,sw,h);
2769                 cairo_fill(context);
2770             }
2771         }
2772 
2773         {
2774             // draw top border with cache
2775             Cairo::Surface top( _helper.windecoTopBorderCache().value(key) );
2776             int left=WinDeco::getMetric(WinDeco::BorderLeft);
2777             int right=WinDeco::getMetric(WinDeco::BorderRight);
2778             int sh=WinDeco::getMetric(WinDeco::BorderTop);
2779             int sw=w-left-right;
2780             if(sh && sw)
2781             {
2782                 if( !top )
2783                 {
2784 
2785                     #if OXYGEN_DEBUG
2786                     std::cerr<<"drawWindowDecoration: drawing top border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2787                     #endif
2788                     top=_helper.createSurface(sw,sh);
2789 
2790                     Cairo::Context context(top);
2791                     renderWindowDecoration( context, wopt, -left, 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2792 
2793                     _helper.windecoTopBorderCache().insert(key,top);
2794 
2795                 } else {
2796 
2797                     #if OXYGEN_DEBUG
2798                     std::cerr << "drawWindowDecoration: using saved top border" << std::endl;
2799                     #endif
2800 
2801                 }
2802 
2803                 cairo_set_source_surface(context, top, x+left, y);
2804                 cairo_rectangle(context,x+left,y,sw,sh);
2805                 cairo_fill(context);
2806 
2807                 // caption shouldn't be saved in the cache
2808                 if( windowStrings && windowStrings[0] )
2809                 {
2810                     // draw caption
2811                     const gchar* &caption(windowStrings[0]);
2812                     const FontInfo& font( _settings.WMFont() );
2813                     gint layoutWidth=w-(titleIndentLeft+titleIndentRight);
2814                     if( font.isValid() && layoutWidth>0 )
2815                     {
2816                         PangoFontDescription* fdesc( pango_font_description_new() );
2817                         const Palette::Group group( wopt & WinDeco::Active ? Palette::Active : Palette::Disabled );
2818                         const int H=WinDeco::getMetric(WinDeco::BorderTop);
2819                         int textHeight;
2820 
2821                         pango_font_description_set_family( fdesc, font.family().c_str() );
2822                         pango_font_description_set_weight( fdesc, PangoWeight( (font.weight()+2)*10 ) );
2823                         pango_font_description_set_style( fdesc, font.italic() ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL );
2824                         pango_font_description_set_absolute_size( fdesc, int(font.size()*PANGO_SCALE) );
2825 
2826                         PangoLayout* layout( pango_cairo_create_layout(context) );
2827                         pango_layout_set_text( layout,caption, -1 );
2828                         pango_layout_set_font_description( layout, fdesc );
2829                         pango_layout_set_width( layout, layoutWidth*PANGO_SCALE );
2830                         pango_layout_set_ellipsize( layout, PANGO_ELLIPSIZE_END );
2831                         pango_layout_set_alignment( layout, _settings.TitleAlignment() );
2832                         pango_layout_get_pixel_size( layout, NULL, &textHeight );
2833 
2834                         cairo_save( context );
2835 
2836                         ColorUtils::Rgba titleContrastColor(ColorUtils::lightColor(_settings.palette().color( Palette::Disabled, Palette::Window )));
2837                         cairo_set_source( context, titleContrastColor );
2838                         cairo_translate( context, x+titleIndentLeft, y+(H-textHeight)/2.+1 );
2839                         pango_cairo_update_layout( context, layout );
2840                         pango_cairo_show_layout( context, layout );
2841 
2842                         ColorUtils::Rgba titleTextColor(_settings.palette().color( group, Palette::WindowText ));
2843                         cairo_set_source( context, titleTextColor );
2844                         cairo_translate( context, 0, -1 );
2845                         pango_cairo_update_layout( context, layout );
2846                         pango_cairo_show_layout( context, layout );
2847 
2848                         cairo_restore( context );
2849 
2850                         g_object_unref(layout);
2851                         pango_font_description_free(fdesc);
2852                     }
2853                 }
2854             }
2855         }
2856 
2857         {
2858             // draw bottom border with cache
2859             Cairo::Surface bottom( _helper.windecoBottomBorderCache().value(key) );
2860             int left=WinDeco::getMetric(WinDeco::BorderLeft);
2861             int right=WinDeco::getMetric(WinDeco::BorderRight);
2862             int sh=WinDeco::getMetric(WinDeco::BorderBottom);
2863             int sw=w-left-right;
2864             int Y=y+h-sh;
2865             if(sh && sw)
2866             {
2867                 if( !bottom)
2868                 {
2869 
2870                     #if OXYGEN_DEBUG
2871                     std::cerr<<"drawWindowDecoration: drawing bottom border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2872                     #endif
2873                     bottom=_helper.createSurface(sw,sh);
2874 
2875                     Cairo::Context context(bottom);
2876                     renderWindowDecoration( context, wopt, -left, y-Y, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2877 
2878                     _helper.windecoBottomBorderCache().insert(key,bottom);
2879 
2880                 } else {
2881 
2882                     #if OXYGEN_DEBUG
2883                     std::cerr << "drawWindowDecoration: using saved bottom border" << std::endl;
2884                     #endif
2885 
2886                 }
2887 
2888                 cairo_set_source_surface(context, bottom, x+left, Y);
2889                 cairo_rectangle(context,x+left,Y,sw,sh);
2890                 cairo_fill(context);
2891             }
2892         }
2893 
2894         #endif
2895 
2896     }
2897 
2898     //__________________________________________________________________
2899     void Style::drawWindowShadow( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h )
2900     {
2901         cairo_set_source_rgba( context, 0, 0, 0, 0 );
2902         cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
2903         cairo_paint( context );
2904         cairo_set_operator( context, CAIRO_OPERATOR_OVER );
2905 
2906         WindowShadow shadow(_settings, _helper);
2907         shadow.setWindowState(wopt);
2908         shadow.render(context, x,y,w,h);
2909     }
2910 
2911     //__________________________________________________________________
2912     void Style::drawWindecoButton( cairo_t* context, WinDeco::ButtonType type, WinDeco::ButtonStatus buttonState, WinDeco::Options windowState, gint x, gint y, gint w,gint h)
2913     {
2914         // validate arguments
2915         if(type>=WinDeco::ButtonTypeCount || buttonState>=WinDeco::ButtonStatusCount)
2916         { return; }
2917 
2918         if( !(windowState & WinDeco::Active) && buttonState == WinDeco::Normal )
2919         {
2920             // draw Oxygen-way disabled button on inactive window
2921             buttonState=WinDeco::Disabled;
2922         }
2923         if( !(windowState & WinDeco::Alpha) && !(windowState & WinDeco::Maximized) )
2924         { y++; }
2925 
2926         WinDeco::Button button( _settings, _helper, type );
2927         button.setState(buttonState);
2928         int buttonSize=_settings.buttonSize();
2929         button.render( context, x+(w-buttonSize)/2+1,y+(h-buttonSize)/2+1, buttonSize, buttonSize );
2930     }
2931 
2932     //__________________________________________________________________
2933     void Style::drawWindecoShapeMask( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h )
2934     {
2935         cairo_save(context);
2936         cairo_set_source_rgba(context,0,0,0,0);
2937         cairo_set_operator(context,CAIRO_OPERATOR_SOURCE);
2938         cairo_paint(context);
2939 
2940         cairo_set_source_rgba(context,1,1,1,1);
2941         cairo_set_operator(context,CAIRO_OPERATOR_OVER);
2942         cairo_set_antialias(context,CAIRO_ANTIALIAS_NONE);
2943         cairo_rounded_rectangle(context,x,y,w,h,6);
2944         cairo_fill(context);
2945         cairo_restore(context);
2946 
2947     }
2948 
2949     //__________________________________________________________________
2950     void Style::sanitizeSize( GdkWindow* window, gint& w, gint& h ) const
2951     {
2952         if( w < 0 && h < 0 ) gdk_drawable_get_size( window, &w, &h );
2953         else if( w < 0 ) gdk_drawable_get_size( window, &w, 0L );
2954         else if( h < 0 ) gdk_drawable_get_size( window, 0L, &h );
2955     }
2956 
2957     //__________________________________________________________________
2958     void Style::adjustScrollBarHole( int& x, int& y, int& w, int& h, const StyleOptions& options ) const
2959     {
2960 
2961         const int buttonSize( 12 );
2962         const int subLineOffset( buttonSize*_settings.scrollBarSubLineButtons() );
2963         const int addLineOffset( buttonSize*_settings.scrollBarAddLineButtons() );
2964         if( options&Vertical )
2965         {
2966 
2967             y += subLineOffset;
2968             h -= (subLineOffset+addLineOffset);
2969 
2970         } else {
2971 
2972             x += subLineOffset;
2973             w -= (subLineOffset+addLineOffset);
2974 
2975         }
2976 
2977         return;
2978 
2979     }
2980 
2981     //____________________________________________________________________________________
2982     void Style::setBackgroundSurface( const std::string& filename )
2983     {
2984         if( _backgroundSurface.isValid() ) _backgroundSurface.free();
2985         _backgroundSurface.set( cairo_image_surface_create_from_png( filename.c_str() ) );
2986     }
2987 
2988     //____________________________________________________________________________________
2989     void Style::renderActiveTab(
2990         GdkWindow* window,
2991         GdkRectangle* clipRect,
2992         gint x, gint y, gint w, gint h,
2993         GtkPositionType side,
2994         const StyleOptions& options,
2995         const TabOptions& tabOptions
2996         )
2997     {
2998 
2999         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
3000         const bool isLastTabAligned( tabOptions & LastTabAligned );
3001 
3002         // get color
3003         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
3004         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
3005 
3006         // create context
3007         Cairo::Context context( window, clipRect );
3008 
3009         // borders and connections to tabs
3010         // this is quite painfull and slipery code.
3011         // the same is true with oxygen-qt
3012         int offset = 2;
3013         int adjust = ( tabOptions&Xul ) ? 0:2;
3014         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
3015 
3016         SlabRect tabSlab;
3017         SlabRect::List slabs;
3018         switch( side )
3019         {
3020             case GTK_POS_BOTTOM:
3021             {
3022                 // main slab
3023                 y += adjust; h -= 2*adjust;
3024                 tabSlab = SlabRect( x, y-offset, w, h+9+offset, tabTiles );
3025                 tabSlab._h+=1;
3026 
3027                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3028                 if( isLastTabAligned ) { tabSlab._w+=1; }
3029 
3030                 // connections to frame
3031                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y+h+offset-6, 8, 18, TileSet::Left ) );
3032                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y+h+offset-6, 8, 18, TileSet::Right ) );
3033 
3034                 break;
3035             }
3036 
3037             case GTK_POS_TOP:
3038             {
3039 
3040                 // main slab
3041                 y += adjust; h -= 2*adjust;
3042                 tabSlab = SlabRect( x, y-9, w, h+11+offset, tabTiles );
3043                 tabSlab._y-=1; tabSlab._h+=1;
3044 
3045                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3046                 if( isLastTabAligned ) { tabSlab._w-=1; }
3047 
3048                 // connections to frame
3049                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y-14, 8, 18, TileSet::Left ) );
3050                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y-14, 8, 18, TileSet::Right ) );
3051 
3052                 break;
3053             }
3054 
3055             case GTK_POS_RIGHT:
3056             {
3057 
3058                 // main slab
3059                 x += adjust; w -= 2*adjust;
3060                 tabSlab = SlabRect( x-offset, y, w+9+offset, h, tabTiles );
3061                 tabSlab._w+=1;
3062                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3063                 if( isLastTabAligned ) { tabSlab._h+=1; }
3064 
3065                 // connections to frame
3066                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x+w+offset-6, y-1, 18, 8, TileSet::Top ) );
3067                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w+offset-6, y+h-7, 18, 8, TileSet::Top ) );
3068 
3069                 break;
3070             }
3071 
3072 
3073             case GTK_POS_LEFT:
3074             {
3075 
3076                 // main slab
3077                 x += adjust; w -= 2*adjust;
3078                 tabSlab = SlabRect( x-9, y, w+11+offset, h, tabTiles );
3079                 tabSlab._x-=1; tabSlab._w+=1;
3080                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3081                 if( isLastTabAligned ) { tabSlab._h+=1; }
3082 
3083                 // connections to frame
3084                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-14, y-1, 18, 8, TileSet::Top ) );
3085                 if( isLastTabAligned )  slabs.push_back( SlabRect( x-14, y+h-7, 18, 8, TileSet::Top ) );
3086 
3087                 break;
3088 
3089             }
3090 
3091             default: return;
3092         }
3093 
3094         // render tab
3095         _helper.slab( base, 0 ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
3096 
3097         // adjust rect for filling
3098         SlabRect fillSlab( tabSlab );
3099         fillSlab._x += 4;
3100         fillSlab._y += 4;
3101         fillSlab._w -= 8;
3102         fillSlab._h -= 8;
3103 
3104         // fill
3105         Cairo::Pattern pattern;
3106         switch( side )
3107         {
3108             case GTK_POS_BOTTOM:
3109             fillSlab._h -= 2;
3110             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y, 0, fillSlab._y + fillSlab._h ) );
3111             break;
3112 
3113             case GTK_POS_TOP:
3114             fillSlab._y += 2;
3115             fillSlab._h -= 2;
3116             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y + fillSlab._h, 0, fillSlab._y ) );
3117             break;
3118 
3119             case GTK_POS_RIGHT:
3120             fillSlab._w -= 2;
3121             pattern.set( cairo_pattern_create_linear( fillSlab._x, 0, fillSlab._x + fillSlab._w, 0 ) );
3122             break;
3123 
3124             case GTK_POS_LEFT:
3125             fillSlab._x += 2;
3126             fillSlab._w -= 2;
3127             pattern.set( cairo_pattern_create_linear( fillSlab._x + fillSlab._w, 0, fillSlab._x, 0 ) );
3128             break;
3129 
3130             default: return;
3131 
3132         }
3133 
3134         cairo_pattern_add_color_stop( pattern, 0.1, ColorUtils::alphaColor( light, 0.5 ) );
3135         cairo_pattern_add_color_stop( pattern, 0.25, ColorUtils::alphaColor( light, 0.3 ) );
3136         cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::alphaColor( light, 0.2 ) );
3137         cairo_pattern_add_color_stop( pattern, 0.75, ColorUtils::alphaColor( light, 0.1 ) );
3138         cairo_pattern_add_color_stop( pattern, 0.9, ColorUtils::Rgba::transparent( light ) );
3139 
3140         // in firefox a solid background must be filled
3141         if( tabOptions&Xul )
3142         {
3143             cairo_set_source( context, base );
3144             cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3145             cairo_fill( context );
3146         }
3147 
3148         // draw pattern
3149         cairo_set_source( context, pattern );
3150         cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3151         cairo_fill( context );
3152 
3153         // render connections to frame
3154         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3155         { _helper.slab(base, 0).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles ); }
3156 
3157     }
3158 
3159     //____________________________________________________________________________________
3160     void Style::renderInactiveTab_Single(
3161         GdkWindow* window,
3162         GdkRectangle* clipRect,
3163         gint x, gint y, gint w, gint h,
3164         GtkPositionType side,
3165         const StyleOptions& options,
3166         const TabOptions& tabOptions,
3167         const AnimationData& data
3168         )
3169     {
3170 
3171         // convenience flags
3172         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
3173         const bool isLastTabAligned( tabOptions & LastTabAligned );
3174 
3175         // get color
3176         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
3177         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
3178         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
3179 
3180         // create context
3181         Cairo::Context context( window, clipRect );
3182 
3183         // borders and connections to tabs
3184         // this is quite painfull and slipery code.
3185         // the same is true with oxygen-qt
3186         int offset = 2;
3187         int adjust = (tabOptions&Xul) ? 0:2;
3188         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
3189 
3190         SlabRect tabSlab;
3191         SlabRect::List slabs;
3192         switch( side )
3193         {
3194             case GTK_POS_BOTTOM:
3195             {
3196                 // main slab
3197                 y += adjust; h -= 2*adjust;
3198                 tabSlab = SlabRect( x, y-offset, w, h+9+offset, tabTiles );
3199                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3200                 if( isLastTabAligned ) { tabSlab._w+=1; }
3201 
3202                 // connections to frame
3203                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y+h+offset-7, 8, 17, TileSet::Left, Hover ) );
3204                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y+h+offset-7, 8, 17, TileSet::Right, Hover ) );
3205                 break;
3206             }
3207 
3208             case GTK_POS_TOP:
3209             {
3210 
3211                 // main slab
3212                 y += adjust; h -= 2*adjust;
3213                 tabSlab = SlabRect( x, y-9, w, h+11+offset, tabTiles );
3214                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3215                 if( isLastTabAligned ) { tabSlab._w-=1; }
3216 
3217                 // connections to frame
3218                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y-13+offset, 8, 16, TileSet::Left, Hover ) );
3219                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y-13+offset, 8, 16, TileSet::Right, Hover ) );
3220                 break;
3221             }
3222 
3223             case GTK_POS_RIGHT:
3224             {
3225 
3226                 // main slab
3227                 x += adjust; w -= 2*adjust;
3228                 tabSlab = SlabRect( x-offset, y, w+9+offset, h, tabTiles );
3229                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3230                 if( isLastTabAligned ) { tabSlab._h+=1; }
3231 
3232                 // connections to frame
3233                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x+w+offset-7, y-1, 17, 8, TileSet::Top, Hover ) );
3234                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w+offset-7, y+h-7, 17, 8, TileSet::Top, Hover ) );
3235                 break;
3236             }
3237 
3238 
3239             case GTK_POS_LEFT:
3240             {
3241 
3242                 // main slab
3243                 x += adjust; w -= 2*adjust;
3244                 tabSlab = SlabRect( x-9, y, w+11+offset, h, tabTiles );
3245                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3246                 if( isLastTabAligned ) { tabSlab._h+=1; }
3247 
3248                 // connections to frame
3249                 SlabRect baseSlab( x-10+1, y-6, 10, h+12, TileSet::Right );
3250                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-13 + offset, y-1, 16, 8, TileSet::Top, Hover ) );
3251                 if( isLastTabAligned )slabs.push_back( SlabRect( x-13 + offset, y+h-7, 16, 8, TileSet::Top, Hover ) );
3252                 break;
3253             }
3254 
3255             default: return;
3256         }
3257 
3258         // render tab
3259         ColorUtils::Rgba glow;
3260         if( data._mode == AnimationHover ) glow = ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3261         else if( options&Hover ) glow = _settings.palette().color( Palette::Hover );
3262 
3263         _helper.slab( base, glow ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
3264 
3265         // adjust rect for filling
3266         SlabRect fillSlab( tabSlab );
3267         fillSlab._x += 4;
3268         fillSlab._y += 4;
3269         fillSlab._w -= 8;
3270         fillSlab._h -= 8;
3271 
3272         // fill
3273         Cairo::Pattern pattern;
3274         switch( side )
3275         {
3276             case GTK_POS_BOTTOM:
3277             fillSlab._h -= 3;
3278             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y, 0, fillSlab._y + fillSlab._h ) );
3279             break;
3280 
3281             case GTK_POS_TOP:
3282             fillSlab._y += 3;
3283             fillSlab._h -= 3;
3284             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y + fillSlab._h, 0, fillSlab._y ) );
3285             break;
3286 
3287             case GTK_POS_RIGHT:
3288             fillSlab._w -= 3;
3289             pattern.set( cairo_pattern_create_linear( fillSlab._x, 0, fillSlab._x + fillSlab._w, 0 ) );
3290             break;
3291 
3292             case GTK_POS_LEFT:
3293             fillSlab._x += 3;
3294             fillSlab._w -= 3;
3295             pattern.set( cairo_pattern_create_linear( fillSlab._x + fillSlab._w, 0, fillSlab._x, 0 ) );
3296             break;
3297 
3298             default: return;
3299 
3300         }
3301 
3302         cairo_pattern_add_color_stop( pattern, 0.0, ColorUtils::alphaColor( light, 0.1 ) );
3303         cairo_pattern_add_color_stop( pattern, 0.4, ColorUtils::alphaColor( dark, 0.5 ) );
3304         cairo_pattern_add_color_stop( pattern, 0.8, ColorUtils::alphaColor( dark, 0.4 ) );
3305 
3306         // draw pattern
3307         cairo_set_source( context, pattern );
3308         cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3309         cairo_fill( context );
3310 
3311         // render connections to frame
3312         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3313         {
3314 
3315             if( (iter->_options&Hover) && glow.isValid() )
3316             {
3317 
3318                 _helper.slab(base, glow).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles );
3319 
3320             } else {
3321 
3322                 _helper.slab(base).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles );
3323 
3324             }
3325         }
3326     }
3327 
3328     //____________________________________________________________________________________
3329     void Style::renderInactiveTab_Plain(
3330         GdkWindow* window,
3331         GdkRectangle* clipRect,
3332         gint x, gint y, gint w, gint h,
3333         GtkPositionType side,
3334         const StyleOptions& options,
3335         const TabOptions& tabOptions,
3336         const AnimationData& data
3337         )
3338     {
3339         // convenience flags
3340         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
3341         const bool isLastTabAligned( tabOptions & LastTabAligned );
3342 
3343         const bool isLeftOfSelected( tabOptions & LeftOfSelected );
3344         const bool isRightOfSelected( tabOptions & RightOfSelected );
3345 
3346         // get color
3347         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
3348 
3349         // create context
3350         Cairo::Context context( window, clipRect );
3351 
3352         // borders and connections to tabs
3353         // this is quite painfull and slipery code.
3354         // the same is true with oxygen-qt
3355         int offset = 2;
3356         int adjust = (tabOptions&Xul) ? 0:2;
3357         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
3358 
3359         SlabRect tabSlab;
3360         SlabRect::List slabs;
3361         switch( side )
3362         {
3363             case GTK_POS_BOTTOM:
3364             {
3365                 // main slab
3366                 y += adjust; h -= 2*adjust;
3367                 tabSlab = SlabRect( x, y-offset, w, h+10 + offset, tabTiles );
3368                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3369                 if( isLastTabAligned ) { tabSlab._w+=1; }
3370 
3371                 // connections to frame
3372                 SlabRect baseSlab( x-4-1, y+h-1, w+8+2, 10, TileSet::Top );
3373                 if( isFirstTabAligned )
3374                 {
3375                     baseSlab._x += 4; baseSlab._w -= 4; baseSlab._tiles |= TileSet::Left;
3376                     slabs.push_back( SlabRect( x-1, y+h+offset-6, 8, 16, TileSet::Left ) );
3377                 }
3378 
3379                 if( isLastTabAligned )
3380                 {
3381                     baseSlab._w -= 4; baseSlab._tiles |= TileSet::Right;
3382                     slabs.push_back( SlabRect( x+w-7, y+h+offset-6, 8, 16, TileSet::Right ) );
3383                 }
3384 
3385                 if( isLeftOfSelected ) { baseSlab._w += 3; }
3386                 else if( isRightOfSelected ) { baseSlab._x -= 3; baseSlab._w += 4; }
3387                 else { baseSlab._w += 2; }
3388 
3389                 slabs.push_back( baseSlab );
3390                 break;
3391             }
3392 
3393             case GTK_POS_TOP:
3394             {
3395 
3396                 // main slab
3397                 y += adjust; h -= 2*adjust;
3398                 tabSlab = SlabRect( x, y-10, w, h+10+offset, tabTiles );
3399                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3400                 if( isLastTabAligned ) { tabSlab._w-=1; }
3401 
3402                 // connections to frame
3403                 SlabRect baseSlab( x-4-1, y-10+1, w+8+2, 10, TileSet::Bottom );
3404                 if( isFirstTabAligned ) { baseSlab._x += 4; baseSlab._w -= 4; baseSlab._tiles |= TileSet::Left; }
3405                 if( isLastTabAligned ) { baseSlab._w -=4; baseSlab._tiles |= TileSet::Right; }
3406                 if( isLeftOfSelected ) { baseSlab._w += 3; }
3407                 else if( isRightOfSelected ) { baseSlab._x -= 3; baseSlab._w += 4; }
3408                 else { baseSlab._w += 2; }
3409 
3410                 slabs.push_back( baseSlab );
3411                 break;
3412             }
3413 
3414             case GTK_POS_RIGHT:
3415             {
3416 
3417                 // main slab
3418                 x += adjust; w -= 2*adjust;
3419                 tabSlab = SlabRect( x-offset, y, w+10+offset, h, tabTiles );
3420                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3421                 if( isLastTabAligned ) { tabSlab._h+=1; }
3422 
3423                 // connections to frame
3424                 SlabRect baseSlab( x+w-1, y-4-1, 10, h+8+3, TileSet::Left );
3425                 if( isFirstTabAligned )
3426                 {
3427                     baseSlab._y += 4; baseSlab._h -= 4; baseSlab._tiles |= TileSet::Top;
3428                     slabs.push_back( SlabRect( x+w+offset-6, y-1, 16, 8, TileSet::Top ) );
3429                 }
3430 
3431                 if( isLastTabAligned )
3432                 {
3433                     baseSlab._h -= 4; baseSlab._tiles |= TileSet::Bottom;
3434                     slabs.push_back( SlabRect( x+w+offset-6, y+h-7, 16, 8, TileSet::Top ) );
3435                 }
3436 
3437                 if( isLeftOfSelected ) { baseSlab._h += 3; }
3438                 else if( isRightOfSelected ) { baseSlab._y -= 3; baseSlab._h += 3; }
3439                 else { baseSlab._h += 1; }
3440 
3441                 slabs.push_back( baseSlab );
3442                 break;
3443             }
3444 
3445 
3446             case GTK_POS_LEFT:
3447             {
3448 
3449                 // main slab
3450                 x += adjust; w -= 2*adjust;
3451                 tabSlab = SlabRect( x-10, y, w+10+offset, h, tabTiles );
3452                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3453                 if( isLastTabAligned ) { tabSlab._h+=1; }
3454 
3455                 // connections to frame
3456                 SlabRect baseSlab( x-10+1, y-4-1, 10, h+8+3, TileSet::Right );
3457                 if( isFirstTabAligned )
3458                 {
3459                     baseSlab._y += 4; baseSlab._h -= 4; baseSlab._tiles |= TileSet::Top;
3460                     slabs.push_back( SlabRect( x-10+1 + offset-5, y-1, 16, 8, TileSet::Top ) );
3461                 }
3462 
3463                 if( isLastTabAligned )
3464                 {
3465                     baseSlab._h -= 4; baseSlab._tiles |= TileSet::Bottom;
3466                     slabs.push_back( SlabRect( x-10+1 + offset-5, y+h-7, 16, 8, TileSet::Top ) );
3467                 }
3468 
3469                 if( isLeftOfSelected ) { baseSlab._h += 3; }
3470                 else if( isRightOfSelected ) { baseSlab._y -= 3; baseSlab._h += 3; }
3471                 else { baseSlab._h += 1; }
3472 
3473                 slabs.push_back( baseSlab );
3474                 break;
3475             }
3476 
3477             default: return;
3478         }
3479 
3480         const bool isFirstTab( tabOptions & FirstTab );
3481         const bool isLastTab( tabOptions & LastTab );
3482 
3483         const double radius( 5 );
3484         double xF( 0.5 + x );
3485         double yF( 0.5 + y );
3486         double wF( w-1 );
3487         double hF( h-1 );
3488 
3489         switch( side )
3490         {
3491 
3492             case GTK_POS_BOTTOM:
3493             {
3494                 xF += 1.0;
3495                 wF -= 1.0;
3496                 hF += 2;
3497                 if( isLeftOfSelected ) wF += 1;
3498                 else if( isRightOfSelected ) { xF -= 2; wF += 2; }
3499 
3500                 if( isFirstTab )
3501                 {
3502 
3503                     if( isFirstTabAligned ) cairo_move_to( context, xF, yF + hF + 2 );
3504                     else cairo_move_to( context, xF, yF + hF );
3505 
3506                     cairo_line_to( context, xF, yF + radius );
3507                     cairo_arc( context, xF + radius, yF + radius, radius, M_PI, 3.0*M_PI/2 );
3508                     cairo_line_to( context, xF + wF, yF );
3509                     cairo_line_to( context, xF + wF, yF + hF );
3510 
3511                 } else if( isLastTab ) {
3512 
3513                     cairo_move_to( context, xF, yF + hF );
3514                     cairo_line_to( context, xF, yF );
3515                     cairo_line_to( context, xF + wF - radius, yF );
3516                     cairo_arc( context, xF + wF - radius, yF + radius, radius, 3.0*M_PI/2, 2.0*M_PI );
3517                     if( isLastTabAligned ) cairo_line_to( context, xF + wF, yF + hF + 2 );
3518                     else cairo_line_to( context, xF + wF, yF + hF );
3519 
3520                 } else {
3521 
3522                     cairo_move_to( context, xF, yF + hF );
3523                     cairo_line_to( context, xF, yF );
3524                     cairo_line_to( context, xF + wF, yF );
3525                     cairo_line_to( context, xF + wF, yF + hF );
3526 
3527                 }
3528 
3529             }
3530 
3531             break;
3532 
3533             case GTK_POS_TOP:
3534             {
3535                 xF += 1.0;
3536                 wF -= 1.0;
3537                 yF -= 1;
3538                 hF += 1;
3539                 if( isLeftOfSelected ) wF += 1;
3540                 else if( isRightOfSelected ) { xF -= 2; wF += 2; }
3541 
3542 
3543                 if( isFirstTab )
3544                 {
3545 
3546                     cairo_move_to( context, xF+wF, yF );
3547                     cairo_line_to( context, xF+wF, yF + hF );
3548                     cairo_line_to( context, xF+radius, yF + hF );
3549                     cairo_arc( context, xF+radius, yF + hF -radius, radius, M_PI/2, M_PI );
3550                     if( isFirstTabAligned ) cairo_line_to( context, xF, yF - 2 );
3551                     else cairo_line_to( context, xF, yF );
3552 
3553                 } else if( isLastTab ) {
3554 
3555                     if( isLastTabAligned ) cairo_move_to( context, xF+wF, yF-2 );
3556                     else  cairo_move_to( context, xF+wF, yF-2 );
3557                     cairo_line_to( context, xF+wF, yF+hF-radius );
3558                     cairo_arc( context, xF+wF-radius, yF+hF-radius, radius, 0, M_PI/2 );
3559                     cairo_line_to( context, xF, yF+hF );
3560                     cairo_line_to( context, xF, yF );
3561 
3562                 } else {
3563 
3564                     cairo_move_to( context, xF+wF, yF );
3565                     cairo_line_to( context, xF+wF, yF + hF );
3566                     cairo_line_to( context, xF, yF+hF );
3567                     cairo_line_to( context, xF, yF );
3568 
3569                 }
3570 
3571             }
3572 
3573             break;
3574 
3575             case GTK_POS_RIGHT:
3576             {
3577 
3578                 yF += 1.0;
3579                 hF -= 1.0;
3580                 wF += 2;
3581 
3582                 if( isLeftOfSelected ) hF += 1;
3583                 else if( isRightOfSelected ) { yF -= 2; hF += 2; }
3584 
3585                 if( isFirstTab )
3586                 {
3587 
3588                     cairo_move_to( context, xF+wF, yF+hF );
3589                     cairo_line_to( context, xF, yF+hF );
3590                     cairo_line_to( context, xF, yF+radius );
3591                     cairo_arc( context, xF+radius, yF+radius, radius, M_PI, 3.0*M_PI/2 );
3592                     if( isFirstTabAligned ) cairo_line_to( context, xF+wF+2, yF );
3593                     else cairo_line_to( context, xF+wF, yF );
3594 
3595                 } else if( isLastTab ) {
3596 
3597                     if( isLastTabAligned ) cairo_line_to( context, xF + wF + 2, yF + hF );
3598                     else cairo_line_to( context, xF + wF, yF + hF );
3599                     cairo_line_to( context, xF+radius, yF+hF );
3600                     cairo_arc( context, xF+radius, yF+hF - radius, radius, M_PI/2, M_PI );
3601                     cairo_line_to( context, xF, yF );
3602                     cairo_line_to( context, xF + wF, yF );
3603 
3604                 } else {
3605 
3606                     cairo_move_to( context, xF+wF, yF+hF );
3607                     cairo_line_to( context, xF, yF+hF );
3608                     cairo_line_to( context, xF, yF );
3609                     cairo_line_to( context, xF+wF, yF );
3610 
3611                 }
3612             }
3613             break;
3614 
3615             case GTK_POS_LEFT:
3616             {
3617                 yF += 1.0;
3618                 hF -= 1.0;
3619                 xF -= 2;
3620                 wF += 2;
3621 
3622                 if( isLeftOfSelected ) hF += 1;
3623                 else if( isRightOfSelected ) { yF -= 2; hF += 2; }
3624 
3625                 if( isFirstTab )
3626                 {
3627 
3628                     if( isFirstTabAligned ) cairo_move_to( context, xF-2, yF );
3629                     else cairo_move_to( context, xF, yF );
3630                     cairo_line_to( context, xF + wF - radius, yF );
3631                     cairo_arc( context, xF + wF - radius, yF + radius, radius, 3.0*M_PI/2, 2*M_PI );
3632                     cairo_line_to( context, xF+wF, yF+hF );
3633                     cairo_line_to( context, xF, yF+hF );
3634 
3635                 } else if( isLastTab ) {
3636 
3637                     cairo_move_to( context, xF, yF );
3638                     cairo_line_to( context, xF+wF, yF );
3639                     cairo_line_to( context, xF+wF, yF + hF - radius );
3640                     cairo_arc( context, xF+wF-radius, yF + hF - radius, radius, 0, M_PI/2 );
3641                     if( isLastTabAligned ) cairo_line_to( context, xF-2, yF+hF );
3642                     else cairo_line_to( context, xF, yF+hF );
3643 
3644                 } else {
3645 
3646                     cairo_move_to( context, xF, yF );
3647                     cairo_line_to( context, xF+wF, yF );
3648                     cairo_line_to( context, xF+wF, yF+hF );
3649                     cairo_line_to( context, xF, yF+hF );
3650 
3651                 }
3652             }
3653             break;
3654 
3655             default: return;
3656 
3657         }
3658 
3659         ColorUtils::Rgba backgroundColor( base );
3660         {
3661 
3662             gint wh, wy;
3663             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
3664             if( wh > 0 )
3665             {  backgroundColor = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 ); }
3666 
3667         }
3668 
3669         const ColorUtils::Rgba midColor( ColorUtils::alphaColor( ColorUtils::darkColor( backgroundColor ), 0.4 ) );
3670         const ColorUtils::Rgba darkColor( ColorUtils::alphaColor( ColorUtils::darkColor( backgroundColor ), 0.8 ) );
3671 
3672         cairo_set_line_width( context, 1.0 );
3673         cairo_set_source( context, midColor );
3674         cairo_fill_preserve( context );
3675 
3676         cairo_set_source( context, darkColor );
3677         cairo_stroke( context );
3678 
3679         ColorUtils::Rgba glow;
3680         if( data._mode == AnimationHover ) glow = ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3681         else if( options&Hover ) glow = _settings.palette().color( Palette::Hover );
3682 
3683         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3684         { _helper.slab(base, glow, 0).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles ); }
3685 
3686     }
3687 
3688     //____________________________________________________________________________________
3689     ColorUtils::Rgba Style::slabShadowColor( const StyleOptions& options, const AnimationData& data ) const
3690     {
3691 
3692         // no glow when widget is disabled
3693         if( options&Disabled ) return ColorUtils::Rgba();
3694 
3695         if( (options&Flat) && !(options&Sunken) )
3696         {
3697 
3698             /*
3699             flat buttons have special handling of colors: hover and focus are both assigned
3700             the hover color. This is consistent with oxygen-qt, though quite inconsistent with
3701             other widgets.
3702             */
3703             if(
3704                 ( data._mode == AnimationHover && (options&Focus) ) ||
3705                 ( data._mode == AnimationFocus && (options&Hover) ) )
3706             {
3707                 return _settings.palette().color( Palette::Focus );
3708 
3709             } else if( data._mode & (AnimationHover|AnimationFocus) ) {
3710 
3711                 return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3712 
3713             } else if( options&(Focus|Hover) ) {
3714 
3715                 return _settings.palette().color( Palette::Focus );
3716 
3717             } else return ColorUtils::Rgba();
3718 
3719         } else if( data._mode == AnimationHover ) {
3720 
3721             if( options & Focus )
3722             {
3723                 return ColorUtils::mix(
3724                     _settings.palette().color( Palette::Focus ),
3725                     _settings.palette().color( Palette::Hover ), data._opacity );
3726 
3727             } else {
3728 
3729                 return ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3730 
3731             }
3732 
3733         } else if( options&Hover ) {
3734 
3735             return _settings.palette().color( Palette::Hover );
3736 
3737         } else if( data._mode == AnimationFocus ) {
3738 
3739             return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3740 
3741         } else if( options&Focus ) {
3742 
3743             return _settings.palette().color( Palette::Focus );
3744 
3745         } else return ColorUtils::Rgba();
3746 
3747     }
3748 
3749     //____________________________________________________________________________________
3750     ColorUtils::Rgba Style::holeShadowColor( const StyleOptions& options, const AnimationData& data ) const
3751     {
3752 
3753         // no glow when widget is disabled
3754         if( options&Disabled ) return ColorUtils::Rgba();
3755 
3756         if( data._mode == AnimationFocus && data._opacity >= 0 )
3757         {
3758 
3759             if( options & Hover )
3760             {
3761 
3762                 return ColorUtils::mix(
3763                     _settings.palette().color( Palette::Hover ),
3764                     _settings.palette().color( Palette::Focus ), data._opacity );
3765 
3766             } else return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3767 
3768         } else if( options & Focus ) {
3769 
3770             return _settings.palette().color( Palette::Focus );
3771 
3772         } else if( data._mode == AnimationHover && data._opacity >= 0 ) {
3773 
3774             return ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3775 
3776         } else if( options & Hover ) {
3777 
3778             return _settings.palette().color( Palette::Hover );
3779 
3780         } else return ColorUtils::Rgba();
3781 
3782     }
3783 
3784     //__________________________________________________________________
3785     void Style::renderGroupBox(
3786         cairo_t* context,
3787         const ColorUtils::Rgba& base,
3788         gint x, gint y, gint w, gint h,
3789         const StyleOptions& options )
3790     {
3791 
3792         cairo_push_group( context );
3793         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, y - h + 12, 0,  y + 2*h - 19 ) );
3794         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
3795         cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( light, 0.4 ) );
3796         cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( light ) );
3797         cairo_set_source( context, pattern );
3798 
3799         if( !_settings.applicationName().useFlatBackground( 0L ) )
3800             _helper.fillSlab( context, x, y, w, h );
3801 
3802         if( !(options&NoFill) )
3803         { _helper.slope( base, 0.0 ).render( context, x, y, w, h ); }
3804 
3805         cairo_pop_group_to_source( context );
3806 
3807         Cairo::Pattern mask( cairo_pattern_create_linear( 0,  y + h - 19, 0,  y + h ) );
3808         cairo_pattern_add_color_stop( mask, 0, ColorUtils::Rgba::black() );
3809         cairo_pattern_add_color_stop( mask, 1.0, ColorUtils::Rgba::transparent() );
3810         cairo_mask( context, mask );
3811 
3812         return;
3813 
3814     }
3815 
3816     //__________________________________________________________________
3817     void Style::renderSlab(
3818         Cairo::Context& context,
3819         gint x, gint y, gint w, gint h,
3820         const ColorUtils::Rgba& base,
3821         const StyleOptions& options,
3822         const AnimationData& animationData,
3823         TileSet::Tiles tiles )
3824     {
3825 
3826         // do nothing if not enough room
3827         if( w<14 || h<14 ) return;
3828 
3829         // additional adjustment for sunken frames
3830         // TODO: double check
3831         if( options & Sunken)
3832         {
3833 
3834             x -= 1;
3835             w += 2;
3836             h += 2;
3837 
3838         }
3839 
3840         // fill
3841         if( !(options & NoFill))
3842         {
3843 
3844             Cairo::Pattern pattern;
3845             const ColorUtils::Rgba shadow( ColorUtils::shadowColor( base ) );
3846             if( shadow.value() > base.value() && (options & Sunken) )
3847             {
3848 
3849                 pattern.set( cairo_pattern_create_linear( 0, y, 0, y+2*h ) );
3850                 cairo_pattern_add_color_stop( pattern, 0, base );
3851                 cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::lightColor( base ) );
3852 
3853             } else {
3854 
3855                 pattern.set( cairo_pattern_create_linear( 0, y-h, 0, y+h ) );
3856                 cairo_pattern_add_color_stop( pattern, 0, ColorUtils::lightColor( base ) );
3857                 cairo_pattern_add_color_stop( pattern, 1.0, base );
3858 
3859             }
3860 
3861             cairo_set_source( context, pattern );
3862             _helper.fillSlab( context, x, y, w, h, tiles );
3863 
3864         }
3865 
3866         if( !(options&Sunken) ) {
3867 
3868             // calculate glow color
3869             const TileSet* tile;
3870             const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
3871             if( glow.isValid() || base.isValid() ) tile = &_helper.slab( base, glow , 0);
3872             else return;
3873 
3874             if( tile ) tile->render( context, x, y, w, h );
3875 
3876         } else if( base.isValid() ) {
3877 
3878             _helper.slabSunken( base ).render( context, x, y, w, h );
3879 
3880         }
3881 
3882     }
3883 
3884     //__________________________________________________________________
3885     void Style::renderScrollBarHole( Cairo::Context& context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& base, bool vertical, TileSet::Tiles tiles )
3886     {
3887 
3888         // use tileset from helper
3889         _helper.scrollHole( base, vertical ).render( context, x, y, w, h, tiles );
3890 
3891     }
3892 
3893     //__________________________________________________________________
3894     Polygon Style::genericArrow( GtkArrowType orientation, QtSettings::ArrowSize size ) const
3895     {
3896 
3897         Polygon a;
3898         switch( orientation )
3899         {
3900 
3901             case GTK_ARROW_UP:
3902             {
3903                 if( size == QtSettings::ArrowTiny ) a << Point( -1.75, 1.125 ) << Point( 0.5, -1.125 ) << Point( 2.75, 1.125 );
3904                 else if( size == QtSettings::ArrowSmall ) a << Point( -2,1.5 ) << Point( 0.5, -1.5 ) << Point( 3,1.5 );
3905                 else a << Point( -3,2.5 ) << Point( 0.5, -1.5 ) << Point( 4,2.5 );
3906                 break;
3907             }
3908 
3909             case GTK_ARROW_DOWN:
3910             {
3911                 if( size == QtSettings::ArrowTiny ) a << Point( -1.75, -1.125 ) << Point( 0.5, 1.125 ) << Point( 2.75, -1.125 );
3912                 else if( size == QtSettings::ArrowSmall ) a << Point( -2,-1.5 ) << Point( 0.5, 1.5 ) << Point( 3,-1.5 );
3913                 else a << Point( -3,-1.5 ) << Point( 0.5, 2.5 ) << Point( 4,-1.5 );
3914                 break;
3915             }
3916 
3917             case GTK_ARROW_LEFT:
3918             {
3919                 if( size == QtSettings::ArrowTiny ) a << Point( 1.125, -1.75 ) << Point( -1.125, 0.5 ) << Point( 1.125, 2.75 );
3920                 else if( size == QtSettings::ArrowSmall ) a << Point( 1.5,-2 ) << Point( -1.5, 0.5 ) << Point( 1.5,3 );
3921                 else a << Point( 2.5,-3 ) << Point( -1.5, 0.5 ) << Point( 2.5,4 );
3922                 break;
3923             }
3924 
3925             case GTK_ARROW_RIGHT:
3926             {
3927                 if( size == QtSettings::ArrowTiny ) a << Point( -1.125, -1.75 ) << Point( 1.125, 0.5 ) << Point( -1.125, 2.75 );
3928                 else if( size == QtSettings::ArrowSmall ) a << Point( -1.5,-2 ) << Point( 1.5, 0.5 ) << Point( -1.5,3 );
3929                 else a << Point( -1.5,-3 ) << Point( 2.5, 0.5 ) << Point( -1.5,4 );
3930                 break;
3931             }
3932 
3933             default: break;
3934 
3935         }
3936 
3937         return a;
3938 
3939 
3940 
3941     }
3942 
3943     //__________________________________________________________________
3944     void Style::renderWindowDots(cairo_t* context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color, WinDeco::Options wopt)
3945     {
3946         bool isMaximized( wopt & WinDeco::Maximized );
3947         bool hasAlpha( wopt & WinDeco::Alpha );
3948         int offset( hasAlpha ? 0 : -1 );
3949         if( _settings.frameBorder() >= QtSettings::BorderTiny )
3950         {
3951             if( !isMaximized )
3952             {
3953                 // Draw right side 3-dots resize handles
3954                 int cenY = int(h/2+y);
3955                 int posX = int(w+x-3) + 1;
3956                 _helper.renderDot(context,color,posX+offset, cenY-3);
3957                 _helper.renderDot(context,color,posX+offset, cenY);
3958                 _helper.renderDot(context,color,posX+offset, cenY+3);
3959             }
3960 
3961             // Draw bottom-right corner 3-dots resize handles
3962             // if( !config.drawSizeGrip )
3963             {
3964                 cairo_save(context);
3965                 cairo_translate(context,x+w-8,y+h-8);
3966                 _helper.renderDot(context,color,2+offset,6+offset);
3967                 _helper.renderDot(context,color,5+offset,5+offset);
3968                 _helper.renderDot(context,color,6+offset,2+offset);
3969                 cairo_restore(context);
3970             }
3971         }
3972     }
3973 
3974     //__________________________________________________________________
3975     void Style::centerRect( GdkRectangle* parent, GdkRectangle* child ) const
3976     {
3977         if( !( parent && child ) ) return;
3978         child->x = parent->x + (parent->width - child->width)/2;
3979         child->y = parent->y + (parent->height - child->height)/2;
3980         return;
3981     }
3982 
3983     //__________________________________________________________________
3984     void Style::generateGapMask( Cairo::Context& context, gint x, gint y, gint w, gint h, const Gtk::Gap& gap ) const
3985     {
3986 
3987         if( gap.width() <= 0 ) return;
3988 
3989         // store current rect in
3990         GdkRectangle mask( Gtk::gdk_rectangle() );
3991 
3992         switch( gap.position() )
3993         {
3994             case GTK_POS_TOP:
3995             {
3996                 mask = Gtk::gdk_rectangle( x+gap.x(), y, gap.width(), gap.height() );
3997                 break;
3998             }
3999 
4000             case GTK_POS_BOTTOM:
4001             {
4002                 mask = Gtk::gdk_rectangle( x+gap.x(), y+h-gap.height(), gap.width(), gap.height() );
4003                 break;
4004             }
4005 
4006             case GTK_POS_LEFT:
4007             {
4008                 mask = Gtk::gdk_rectangle( x, y+gap.x(), gap.height(), gap.width() );
4009                 break;
4010             }
4011 
4012             case GTK_POS_RIGHT:
4013             {
4014                 mask = Gtk::gdk_rectangle( x + w - gap.height(), y+gap.x(), gap.height(), gap.width() );
4015                 break;
4016             }
4017 
4018             default: return;
4019         }
4020 
4021         if( false )
4022         {
4023             cairo_set_source( context, ColorUtils::Rgba( 1, 0, 0, 0.3 ) );
4024             gdk_cairo_rectangle( context, &mask );
4025             cairo_fill( context );
4026         }
4027 
4028         cairo_rectangle( context, x, y, w, h );
4029         cairo_rectangle_negative( context, mask.x, mask.y, mask.width, mask.height );
4030         cairo_clip( context );
4031 
4032         return;
4033 
4034     }
4035 
4036     //_________________________________________________________
4037     void Style::fileChanged( GFileMonitor*, GFile* file, GFile*, GFileMonitorEvent event, gpointer data )
4038     {
4039 
4040         #if OXYGEN_DEBUG
4041         std::cerr << "Oxygen::Style::fileChanged -"
4042             << " file: " << g_file_get_path( file )
4043             << " event: " << Gtk::TypeNames::fileMonitorEvent( event )
4044             << std::endl;
4045         #endif
4046 
4047         Style& style( *static_cast<Style*>( data ) );
4048         if( style.initialize( QtSettings::All|QtSettings::Forced ) )
4049         { gtk_rc_reset_styles( gtk_settings_get_default() ); }
4050 
4051     }
4052 
4053     //_______________________________________________________________________
4054     void Style::renderTabCloseIcon(cairo_t* context, GdkRectangle* r) const
4055     {
4056         cairo_save(context);
4057         cairo_translate(context,r->x,r->y);
4058 
4059         cairo_move_to( context, 5.5, 5.5 ); cairo_line_to( context, 10.5, 10.5 );
4060         cairo_move_to( context, 10.5, 5.5 ); cairo_line_to( context, 5.5, 10.5 );
4061         cairo_stroke( context );
4062 
4063         cairo_restore(context);
4064     }
4065 
4066     //_______________________________________________________________________________
4067     void Style::renderTabCloseButton(cairo_t* context, GdkRectangle* r, const ColorUtils::Rgba& base, const ColorUtils::Rgba& color)
4068     {
4069         cairo_save(context);
4070 
4071         cairo_set_source_surface(context,_helper.dockWidgetButton(base,true,r->width),0,0);
4072         cairo_rectangle(context,r->x,r->y,r->width,r->height);
4073         cairo_fill(context);
4074 
4075         const double width(1.1);
4076 
4077         // contrast
4078         cairo_translate(context,0,0.5);
4079         cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
4080         cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
4081         cairo_set_line_width(context,width);
4082         cairo_set_source(context,ColorUtils::lightColor( base ));
4083 
4084         renderTabCloseIcon(context,r);
4085 
4086         // main icon painting
4087         cairo_translate(context,0,-1);
4088         cairo_set_source(context,color);
4089 
4090         renderTabCloseIcon(context,r);
4091 
4092         cairo_restore(context);
4093     }
4094 
4095     //_________________________________________________________
4096     void Style::adjustMask( GtkWidget* widget, int width, int height, bool alpha )
4097     {
4098 
4099         // get window and decide offset
4100         GdkWindow* window(0);
4101         int verticalMaskOffset(0);
4102         if( GTK_IS_MENU( widget ) )
4103         {
4104 
4105             window = gtk_widget_get_parent_window( widget );
4106             verticalMaskOffset=Oxygen::Menu_VerticalOffset;
4107 
4108         } else if(
4109             Gtk::gtk_is_tooltip( widget ) ||
4110             Gtk::gtk_combobox_is_popup( widget ) ||
4111             Gtk::gtk_combo_is_popup( widget ) ) {
4112 
4113             window=gtk_widget_get_window( widget );
4114 
4115         } else {
4116 
4117             std::cerr << "FIXME: Oxygen::WidgetSizeData: unknown window type: \""<< Gtk::gtk_widget_path( widget )<<"\"\n";
4118             return;
4119 
4120         }
4121 
4122         // adjust mask
4123         if(!alpha)
4124         {
4125 
4126             // make menus/tooltips/combo lists appear rounded using XShape extension if screen isn't composited
4127             GdkPixmap* mask( _helper.roundMask( width, height - 2*verticalMaskOffset) );
4128             gdk_window_shape_combine_mask( window, mask, 0, verticalMaskOffset );
4129             gdk_pixmap_unref(mask);
4130 
4131         } else {
4132 
4133             // reset mask if compositing has appeared after we had set a mask
4134             gdk_window_shape_combine_mask( window, NULL, 0, 0);
4135 
4136         }
4137 
4138     }
4139 
4140     //_________________________________________________________
4141     void Style::setWindowBlur( GtkWidget* widget, bool enable )
4142     {
4143 
4144         #ifdef GDK_WINDOWING_X11
4145         GdkWindow* window( 0L );
4146         if( GTK_IS_MENU( widget ) )
4147         {
4148 
4149             window = gtk_widget_get_parent_window( widget );
4150 
4151         } else if(
4152             Gtk::gtk_is_tooltip( widget ) ||
4153             Gtk::gtk_combobox_is_popup( widget ) ||
4154             Gtk::gtk_combo_is_popup( widget ) ) {
4155 
4156             window=gtk_widget_get_window( widget );
4157 
4158         } else return;
4159 
4160         // Make whole window blurred
4161         // FIXME: should roundedness be taken into account?
4162         #if GTK_CHECK_VERSION(2,24,0)
4163         const int w( gdk_window_get_width( window ) );
4164         const int h( gdk_window_get_height( window ) );
4165         GdkDisplay* gdkDisplay = gdk_window_get_display( window );
4166         #else
4167         int w(0), h(0);
4168         gdk_drawable_get_size( window, &w, &h );
4169         GdkDisplay* gdkDisplay = gdk_drawable_get_display( window );
4170         #endif
4171 
4172         const unsigned long rects[4] = { 0, 0, (unsigned long)w, (unsigned long)h };
4173         const XID id( GDK_WINDOW_XID( window ) );
4174         Display* display( GDK_DISPLAY_XDISPLAY( gdkDisplay ) );
4175 
4176         if(enable)
4177         {
4178 
4179             XChangeProperty( display, id, _blurAtom, XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<const unsigned char*>(rects), 4 );
4180 
4181         } else XDeleteProperty( display, id, _blurAtom );
4182 
4183         #endif
4184 
4185     }
4186 
4187 }