1 /*
2 * this file is part of the oxygen gtk engine
3 * Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
4 * Copyright (c) 2010 Ruslan Kabatsayev <b7.10110111@gmail.com>
5 *
6 * This  library is free  software; you can  redistribute it and/or
7 * modify it  under  the terms  of the  GNU Lesser  General  Public
8 * License  as published  by the Free  Software  Foundation; either
9 * version 2 of the License, or( at your option ) any later version.
10 *
11 * This library is distributed  in the hope that it will be useful,
12 * but  WITHOUT ANY WARRANTY; without even  the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License  along  with  this library;  if not,  write to  the Free
18 * Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21 
22 #include "oxygenstyle.h"
23 #include "oxygencairocontext.h"
24 #include "oxygencairoutils.h"
25 #include "oxygencolorutils.h"
26 #include "oxygenfontinfo.h"
27 #include "oxygengtkutils.h"
28 #include "oxygenmetrics.h"
29 #include "oxygenwindecobutton.h"
30 #include "oxygenwindowshadow.h"
31 
32 #include "oxygengtktypenames.h"
33 
34 #include <algorithm>
35 #include <cmath>
36 
37 #ifdef GDK_WINDOWING_X11
38 #include <X11/Xatom.h>
39 #endif
40 
41 namespace Oxygen
42 {
43 
44     //__________________________________________________________________
45     Style* Style::_instance = 0;
instance(void)46     Style& Style::instance( void )
47     {
48         if( !_instance )
49         {
50             _instance = new Style();
51             _instance->initialize();
52         }
53 
54         return *_instance;
55     }
56 
57     //__________________________________________________________________
Style(void)58     Style::Style( void )
59     {
60         #ifdef GDK_WINDOWING_X11
61         _blurAtom = None;
62         #endif
63     }
64 
65     //__________________________________________________________________
initialize(unsigned int flags)66     bool Style::initialize( unsigned int flags )
67     {
68 
69         // initialize ref surface
70         _helper.initializeRefSurface();
71 
72         // reinitialize settings
73         if( !_settings.initialize( flags ) ) return false;
74 
75         // reset caches if colors have changed
76         if( flags&QtSettings::Colors )
77         {
78             _helper.clearCaches();
79             ColorUtils::clearCaches();
80         }
81 
82         // connect files
83         QtSettings::FileMap& monitoredFiles( _settings.monitoredFiles() );
84         for( QtSettings::FileMap::iterator iter = monitoredFiles.begin(); iter != monitoredFiles.end(); ++iter )
85         {
86             if( !iter->second.signal.isConnected() )
87             { iter->second.signal.connect( G_OBJECT( iter->second.monitor ), "changed", G_CALLBACK(fileChanged), this ); }
88         }
89 
90         // reinitialize animations
91         _animations.initialize( _settings );
92 
93         if( flags&QtSettings::Oxygen )
94         {
95 
96             // widget explorer
97             _widgetExplorer.setEnabled( _settings.widgetExplorerEnabled() );
98 
99             // pass window drag mode to window manager
100             if( !_settings.windowDragEnabled() ) _windowManager.setDragMode( WindowManager::Disabled );
101             else if( _settings.windowDragMode() == QtSettings::WD_MINIMAL ) _windowManager.setDragMode( WindowManager::Minimal );
102             else _windowManager.setDragMode( WindowManager::Full );
103 
104             // use window manager to handle window drag
105             _windowManager.setUseWMMoveResize( _settings.useWMMoveResize() );
106 
107         }
108 
109         if( flags&QtSettings::KdeGlobals )
110         {
111             // pass drag distance and delay to window manager
112             _windowManager.setDragDistance( _settings.startDragDist() );
113             _windowManager.setDragDelay( _settings.startDragTime() );
114         }
115 
116         // background surface
117         if( !_settings.backgroundPixmap().empty() ) setBackgroundSurface( _settings.backgroundPixmap() );
118 
119         // create window shadow
120         WindowShadow shadow( _settings, _helper );
121         _shadowHelper.setSupported( _settings.isWMShadowsSupported() );
122         _shadowHelper.setApplicationName( _settings.applicationName() );
123         _shadowHelper.initialize( _settings.palette().color(Palette::Window), shadow );
124 
125         #ifdef GDK_WINDOWING_X11
126         if( _blurAtom == None )
127         {
128             GdkDisplay *display( gdk_display_get_default () );
129             if( display && GDK_IS_X11_DISPLAY( display ) )
130             { _blurAtom = XInternAtom(GDK_DISPLAY_XDISPLAY( display ),"_KDE_NET_WM_BLUR_BEHIND_REGION",False); }
131         }
132         #endif
133 
134         return true;
135 
136     }
137 
138     //__________________________________________________________________
tabCloseButton(const StyleOptions & options)139     Cairo::Surface Style::tabCloseButton( const StyleOptions& options )
140     {
141 
142         // active tab
143         if( options&Focus )
144         {
145             // create button
146             if( !_tabCloseButtons.active )
147             {
148                 const std::string filename( std::string(GTK_THEME_DIR)+ "/special-icons/standardbutton-closetab-down-16.png" );
149                 _tabCloseButtons.active = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
150             }
151 
152             return _tabCloseButtons.active;
153         }
154 
155         // prelight
156         if( options&Hover )
157         {
158             // create button
159             if( !_tabCloseButtons.prelight )
160             {
161                 const std::string filename( std::string(GTK_THEME_DIR) + "/special-icons/standardbutton-closetab-hover-16.png" );
162                 _tabCloseButtons.prelight = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
163             }
164 
165             return _tabCloseButtons.prelight;
166 
167         }
168 
169         // normal or inactive
170         if( !_tabCloseButtons.normal )
171         {
172             const std::string filename( std::string(GTK_THEME_DIR) + "/special-icons/standardbutton-closetab-16.png" );
173             _tabCloseButtons.normal = Cairo::Surface( cairo_image_surface_create_from_png( filename.c_str() ) );
174         }
175 
176         // inactive
177         if( (options&Disabled) && _tabCloseButtons.normal )
178         {
179 
180             if( !_tabCloseButtons.inactive )
181             {
182 
183                 // make deep copy of the normal image
184                 _tabCloseButtons.inactive = Cairo::Surface( cairo_surface_copy( _tabCloseButtons.normal ) );
185                 cairo_surface_add_alpha(  _tabCloseButtons.inactive, 0.5 );
186                 cairo_image_surface_saturate( _tabCloseButtons.inactive, 0.1 );
187 
188             }
189 
190             return _tabCloseButtons.inactive;
191 
192         }
193 
194         // fallback to normal
195         return _tabCloseButtons.normal;
196 
197     }
198 
199     //____________________________________________________________________________________
hasBackgroundSurface(void) const200     bool Style::hasBackgroundSurface( void ) const
201     {
202         if( !_backgroundSurface.isValid() ) return false;
203         const cairo_status_t status( cairo_surface_status( _backgroundSurface ) );
204         return
205             status != CAIRO_STATUS_NO_MEMORY &&
206             status != CAIRO_STATUS_FILE_NOT_FOUND &&
207             status != CAIRO_STATUS_READ_ERROR;
208     }
209 
210     //__________________________________________________________________
fill(cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & color) const211     void Style::fill( cairo_t* context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color ) const
212     {
213 
214         cairo_save( context );
215 
216         // define colors
217         cairo_rectangle( context, x, y, w, h );
218         cairo_set_source( context, color );
219         cairo_fill( context );
220 
221         cairo_restore( context );
222 
223     }
224 
225     //__________________________________________________________________
outline(cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & color) const226     void Style::outline( cairo_t* context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color ) const
227     {
228 
229         // define colors
230         cairo_save( context );
231         cairo_rectangle( context, 0.5+x, 0.5+y, w-1, h-1 );
232         cairo_set_line_width( context, 1 );
233         cairo_set_source( context, color );
234         cairo_stroke( context );
235         cairo_restore( context );
236 
237     }
238 
239     //__________________________________________________________________
drawSeparator(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options)240     void Style::drawSeparator( GtkWidget* widget, cairo_t* context, gint x, gint y, gint w, gint h, const StyleOptions& options )
241     {
242 
243         // define colors
244         ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
245         if( widget && (options&Blend) )
246         {
247 
248             gint wh, wy;
249             Gtk::gdk_map_to_toplevel( 0L, widget, 0L, &wy, 0L, &wh );
250             if( wh > 0 )
251             {
252                 if( options & Menu ) base = ColorUtils::menuBackgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
253                 else base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
254             }
255 
256         }
257 
258         cairo_save( context );
259         _helper.drawSeparator( context, base, x, y, w, h, (options&Vertical) );
260         cairo_restore( context );
261 
262     }
263 
264     //__________________________________________________________________
renderWindowBackground(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options,bool isMaximized)265     bool Style::renderWindowBackground(
266         cairo_t* context, GdkWindow* window, GtkWidget* widget,
267         gint x, gint y, gint w, gint h,
268         const StyleOptions& options,
269         bool isMaximized )
270     {
271 
272         if( _settings.useBackgroundGradient() )
273         {
274 
275             // gradient
276             if( !renderBackgroundGradient( context, window, widget, x, y, w, h, options, isMaximized ) )
277             { return false; }
278 
279         } else {
280 
281             // flat background
282             cairo_save( context );
283             const ColorUtils::Rgba base( color( Palette::Window, options ) );
284             cairo_set_source(context,base);
285             cairo_rectangle(context,x,y,w,h);
286             cairo_fill(context);
287             cairo_restore( context );
288 
289         }
290 
291         // pixmap
292         renderBackgroundPixmap( context, window, widget, x, y, w, h, isMaximized );
293         return true;
294 
295     }
296 
297     //__________________________________________________________________
renderBackgroundGradient(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options,bool isMaximized)298     bool Style::renderBackgroundGradient(
299         cairo_t* context, GdkWindow* window, GtkWidget* widget,
300         gint x, gint y, gint w, gint h,
301         const StyleOptions& options,
302         bool isMaximized )
303     {
304 
305         // always save context
306         cairo_save(context);
307 
308         // define colors
309         const ColorUtils::Rgba base( color( Palette::Window, options ) );
310 
311         // the hard-coded metrics are copied for
312         // kdebase/workspace/libs/oxygen/oxygenhelper.cpp
313         // vertical shift to account for window decoration
314         const int yShift = 23;
315 
316         // toplevel window information and relative positioning
317         gint ww(0), wh(0);
318         gint wx(0), wy(0);
319 
320         // TODO: make sure this new test (on window) does not break Ruslan's decorations
321         if( window || widget )
322         {
323             // get window dimension and position
324             if( !Gtk::gdk_map_to_toplevel( window, widget, &wx, &wy, &ww, &wh, true ) )
325             {
326 
327                 #if OXYGEN_DEBUG
328                 std::cerr << "Oxygen::Style::renderWindowBackground - map_to_toplevel failed" << std::endl;
329                 std::cerr << "original xywh: ("<<x<<","<<y<<","<<w<<","<<h<<")\n";
330                 #endif
331 
332                 // flat painting for all other apps
333                 cairo_set_source(context,base);
334                 cairo_rectangle(context,x,y,w,h);
335                 cairo_fill(context);
336                 cairo_restore( context );
337                 return false;
338             }
339 
340             // translate to toplevel coordinates
341             wy += yShift;
342             x+=wx;
343             y+=wy;
344 
345             // no sense in context saving since it will be destroyed
346             cairo_translate( context, -wx, -wy );
347 
348         } else {
349 
350             // drawing window decorations, so logic is simplified
351             ww=w;
352             wh=h;
353             cairo_translate(context,x,y);
354             x=0;
355             y=0;
356 
357         }
358 
359         // split
360         const int splitY( std::min(300, 3*wh/4 ) );
361 
362         // store rectangle
363         GdkRectangle rect = { x, y, w, h };
364 
365         // upper rect
366         GdkRectangle upperRect = { 0, 0, ww, splitY };
367         if( gdk_rectangle_intersect( &rect, &upperRect, &upperRect ) )
368         {
369 
370             const Cairo::Surface& surface( _helper.verticalGradient( base, splitY ) );
371             cairo_set_source_surface( context, surface, 0, 0 );
372             cairo_pattern_set_extend( cairo_get_source( context ), CAIRO_EXTEND_REPEAT );
373             gdk_cairo_rectangle( context, &upperRect );
374             cairo_fill( context );
375 
376         }
377 
378         // fill lower rect
379         GdkRectangle lowerRect = { 0, splitY, ww, std::max(wh,h) - splitY + yShift };
380         if( gdk_rectangle_intersect( &rect, &lowerRect, &lowerRect ) )
381         {
382 
383             ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
384             gdk_cairo_rectangle( context, &lowerRect );
385             cairo_set_source( context, bottom );
386             cairo_fill( context );
387 
388         }
389 
390         // radial pattern
391         const int patternHeight = 64;
392         const int radialW( std::min(600, ww ) );
393 
394         GdkRectangle radialRect = {  (ww - radialW)/2, 0, radialW, patternHeight };
395         if( gdk_rectangle_intersect( &rect, &radialRect, &radialRect ) )
396         {
397 
398             const Cairo::Surface& surface( _helper.radialGradient( base, 64 ) );
399             cairo_set_source_surface( context, surface, 0, 0 );
400 
401             // add matrix transformation
402             cairo_matrix_t transformation;
403             cairo_matrix_init_identity( &transformation );
404             cairo_matrix_scale( &transformation, 128.0/radialW, 1.0 );
405             cairo_matrix_translate( &transformation, -(ww - radialW)/2, 0 );
406             cairo_pattern_set_matrix( cairo_get_source( context ), &transformation );
407 
408             gdk_cairo_rectangle( context, &radialRect );
409             cairo_fill( context );
410 
411         }
412 
413         // restore context
414         cairo_restore(context);
415 
416         return true;
417 
418     }
419 
420     //__________________________________________________________________
renderBackgroundPixmap(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h,bool isMaximized)421     bool Style::renderBackgroundPixmap(
422         cairo_t* context, GdkWindow* window, GtkWidget* widget,
423         gint x, gint y, gint w, gint h,
424         bool isMaximized )
425     {
426 
427         // do nothing if no background surface
428         if( !hasBackgroundSurface() ) return false;
429 
430         // always save context
431         cairo_save(context);
432 
433         bool renderingWindeco(context && !window);
434 
435         // the hard-coded metrics are copied for
436         // kdebase/workspace/libs/oxygen/oxygenhelper.cpp
437         // vertical shift to account for window decoration
438         const int yShift = 23;
439 
440         // toplevel window information and relative positioning
441         gint ww(0), wh(0);
442         gint wx(0), wy(0);
443 
444         // TODO: make sure this new test (on window) does not break Ruslan's decorations
445         if( window || widget )
446         {
447             // get window dimension and position
448             if( !Gtk::gdk_map_to_toplevel( window, widget, &wx, &wy, &ww, &wh, true ) )
449             { return false; }
450 
451             // translate to toplevel coordinates
452             wy += yShift;
453             x+=wx;
454             y+=wy;
455 
456             // no sense in context saving since it will be destroyed
457             cairo_translate( context, -wx, -wy );
458 
459         } else {
460 
461             // drawing window decorations, so logic is simplified
462             ww=w;
463             wh=h;
464             cairo_translate(context,x,y);
465             x=0;
466             y=0;
467 
468         }
469 
470         // Additional clip constraint so that no extra space is filled (important for LibreOffice)
471         cairo_rectangle(context,x,y,w,h);
472         cairo_clip(context);
473 
474         if(renderingWindeco)
475         {
476             // Take border sizes into account
477             int bgShiftX=isMaximized?0:WinDeco::getMetric(WinDeco::BorderLeft);
478             int bgShiftY=WinDeco::getMetric(WinDeco::BorderTop)-yShift;
479             cairo_translate(context,bgShiftX,bgShiftY);
480         }
481 
482         // no sense in context saving since it will be either destroyed or restored to earlier state
483         cairo_translate( context, -40, -(48-20) );
484         cairo_set_source_surface( context, _backgroundSurface, 0, 0 );
485         cairo_rectangle( context, 0, 0, ww + wx + 40, wh + wy + 48 - 20 );
486         cairo_fill( context );
487 
488         // restore context
489         cairo_restore(context);
490 
491         return true;
492 
493     }
494 
495     //__________________________________________________________________
renderTitleBarBackground(cairo_t * context,GtkWidget * widget,gint x,gint y,gint w,gint h)496     bool Style::renderTitleBarBackground(
497         cairo_t* context, GtkWidget* widget,
498         gint x, gint y, gint w, gint h )
499     {
500         cairo_push_group( context );
501         const bool success( Style::instance().renderWindowBackground( context, 0L, widget, x, y, w, h ) );
502         cairo_pop_group_to_source( context );
503 
504         Cairo::Surface localSurface( Style::instance().helper().createSurface( w, h ) );
505 
506         Cairo::Context localContext( localSurface );
507         cairo_set_source( localContext, ColorUtils::Rgba::black() );
508         cairo_rounded_rectangle( localContext, 0, 0, w, h, 4, CornersTopLeft|CornersTopRight );
509         cairo_fill( localContext );
510 
511         cairo_mask_surface( context, localSurface, x, y );
512 
513         return success;
514 
515     }
516 
517     //__________________________________________________________________
renderGroupBoxBackground(cairo_t * context,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options,TileSet::Tiles tiles)518     bool Style::renderGroupBoxBackground(
519         cairo_t* context, GtkWidget* widget,
520         gint x, gint y, gint w, gint h,
521         const StyleOptions& options,
522         TileSet::Tiles tiles )
523     {
524 
525         // find groupbox parent
526         GtkWidget* parent( Gtk::gtk_parent_groupbox( widget ) );
527         if( !( parent && _animations.groupBoxEngine().contains( parent ) ) ) return false;
528 
529         // toplevel window information and relative positioning
530         gint ww(0), wh(0);
531         gint wx(0), wy(0);
532 
533         // map to parent
534         if( !Gtk::gtk_widget_map_to_parent( widget, parent, &wx, &wy, &ww, &wh ) )
535         { return false; }
536 
537         const int margin( 1 );
538         wh += 2*margin;
539         ww += 2*margin;
540         x+=wx;
541         y+=wy;
542         cairo_save( context );
543         cairo_translate( context, -wx, -wy );
544 
545         // define colors
546         ColorUtils::Rgba base;
547         if( options&Blend )
548         {
549 
550             gint wwh, wwy;
551             Gtk::gtk_widget_map_to_toplevel( parent, 0L, &wwy, 0L, &wwh );
552             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wwh, wwy-1+wh/2 );
553 
554         } else {
555 
556             base = _settings.palette().color( Palette::Window );
557 
558         }
559 
560         const int xGroupBox = x - wx - margin;
561         const int yGroupBox = y - wy - margin;
562         renderGroupBox( context, base, xGroupBox, yGroupBox, ww, wh, options );
563         cairo_restore( context );
564 
565         return true;
566 
567     }
568 
569     //__________________________________________________________________
renderMenuBackground(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options) const570     bool Style::renderMenuBackground( cairo_t* context, gint x, gint y, gint w, gint h, const StyleOptions& options ) const
571     {
572 
573         // define colors
574         ColorUtils::Rgba base( color( Palette::Window, options ) );
575         ColorUtils::Rgba top( ColorUtils::backgroundTopColor( base ) );
576         ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
577 
578         // create context and translate
579         cairo_save( context );
580         const bool hasAlpha( options&Alpha );
581         const bool isMenu( options&Menu );
582         const bool round( options&Round );
583 
584         // paint translucent first
585         if( hasAlpha )
586         {
587             cairo_rectangle( context, x, y, w, h );
588             cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
589             cairo_set_source( context, ColorUtils::alphaColor( base, 0 ) );
590             cairo_fill( context );
591         }
592 
593         const int splitY( std::min(200, 3*h/4 ) );
594         const int verticalOffset( (isMenu && round) ? Menu_VerticalOffset:0 );
595 
596         GdkRectangle rect = { x, y, w, h };
597         GdkRectangle upperRect = { x, y + verticalOffset, w, splitY - verticalOffset };
598         if( gdk_rectangle_intersect( &rect, &upperRect, &upperRect ) )
599         {
600             // upper rect
601             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, y + verticalOffset, 0, y + splitY ) );
602             cairo_pattern_add_color_stop( pattern, 0, top );
603             cairo_pattern_add_color_stop( pattern, 1, bottom );
604 
605             gdk_cairo_rounded_rectangle( context, &upperRect, 3.5, round ? CornersTop:CornersNone );
606             cairo_set_source( context, pattern );
607             cairo_fill( context );
608 
609         }
610 
611         GdkRectangle lowerRect = { x, y + splitY, w, h - splitY - verticalOffset };
612         if( gdk_rectangle_intersect( &rect, &lowerRect, &lowerRect ) )
613         {
614 
615             // lower part
616             gdk_cairo_rounded_rectangle( context, &lowerRect, 3.5, round ? CornersBottom:CornersNone );
617             cairo_set_source( context, bottom );
618             cairo_fill( context );
619 
620         }
621 
622         // restore
623         cairo_restore( context );
624 
625         return true;
626 
627     }
628 
629     //__________________________________________________________________
renderTooltipBackground(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options) const630     void Style::renderTooltipBackground( cairo_t* context, gint x, gint y, gint w, gint h, const StyleOptions& options ) const
631     {
632 
633         // save context and translate
634         cairo_save( context );
635         cairo_translate( context, x, y );
636 
637         // define colors
638         ColorUtils::Rgba base(_settings.palette().color( Palette::Tooltip ) );
639         ColorUtils::Rgba top( ColorUtils::backgroundTopColor( base ) );
640         ColorUtils::Rgba bottom( ColorUtils::backgroundBottomColor( base ) );
641 
642 
643         // paint translucent first
644         const bool hasAlpha( (options&Alpha) );
645         //bool round( GDK_IS_WINDOW( window ) && (options&Round) );
646         const bool round( options&Round );
647 
648         if( hasAlpha )
649         {
650             if( _settings.tooltipTransparent() )
651             {
652                 top.setAlpha( 0.86 );
653                 bottom.setAlpha( 0.86 );
654             }
655 
656             cairo_rectangle( context, 0, 0, w, h );
657             cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
658             cairo_set_source( context, ColorUtils::alphaColor( base, 0 ) );
659             cairo_fill( context );
660         }
661 
662         // fill
663         {
664             GdkRectangle rect = { 0, 0, w, h };
665             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, h ) );
666             cairo_pattern_add_color_stop( pattern, 0, top );
667             cairo_pattern_add_color_stop( pattern, 1, bottom );
668 
669             gdk_cairo_rounded_rectangle( context, &rect, 3.5, round ? CornersAll:CornersNone );
670             cairo_set_source( context, pattern );
671             cairo_fill( context );
672 
673         }
674 
675         // contrast pixel
676         {
677             Cairo::Pattern pattern( cairo_pattern_create_linear( 0, 0, 0, h ) );
678             cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::lightColor( bottom ) );
679             cairo_pattern_add_color_stop( pattern, 0.9, bottom );
680 
681             cairo_rounded_rectangle( context, 0.5, 0.5, w-1, h-1, 3.5, round ? CornersAll:CornersNone );
682             cairo_set_line_width( context, 1.0 );
683             cairo_set_source( context, pattern );
684             cairo_stroke( context );
685         }
686 
687         // restore
688         cairo_restore( context );
689 
690         return;
691 
692     }
693 
694     //__________________________________________________________________
renderHeaderBackground(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h)695     void Style::renderHeaderBackground( cairo_t* context, GdkWindow* window, GtkWidget* widget, gint x, gint y, gint w, gint h )
696     {
697 
698         // load color
699         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
700 
701         // render normal window background
702         renderWindowBackground( context, window, widget, x, y, w, h );
703 
704         // render lines
705         renderHeaderLines( context, x, y, w, h );
706 
707         // render side dots
708         int yCenter( y + h/2 );
709         int xDots( x + w - 1 );
710         _helper.renderDot( context, base, xDots, yCenter - 3 );
711         _helper.renderDot( context, base, xDots, yCenter );
712         _helper.renderDot( context, base, xDots, yCenter + 3 );
713 
714     }
715 
716 
717     //__________________________________________________________________
renderHeaderLines(cairo_t * context,gint x,gint y,gint w,gint h) const718     void Style::renderHeaderLines( cairo_t* context, gint x, gint y, gint w, gint h ) const
719     {
720 
721         // save context
722         cairo_save( context );
723         cairo_set_line_width( context, 1.0 );
724 
725         // store colors
726         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
727         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
728         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
729 
730         // dark line
731         cairo_set_source( context, dark );
732         cairo_move_to( context, x, y+h-0.5 );
733         cairo_line_to( context, x+w, y+h-0.5 );
734         cairo_stroke( context );
735 
736         // light line
737         cairo_set_source( context, light );
738         cairo_move_to( context, x, y+h-1.5 );
739         cairo_line_to( context, x+w, y+h-1.5 );
740         cairo_stroke( context );
741 
742         // restore
743         cairo_restore( context );
744 
745     }
746 
747     //____________________________________________________________________________________
renderTreeLines(cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::CellInfoFlags & cellFlags,const StyleOptions & options) const748     void Style::renderTreeLines( cairo_t* context, gint x, gint y, gint w, gint h, const Gtk::CellInfoFlags& cellFlags, const StyleOptions& options ) const
749     {
750 
751         // define pen color
752         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
753         const ColorUtils::Rgba base( ColorUtils::mix(
754             _settings.palette().color( group, Palette::Text ),
755             _settings.palette().color( group, Palette::Window ),
756             0.8 ) );
757 
758         // save context
759         cairo_save( context );
760         cairo_set_source( context, base );
761         cairo_set_line_width( context, 1.0 );
762 
763         const bool reversed( cellFlags._flags & Gtk::CellInfoFlags::Reversed );
764 
765         int cellIndent( cellFlags._levelIndent + cellFlags._expanderSize );
766         int xStart( x + cellIndent/2 + 2 );
767 
768         if( reversed ) {
769 
770             xStart += w - cellIndent - 2;
771             cellIndent *= -1;
772 
773         }
774 
775         for( unsigned int i=0; i< cellFlags._depth; ++i  )
776         {
777 
778             const bool isLastCell( cellFlags._isLast[i] );
779             const bool last( i == cellFlags._depth -1 );
780             double xCenter = xStart;
781 
782             if( last )
783             {
784 
785                 double yCenter = int(y+h/2);
786                 const bool hasChildren( cellFlags._flags & Gtk::CellInfoFlags::HasChildren );
787 
788                 if( hasChildren )
789                 {
790                     // first vertical line
791                     cairo_move_to( context, xCenter + 0.5, y );
792                     cairo_line_to( context, xCenter + 0.5, yCenter - int(cellFlags._expanderSize/3 )-1 );
793 
794                     // second vertical line
795                     if( !isLastCell )
796                     {
797 
798                         cairo_move_to( context, xCenter + 0.5, y+h );
799                         cairo_line_to( context, xCenter + 0.5, yCenter + int( cellFlags._expanderSize/3 )+2 );
800                     }
801 
802                     // horizontal line
803                     if( reversed )
804                     {
805 
806                         cairo_move_to( context, xCenter - 1 - int( cellFlags._expanderSize/3 ), yCenter + 0.5 );
807                         cairo_line_to( context, xCenter - cellFlags._expanderSize*2/3, yCenter + 0.5 );
808 
809                     } else {
810 
811                         cairo_move_to( context, xCenter + int( cellFlags._expanderSize/3 ) + 1, yCenter + 0.5 );
812                         cairo_line_to( context, xCenter + cellFlags._expanderSize*2/3 - 1, yCenter + 0.5 );
813 
814                     }
815 
816                 } else {
817 
818                     // vertical line
819                     cairo_move_to( context, xCenter + 0.5, y );
820                     if( isLastCell ) cairo_line_to( context, xCenter + 0.5, yCenter );
821                     else cairo_line_to( context, xCenter + 0.5, y+h );
822 
823                     // horizontal line
824                     if( reversed )
825                     {
826 
827                         cairo_move_to( context, xCenter + 1 , yCenter + 0.5 );
828                         cairo_line_to( context, xCenter - cellFlags._expanderSize*2/3, yCenter + 0.5 );
829 
830                     } else {
831 
832                         cairo_move_to( context, xCenter, yCenter + 0.5 );
833                         cairo_line_to( context, xCenter + cellFlags._expanderSize*2/3 - 1, yCenter + 0.5 );
834 
835                     }
836 
837                 }
838 
839             } else if( !isLastCell ) {
840 
841                 // vertical line
842                 cairo_move_to( context, xCenter + 0.5, y );
843                 cairo_line_to( context, xCenter + 0.5, y + h );
844 
845             }
846 
847             // render
848             cairo_stroke( context );
849 
850             // increment
851             xStart += cellIndent;
852 
853         }
854 
855         // restore
856         cairo_restore( context );
857         return;
858     }
859 
860     //____________________________________________________________________________________
renderHoleBackground(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options,TileSet::Tiles tiles,gint sideMargin)861     void Style::renderHoleBackground(
862         cairo_t* context,
863         GdkWindow* window,
864         GtkWidget* widget,
865         gint x, gint y, gint w, gint h, const StyleOptions& options, TileSet::Tiles tiles,
866         gint sideMargin )
867     {
868 
869         // do nothing if not enough room
870         if( w < 14 || h < 14 )  return;
871 
872         // save context
873         cairo_save( context );
874 
875         // add hole
876         renderHoleMask( context, x, y, w, h, tiles, sideMargin );
877 
878         // test for flatness
879         if( options&Flat )
880         {
881 
882             // create a rounded-rect antimask for renderHoleBackground
883             cairo_set_source( context, _settings.palette().color( Palette::Window ) );
884             cairo_rectangle( context, x, y, w, h );
885             cairo_fill( context );
886 
887         } else {
888 
889             // normal window background
890             renderWindowBackground( context, window, widget, x, y, w, h, options, tiles);
891 
892             // possible groupbox background
893             // Pass NoFill option in order not to render the surrounding frame
894             if( widget )
895             { renderGroupBoxBackground( context, widget, x, y, w, h, options | Blend | NoFill, tiles ); }
896 
897         }
898 
899         // restore
900         cairo_restore( context );
901 
902     }
903 
904     //__________________________________________________________________
renderSplitter(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,const AnimationData & data) const905     void Style::renderSplitter(
906         cairo_t* context,
907         gint x, gint y, gint w, gint h,
908         const StyleOptions& options,
909         const AnimationData& data ) const
910     {
911 
912         // orientation
913         const bool vertical( options&Vertical );
914 
915         // colors
916         const ColorUtils::Rgba& base( _settings.palette().color( Palette::Window ) );
917 
918         // context
919         cairo_save( context );
920 
921         // hover color
922         ColorUtils::Rgba highlight;
923         if( data._mode == AnimationHover )
924         {
925 
926             highlight = ColorUtils::alphaColor( ColorUtils::lightColor( base ), 0.5*data._opacity );
927 
928         } else if( options&Hover ) {
929 
930             highlight = ColorUtils::alphaColor( ColorUtils::lightColor( base ), 0.5 );
931 
932         }
933 
934         // render hover rect
935         if( highlight.isValid() )
936         {
937 
938             Cairo::Pattern pattern;
939             double a(0.1);
940             if( vertical )
941             {
942 
943                 if( w > 30 ) a = 10.0/w;
944                 pattern.set( cairo_pattern_create_linear( x, 0, x+w, 0 ) );
945 
946             } else {
947 
948                 if( h>30 ) a = 10.0/h;
949                 pattern.set( cairo_pattern_create_linear( 0, y, 0, y+h ) );
950 
951             }
952 
953             cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( highlight, 0 ) );
954             cairo_pattern_add_color_stop( pattern, a, highlight );
955             cairo_pattern_add_color_stop( pattern, 1.0-a, highlight );
956             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( highlight, 0 ) );
957 
958             cairo_set_source( context, pattern );
959             cairo_rectangle( context, x, y, w, h );
960             cairo_fill( context );
961         }
962 
963         // dots
964         if( vertical )
965         {
966 
967             y += h/2;
968             const int ngroups( std::max( 1, w/250 ) );
969             int center = ( w-( ngroups-1 )*250 )/2 + x;
970             for( int k = 0; k < ngroups; k++, center += 250 )
971             {
972                 _helper.renderDot( context, base, center-3, y );
973                 _helper.renderDot( context, base, center, y );
974                 _helper.renderDot( context, base, center+3, y );
975             }
976 
977         } else {
978 
979             x += w/2;
980             const int ngroups( std::max( 1, h/250 ) );
981             int center = ( h-( ngroups-1 )*250 )/2 + y;
982             for( int k = 0; k < ngroups; k++, center += 250 )
983             {
984                 _helper.renderDot( context, base, x, center-3 );
985                 _helper.renderDot( context, base, x, center );
986                 _helper.renderDot( context, base, x, center+3 );
987             }
988 
989         }
990 
991         cairo_restore( context );
992 
993     }
994 
995     //____________________________________________________________________________________
renderProgressBarHole(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options)996     void Style::renderProgressBarHole(
997         cairo_t* context,
998         gint x, gint y, gint w, gint h, const StyleOptions& options )
999     {
1000 
1001         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1002         const ColorUtils::Rgba base(_settings.palette().color( group, Palette::Window ) );
1003 
1004         cairo_save( context );
1005         renderScrollBarHole( context, x, y, w, h, base, (options&Vertical) );
1006         cairo_restore( context );
1007 
1008     }
1009 
1010     //____________________________________________________________________________________
renderProgressBarHandle(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options)1011     void Style::renderProgressBarHandle(
1012         cairo_t* context,
1013         gint x, gint y, gint w, gint h, const StyleOptions& options )
1014     {
1015 
1016         // colors
1017         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1018         const ColorUtils::Rgba base( _settings.palette().color( Palette::Active, Palette::Window ) );
1019         const ColorUtils::Rgba glow( _settings.palette().color( group, Palette::Selected ) );
1020 
1021         // validate rect
1022         if(w<0 || h<0) return;
1023 
1024         // context
1025         cairo_save( context );
1026 
1027         // make sure that width is large enough
1028         const int indicatorSize( (options&Vertical ? h:w ) );
1029 
1030         if( indicatorSize >= 3 && w > 0 && h > 1 )
1031         {
1032             // get surface
1033             const Cairo::Surface& surface( _helper.progressBarIndicator( base, glow, w, h+1 ) );
1034             cairo_translate( context, x, y-1 );
1035             cairo_rectangle( context, 0, 0, w,  h+1 );
1036             cairo_set_source_surface( context, surface, 0, 0 );
1037             cairo_fill( context );
1038         }
1039 
1040         cairo_restore( context );
1041 
1042     }
1043 
1044     //____________________________________________________________________________________
renderScrollBarHole(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options)1045     void Style::renderScrollBarHole(
1046         cairo_t* context,
1047         gint x, gint y, gint w, gint h, const StyleOptions& options )
1048     {
1049 
1050         // colors
1051         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1052         const ColorUtils::Rgba base(_settings.palette().color( group, Palette::Window ) );
1053 
1054         // context
1055         cairo_save( context );
1056         renderScrollBarHole( context, x, y, w, h, base, options&Vertical );
1057         cairo_restore( context );
1058 
1059     }
1060 
1061     //____________________________________________________________________________________
renderHoleMask(cairo_t * context,gint x,gint y,gint w,gint h,TileSet::Tiles tiles,gint sideMargin)1062     void Style::renderHoleMask( cairo_t* context, gint x, gint y, gint w, gint h, TileSet::Tiles tiles, gint sideMargin )
1063     {
1064 
1065         GdkRectangle mask = {x+2, y+1, w-4, h-3 };
1066         const double maskRadius = 3.5;
1067         Corners corners( CornersNone );
1068         if( tiles & TileSet::Left )
1069         {
1070             mask.x += sideMargin;
1071             mask.width -= sideMargin;
1072             if( tiles & TileSet::Top ) corners |= CornersTopLeft;
1073             if( tiles & TileSet::Bottom ) corners |= CornersBottomLeft;
1074         }
1075 
1076         if( tiles & TileSet::Right )
1077         {
1078             mask.width -= sideMargin;
1079             if( tiles & TileSet::Top ) corners |= CornersTopRight;
1080             if( tiles & TileSet::Bottom ) corners |= CornersBottomRight;
1081         }
1082 
1083         // set clipping mask
1084         gdk_cairo_rounded_rectangle_negative(context,&mask,maskRadius,CornersAll);
1085         cairo_rectangle(context,x,y,w,h);
1086         cairo_clip(context);
1087 
1088         return;
1089 
1090     }
1091 
1092     //____________________________________________________________________________________
renderScrollBarHandle(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,const AnimationData & data)1093     void Style::renderScrollBarHandle(
1094         cairo_t* context,
1095         gint x, gint y, gint w, gint h,
1096         const StyleOptions& options,
1097         const AnimationData& data )
1098     {
1099 
1100         // vertical
1101         const bool vertical( options&Vertical );
1102 
1103         const double xf( vertical ? x+3 : x+4 );
1104         const double yf( vertical ? y+3 : y+2 );
1105         const double wf( vertical ? w-6 : w-8 );
1106         const double hf( vertical ? h-6 : h-5 );
1107 
1108         if( wf <= 0 || hf <= 0 ) return;
1109 
1110         // context
1111         cairo_save( context );
1112 
1113         // store colors
1114         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1115         const ColorUtils::Rgba color( _settings.palette().color( group, Palette::Button ) );
1116 
1117         const double radius( 3.5 );
1118 
1119         // glow color
1120         ColorUtils::Rgba glow;
1121         const ColorUtils::Rgba shadow( ColorUtils::alphaColor( ColorUtils::shadowColor( color ), 0.4 ) );
1122         const ColorUtils::Rgba hovered( _settings.palette().color( Palette::Hover ) );
1123         if( data._mode == AnimationHover ) glow = ColorUtils::mix( shadow, hovered, data._opacity );
1124         else if( options&Hover ) glow = hovered;
1125         else glow = shadow;
1126 
1127         _helper.scrollHandle( color, glow ).render( context, xf-3, yf-3, wf+6, hf+6, TileSet::Full );
1128 
1129         // contents
1130         const ColorUtils::Rgba mid( ColorUtils::midColor( color ) );
1131         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, yf, 0, yf+hf ) );
1132         cairo_pattern_add_color_stop( pattern, 0, color );
1133         cairo_pattern_add_color_stop( pattern, 1, mid );
1134         cairo_set_source( context, pattern );
1135         cairo_rounded_rectangle( context, xf+1, yf+1, wf-2, hf-2, radius - 2 );
1136         cairo_fill( context );
1137 
1138         // bevel pattern
1139         {
1140             const ColorUtils::Rgba light( ColorUtils::lightColor( color ) );
1141             Cairo::Pattern pattern;
1142             if( vertical ) pattern.set( cairo_pattern_create_linear( 0, 0, 0, 30 ) );
1143             else pattern.set( cairo_pattern_create_linear( 0, 0, 30, 0 ) );
1144             cairo_pattern_set_extend( pattern, CAIRO_EXTEND_REFLECT );
1145 
1146             cairo_pattern_add_color_stop( pattern, 0, ColorUtils::Rgba::transparent() );
1147             cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::alphaColor( light, 0.1 ) );
1148 
1149             cairo_set_source( context, pattern );
1150             if( vertical ) cairo_rectangle( context, xf+3, yf, wf-6, hf );
1151             else cairo_rectangle( context, xf, yf+3, wf, hf-6 );
1152             cairo_fill( context );
1153         }
1154 
1155         // restore
1156         cairo_restore( context );
1157 
1158     }
1159 
1160     //____________________________________________________________________________________
renderToolBarHandle(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options) const1161     void Style::renderToolBarHandle(
1162         cairo_t* context,
1163         gint x, gint y, gint w, gint h, const StyleOptions& options ) const
1164     {
1165 
1166         const bool vertical( options & Vertical );
1167         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
1168 
1169         cairo_save( context );
1170         int counter(0);
1171         if( vertical )
1172         {
1173 
1174             const int xcenter( x+w/2 );
1175             for( int ycenter = y+2; ycenter <= y+h-3; ycenter+=3, ++counter )
1176             {
1177                 if( counter%2 == 0 ) _helper.renderDot( context, base, xcenter+1, ycenter );
1178                 else _helper.renderDot( context, base, xcenter-2, ycenter );
1179             }
1180 
1181         } else {
1182 
1183             const int ycenter( y + h/2 );
1184             for( int xcenter = x+2; xcenter < x+w-3; xcenter+=3, ++counter )
1185             {
1186                 if( counter%2 == 0 ) _helper.renderDot( context, base, xcenter, ycenter+1 );
1187                 else _helper.renderDot( context, base, xcenter, ycenter-2 );
1188             }
1189 
1190         }
1191 
1192         cairo_restore( context );
1193         return;
1194 
1195     }
1196 
1197     //__________________________________________________________________
drawFloatFrame(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,Palette::Role role) const1198     void Style::drawFloatFrame( cairo_t* context, gint x, gint y, gint w, gint h, const StyleOptions& options, Palette::Role role ) const
1199     {
1200 
1201         // define colors
1202         ColorUtils::Rgba base(_settings.palette().color( role ) );
1203         ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1204         ColorUtils::Rgba dark( ColorUtils::darkColor( ColorUtils::backgroundBottomColor( base ) ) );
1205 
1206         const bool hasAlpha( options&Alpha );
1207         const bool isMenu( options&Menu );
1208         const bool drawUglyShadow( !hasAlpha );
1209         const bool rounded( options&Round );
1210 
1211         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, double(y)+0.5, 0, y+h-1 ) );
1212 
1213         // add vertical offset
1214         if( isMenu && rounded )
1215         {
1216             y += Menu_VerticalOffset;
1217             h -= 2*Menu_VerticalOffset;
1218         }
1219 
1220         // save context
1221         cairo_save( context );
1222 
1223         if( drawUglyShadow )
1224         {
1225 
1226             // adjust rectangle
1227             x++;
1228             y++;
1229             w-=2;
1230             h-=2;
1231 
1232             cairo_set_line_width(context,1);
1233 
1234             if( options&Focus )
1235             {
1236 
1237                 // window is active - it's a glow, not a shadow
1238                 const ColorUtils::Rgba frameColor( _settings.palette().color( Palette::ActiveWindowBackground ) );
1239                 const ColorUtils::Rgba glow = ColorUtils::mix(ColorUtils::Rgba(0.5,0.5,0.5),frameColor,0.7);
1240                 cairo_set_source(context,glow);
1241 
1242                 const double radius( 11*0.5 );
1243                 cairo_move_to( context, x+4, y-0.5 ); cairo_line_to( context, x+w-4, y-0.5 );
1244                 cairo_stroke( context );
1245 
1246                 cairo_arc_negative( context, x-0.5+radius, y-0.5+radius, radius, -0.5*M_PI, -M_PI );
1247                 cairo_stroke( context );
1248                 cairo_arc_negative( context, x+w-11+0.5+radius, y-0.5+radius, radius, 0, -0.5*M_PI );
1249                 cairo_stroke( context );
1250 
1251                 cairo_move_to( context, x-0.5, y+4 ); cairo_line_to( context, x-0.5, y+h-4 );
1252                 cairo_move_to( context, x+w+0.5, y+4 ); cairo_line_to( context, x+w+0.5, y+h-4 );
1253                 cairo_stroke( context );
1254 
1255                 cairo_arc_negative( context, x-0.5+radius, y+h-11+0.5+radius, radius, -M_PI, -1.5*M_PI );
1256                 cairo_stroke( context );
1257                 cairo_arc_negative( context, x+w-11+0.5+radius, y+h-11+0.5+radius, radius, 0.5*M_PI, 0 );
1258                 cairo_stroke( context );
1259 
1260                 cairo_move_to( context, x+4, y+h+0.5 ); cairo_line_to( context, x+w-4, y+h+0.5 );
1261                 cairo_stroke( context );
1262 
1263 
1264                 light=ColorUtils::mix(light, frameColor);
1265                 dark=ColorUtils::mix(dark,frameColor);
1266 
1267             } else {
1268 
1269                 // window inactive - draw something resembling shadow
1270                 // fully desaturate
1271                 ColorUtils::Rgba shadow( ColorUtils::darken( base, 0., 0. ) );
1272 
1273                 if(rounded)
1274                 {
1275                     const double radius( 11*0.5 );
1276                     cairo_set_source( context, ColorUtils::darken( shadow, 0.2 ));
1277                     cairo_move_to( context, x+4, y-0.5 ); cairo_line_to( context, x+w-4, y-0.5 );
1278                     cairo_stroke( context );
1279 
1280                     cairo_arc_negative( context, x-0.5+radius, y-0.5+radius, radius, -0.5*M_PI, -M_PI );
1281                     cairo_stroke( context );
1282                     cairo_arc_negative( context, x+w-11+0.5+radius, y-0.5+radius, radius, 0, -0.5*M_PI );
1283                     cairo_stroke( context );
1284 
1285                     cairo_set_source( context, ColorUtils::darken( shadow, 0.35 ));
1286                     cairo_move_to( context, x-0.5, y+4 ); cairo_line_to( context, x-0.5, y+h-4 );
1287                     cairo_move_to( context, x+w+0.5, y+4 ); cairo_line_to( context, x+w+0.5, y+h-4 );
1288                     cairo_stroke( context );
1289 
1290                     cairo_set_source( context, ColorUtils::darken( shadow, 0.45 ));
1291                     cairo_arc_negative( context, x-0.5+radius, y+h-11+0.5+radius, radius, -M_PI, -1.5*M_PI );
1292                     cairo_stroke( context );
1293                     cairo_arc_negative( context, x+w-11+0.5+radius, y+h-11+0.5+radius, radius, 0.5*M_PI, 0 );
1294                     cairo_stroke( context );
1295 
1296                     cairo_set_source( context, ColorUtils::darken( shadow, 0.6 ));
1297                     cairo_move_to( context, x+4, y+h+0.5 ); cairo_line_to( context, x+w-4, y+h+0.5 );
1298                     cairo_stroke( context );
1299 
1300                 } else {
1301 
1302                     cairo_set_source( context, ColorUtils::darken( shadow, 0.2 ));
1303                     cairo_move_to( context, x-0.5, y-0.5 ); cairo_line_to( context, x+w+0.5, y-0.5 );
1304                     cairo_stroke( context );
1305 
1306                     cairo_set_source( context, ColorUtils::darken( shadow, 0.35 ));
1307                     cairo_move_to( context, x-0.5, y-0.5 ); cairo_line_to( context, x-0.5, y+h+0.5 );
1308                     cairo_move_to( context, x+w+0.5, y-0.5 ); cairo_line_to( context, x+w+0.5, y+h+0.5 );
1309                     cairo_stroke( context );
1310 
1311                     cairo_set_source( context, ColorUtils::darken( shadow, 0.6 ));
1312                     cairo_move_to( context, x-0.5, y+h+0.5 ); cairo_line_to( context, x+w+0.5, y+h+0.5 );
1313                     cairo_stroke( context );
1314 
1315                 }
1316             }
1317         }
1318 
1319         cairo_pattern_add_color_stop( pattern, 0, light );
1320 
1321         if( !rounded )
1322         {
1323 
1324             cairo_pattern_add_color_stop( pattern, 1, ColorUtils::alphaColor( dark, 0 ) );
1325 
1326         } else {
1327 
1328             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 ) );
1329             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 ) );
1330             cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( light ) );
1331 
1332         }
1333 
1334         cairo_rounded_rectangle( context, x+0.5, y+0.5, w-1, h-1, 3.5, rounded ? CornersAll : CornersNone );
1335 
1336         cairo_set_source( context, pattern );
1337         cairo_set_line_width( context, 0.8 );
1338         cairo_stroke( context );
1339 
1340         // restore
1341         cairo_restore( context );
1342     }
1343 
1344     //__________________________________________________________________
renderButtonSlab(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,const AnimationData & animationData,TileSet::Tiles tiles)1345     void Style::renderButtonSlab(
1346         GtkWidget* widget,
1347         cairo_t* context,
1348         gint x, gint y, gint w, gint h,
1349         const StyleOptions& options,
1350         const AnimationData& animationData,
1351         TileSet::Tiles tiles
1352         )
1353     {
1354 
1355         // flat buttons are only rendered with a simple Rect, and only when either focused or sunken
1356         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1357 
1358         // glow color (depending on hover/glow
1359         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
1360 
1361         if( options & Flat )
1362         {
1363             if( options & Sunken )
1364             {
1365 
1366                 const ColorUtils::Rgba base( color( group, Palette::Window, options ) );
1367 
1368                 const double bias( 0.75 );
1369                 double opacity( 1.0 );
1370                 if( glow.isValid() ) opacity -= bias*glow.alpha();
1371 
1372                 // fill hole
1373                 ColorUtils::Rgba color( ColorUtils::midColor( base ) );
1374                 color = ColorUtils::alphaColor( color, opacity );
1375                 cairo_save( context );
1376                 cairo_set_source( context, color );
1377                 cairo_rounded_rectangle( context, x+1, y+1, w-2, h-2, 3.5 );
1378                 cairo_fill( context );
1379                 cairo_restore( context );
1380 
1381                 if( glow.isValid() ) _helper.holeFocused( base, glow, 7, true ).render( context, x, y, w, h );
1382                 else _helper.hole( base, 7, true ).render( context, x, y, w, h );
1383 
1384             } else if( glow.isValid() ) {
1385 
1386                 _helper.slitFocused( glow ).render( context, x, y, w, h, tiles );
1387 
1388             }
1389 
1390             return;
1391 
1392         }
1393 
1394         // forces minimum size to 14x14
1395         if( w < 14 || h < 14 )
1396         {
1397             GdkRectangle parent( Gtk::gdk_rectangle( x, y, w, h ) );
1398             GdkRectangle child( Gtk::gdk_rectangle( x, y, std::max( w, 14), std::max( h, 14 ) ) );
1399             centerRect( &parent, &child );
1400             x = child.x;
1401             y = child.y;
1402             w = child.width;
1403             h = child.height;
1404         }
1405 
1406 
1407         // define colors
1408         ColorUtils::Rgba base( color( group, Palette::Button, options ) );
1409         if( widget && (options&Blend) )
1410         {
1411 
1412             gint wh, wy;
1413             Gtk::gdk_map_to_toplevel( 0L, widget, 0L, &wy, 0L, &wh );
1414             base = ColorUtils::backgroundColor( base, wh, y+wy+h/2 );
1415 
1416         }
1417 
1418         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
1419 
1420         // create context
1421         cairo_save( context );
1422 
1423         // fill with appropriate pattern
1424         Cairo::Pattern pattern;
1425         if( options&Sunken )
1426         {
1427 
1428             pattern.set( cairo_pattern_create_linear( 0, y-h, 0, y+h ) );
1429             cairo_pattern_add_color_stop( pattern, 0, light );
1430             cairo_pattern_add_color_stop( pattern, 1.0, base );
1431 
1432         } else {
1433 
1434             pattern.set( cairo_pattern_create_linear( 0, double(y)-0.2*h, 0, double(y) + h + 0.4*h ) );
1435             cairo_pattern_add_color_stop( pattern, 0, light );
1436             cairo_pattern_add_color_stop( pattern, 0.6, base );
1437 
1438         }
1439 
1440         cairo_set_source( context, pattern );
1441         _helper.fillSlab( context, x, y, w, h+1, tiles );
1442         cairo_restore( context );
1443 
1444         if( options&Sunken )
1445         {
1446 
1447             _helper.slabSunken( base ).render( context, x, y, w, h, tiles );
1448 
1449         } else {
1450 
1451             _helper.slab( base, glow, 0 ).render( context, x, y, w, h, tiles );
1452 
1453         }
1454 
1455     }
1456 
1457     //__________________________________________________________________
renderSlab(cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::Gap & gap,const StyleOptions & options,const AnimationData & animationData)1458     void Style::renderSlab(
1459         cairo_t* context,
1460         gint x, gint y, gint w, gint h, const Gtk::Gap& gap,
1461         const StyleOptions& options,
1462         const AnimationData& animationData )
1463     {
1464 
1465 // TODO: reimplement for gtk3
1466 //         // define colors
1467 //         ColorUtils::Rgba base;
1468 //         if( options&Blend )
1469 //         {
1470 //
1471 //             gint wh, wy;
1472 //             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
1473 //             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
1474 //
1475 //         } else {
1476 //
1477 //             base = _settings.palette().color( Palette::Window );
1478 //
1479 //         }
1480 
1481         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
1482 
1483         cairo_save( context );
1484         generateGapMask( context, x, y, w, h, gap );
1485         renderSlab( context, x, y, w, h, base, options, animationData, TileSet::Ring );
1486         cairo_restore( context );
1487 
1488     }
1489 
1490     //__________________________________________________________________
renderInfoBar(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & glow)1491     void Style::renderInfoBar(
1492         GtkWidget* widget,
1493         cairo_t* context,
1494         gint x, gint y, gint w, gint h,
1495         const ColorUtils::Rgba& glow )
1496     {
1497 
1498         //  save context
1499         cairo_save( context );
1500 
1501         // content
1502         cairo_rounded_rectangle( context, x+1, y+1, w-2, h-2, 5 );
1503         cairo_set_source( context, glow );
1504         cairo_fill( context );
1505 
1506         // border
1507         cairo_set_line_width( context, 1.0 );
1508         cairo_rounded_rectangle( context, 1.5+x, 1.5+y, w-3, h-3, 4.5 );
1509         cairo_set_source( context, ColorUtils::darken( glow ) );
1510         cairo_stroke( context );
1511 
1512         // restore
1513         cairo_restore( context );
1514 
1515     }
1516 
1517     //__________________________________________________________________
renderCheckBox(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,GtkShadowType shadow,const StyleOptions & options,const AnimationData & animationData)1518     void Style::renderCheckBox(
1519         GtkWidget* widget,
1520         cairo_t* context,
1521         gint x, gint y, gint w, gint h, GtkShadowType shadow,
1522         const StyleOptions& options,
1523         const AnimationData& animationData )
1524     {
1525 
1526         // define checkbox rect
1527         gint cbw = CheckBox_Size;
1528         if( options & Flat ) cbw -= 5;
1529         GdkRectangle parent = {x, y, w, h };
1530         GdkRectangle child = {0, 0, cbw, cbw };
1531         centerRect( &parent, &child );
1532 
1533         // define colors
1534         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1535         const Palette::Role role( options&Flat ? Palette::Window : Palette::Button );
1536 
1537         ColorUtils::Rgba base;
1538         if( options&Blend )
1539         {
1540 
1541             gint wh, wy;
1542             Gtk::gdk_map_to_toplevel( 0L, widget, 0L, &wy, 0L, &wh );
1543             base = ColorUtils::backgroundColor( _settings.palette().color( group, role ), wh, y+wy+h/2 );
1544 
1545         } else {
1546 
1547             base = _settings.palette().color( group, role );
1548 
1549         }
1550 
1551         // save context
1552         cairo_save( context );
1553 
1554         // slab
1555         if( options & Flat )
1556         {
1557 
1558             _helper.holeFlat( base, 0, false ).render( context, child.x+1, child.y-1, child.width, child.height, TileSet::Full );
1559             cairo_translate( context, 0, -2 );
1560 
1561         } else {
1562 
1563             StyleOptions localOptions( options );
1564             localOptions &= ~Sunken;
1565             renderSlab( context, child.x, child.y, child.width, child.height, base, localOptions, animationData );
1566 
1567         }
1568 
1569         // draw mark
1570         x = int( double(child.x + child.width/2) - 3.5 );
1571         y = int( double(child.y + child.height/2) - 2.5 );
1572 
1573         if( shadow == GTK_SHADOW_IN || shadow == GTK_SHADOW_ETCHED_IN || options&Active )
1574         {
1575 
1576             cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
1577             cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
1578             if( shadow == GTK_SHADOW_IN ) cairo_set_line_width( context, 2.0 );
1579 
1580             Palette::Group group( (options&Disabled) ? Palette::Disabled : Palette::Active );
1581             const ColorUtils::Rgba& color( _settings.palette().color( group, ( options&Flat ) ? Palette::WindowText : Palette::ButtonText ) );
1582             const ColorUtils::Rgba& background( _settings.palette().color( ( options&Flat ) ? Palette::Window : Palette::Button ) );
1583 
1584             ColorUtils::Rgba base( ColorUtils::decoColor( background, color ) );
1585             ColorUtils::Rgba contrast( ColorUtils::lightColor( background ) );
1586 
1587             // We don't want to change color on active state for menu checkboxes (it's never passed by GTK)
1588             // Also, if we ignore active state, we get correct render for LibreOffice
1589             if( options&Active && !(options&Flat) )
1590             {
1591                 base = ColorUtils::alphaColor( base, 0.3 );
1592                 contrast = ColorUtils::alphaColor( contrast, 0.3 );
1593             }
1594 
1595             cairo_translate( context, 0.5, 0.5 );
1596 
1597             const double offset( 1.0 );
1598             if( _settings.checkBoxStyle() == QtSettings::CS_CHECK )
1599             {
1600 
1601                 // dask pattern for tristate buttons
1602                 if( shadow == GTK_SHADOW_ETCHED_IN )
1603                 {
1604                     cairo_set_line_width( context, 1.3 );
1605                     double dashes[2] = { 1.3, 2.6 };
1606                     cairo_set_dash( context, &dashes[0], 2, 0 );
1607                 }
1608 
1609                 cairo_save( context );
1610                 cairo_translate( context, 0, offset );
1611                 cairo_move_to( context, x+9, y );
1612                 cairo_line_to( context, x+3, y+7 );
1613                 cairo_line_to( context, x, y+4 );
1614                 cairo_restore( context );
1615                 cairo_set_source( context, contrast );
1616                 cairo_stroke( context );
1617 
1618                 cairo_move_to( context, x+9, y );
1619                 cairo_line_to( context, x+3, y+7 );
1620                 cairo_line_to( context, x, y+4 );
1621                 cairo_set_source( context, base );
1622                 cairo_stroke( context );
1623 
1624             } else {
1625 
1626                 // dask pattern for tristate buttons
1627                 if( shadow == GTK_SHADOW_ETCHED_IN )
1628                 {
1629                     double dashes[2] = { 0.8, 4.0 };
1630                     cairo_set_dash( context, &dashes[0], 2, 0 );
1631                 }
1632 
1633                 if( options&Flat )
1634                 {
1635 
1636                     cairo_save( context );
1637                     cairo_translate( context, 0, offset );
1638                     cairo_move_to( context, x+8, y ); cairo_line_to( context, x+1, y+7 );
1639                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x+1, y );
1640                     cairo_restore( context );
1641                     cairo_set_source( context, contrast );
1642                     cairo_stroke( context );
1643 
1644                     cairo_move_to( context, x+8, y ); cairo_line_to( context, x+1, y+7 );
1645                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x+1, y );
1646                     cairo_set_source( context, base );
1647                     cairo_stroke( context );
1648 
1649                 } else {
1650 
1651                     cairo_save( context );
1652                     cairo_translate( context, 0, offset );
1653                     cairo_move_to( context, x+8, y-1 ); cairo_line_to( context, x, y+7 );
1654                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x, y-1 );
1655                     cairo_restore( context );
1656                     cairo_set_source( context, contrast );
1657                     cairo_stroke( context );
1658 
1659                     cairo_move_to( context, x+8, y-1 ); cairo_line_to( context, x, y+7 );
1660                     cairo_move_to( context, x+8, y+7 ); cairo_line_to( context, x, y-1 );
1661                     cairo_set_source( context, base );
1662                     cairo_stroke( context );
1663 
1664                 }
1665 
1666             }
1667 
1668         }
1669 
1670         cairo_restore( context );
1671 
1672     }
1673 
1674     //___________________________________________________________________
renderRadioButton(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,GtkShadowType shadow,const StyleOptions & options,const AnimationData & animationData)1675     void Style::renderRadioButton(
1676         GtkWidget* widget,
1677         cairo_t* context,
1678         gint x, gint y, gint w, gint h, GtkShadowType shadow,
1679         const StyleOptions& options,
1680         const AnimationData& animationData )
1681     {
1682 
1683         // define checkbox rect
1684         gint cbw( CheckBox_Size );
1685         gint tileSize( CheckBox_Size/3 );
1686         double scale( 1.0 );
1687 
1688         GdkRectangle parent = {x, y, w, h };
1689         GdkRectangle child = {0, 0, cbw, cbw };
1690         centerRect( &parent, &child );
1691         x = child.x;
1692         y = child.y;
1693 
1694         // define colors
1695         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
1696 
1697         ColorUtils::Rgba base;
1698         if( options&Blend )
1699         {
1700 
1701             gint wh, wy;
1702             Gtk::gdk_map_to_toplevel( 0L, widget, 0L, &wy, 0L, &wh );
1703 
1704             if( options & Menu ) base = ColorUtils::menuBackgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
1705             else base = ColorUtils::backgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
1706 
1707         } else {
1708 
1709             base = _settings.palette().color( group, Palette::Button );
1710 
1711         }
1712 
1713         // glow
1714         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
1715 
1716         // get the pixmap
1717         const Cairo::Surface& surface( _helper.roundSlab( base, glow, 0, tileSize ) );
1718 
1719         // create context
1720         cairo_save( context );
1721         cairo_translate( context, x, y );
1722         if(options&NoFill)
1723         {
1724             // Only render the glow, leave the bullet clipped out
1725             const int border(4);
1726             cairo_ellipse_negative(context,border,border,child.width-border*2,child.height-border*2);
1727             cairo_rectangle(context,0,0,child.width,child.height);
1728             cairo_clip(context);
1729         }
1730         cairo_rectangle( context, 0, 0, child.width, child.height );
1731         cairo_set_source_surface( context, surface, 0, 0 );
1732         cairo_fill( context );
1733         cairo_restore( context );
1734 
1735         cairo_save( context );
1736         if( shadow == GTK_SHADOW_IN || shadow == GTK_SHADOW_ETCHED_IN || options&Active )
1737         {
1738             double radius( shadow == GTK_SHADOW_ETCHED_IN ? 1.4:2.6 );
1739             radius *= scale;
1740 
1741             const double dx( 0.5*child.width - radius );
1742             const double dy( 0.5*child.height - radius );
1743 
1744             const ColorUtils::Rgba& background( _settings.palette().color( Palette::Button ) );
1745             const ColorUtils::Rgba& color( _settings.palette().color( group, Palette::ButtonText ) );
1746 
1747             ColorUtils::Rgba base( ColorUtils::decoColor( background, color ) );
1748             ColorUtils::Rgba contrast( ColorUtils::lightColor( background ) );
1749 
1750             // We don't want to change color on active state for menu radiobuttons (it's never passed by GTK)
1751             // Also, if we ignore active state, we get correct render for LibreOffice
1752             if( options&Active && !(options&Menu ) )
1753             {
1754                 base = ColorUtils::alphaColor( base, 0.3 );
1755                 contrast = ColorUtils::alphaColor( contrast, 0.3 );
1756             }
1757 
1758 
1759             cairo_save( context );
1760             cairo_translate( context, 0, radius/2 );
1761             cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1762             cairo_restore( context );
1763             if( shadow == GTK_SHADOW_ETCHED_IN )
1764             {
1765 
1766                 cairo_set_line_width( context, 1.3 );
1767                 cairo_set_source( context, contrast );
1768                 cairo_stroke( context );
1769 
1770                 cairo_set_source( context, base );
1771                 cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1772                 cairo_stroke( context );
1773 
1774             } else {
1775 
1776                 cairo_set_source( context, contrast );
1777                 cairo_fill( context );
1778 
1779                 cairo_set_source( context, base );
1780                 cairo_ellipse( context, x+dx, y+dy, child.width - 2*dx, child.height -2*dy );
1781                 cairo_fill( context );
1782 
1783             }
1784 
1785         }
1786 
1787         cairo_restore( context );
1788 
1789         return;
1790     }
1791 
1792     //____________________________________________________________________________________
renderHole(cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::Gap & gap,const StyleOptions & options,const AnimationData & animationData,TileSet::Tiles tiles)1793     void Style::renderHole(
1794         cairo_t* context,
1795         gint x, gint y, gint w, gint h, const Gtk::Gap& gap,
1796         const StyleOptions& options,
1797         const AnimationData& animationData,
1798         TileSet::Tiles tiles )
1799     {
1800 
1801         // do nothing if not enough room
1802         if( w < 14 || h < 14 )  return;
1803 
1804         // enable state
1805         const bool enabled( !(options&Disabled ) );
1806 
1807         // load color
1808         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
1809         ColorUtils::Rgba fill;
1810         if( !( options&NoFill ) )
1811         {
1812             const Palette::Group group( enabled ? Palette::Active : Palette::Disabled );
1813             fill = _settings.palette().color( group, Palette::Base );
1814         }
1815 
1816         // save context, add mask, and render hole
1817         cairo_save( context );
1818         generateGapMask( context, x, y, w, h, gap );
1819 
1820         if( fill.isValid() ) tiles |= TileSet::Center;
1821 
1822         const ColorUtils::Rgba glow( holeShadowColor( options, animationData ) );
1823         if( glow.isValid() ) _helper.holeFocused( base, fill, glow ).render( context, x, y, w, h, tiles );
1824         else _helper.hole( base, fill ).render( context, x, y, w, h, tiles );
1825 
1826         // restore
1827         cairo_restore( context );
1828 
1829     }
1830 
1831     //____________________________________________________________________________________
renderDockFrame(GtkWidget * widget,cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::Gap & gap,const StyleOptions & options)1832     void Style::renderDockFrame(
1833         GtkWidget* widget,
1834         cairo_t* context,
1835         gint x, gint y, gint w, gint h, const Gtk::Gap& gap, const StyleOptions& options )
1836     {
1837 
1838         // do nothing if not enough room
1839         if( w < 9 || h < 9 )  return;
1840 
1841         // define colors
1842         ColorUtils::Rgba top;
1843         ColorUtils::Rgba bottom;
1844         if( options&Blend )
1845         {
1846 
1847             gint wh, wy;
1848             Gtk::gdk_map_to_toplevel( 0L, widget, 0L, &wy, 0L, &wh );
1849             top = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy );
1850             bottom = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+h+wy );
1851 
1852         } else {
1853 
1854             top = _settings.palette().color( Palette::Window );
1855             bottom = _settings.palette().color( Palette::Window );
1856 
1857         }
1858 
1859         // create context, add mask, and render
1860         cairo_save( context );
1861         generateGapMask( context, x, y, w, h, gap );
1862         _helper.dockFrame( top, bottom ).render( context, x, y, w, h );
1863         cairo_restore( context );
1864 
1865     }
1866 
1867     //____________________________________________________________________________________
renderGroupBoxFrame(cairo_t * context,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options)1868     void Style::renderGroupBoxFrame(
1869         cairo_t* context,
1870         GtkWidget* widget,
1871         gint x, gint y, gint w, gint h, const StyleOptions& options )
1872     {
1873 
1874         // register
1875         if( widget )
1876         { _animations.groupBoxEngine().registerWidget( widget ); }
1877 
1878         // define colors
1879         ColorUtils::Rgba base;
1880         if( options&Blend )
1881         {
1882 
1883             gint wh, wy;
1884             Gtk::gtk_widget_map_to_toplevel( widget, 0L, &wy, 0L, &wh );
1885             base = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 );
1886 
1887         } else {
1888 
1889             base = _settings.palette().color( Palette::Window );
1890 
1891         }
1892 
1893         renderGroupBox( context, base, x, y, w, h, options );
1894 
1895     }
1896 
1897     //____________________________________________________________________________________
renderMenuItemRect(cairo_t * context,GdkWindow * window,GtkWidget * widget,gint x,gint y,gint w,gint h,const StyleOptions & options,const AnimationData & data)1898     void Style::renderMenuItemRect(
1899         cairo_t* context,
1900         GdkWindow* window,
1901         GtkWidget* widget,
1902         gint x, gint y, gint w, gint h,
1903         const StyleOptions& options,
1904         const AnimationData& data )
1905     {
1906         ColorUtils::Rgba base;
1907 
1908         gint wh, wy;
1909         Gtk::gdk_map_to_toplevel( window, widget, 0L, &wy, 0L, &wh );
1910         const bool isInMenu( Gtk::gtk_parent_menu( widget ) );
1911         const bool isInMenuBar( Gtk::gtk_parent_menubar( widget ) );
1912 
1913         if( wh > 0 )
1914         {
1915             if( isInMenu ) base = ColorUtils::menuBackgroundColor( ColorUtils::midColor( _settings.palette().color( Palette::Window ) ), wh, y+wy+h/2 );
1916             else if( options&Blend ) base = ColorUtils::backgroundColor( ColorUtils::midColor( _settings.palette().color( Palette::Window ) ), wh, y+wy+h/2 );
1917             else base = ColorUtils::midColor( _settings.palette().color( Palette::Window ) );
1918 
1919         } else {
1920 
1921             base = ColorUtils::midColor( _settings.palette().color( Palette::Window ) );
1922 
1923         }
1924 
1925         // more color customization, based on menuHighlight mode
1926         ColorUtils::Rgba color( base );
1927         if( _settings.menuHighlightMode() == QtSettings::MM_STRONG )
1928         {
1929 
1930             if( (options & Sunken) || isInMenu ) color = _settings.palette().color( Palette::Selected );
1931             else color = ColorUtils::tint( color, _settings.palette().color( Palette::Hover ) );
1932 
1933         } else if( _settings.menuHighlightMode() == QtSettings::MM_SUBTLE ) {
1934 
1935             if( (options & Sunken) || isInMenu ) color = ColorUtils::mix( color, ColorUtils::tint( color, _settings.palette().color( Palette::Selected ), 0.6 ) );
1936             else color = ColorUtils::mix( color, ColorUtils::tint( color, _settings.palette().color( Palette::Hover ), 0.6 ) );
1937 
1938         }
1939 
1940         // apply animation data
1941         if( data._mode == AnimationHover )
1942         {
1943             if( data._opacity > 0 ) color = ColorUtils::alphaColor( color, data._opacity );
1944             else return;
1945         }
1946 
1947 
1948         if( isInMenuBar )
1949         {
1950 
1951             x+=1;
1952             w-=2;
1953 
1954         } else if ( isInMenu ) {
1955 
1956             // we force ytickness to 5 in gtkrc to emulate Qt menuitems space separation
1957             // so adjust vertical extent of the rect in menus to 21 (size with standard ytickness)
1958             if( h > 27 )
1959             {
1960 
1961                 y+=4;
1962                 h-=7;
1963 
1964             } else {
1965 
1966                 y+=(h-21)/2;
1967                 h=21;
1968 
1969             }
1970 
1971         }
1972 
1973         // save context
1974         cairo_save( context );
1975 
1976         // perform painting
1977         bool hasSubMenu( isInMenu && GTK_IS_MENU_ITEM( widget ) && gtk_menu_item_get_submenu( GTK_MENU_ITEM( widget ) ) );
1978         if( hasSubMenu )
1979         {
1980             cairo_translate( context, x, y );
1981 
1982             // draw item rect in a group
1983             cairo_push_group( context );
1984             _helper.holeFlat( color, 0 ).render( context, 0, 0, w, h, TileSet::Full );
1985             cairo_pop_group_to_source( context );
1986 
1987             // generate linear gradient for masking
1988             if( Gtk::gtk_widget_layout_is_reversed( widget ) )
1989             {
1990 
1991                 Cairo::Pattern mask( cairo_pattern_create_linear( 4, 0, 40, 0 ) );
1992                 cairo_pattern_add_color_stop( mask, 0,  ColorUtils::Rgba::transparent() );
1993                 cairo_pattern_add_color_stop( mask, 1,  ColorUtils::Rgba::black() );
1994                 cairo_mask( context, mask );
1995 
1996             } else {
1997 
1998                 Cairo::Pattern mask( cairo_pattern_create_linear( w-40, 0, w-4, 0 ) );
1999                 cairo_pattern_add_color_stop( mask, 0,  ColorUtils::Rgba::black() );
2000                 cairo_pattern_add_color_stop( mask, 1,  ColorUtils::Rgba::transparent() );
2001                 cairo_mask( context, mask );
2002 
2003             }
2004 
2005         } else {
2006 
2007             _helper.holeFlat( color, 0 ).render( context, x, y, w, h, TileSet::Full  );
2008 
2009         }
2010 
2011         // restore
2012         cairo_restore( context );
2013 
2014     }
2015 
2016     //____________________________________________________________________________________
renderSelection(cairo_t * context,gint x,gint y,gint w,gint h,TileSet::Tiles tiles,const StyleOptions & options)2017     void Style::renderSelection(
2018         cairo_t* context,
2019         gint x, gint y, gint w, gint h,
2020         TileSet::Tiles tiles,
2021         const StyleOptions& options
2022         )
2023     {
2024 
2025         // do nothing if not selected nor hovered
2026         if( !options & (Hover|Selected ) ) return;
2027 
2028         cairo_save( context );
2029 
2030         Palette::Group group( (options & Focus) ? Palette::Active : Palette::Inactive );
2031         ColorUtils::Rgba base( _settings.palette().color( group, Palette::Selected ) );
2032         if( options & Hover  )
2033         {
2034             if( !( options & Selected ) ) base.setAlpha( 0.2 );
2035             else base = base.light( 110 );
2036         }
2037 
2038         if( !(tiles&TileSet::Left) ) { x -= 8; w+=8; }
2039         if( !(tiles&TileSet::Right) ) { w += 8; }
2040         _helper.selection( base, h, false ).render( context, x, y, w, h, tiles );
2041 
2042         cairo_restore( context );
2043 
2044     }
2045 
2046     //____________________________________________________________________________________
renderArrow(cairo_t * context,GtkArrowType orientation,gint x,gint y,gint w,gint h,QtSettings::ArrowSize arrowSize,const StyleOptions & options,const AnimationData & data,Palette::Role role) const2047     void Style::renderArrow(
2048         cairo_t* context,
2049         GtkArrowType orientation,
2050         gint x, gint y, gint w, gint h,
2051         QtSettings::ArrowSize arrowSize,
2052         const StyleOptions& options,
2053         const AnimationData& data,
2054         Palette::Role role ) const
2055     {
2056 
2057         // get polygon
2058         const Polygon arrow( genericArrow( orientation, arrowSize ) );
2059 
2060         // retrieve colors
2061         ColorUtils::Rgba base;
2062         if( options&Disabled ) base = _settings.palette().color( Palette::Disabled, role );
2063         else if( data._mode == AnimationHover ) base = ColorUtils::mix(
2064             _settings.palette().color( Palette::Active, role ),
2065             _settings.palette().color( Palette::Hover ),
2066             data._opacity );
2067         else if( options&Hover ) base = _settings.palette().color( Palette::Hover );
2068         else base = _settings.palette().color( Palette::Active, role );
2069 
2070         // merge base color with relevant background, if needed
2071         const Palette::Group group( (options&Disabled) ? Palette::Disabled : Palette::Active );
2072         switch( role )
2073         {
2074 
2075             case Palette::WindowText:
2076             base = ColorUtils::decoColor( _settings.palette().color( group, Palette::Window ), base );
2077             break;
2078 
2079             case Palette::ButtonText:
2080             base = ColorUtils::decoColor( _settings.palette().color( group, Palette::Button ), base );
2081             break;
2082 
2083             default: break;
2084 
2085         }
2086 
2087         // need odd width and height
2088         if( !(w%2) ) w--;
2089         if( !(h%2) ) h--;
2090         const int xcenter = x + w/2;
2091         const int ycenter = y + h/2;
2092 
2093         // save context and translate to center
2094         cairo_save( context );
2095         cairo_translate( context, xcenter, ycenter );
2096 
2097         switch( orientation )
2098         {
2099             case GTK_ARROW_UP:
2100             break;
2101 
2102             case GTK_ARROW_DOWN:
2103             if( arrowSize == QtSettings::ArrowSmall ) cairo_translate( context, 0, 0.5 );
2104             else cairo_translate( context, 0, 1 );
2105             break;
2106 
2107             case GTK_ARROW_LEFT:
2108             case GTK_ARROW_RIGHT:
2109             if( arrowSize == QtSettings::ArrowSmall )
2110             { cairo_translate( context, 0.5, 0 ); }
2111             break;
2112 
2113             default:
2114             break;
2115         }
2116 
2117         switch( arrowSize )
2118         {
2119             case QtSettings::ArrowTiny:
2120             case QtSettings::ArrowSmall:
2121             cairo_set_line_width( context, 1.2 );
2122             break;
2123 
2124             default:
2125             case QtSettings::ArrowNormal:
2126             cairo_set_line_width( context, 1.6 );
2127             break;
2128         }
2129 
2130         cairo_set_line_cap( context, CAIRO_LINE_CAP_ROUND );
2131         cairo_set_line_join( context, CAIRO_LINE_JOIN_ROUND );
2132 
2133         // contrast
2134         if( options&Contrast )
2135         {
2136 
2137             // contrast color
2138             const ColorUtils::Rgba contrast( ColorUtils::lightColor( _settings.palette().color( Palette::Window ) ) );
2139 
2140             cairo_save( context );
2141             cairo_translate( context, 0, 1 );
2142             cairo_polygon( context, arrow );
2143             cairo_restore( context );
2144 
2145             cairo_set_source( context, contrast );
2146             cairo_stroke( context );
2147         }
2148 
2149         // base
2150         cairo_polygon( context, arrow );
2151         cairo_set_source( context, base );
2152         cairo_stroke( context );
2153 
2154         // restore
2155         cairo_restore( context );
2156 
2157     }
2158 
2159     //____________________________________________________________________________________
renderSliderGroove(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,TileSet::Tiles tiles)2160     void Style::renderSliderGroove(
2161         cairo_t* context,
2162         gint x, gint y, gint w, gint h, const StyleOptions& options, TileSet::Tiles tiles )
2163     {
2164 
2165         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2166 
2167         const bool vertical( options & Vertical );
2168         GdkRectangle parent = { x, y, w, h };
2169 
2170         GdkRectangle child;
2171         if( vertical ) child = Gtk::gdk_rectangle( 0, 0, Slider_GrooveWidth, h );
2172         else child = Gtk::gdk_rectangle( 0, 0, w, Slider_GrooveWidth );
2173         centerRect( &parent, &child );
2174 
2175         if( !vertical )
2176         {
2177             // more adjustment needed due to contrast pixel
2178             child.y += 1;
2179             child.height -= 1;
2180         }
2181 
2182         cairo_save( context );
2183         _helper.scrollHole( base, vertical, true ).render( context, child.x, child.y, child.width, child.height, tiles );
2184         cairo_restore( context );
2185 
2186     }
2187 
2188     //____________________________________________________________________________________
renderSliderHandle(cairo_t * context,gint x,gint y,gint w,gint h,const StyleOptions & options,const AnimationData & animationData)2189     void Style::renderSliderHandle(
2190         cairo_t* context,
2191         gint x, gint y, gint w, gint h,
2192         const StyleOptions& options,
2193         const AnimationData& animationData )
2194     {
2195 
2196         // define colors
2197         const Palette::Group group( options&Disabled ? Palette::Disabled : Palette::Active );
2198 //         ColorUtils::Rgba base;
2199 //         if( options&Blend )
2200 //         {
2201 //
2202 //             gint wh, wy;
2203 //             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
2204 //             base = ColorUtils::backgroundColor( _settings.palette().color( group, Palette::Button ), wh, y+wy+h/2 );
2205 //
2206 //         } else {
2207 //
2208 //             base = _settings.palette().color( group, Palette::Button );
2209 //
2210 //         }
2211 
2212         const ColorUtils::Rgba base( _settings.palette().color( group, Palette::Button ) );
2213 
2214         // render slab
2215         cairo_save( context );
2216 
2217         GdkRectangle parent = { x, y, w, h };
2218         GdkRectangle child = {0, 0, 21, 21 };
2219         centerRect( &parent, &child );
2220 
2221         x = child.x;
2222         y = child.y;
2223 
2224         const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
2225         const Cairo::Surface& surface( _helper.sliderSlab( base, glow, (options&Sunken), 0 ) );
2226         cairo_translate( context, x, y );
2227         cairo_rectangle( context, 0, 0, child.width, child.height );
2228         cairo_set_source_surface( context, surface, 0, 0 );
2229         cairo_fill( context );
2230         cairo_restore( context );
2231 
2232     }
2233 
2234     //____________________________________________________________________________________
renderSizeGrip(cairo_t * context,GdkWindowEdge edge,gint x,gint y,gint w,gint h) const2235     void Style::renderSizeGrip(
2236         cairo_t* context,
2237         GdkWindowEdge edge,
2238         gint x, gint y, gint w, gint h ) const
2239     {
2240 
2241         gint dimension = std::min( w, h );
2242 
2243         // edges
2244         Polygon a;
2245         switch( edge )
2246         {
2247 
2248             case GDK_WINDOW_EDGE_NORTH_WEST:
2249             w = dimension;
2250             h = dimension;
2251             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);
2252             break;
2253 
2254             case GDK_WINDOW_EDGE_NORTH_EAST:
2255             x+= w-dimension;
2256             w = dimension;
2257             h = dimension;
2258             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 );
2259             break;
2260 
2261             case GDK_WINDOW_EDGE_SOUTH_WEST:
2262             y+= h-dimension;
2263             w = dimension;
2264             h = dimension;
2265             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 );
2266             break;
2267 
2268             case GDK_WINDOW_EDGE_SOUTH_EAST:
2269             x+= w-dimension;
2270             y+= h-dimension;
2271             w = dimension;
2272             h = dimension;
2273             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 );
2274             break;
2275 
2276             default: return;
2277 
2278         }
2279 
2280 
2281         // colors
2282         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2283         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
2284         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
2285 
2286         // context
2287         cairo_save( context );
2288         cairo_set_line_width( context, 1.0 );
2289 
2290         // fill
2291         cairo_polygon( context, a );
2292         cairo_set_source( context, base );
2293         cairo_fill( context );
2294 
2295         // draw edges
2296         cairo_move_to( context, a[0].x(), a[0].y() );
2297         cairo_line_to( context, a[1].x(), a[1].y() );
2298         cairo_set_source( context, dark );
2299         cairo_stroke( context );
2300 
2301         cairo_move_to( context, a[1].x(), a[1].y() );
2302         cairo_line_to( context, a[2].x(), a[2].y() );
2303         cairo_line_to( context, a[0].x(), a[0].y() );
2304         cairo_set_source( context, light );
2305         cairo_stroke( context );
2306         cairo_restore( context );
2307 
2308     }
2309 
2310     //____________________________________________________________________________________
renderTab(cairo_t * context,gint x,gint y,gint w,gint h,GtkPositionType side,const StyleOptions & options,const TabOptions & tabOptions,const AnimationData & data)2311     void Style::renderTab(
2312         cairo_t* context,
2313         gint x, gint y, gint w, gint h,
2314         GtkPositionType side,
2315         const StyleOptions& options,
2316         const TabOptions& tabOptions,
2317         const AnimationData& data
2318         )
2319     {
2320 
2321         if( tabOptions & CurrentTab )
2322         {
2323 
2324             return renderActiveTab( context, x, y, w, h, side, options, tabOptions );
2325 
2326         } else {
2327 
2328             switch( _settings.tabStyle() )
2329             {
2330                 case QtSettings::TS_SINGLE: return renderInactiveTab_Single( context, x, y, w, h, side, options, tabOptions, data );
2331                 case QtSettings::TS_PLAIN: return renderInactiveTab_Plain( context, x, y, w, h, side, options, tabOptions, data );
2332                 default: return;
2333             }
2334 
2335         }
2336 
2337     }
2338 
2339     //____________________________________________________________________________________
renderTabBarBase(cairo_t * context,gint x,gint y,gint w,gint h,GtkPositionType side,Gtk::Gap gap,const StyleOptions & options,const TabOptions & tabOptions)2340     void Style::renderTabBarBase(
2341         cairo_t* context,
2342         gint x, gint y, gint w, gint h,
2343         GtkPositionType side,
2344         Gtk::Gap gap,
2345         const StyleOptions& options,
2346         const TabOptions& tabOptions
2347         )
2348     {
2349 
2350         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2351 
2352         // adjust gap
2353         if( tabOptions & FirstTabAligned ) { gap.setX( gap.x()-3 ); gap.setWidth( gap.width()+3 ); }
2354         if( tabOptions & LastTabAligned ) { gap.setWidth( gap.width()+3 ); }
2355 
2356         // context
2357         cairo_save( context );
2358 
2359         // generate mask and main slab
2360         SlabRect tabSlab;
2361         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
2362         switch( side )
2363         {
2364             case GTK_POS_BOTTOM:
2365             tabSlab = SlabRect( x, y+h-4, w, 15, tabTiles );
2366             generateGapMask( context, x-1, y-4, w+2, h+8, gap );
2367             break;
2368 
2369             case GTK_POS_TOP:
2370             tabSlab = SlabRect( x, y-11, w, 15, tabTiles );
2371             generateGapMask( context, x-1, y-4, w+2, h+8, gap );
2372             break;
2373 
2374             case GTK_POS_RIGHT:
2375             tabSlab = SlabRect( x+w-4, y, 15, h, tabTiles );
2376             generateGapMask( context, x-4, y-1, w+8, h+2, gap );
2377             break;
2378 
2379 
2380             case GTK_POS_LEFT:
2381             tabSlab = SlabRect( x-11, y, 15, h, tabTiles );
2382             generateGapMask( context, x-4, y-1, w+8, h+2, gap );
2383             break;
2384 
2385             default: break;
2386 
2387         }
2388 
2389         // render
2390         _helper.slab( base, 0 ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
2391         cairo_restore( context );
2392 
2393         return;
2394 
2395     }
2396 
2397     //__________________________________________________________________
renderTabBarFrame(cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::Gap & gap,const StyleOptions & options)2398     void Style::renderTabBarFrame(
2399         cairo_t* context,
2400         gint x, gint y, gint w, gint h,
2401         const Gtk::Gap& gap,
2402         const StyleOptions& options )
2403     {
2404 
2405         // define colors
2406         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2407 
2408         // create context
2409         cairo_save( context );
2410         generateGapMask( context, x, y, w, h, gap );
2411         renderSlab( context, x, y, w, h, base, options );
2412         cairo_restore( context );
2413 
2414     }
2415 
2416     //____________________________________________________________________________________
renderTreeExpander(cairo_t * context,gint x,gint y,gint w,gint h,GtkExpanderStyle style,const StyleOptions & options,const AnimationData & data,Palette::Role role) const2417     void Style::renderTreeExpander(
2418         cairo_t* context,
2419         gint x, gint y, gint w, gint h,
2420         GtkExpanderStyle style,
2421         const StyleOptions& options,
2422         const AnimationData& data,
2423         Palette::Role role
2424         ) const
2425     {
2426 
2427         // retrieve colors
2428         ColorUtils::Rgba base;
2429         if( options&Disabled ) base = _settings.palette().color( Palette::Disabled, role );
2430         else if( data._mode == AnimationHover ) base = ColorUtils::mix(
2431             _settings.palette().color( Palette::Active, role ),
2432             _settings.palette().color( Palette::Hover ),
2433             data._opacity );
2434         else if( options&Hover ) base = _settings.palette().color( Palette::Hover );
2435         else base = _settings.palette().color( Palette::Active, role );
2436 
2437         const int xcenter( x + w/2 );
2438         const int ycenter( y + h/2 );
2439 
2440         // expander 'radius' (copied from oxygen-qt)
2441         const int radius( ( 9 - 4 ) / 2 );
2442 
2443         // create context and translate to center
2444         cairo_save( context );
2445         cairo_set_line_width( context, 1.0 );
2446         cairo_set_source( context, base );
2447 
2448         cairo_translate( context, -0.5+xcenter, -0.5+ycenter );
2449 
2450         // horizontal line
2451         cairo_move_to( context, -radius, 0 );
2452         cairo_line_to( context, radius, 0 );
2453 
2454         // vertical line
2455         if( style == GTK_EXPANDER_COLLAPSED || style == GTK_EXPANDER_SEMI_COLLAPSED )
2456         {
2457             cairo_move_to( context, 0, -radius );
2458             cairo_line_to( context, 0, radius );
2459         }
2460 
2461         cairo_stroke( context );
2462         cairo_restore( context );
2463 
2464     }
2465 
2466     //__________________________________________________________________
renderWindowDecoration(cairo_t * context,WinDeco::Options wopt,gint x,gint y,gint w,gint h,const gchar ** windowStrings,gint titleIndentLeft,gint titleIndentRight,bool gradient)2467     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 )
2468     {
2469         bool hasAlpha( wopt & WinDeco::Alpha );
2470         bool drawResizeHandle( !(wopt & WinDeco::Shaded) && (wopt & WinDeco::Resizable) );
2471         bool isMaximized( wopt & WinDeco::Maximized );
2472 
2473         if( hasAlpha )
2474         {
2475             // cut round corners using alpha
2476             cairo_rounded_rectangle(context,x,y,w,h,3.5);
2477             cairo_clip(context);
2478         }
2479 
2480         if( gradient )
2481             renderWindowBackground( context, x, y, w, h, isMaximized );
2482         else
2483         {
2484             cairo_set_source( context, _settings.palette().color( Palette::Active, Palette::Window ) );
2485             cairo_paint( context );
2486         }
2487 
2488         StyleOptions options( hasAlpha ? Alpha : Blend );
2489 
2490         options|=Round;
2491 
2492         // focus
2493         if(wopt & WinDeco::Active) options|=Focus;
2494 
2495         if( !isMaximized )
2496         { drawFloatFrame( context, x, y, w, h, options ); }
2497 
2498         if( drawResizeHandle )
2499         {
2500             ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2501             renderWindowDots( context, x, y, w, h, base, wopt);
2502         }
2503 
2504         if(windowStrings)
2505         {
2506             // caption is drawn in drawWindowDecoration
2507             if( windowStrings[1] )
2508             {
2509                 // TODO: use WMCLASS and caption to enable per-window style exceptions
2510             }
2511         }
2512     }
2513 
2514     //__________________________________________________________________
drawWindowDecoration(cairo_t * context,WinDeco::Options wopt,gint x,gint y,gint w,gint h,const gchar ** windowStrings,gint titleIndentLeft,gint titleIndentRight)2515     void Style::drawWindowDecoration( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h, const gchar** windowStrings, gint titleIndentLeft, gint titleIndentRight )
2516     {
2517         #ifdef GDK_WINDOWING_X11
2518 
2519         /*
2520            (any element of windowStrings[] may be NULL - will be understood as "")
2521            windowStrings may also be NULL
2522 
2523            elements:
2524             windowStrings[0]: caption
2525             windowStrings[1]: WMCLASS
2526             windowStrings[2]: (gpointer)XID
2527         */
2528         /*
2529            caches layout:
2530                left&right border height: h
2531                top&bottom border width: w-BorderLeft-BorderRight
2532         */
2533 
2534         // enable gradient if XID is not passed
2535         bool gradient=true;
2536 
2537         QtSettings::WindecoBlendType blendType(_settings.windecoBlendType());
2538         if( blendType==QtSettings::SolidColor )
2539         {
2540 
2541             gradient=false;
2542 
2543         } else if( blendType==QtSettings::FollowStyleHint && windowStrings && windowStrings[2] ) {
2544 
2545             Window window((Window)windowStrings[2]);
2546             Display* display( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()) );
2547             if( _animations.backgroundHintEngine().backgroundGradientAtom() != None )
2548             {
2549                 Atom typeRet;
2550                 int formatRet;
2551                 unsigned long itemsRet;
2552                 unsigned long afterRet;
2553                 unsigned char *data = 0;
2554 
2555                 if( !( XGetWindowProperty(display, window, _animations.backgroundHintEngine().backgroundGradientAtom(), 0, G_MAXLONG, False,
2556                     XA_CARDINAL, &typeRet, &formatRet, &itemsRet, &afterRet, &data) == Success
2557                     && itemsRet == 1
2558                     && formatRet == 32) )
2559                 {
2560                     // if the window doesn't have this property set, it's likely
2561                     // non-oxygenized, thus shouldn't have windeco bg gradient
2562                     gradient=false;
2563                 }
2564             }
2565         }
2566 
2567         WindecoBorderKey key(wopt,w,h,gradient);
2568 
2569         {
2570             // draw left border with cache
2571             Cairo::Surface left( _helper.windecoLeftBorderCache().value(key) );
2572             int sw=WinDeco::getMetric(WinDeco::BorderLeft);
2573             if(sw)
2574             {
2575 
2576                 if( !left )
2577                 {
2578 
2579                     #if OXYGEN_DEBUG
2580                     std::cerr<<"drawWindowDecoration: drawing left border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2581                     #endif
2582                     left=_helper.createSurface(sw,h);
2583 
2584                     Cairo::Context context(left);
2585                     renderWindowDecoration( context, wopt, 0, 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient);
2586 
2587                     _helper.windecoLeftBorderCache().insert(key,left);
2588 
2589                 } else {
2590 
2591                     #if OXYGEN_DEBUG
2592                     std::cerr << "drawWindowDecoration: using saved left border" << std::endl;
2593                     #endif
2594 
2595                 }
2596 
2597                 cairo_set_source_surface(context, left, x, y);
2598                 cairo_rectangle(context,x,y,sw,h);
2599                 cairo_fill(context);
2600             }
2601         }
2602 
2603         {
2604             // draw right border with cache
2605             Cairo::Surface right( _helper.windecoRightBorderCache().value(key) );
2606             int sw=WinDeco::getMetric(WinDeco::BorderRight);
2607             if(sw)
2608             {
2609 
2610                 if( !right )
2611                 {
2612 
2613                     #if OXYGEN_DEBUG
2614                     std::cerr<<"drawWindowDecoration: drawing right border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2615                     #endif
2616 
2617                     right=_helper.createSurface(sw,h);
2618 
2619                     Cairo::Context context(right);
2620                     renderWindowDecoration( context, wopt, -(w-sw), 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2621 
2622                     _helper.windecoRightBorderCache().insert(key,right);
2623 
2624                 } else {
2625 
2626                     #if OXYGEN_DEBUG
2627                     std::cerr << "drawWindowDecoration: using saved right border" << std::endl;
2628                     #endif
2629 
2630                 }
2631 
2632                 cairo_set_source_surface(context, right, x+w-sw, y);
2633                 cairo_rectangle(context,x+w-sw,y,sw,h);
2634                 cairo_fill(context);
2635             }
2636         }
2637 
2638         {
2639             // draw top border with cache
2640             Cairo::Surface top( _helper.windecoTopBorderCache().value(key) );
2641             int left=WinDeco::getMetric(WinDeco::BorderLeft);
2642             int right=WinDeco::getMetric(WinDeco::BorderRight);
2643             int sh=WinDeco::getMetric(WinDeco::BorderTop);
2644             int sw=w-left-right;
2645             if(sh && sw)
2646             {
2647                 if( !top )
2648                 {
2649 
2650                     #if OXYGEN_DEBUG
2651                     std::cerr<<"drawWindowDecoration: drawing top border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2652                     #endif
2653                     top=_helper.createSurface(sw,sh);
2654 
2655                     Cairo::Context context(top);
2656                     renderWindowDecoration( context, wopt, -left, 0, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2657 
2658                     _helper.windecoTopBorderCache().insert(key,top);
2659 
2660                 } else {
2661 
2662                     #if OXYGEN_DEBUG
2663                     std::cerr << "drawWindowDecoration: using saved top border" << std::endl;
2664                     #endif
2665 
2666                 }
2667 
2668                 cairo_set_source_surface(context, top, x+left, y);
2669                 cairo_rectangle(context,x+left,y,sw,sh);
2670                 cairo_fill(context);
2671 
2672                 // caption shouldn't be saved in the cache
2673                 if( windowStrings && windowStrings[0] )
2674                 {
2675                     // draw caption
2676                     const gchar* &caption(windowStrings[0]);
2677                     const FontInfo& font( _settings.WMFont() );
2678                     gint layoutWidth=w-(titleIndentLeft+titleIndentRight);
2679                     if( font.isValid() && layoutWidth>0 )
2680                     {
2681                         PangoFontDescription* fdesc( pango_font_description_new() );
2682                         const Palette::Group group( wopt & WinDeco::Active ? Palette::Active : Palette::Disabled );
2683                         const int H=WinDeco::getMetric(WinDeco::BorderTop);
2684                         int textHeight;
2685 
2686                         pango_font_description_set_family( fdesc, font.family().c_str() );
2687                         pango_font_description_set_weight( fdesc, PangoWeight( (font.weight()+2)*10 ) );
2688                         pango_font_description_set_style( fdesc, font.italic() ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL );
2689                         pango_font_description_set_absolute_size( fdesc, int(font.size()*PANGO_SCALE) );
2690 
2691                         PangoLayout* layout( pango_cairo_create_layout(context) );
2692                         pango_layout_set_text( layout,caption, -1 );
2693                         pango_layout_set_font_description( layout, fdesc );
2694                         pango_layout_set_width( layout, layoutWidth*PANGO_SCALE );
2695                         pango_layout_set_ellipsize( layout, PANGO_ELLIPSIZE_END );
2696                         pango_layout_set_alignment( layout, _settings.TitleAlignment() );
2697                         pango_layout_get_pixel_size( layout, NULL, &textHeight );
2698 
2699                         cairo_save( context );
2700 
2701                         cairo_set_source( context, _settings.palette().color( group, Palette::WindowText ) );
2702                         cairo_translate( context, x+titleIndentLeft, y+(H-textHeight)/2. );
2703                         pango_cairo_update_layout( context, layout );
2704                         pango_cairo_show_layout( context, layout );
2705 
2706                         cairo_restore( context );
2707 
2708                         g_object_unref(layout);
2709                         pango_font_description_free(fdesc);
2710                     }
2711                 }
2712             }
2713         }
2714 
2715         {
2716             // draw bottom border with cache
2717             Cairo::Surface bottom( _helper.windecoBottomBorderCache().value(key) );
2718             int left=WinDeco::getMetric(WinDeco::BorderLeft);
2719             int right=WinDeco::getMetric(WinDeco::BorderRight);
2720             int sh=WinDeco::getMetric(WinDeco::BorderBottom);
2721             int sw=w-left-right;
2722             int Y=y+h-sh;
2723             if(sh && sw)
2724             {
2725                 if( !bottom)
2726                 {
2727 
2728                     #if OXYGEN_DEBUG
2729                     std::cerr<<"drawWindowDecoration: drawing bottom border; width: " << w << "; height: " << h << "; wopt: " << wopt << std::endl;
2730                     #endif
2731                     bottom=_helper.createSurface(sw,sh);
2732 
2733                     Cairo::Context context(bottom);
2734                     renderWindowDecoration( context, wopt, -left, y-Y, w, h, windowStrings, titleIndentLeft, titleIndentRight, gradient );
2735 
2736                     _helper.windecoBottomBorderCache().insert(key,bottom);
2737 
2738                 } else {
2739 
2740                     #if OXYGEN_DEBUG
2741                     std::cerr << "drawWindowDecoration: using saved bottom border" << std::endl;
2742                     #endif
2743 
2744                 }
2745 
2746                 cairo_set_source_surface(context, bottom, x+left, Y);
2747                 cairo_rectangle(context,x+left,Y,sw,sh);
2748                 cairo_fill(context);
2749             }
2750         }
2751 
2752         #endif
2753 
2754     }
2755 
2756     //__________________________________________________________________
drawWindowShadow(cairo_t * context,WinDeco::Options wopt,gint x,gint y,gint w,gint h)2757     void Style::drawWindowShadow( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h )
2758     {
2759         cairo_set_source_rgba( context, 0, 0, 0, 0 );
2760         cairo_set_operator( context, CAIRO_OPERATOR_SOURCE );
2761         cairo_paint( context );
2762         cairo_set_operator( context, CAIRO_OPERATOR_OVER );
2763 
2764         WindowShadow shadow(_settings, _helper);
2765         shadow.setWindowState(wopt);
2766         shadow.render(context, x,y,w,h);
2767     }
2768 
2769     //__________________________________________________________________
drawWindecoButton(cairo_t * context,WinDeco::ButtonType type,WinDeco::ButtonStatus buttonState,WinDeco::Options windowState,gint x,gint y,gint w,gint h)2770     void Style::drawWindecoButton( cairo_t* context, WinDeco::ButtonType type, WinDeco::ButtonStatus buttonState, WinDeco::Options windowState, gint x, gint y, gint w,gint h)
2771     {
2772         // validate arguments
2773         if(type>=WinDeco::ButtonTypeCount || buttonState>=WinDeco::ButtonStatusCount)
2774         { return; }
2775 
2776         if( !(windowState & WinDeco::Active) && buttonState == WinDeco::Normal )
2777         {
2778             // draw Oxygen-way disabled button on inactive window
2779             buttonState=WinDeco::Disabled;
2780         }
2781         if( !(windowState & WinDeco::Alpha) && !(windowState & WinDeco::Maximized) )
2782         { y++; }
2783 
2784         WinDeco::Button button( _settings, _helper, type );
2785         button.setState(buttonState);
2786         int buttonSize=_settings.buttonSize();
2787         button.render( context, x+(w-buttonSize)/2+1,y+(h-buttonSize)/2+1, buttonSize, buttonSize );
2788     }
2789 
2790     //__________________________________________________________________
drawWindecoShapeMask(cairo_t * context,WinDeco::Options wopt,gint x,gint y,gint w,gint h)2791     void Style::drawWindecoShapeMask( cairo_t* context, WinDeco::Options wopt, gint x, gint y, gint w, gint h )
2792     {
2793         cairo_save(context);
2794         cairo_set_source_rgba(context,0,0,0,0);
2795         cairo_set_operator(context,CAIRO_OPERATOR_SOURCE);
2796         cairo_paint(context);
2797 
2798         cairo_set_source_rgba(context,1,1,1,1);
2799         cairo_set_operator(context,CAIRO_OPERATOR_OVER);
2800         cairo_set_antialias(context,CAIRO_ANTIALIAS_NONE);
2801         cairo_rounded_rectangle(context,x,y,w,h,6);
2802         cairo_fill(context);
2803         cairo_restore(context);
2804 
2805     }
2806 
2807     //__________________________________________________________________
sanitizeSize(GdkWindow * window,gint & w,gint & h) const2808     void Style::sanitizeSize( GdkWindow* window, gint& w, gint& h ) const
2809     {
2810         if( w < 0 ) w = gdk_window_get_width( window );
2811         if( h < 0 ) h = gdk_window_get_height( window );
2812     }
2813 
2814     //__________________________________________________________________
adjustScrollBarHole(gdouble & x,gdouble & y,gdouble & w,gdouble & h,const StyleOptions & options) const2815     void Style::adjustScrollBarHole( gdouble& x, gdouble& y, gdouble& w, gdouble& h, const StyleOptions& options ) const
2816     {
2817 
2818         const int buttonSize( 12 );
2819         const int subLineOffset( buttonSize*_settings.scrollBarSubLineButtons() );
2820         const int addLineOffset( buttonSize*_settings.scrollBarAddLineButtons() );
2821         if( options&Vertical )
2822         {
2823 
2824             y += subLineOffset;
2825             h -= (subLineOffset+addLineOffset);
2826 
2827         } else {
2828 
2829             x += subLineOffset;
2830             w -= (subLineOffset+addLineOffset);
2831 
2832         }
2833 
2834         return;
2835 
2836     }
2837 
2838     //____________________________________________________________________________________
setBackgroundSurface(const std::string & filename)2839     void Style::setBackgroundSurface( const std::string& filename )
2840     {
2841         if( _backgroundSurface.isValid() ) _backgroundSurface.free();
2842         _backgroundSurface.set( cairo_image_surface_create_from_png( filename.c_str() ) );
2843     }
2844 
2845     //____________________________________________________________________________________
renderActiveTab(cairo_t * context,gint x,gint y,gint w,gint h,GtkPositionType side,const StyleOptions & options,const TabOptions & tabOptions)2846     void Style::renderActiveTab(
2847         cairo_t* context,
2848         gint x, gint y, gint w, gint h,
2849         GtkPositionType side,
2850         const StyleOptions& options,
2851         const TabOptions& tabOptions
2852         )
2853     {
2854 
2855         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
2856         const bool isLastTabAligned( tabOptions & LastTabAligned );
2857 
2858         // get color
2859         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
2860         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
2861 
2862         // save context
2863         cairo_save( context );
2864 
2865         // borders and connections to tabs
2866         // this is quite painfull and slipery code.
2867         // the same is true with oxygen-qt
2868         int offset = 2;
2869         int adjust = ( tabOptions&Xul ) ? 0:2;
2870         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
2871 
2872         SlabRect tabSlab;
2873         SlabRect::List slabs;
2874         switch( side )
2875         {
2876             case GTK_POS_BOTTOM:
2877             {
2878                 // main slab
2879                 y += adjust; h -= 2*adjust;
2880                 tabSlab = SlabRect( x, y-offset, w, h+9+offset, tabTiles );
2881                 tabSlab._h+=1;
2882 
2883                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
2884                 if( isLastTabAligned ) { tabSlab._w+=1; }
2885 
2886                 // connections to frame
2887                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y+h+offset-6, 8, 18, TileSet::Left ) );
2888                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y+h+offset-6, 8, 18, TileSet::Right ) );
2889 
2890                 break;
2891             }
2892 
2893             case GTK_POS_TOP:
2894             {
2895 
2896                 // main slab
2897                 y += adjust; h -= 2*adjust;
2898                 tabSlab = SlabRect( x, y-9, w, h+11+offset, tabTiles );
2899                 tabSlab._y-=1; tabSlab._h+=1;
2900 
2901                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
2902                 if( isLastTabAligned ) { tabSlab._w-=1; }
2903 
2904                 // connections to frame
2905                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y-14, 8, 18, TileSet::Left ) );
2906                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y-14, 8, 18, TileSet::Right ) );
2907 
2908                 break;
2909             }
2910 
2911             case GTK_POS_RIGHT:
2912             {
2913 
2914                 // main slab
2915                 x += adjust; w -= 2*adjust;
2916                 tabSlab = SlabRect( x-offset, y, w+9+offset, h, tabTiles );
2917                 tabSlab._w+=1;
2918                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
2919                 if( isLastTabAligned ) { tabSlab._h+=1; }
2920 
2921                 // connections to frame
2922                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x+w+offset-6, y-1, 18, 8, TileSet::Top ) );
2923                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w+offset-6, y+h-7, 18, 8, TileSet::Top ) );
2924 
2925                 break;
2926             }
2927 
2928 
2929             case GTK_POS_LEFT:
2930             {
2931 
2932                 // main slab
2933                 x += adjust; w -= 2*adjust;
2934                 tabSlab = SlabRect( x-9, y, w+11+offset, h, tabTiles );
2935                 tabSlab._x-=1; tabSlab._w+=1;
2936                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
2937                 if( isLastTabAligned ) { tabSlab._h+=1; }
2938 
2939                 // connections to frame
2940                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-14, y-1, 18, 8, TileSet::Top ) );
2941                 if( isLastTabAligned )  slabs.push_back( SlabRect( x-14, y+h-7, 18, 8, TileSet::Top ) );
2942 
2943                 break;
2944 
2945             }
2946 
2947             default:
2948             assert( false );
2949             return;
2950         }
2951 
2952         // render tab
2953         _helper.slab( base, 0 ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
2954 
2955         // adjust rect for filling
2956         SlabRect fillSlab( tabSlab );
2957         fillSlab._x += 4;
2958         fillSlab._y += 4;
2959         fillSlab._w -= 8;
2960         fillSlab._h -= 8;
2961 
2962         // fill
2963         Cairo::Pattern pattern;
2964         switch( side )
2965         {
2966             case GTK_POS_BOTTOM:
2967             fillSlab._h -= 2;
2968             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y, 0, fillSlab._y + fillSlab._h ) );
2969             break;
2970 
2971             case GTK_POS_TOP:
2972             fillSlab._y += 2;
2973             fillSlab._h -= 2;
2974             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y + fillSlab._h, 0, fillSlab._y ) );
2975             break;
2976 
2977             case GTK_POS_RIGHT:
2978             fillSlab._w -= 2;
2979             pattern.set( cairo_pattern_create_linear( fillSlab._x, 0, fillSlab._x + fillSlab._w, 0 ) );
2980             break;
2981 
2982             case GTK_POS_LEFT:
2983             fillSlab._x += 2;
2984             fillSlab._w -= 2;
2985             pattern.set( cairo_pattern_create_linear( fillSlab._x + fillSlab._w, 0, fillSlab._x, 0 ) );
2986             break;
2987 
2988             default:
2989             assert( false );
2990             return;
2991 
2992         }
2993 
2994         cairo_pattern_add_color_stop( pattern, 0.1, ColorUtils::alphaColor( light, 0.5 ) );
2995         cairo_pattern_add_color_stop( pattern, 0.25, ColorUtils::alphaColor( light, 0.3 ) );
2996         cairo_pattern_add_color_stop( pattern, 0.5, ColorUtils::alphaColor( light, 0.2 ) );
2997         cairo_pattern_add_color_stop( pattern, 0.75, ColorUtils::alphaColor( light, 0.1 ) );
2998         cairo_pattern_add_color_stop( pattern, 0.9, ColorUtils::Rgba::transparent( light ) );
2999 
3000         // in firefox a solid background must be filled
3001         if( tabOptions&Xul )
3002         {
3003             cairo_set_source( context, base );
3004             cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3005             cairo_fill( context );
3006         }
3007 
3008         // draw pattern
3009         cairo_set_source( context, pattern );
3010         cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3011         cairo_fill( context );
3012 
3013         // render connections to frame
3014         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3015         { _helper.slab(base, 0).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles ); }
3016 
3017         // restore
3018         cairo_restore( context );
3019 
3020     }
3021 
3022     //____________________________________________________________________________________
renderInactiveTab_Single(cairo_t * context,gint x,gint y,gint w,gint h,GtkPositionType side,const StyleOptions & options,const TabOptions & tabOptions,const AnimationData & data)3023     void Style::renderInactiveTab_Single(
3024         cairo_t* context,
3025         gint x, gint y, gint w, gint h,
3026         GtkPositionType side,
3027         const StyleOptions& options,
3028         const TabOptions& tabOptions,
3029         const AnimationData& data
3030         )
3031     {
3032 
3033         // convenience flags
3034         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
3035         const bool isLastTabAligned( tabOptions & LastTabAligned );
3036 
3037         // get color
3038         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
3039         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
3040         const ColorUtils::Rgba dark( ColorUtils::darkColor( base ) );
3041 
3042         // save context
3043         cairo_save( context );
3044 
3045         // borders and connections to tabs
3046         // this is quite painfull and slipery code.
3047         // the same is true with oxygen-qt
3048         int offset = 2;
3049         int adjust = (tabOptions&Xul) ? 0:2;
3050         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
3051 
3052         SlabRect tabSlab;
3053         SlabRect::List slabs;
3054         switch( side )
3055         {
3056             case GTK_POS_BOTTOM:
3057             {
3058                 // main slab
3059                 y += adjust; h -= 2*adjust;
3060                 tabSlab = SlabRect( x, y-offset, w, h+9+offset, tabTiles );
3061                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3062                 if( isLastTabAligned ) { tabSlab._w+=1; }
3063 
3064                 // connections to frame
3065                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y+h+offset-7, 8, 17, TileSet::Left, Hover ) );
3066                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y+h+offset-7, 8, 17, TileSet::Right, Hover ) );
3067                 break;
3068             }
3069 
3070             case GTK_POS_TOP:
3071             {
3072 
3073                 // main slab
3074                 y += adjust; h -= 2*adjust;
3075                 tabSlab = SlabRect( x, y-9, w, h+11+offset, tabTiles );
3076                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3077                 if( isLastTabAligned ) { tabSlab._w-=1; }
3078 
3079                 // connections to frame
3080                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-1, y-13+offset, 8, 16, TileSet::Left, Hover ) );
3081                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w-7, y-13+offset, 8, 16, TileSet::Right, Hover ) );
3082                 break;
3083             }
3084 
3085             case GTK_POS_RIGHT:
3086             {
3087 
3088                 // main slab
3089                 x += adjust; w -= 2*adjust;
3090                 tabSlab = SlabRect( x-offset, y, w+9+offset, h, tabTiles );
3091                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3092                 if( isLastTabAligned ) { tabSlab._h+=1; }
3093 
3094                 // connections to frame
3095                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x+w+offset-7, y-1, 17, 8, TileSet::Top, Hover ) );
3096                 if( isLastTabAligned ) slabs.push_back( SlabRect( x+w+offset-7, y+h-7, 17, 8, TileSet::Top, Hover ) );
3097                 break;
3098             }
3099 
3100 
3101             case GTK_POS_LEFT:
3102             {
3103 
3104                 // main slab
3105                 x += adjust; w -= 2*adjust;
3106                 tabSlab = SlabRect( x-9, y, w+11+offset, h, tabTiles );
3107                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3108                 if( isLastTabAligned ) { tabSlab._h+=1; }
3109 
3110                 // connections to frame
3111                 SlabRect baseSlab( x-10+1, y-6, 10, h+12, TileSet::Right );
3112                 if( isFirstTabAligned ) slabs.push_back( SlabRect( x-13 + offset, y-1, 16, 8, TileSet::Top, Hover ) );
3113                 if( isLastTabAligned )slabs.push_back( SlabRect( x-13 + offset, y+h-7, 16, 8, TileSet::Top, Hover ) );
3114                 break;
3115             }
3116 
3117             default:
3118             assert( false );
3119             return;
3120         }
3121 
3122         // render tab
3123         ColorUtils::Rgba glow;
3124         if( data._mode == AnimationHover ) glow = ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3125         else if( options&Hover ) glow = _settings.palette().color( Palette::Hover );
3126 
3127         _helper.slab( base, glow ).render( context, tabSlab._x, tabSlab._y, tabSlab._w, tabSlab._h, tabSlab._tiles );
3128 
3129         // adjust rect for filling
3130         SlabRect fillSlab( tabSlab );
3131         fillSlab._x += 4;
3132         fillSlab._y += 4;
3133         fillSlab._w -= 8;
3134         fillSlab._h -= 8;
3135 
3136         // fill
3137         Cairo::Pattern pattern;
3138         switch( side )
3139         {
3140             case GTK_POS_BOTTOM:
3141             fillSlab._h -= 3;
3142             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y, 0, fillSlab._y + fillSlab._h ) );
3143             break;
3144 
3145             case GTK_POS_TOP:
3146             fillSlab._y += 3;
3147             fillSlab._h -= 3;
3148             pattern.set( cairo_pattern_create_linear( 0, fillSlab._y + fillSlab._h, 0, fillSlab._y ) );
3149             break;
3150 
3151             case GTK_POS_RIGHT:
3152             fillSlab._w -= 3;
3153             pattern.set( cairo_pattern_create_linear( fillSlab._x, 0, fillSlab._x + fillSlab._w, 0 ) );
3154             break;
3155 
3156             case GTK_POS_LEFT:
3157             fillSlab._x += 3;
3158             fillSlab._w -= 3;
3159             pattern.set( cairo_pattern_create_linear( fillSlab._x + fillSlab._w, 0, fillSlab._x, 0 ) );
3160             break;
3161 
3162             default:
3163             assert( false );
3164             return;
3165 
3166         }
3167 
3168         cairo_pattern_add_color_stop( pattern, 0.0, ColorUtils::alphaColor( light, 0.1 ) );
3169         cairo_pattern_add_color_stop( pattern, 0.4, ColorUtils::alphaColor( dark, 0.5 ) );
3170         cairo_pattern_add_color_stop( pattern, 0.8, ColorUtils::alphaColor( dark, 0.4 ) );
3171 
3172         // draw pattern
3173         cairo_set_source( context, pattern );
3174         cairo_rectangle( context, fillSlab._x, fillSlab._y, fillSlab._w, fillSlab._h );
3175         cairo_fill( context );
3176 
3177         // render connections to frame
3178         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3179         {
3180 
3181             if( (iter->_options&Hover) && glow.isValid() )
3182             {
3183 
3184                 _helper.slab(base, glow).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles );
3185 
3186             } else {
3187 
3188                 _helper.slab(base).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles );
3189 
3190             }
3191         }
3192 
3193         // restore
3194         cairo_restore( context );
3195 
3196     }
3197 
3198     //____________________________________________________________________________________
renderInactiveTab_Plain(cairo_t * context,gint x,gint y,gint w,gint h,GtkPositionType side,const StyleOptions & options,const TabOptions & tabOptions,const AnimationData & data)3199     void Style::renderInactiveTab_Plain(
3200         cairo_t* context,
3201         gint x, gint y, gint w, gint h,
3202         GtkPositionType side,
3203         const StyleOptions& options,
3204         const TabOptions& tabOptions,
3205         const AnimationData& data
3206         )
3207     {
3208         // convenience flags
3209         const bool isFirstTabAligned( tabOptions & FirstTabAligned );
3210         const bool isLastTabAligned( tabOptions & LastTabAligned );
3211 
3212         const bool isLeftOfSelected( tabOptions & LeftOfSelected );
3213         const bool isRightOfSelected( tabOptions & RightOfSelected );
3214 
3215         // get color
3216         const ColorUtils::Rgba base( _settings.palette().color( Palette::Window ) );
3217 
3218         // save context
3219         cairo_save( context );
3220 
3221         // borders and connections to tabs
3222         // this is quite painfull and slipery code.
3223         // the same is true with oxygen-qt
3224         int offset = 2;
3225         int adjust = (tabOptions&Xul) ? 0:2;
3226         const TileSet::Tiles tabTiles( Style::tabTiles( side )  );
3227 
3228         SlabRect tabSlab;
3229         SlabRect::List slabs;
3230         switch( side )
3231         {
3232             case GTK_POS_BOTTOM:
3233             {
3234                 // main slab
3235                 y += adjust; h -= 2*adjust;
3236                 tabSlab = SlabRect( x, y-offset, w, h+10 + offset, tabTiles );
3237                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3238                 if( isLastTabAligned ) { tabSlab._w+=1; }
3239 
3240                 // connections to frame
3241                 SlabRect baseSlab( x-4-1, y+h-1, w+8+2, 10, TileSet::Top );
3242                 if( isFirstTabAligned )
3243                 {
3244                     baseSlab._x += 4; baseSlab._w -= 4; baseSlab._tiles |= TileSet::Left;
3245                     slabs.push_back( SlabRect( x-1, y+h+offset-6, 8, 16, TileSet::Left ) );
3246                 }
3247 
3248                 if( isLastTabAligned )
3249                 {
3250                     baseSlab._w -= 4; baseSlab._tiles |= TileSet::Right;
3251                     slabs.push_back( SlabRect( x+w-7, y+h+offset-6, 8, 16, TileSet::Right ) );
3252                 }
3253 
3254                 if( isLeftOfSelected ) { baseSlab._w += 3; }
3255                 else if( isRightOfSelected ) { baseSlab._x -= 3; baseSlab._w += 4; }
3256                 else { baseSlab._w += 2; }
3257 
3258                 slabs.push_back( baseSlab );
3259                 break;
3260             }
3261 
3262             case GTK_POS_TOP:
3263             {
3264 
3265                 // main slab
3266                 y += adjust; h -= 2*adjust;
3267                 tabSlab = SlabRect( x, y-10, w, h+10+offset, tabTiles );
3268                 if( isFirstTabAligned ) { tabSlab._x-=1; tabSlab._w+=1; }
3269                 if( isLastTabAligned ) { tabSlab._w-=1; }
3270 
3271                 // connections to frame
3272                 SlabRect baseSlab( x-4-1, y-10+1, w+8+2, 10, TileSet::Bottom );
3273                 if( isFirstTabAligned ) { baseSlab._x += 4; baseSlab._w -= 4; baseSlab._tiles |= TileSet::Left; }
3274                 if( isLastTabAligned ) { baseSlab._w -=4; baseSlab._tiles |= TileSet::Right; }
3275                 if( isLeftOfSelected ) { baseSlab._w += 3; }
3276                 else if( isRightOfSelected ) { baseSlab._x -= 3; baseSlab._w += 4; }
3277                 else { baseSlab._w += 2; }
3278 
3279                 slabs.push_back( baseSlab );
3280                 break;
3281             }
3282 
3283             case GTK_POS_RIGHT:
3284             {
3285 
3286                 // main slab
3287                 x += adjust; w -= 2*adjust;
3288                 tabSlab = SlabRect( x-offset, y, w+10+offset, h, tabTiles );
3289                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3290                 if( isLastTabAligned ) { tabSlab._h+=1; }
3291 
3292                 // connections to frame
3293                 SlabRect baseSlab( x+w-1, y-4-1, 10, h+8+3, TileSet::Left );
3294                 if( isFirstTabAligned )
3295                 {
3296                     baseSlab._y += 4; baseSlab._h -= 4; baseSlab._tiles |= TileSet::Top;
3297                     slabs.push_back( SlabRect( x+w+offset-6, y-1, 16, 8, TileSet::Top ) );
3298                 }
3299 
3300                 if( isLastTabAligned )
3301                 {
3302                     baseSlab._h -= 4; baseSlab._tiles |= TileSet::Bottom;
3303                     slabs.push_back( SlabRect( x+w+offset-6, y+h-7, 16, 8, TileSet::Top ) );
3304                 }
3305 
3306                 if( isLeftOfSelected ) { baseSlab._h += 3; }
3307                 else if( isRightOfSelected ) { baseSlab._y -= 3; baseSlab._h += 3; }
3308                 else { baseSlab._h += 1; }
3309 
3310                 slabs.push_back( baseSlab );
3311                 break;
3312             }
3313 
3314 
3315             case GTK_POS_LEFT:
3316             {
3317 
3318                 // main slab
3319                 x += adjust; w -= 2*adjust;
3320                 tabSlab = SlabRect( x-10, y, w+10+offset, h, tabTiles );
3321                 if( isFirstTabAligned ) { tabSlab._y-=1; tabSlab._h+=1; }
3322                 if( isLastTabAligned ) { tabSlab._h+=1; }
3323 
3324                 // connections to frame
3325                 SlabRect baseSlab( x-10+1, y-4-1, 10, h+8+3, TileSet::Right );
3326                 if( isFirstTabAligned )
3327                 {
3328                     baseSlab._y += 4; baseSlab._h -= 4; baseSlab._tiles |= TileSet::Top;
3329                     slabs.push_back( SlabRect( x-10+1 + offset-5, y-1, 16, 8, TileSet::Top ) );
3330                 }
3331 
3332                 if( isLastTabAligned )
3333                 {
3334                     baseSlab._h -= 4; baseSlab._tiles |= TileSet::Bottom;
3335                     slabs.push_back( SlabRect( x-10+1 + offset-5, y+h-7, 16, 8, TileSet::Top ) );
3336                 }
3337 
3338                 if( isLeftOfSelected ) { baseSlab._h += 3; }
3339                 else if( isRightOfSelected ) { baseSlab._y -= 3; baseSlab._h += 3; }
3340                 else { baseSlab._h += 1; }
3341 
3342                 slabs.push_back( baseSlab );
3343                 break;
3344             }
3345 
3346             default:
3347             assert( false );
3348             return;
3349 
3350         }
3351 
3352         const bool isFirstTab( tabOptions & FirstTab );
3353         const bool isLastTab( tabOptions & LastTab );
3354 
3355         const double radius( 5 );
3356         double xF( 0.5 + x );
3357         double yF( 0.5 + y );
3358         double wF( w-1 );
3359         double hF( h-1 );
3360 
3361         switch( side )
3362         {
3363 
3364             case GTK_POS_BOTTOM:
3365             {
3366                 xF += 1.0;
3367                 wF -= 1.0;
3368                 hF += 2;
3369                 if( isLeftOfSelected ) wF += 1;
3370                 else if( isRightOfSelected ) { xF -= 2; wF += 2; }
3371 
3372                 if( isFirstTab )
3373                 {
3374 
3375                     if( isFirstTabAligned ) cairo_move_to( context, xF, yF + hF + 2 );
3376                     else cairo_move_to( context, xF, yF + hF );
3377 
3378                     cairo_line_to( context, xF, yF + radius );
3379                     cairo_arc( context, xF + radius, yF + radius, radius, M_PI, 3.0*M_PI/2 );
3380                     cairo_line_to( context, xF + wF, yF );
3381                     cairo_line_to( context, xF + wF, yF + hF );
3382 
3383                 } else if( isLastTab ) {
3384 
3385                     cairo_move_to( context, xF, yF + hF );
3386                     cairo_line_to( context, xF, yF );
3387                     cairo_line_to( context, xF + wF - radius, yF );
3388                     cairo_arc( context, xF + wF - radius, yF + radius, radius, 3.0*M_PI/2, 2.0*M_PI );
3389                     if( isLastTabAligned ) cairo_line_to( context, xF + wF, yF + hF + 2 );
3390                     else cairo_line_to( context, xF + wF, yF + hF );
3391 
3392                 } else {
3393 
3394                     cairo_move_to( context, xF, yF + hF );
3395                     cairo_line_to( context, xF, yF );
3396                     cairo_line_to( context, xF + wF, yF );
3397                     cairo_line_to( context, xF + wF, yF + hF );
3398 
3399                 }
3400 
3401             }
3402 
3403             break;
3404 
3405             case GTK_POS_TOP:
3406             {
3407                 xF += 1.0;
3408                 wF -= 1.0;
3409                 yF -= 1;
3410                 hF += 1;
3411                 if( isLeftOfSelected ) wF += 1;
3412                 else if( isRightOfSelected ) { xF -= 2; wF += 2; }
3413 
3414 
3415                 if( isFirstTab )
3416                 {
3417 
3418                     cairo_move_to( context, xF+wF, yF );
3419                     cairo_line_to( context, xF+wF, yF + hF );
3420                     cairo_line_to( context, xF+radius, yF + hF );
3421                     cairo_arc( context, xF+radius, yF + hF -radius, radius, M_PI/2, M_PI );
3422                     if( isFirstTabAligned ) cairo_line_to( context, xF, yF - 2 );
3423                     else cairo_line_to( context, xF, yF );
3424 
3425                 } else if( isLastTab ) {
3426 
3427                     if( isLastTabAligned ) cairo_move_to( context, xF+wF, yF-2 );
3428                     else  cairo_move_to( context, xF+wF, yF-2 );
3429                     cairo_line_to( context, xF+wF, yF+hF-radius );
3430                     cairo_arc( context, xF+wF-radius, yF+hF-radius, radius, 0, M_PI/2 );
3431                     cairo_line_to( context, xF, yF+hF );
3432                     cairo_line_to( context, xF, yF );
3433 
3434                 } else {
3435 
3436                     cairo_move_to( context, xF+wF, yF );
3437                     cairo_line_to( context, xF+wF, yF + hF );
3438                     cairo_line_to( context, xF, yF+hF );
3439                     cairo_line_to( context, xF, yF );
3440 
3441                 }
3442 
3443             }
3444 
3445             break;
3446 
3447             case GTK_POS_RIGHT:
3448             {
3449 
3450                 yF += 1.0;
3451                 hF -= 1.0;
3452                 wF += 2;
3453 
3454                 if( isLeftOfSelected ) hF += 1;
3455                 else if( isRightOfSelected ) { yF -= 2; hF += 2; }
3456 
3457                 if( isFirstTab )
3458                 {
3459 
3460                     cairo_move_to( context, xF+wF, yF+hF );
3461                     cairo_line_to( context, xF, yF+hF );
3462                     cairo_line_to( context, xF, yF+radius );
3463                     cairo_arc( context, xF+radius, yF+radius, radius, M_PI, 3.0*M_PI/2 );
3464                     if( isFirstTabAligned ) cairo_line_to( context, xF+wF+2, yF );
3465                     else cairo_line_to( context, xF+wF, yF );
3466 
3467                 } else if( isLastTab ) {
3468 
3469                     if( isLastTabAligned ) cairo_line_to( context, xF + wF + 2, yF + hF );
3470                     else cairo_line_to( context, xF + wF, yF + hF );
3471                     cairo_line_to( context, xF+radius, yF+hF );
3472                     cairo_arc( context, xF+radius, yF+hF - radius, radius, M_PI/2, M_PI );
3473                     cairo_line_to( context, xF, yF );
3474                     cairo_line_to( context, xF + wF, yF );
3475 
3476                 } else {
3477 
3478                     cairo_move_to( context, xF+wF, yF+hF );
3479                     cairo_line_to( context, xF, yF+hF );
3480                     cairo_line_to( context, xF, yF );
3481                     cairo_line_to( context, xF+wF, yF );
3482 
3483                 }
3484             }
3485             break;
3486 
3487             case GTK_POS_LEFT:
3488             {
3489                 yF += 1.0;
3490                 hF -= 1.0;
3491                 xF -= 2;
3492                 wF += 2;
3493 
3494                 if( isLeftOfSelected ) hF += 1;
3495                 else if( isRightOfSelected ) { yF -= 2; hF += 2; }
3496 
3497                 if( isFirstTab )
3498                 {
3499 
3500                     if( isFirstTabAligned ) cairo_move_to( context, xF-2, yF );
3501                     else cairo_move_to( context, xF, yF );
3502                     cairo_line_to( context, xF + wF - radius, yF );
3503                     cairo_arc( context, xF + wF - radius, yF + radius, radius, 3.0*M_PI/2, 2*M_PI );
3504                     cairo_line_to( context, xF+wF, yF+hF );
3505                     cairo_line_to( context, xF, yF+hF );
3506 
3507                 } else if( isLastTab ) {
3508 
3509                     cairo_move_to( context, xF, yF );
3510                     cairo_line_to( context, xF+wF, yF );
3511                     cairo_line_to( context, xF+wF, yF + hF - radius );
3512                     cairo_arc( context, xF+wF-radius, yF + hF - radius, radius, 0, M_PI/2 );
3513                     if( isLastTabAligned ) cairo_line_to( context, xF-2, yF+hF );
3514                     else cairo_line_to( context, xF, yF+hF );
3515 
3516                 } else {
3517 
3518                     cairo_move_to( context, xF, yF );
3519                     cairo_line_to( context, xF+wF, yF );
3520                     cairo_line_to( context, xF+wF, yF+hF );
3521                     cairo_line_to( context, xF, yF+hF );
3522 
3523                 }
3524             }
3525             break;
3526 
3527             default:
3528             assert( false );
3529             return;
3530 
3531         }
3532 
3533         ColorUtils::Rgba backgroundColor( base );
3534 
3535 // TODO: reimplement for gtk3
3536 //         {
3537 //
3538 //             gint wh, wy;
3539 //             Gtk::gdk_map_to_toplevel( window, 0L, &wy, 0L, &wh );
3540 //             if( wh > 0 )
3541 //             {  backgroundColor = ColorUtils::backgroundColor( _settings.palette().color( Palette::Window ), wh, y+wy+h/2 ); }
3542 //
3543 //         }
3544 
3545         const ColorUtils::Rgba midColor( ColorUtils::alphaColor( ColorUtils::darkColor( backgroundColor ), 0.4 ) );
3546         const ColorUtils::Rgba darkColor( ColorUtils::alphaColor( ColorUtils::darkColor( backgroundColor ), 0.8 ) );
3547 
3548         cairo_set_line_width( context, 1.0 );
3549         cairo_set_source( context, midColor );
3550         cairo_fill_preserve( context );
3551 
3552         cairo_set_source( context, darkColor );
3553         cairo_stroke( context );
3554 
3555         ColorUtils::Rgba glow;
3556         if( data._mode == AnimationHover ) glow = ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3557         else if( options&Hover ) glow = _settings.palette().color( Palette::Hover );
3558 
3559         for( SlabRect::List::const_iterator iter = slabs.begin(); iter != slabs.end(); ++iter )
3560         { _helper.slab(base, glow, 0).render( context, iter->_x, iter->_y, iter->_w, iter->_h, iter->_tiles ); }
3561 
3562         // restore
3563         cairo_restore( context );
3564 
3565     }
3566 
3567     //____________________________________________________________________________________
slabShadowColor(const StyleOptions & options,const AnimationData & data) const3568     ColorUtils::Rgba Style::slabShadowColor( const StyleOptions& options, const AnimationData& data ) const
3569     {
3570 
3571         // no glow when widget is disabled
3572         if( options&Disabled ) return ColorUtils::Rgba();
3573 
3574         if( (options&Flat) && !(options&Sunken) )
3575         {
3576 
3577             /*
3578             flat buttons have special handling of colors: hover and focus are both assigned
3579             the hover color. This is consistent with oxygen-qt, though quite inconsistent with
3580             other widgets.
3581             */
3582             if(
3583                 ( data._mode == AnimationHover && (options&Focus) ) ||
3584                 ( data._mode == AnimationFocus && (options&Hover) ) )
3585             {
3586                 return _settings.palette().color( Palette::Focus );
3587 
3588             } else if( data._mode & (AnimationHover|AnimationFocus) ) {
3589 
3590                 return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3591 
3592             } else if( options&(Focus|Hover) ) {
3593 
3594                 return _settings.palette().color( Palette::Focus );
3595 
3596             } else return ColorUtils::Rgba();
3597 
3598         } else if( data._mode == AnimationHover ) {
3599 
3600             if( options & Focus )
3601             {
3602                 return ColorUtils::mix(
3603                     _settings.palette().color( Palette::Focus ),
3604                     _settings.palette().color( Palette::Hover ), data._opacity );
3605 
3606             } else {
3607 
3608                 return ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3609 
3610             }
3611 
3612         } else if( options&Hover ) {
3613 
3614             return _settings.palette().color( Palette::Hover );
3615 
3616         } else if( data._mode == AnimationFocus ) {
3617 
3618             return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3619 
3620         } else if( options&Focus ) {
3621 
3622             return _settings.palette().color( Palette::Focus );
3623 
3624         } else return ColorUtils::Rgba();
3625 
3626     }
3627 
3628     //____________________________________________________________________________________
holeShadowColor(const StyleOptions & options,const AnimationData & data) const3629     ColorUtils::Rgba Style::holeShadowColor( const StyleOptions& options, const AnimationData& data ) const
3630     {
3631 
3632         // no glow when widget is disabled
3633         if( options&Disabled ) return ColorUtils::Rgba();
3634 
3635         if( data._mode == AnimationFocus && data._opacity >= 0 )
3636         {
3637 
3638             if( options & Hover )
3639             {
3640 
3641                 return ColorUtils::mix(
3642                     _settings.palette().color( Palette::Hover ),
3643                     _settings.palette().color( Palette::Focus ), data._opacity );
3644 
3645             } else return ColorUtils::alphaColor( _settings.palette().color( Palette::Focus ), data._opacity );
3646 
3647         } else if( options & Focus ) {
3648 
3649             return _settings.palette().color( Palette::Focus );
3650 
3651         } else if( data._mode == AnimationHover && data._opacity >= 0 ) {
3652 
3653             return ColorUtils::alphaColor( _settings.palette().color( Palette::Hover ), data._opacity );
3654 
3655         } else if( options & Hover ) {
3656 
3657             return _settings.palette().color( Palette::Hover );
3658 
3659         } else return ColorUtils::Rgba();
3660 
3661     }
3662 
3663     //__________________________________________________________________
renderGroupBox(cairo_t * context,const ColorUtils::Rgba & base,gint x,gint y,gint w,gint h,const StyleOptions & options)3664     void Style::renderGroupBox(
3665         cairo_t* context,
3666         const ColorUtils::Rgba& base,
3667         gint x, gint y, gint w, gint h,
3668         const StyleOptions& options )
3669     {
3670 
3671         cairo_push_group( context );
3672         Cairo::Pattern pattern( cairo_pattern_create_linear( 0, y - h + 12, 0,  y + 2*h - 19 ) );
3673         const ColorUtils::Rgba light( ColorUtils::lightColor( base ) );
3674         cairo_pattern_add_color_stop( pattern, 0, ColorUtils::alphaColor( light, 0.4 ) );
3675         cairo_pattern_add_color_stop( pattern, 1, ColorUtils::Rgba::transparent( light ) );
3676         cairo_set_source( context, pattern );
3677 
3678         _helper.fillSlab( context, x, y, w, h );
3679 
3680         if( !(options&NoFill) )
3681         { _helper.slope( base, 0.0 ).render( context, x, y, w, h ); }
3682 
3683         cairo_pop_group_to_source( context );
3684 
3685         Cairo::Pattern mask( cairo_pattern_create_linear( 0,  y + h - 19, 0,  y + h ) );
3686         cairo_pattern_add_color_stop( mask, 0, ColorUtils::Rgba::black() );
3687         cairo_pattern_add_color_stop( mask, 1.0, ColorUtils::Rgba::transparent() );
3688         cairo_mask( context, mask );
3689 
3690         return;
3691 
3692     }
3693 
3694     //__________________________________________________________________
renderSlab(cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & base,const StyleOptions & options,const AnimationData & animationData,TileSet::Tiles tiles)3695     void Style::renderSlab(
3696         cairo_t* context,
3697         gint x, gint y, gint w, gint h,
3698         const ColorUtils::Rgba& base,
3699         const StyleOptions& options,
3700         const AnimationData& animationData,
3701         TileSet::Tiles tiles )
3702     {
3703 
3704         // do nothing if not enough room
3705         if( w<14 || h<14 ) return;
3706 
3707         // additional adjustment for sunken frames
3708         // TODO: double check
3709         if( options & Sunken)
3710         {
3711 
3712             x -= 1;
3713             w += 2;
3714             h += 2;
3715 
3716         }
3717 
3718         // fill
3719         if( !(options & NoFill))
3720         {
3721 
3722             Cairo::Pattern pattern;
3723             const ColorUtils::Rgba shadow( ColorUtils::shadowColor( base ) );
3724             if( shadow.value() > base.value() && (options & Sunken) )
3725             {
3726 
3727                 pattern.set( cairo_pattern_create_linear( 0, y, 0, y+2*h ) );
3728                 cairo_pattern_add_color_stop( pattern, 0, base );
3729                 cairo_pattern_add_color_stop( pattern, 1.0, ColorUtils::lightColor( base ) );
3730 
3731             } else {
3732 
3733                 pattern.set( cairo_pattern_create_linear( 0, y-h, 0, y+h ) );
3734                 cairo_pattern_add_color_stop( pattern, 0, ColorUtils::lightColor( base ) );
3735                 cairo_pattern_add_color_stop( pattern, 1.0, base );
3736 
3737             }
3738 
3739             cairo_set_source( context, pattern );
3740             _helper.fillSlab( context, x, y, w, h, tiles );
3741 
3742         }
3743 
3744         if( !(options&Sunken) ) {
3745 
3746             // calculate glow color
3747             const TileSet* tile;
3748             const ColorUtils::Rgba glow( slabShadowColor( options, animationData ) );
3749             if( glow.isValid() || base.isValid() ) tile = &_helper.slab( base, glow , 0);
3750             else return;
3751 
3752             if( tile ) tile->render( context, x, y, w, h );
3753 
3754         } else if( base.isValid() ) {
3755 
3756             _helper.slabSunken( base ).render( context, x, y, w, h );
3757 
3758         }
3759 
3760     }
3761 
3762     //__________________________________________________________________
renderScrollBarHole(cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & base,bool vertical,TileSet::Tiles tiles)3763     void Style::renderScrollBarHole( cairo_t* context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& base, bool vertical, TileSet::Tiles tiles )
3764     {
3765 
3766         // use tileset from helper
3767         _helper.scrollHole( base, vertical ).render( context, x, y, w, h, tiles );
3768 
3769     }
3770 
3771     //__________________________________________________________________
genericArrow(GtkArrowType orientation,QtSettings::ArrowSize size) const3772     Polygon Style::genericArrow( GtkArrowType orientation, QtSettings::ArrowSize size ) const
3773     {
3774 
3775         Polygon a;
3776         switch( orientation )
3777         {
3778 
3779             case GTK_ARROW_UP:
3780             {
3781                 if( size == QtSettings::ArrowTiny ) a << Point( -1.75, 1.125 ) << Point( 0.5, -1.125 ) << Point( 2.75, 1.125 );
3782                 else if( size == QtSettings::ArrowSmall ) a << Point( -2,1.5 ) << Point( 0.5, -1.5 ) << Point( 3,1.5 );
3783                 else a << Point( -3,2.5 ) << Point( 0.5, -1.5 ) << Point( 4,2.5 );
3784                 break;
3785             }
3786 
3787             case GTK_ARROW_DOWN:
3788             {
3789                 if( size == QtSettings::ArrowTiny ) a << Point( -1.75, -1.125 ) << Point( 0.5, 1.125 ) << Point( 2.75, -1.125 );
3790                 else if( size == QtSettings::ArrowSmall ) a << Point( -2,-1.5 ) << Point( 0.5, 1.5 ) << Point( 3,-1.5 );
3791                 else a << Point( -3,-1.5 ) << Point( 0.5, 2.5 ) << Point( 4,-1.5 );
3792                 break;
3793             }
3794 
3795             case GTK_ARROW_LEFT:
3796             {
3797                 if( size == QtSettings::ArrowTiny ) a << Point( 1.125, -1.75 ) << Point( -1.125, 0.5 ) << Point( 1.125, 2.75 );
3798                 else if( size == QtSettings::ArrowSmall ) a << Point( 1.5,-2 ) << Point( -1.5, 0.5 ) << Point( 1.5,3 );
3799                 else a << Point( 2.5,-3 ) << Point( -1.5, 0.5 ) << Point( 2.5,4 );
3800                 break;
3801             }
3802 
3803             case GTK_ARROW_RIGHT:
3804             {
3805                 if( size == QtSettings::ArrowTiny ) a << Point( -1.125, -1.75 ) << Point( 1.125, 0.5 ) << Point( -1.125, 2.75 );
3806                 else if( size == QtSettings::ArrowSmall ) a << Point( -1.5,-2 ) << Point( 1.5, 0.5 ) << Point( -1.5,3 );
3807                 else a << Point( -1.5,-3 ) << Point( 2.5, 0.5 ) << Point( -1.5,4 );
3808                 break;
3809             }
3810 
3811             default: break;
3812 
3813         }
3814 
3815         return a;
3816 
3817 
3818 
3819     }
3820 
3821     //__________________________________________________________________
renderWindowDots(cairo_t * context,gint x,gint y,gint w,gint h,const ColorUtils::Rgba & color,WinDeco::Options wopt)3822     void Style::renderWindowDots(cairo_t* context, gint x, gint y, gint w, gint h, const ColorUtils::Rgba& color, WinDeco::Options wopt)
3823     {
3824         bool isMaximized( wopt & WinDeco::Maximized );
3825         bool hasAlpha( wopt & WinDeco::Alpha );
3826         int offset( hasAlpha ? 0 : -1 );
3827         if( _settings.frameBorder() >= QtSettings::BorderTiny )
3828         {
3829             if( !isMaximized )
3830             {
3831                 // Draw right side 3-dots resize handles
3832                 int cenY = int(h/2+y);
3833                 int posX = int(w+x-3) + 1;
3834                 _helper.renderDot(context,color,posX+offset, cenY-3);
3835                 _helper.renderDot(context,color,posX+offset, cenY);
3836                 _helper.renderDot(context,color,posX+offset, cenY+3);
3837             }
3838 
3839             // Draw bottom-right corner 3-dots resize handles
3840             // if( !config.drawSizeGrip )
3841             {
3842                 cairo_save(context);
3843                 cairo_translate(context,x+w-8,y+h-8);
3844                 _helper.renderDot(context,color,2+offset,6+offset);
3845                 _helper.renderDot(context,color,5+offset,5+offset);
3846                 _helper.renderDot(context,color,6+offset,2+offset);
3847                 cairo_restore(context);
3848             }
3849         }
3850     }
3851 
3852     //__________________________________________________________________
centerRect(GdkRectangle * parent,GdkRectangle * child) const3853     void Style::centerRect( GdkRectangle* parent, GdkRectangle* child ) const
3854     {
3855         if( !( parent && child ) ) return;
3856         child->x = parent->x + (parent->width - child->width)/2;
3857         child->y = parent->y + (parent->height - child->height)/2;
3858         return;
3859     }
3860 
3861     //__________________________________________________________________
generateGapMask(cairo_t * context,gint x,gint y,gint w,gint h,const Gtk::Gap & gap) const3862     void Style::generateGapMask( cairo_t* context, gint x, gint y, gint w, gint h, const Gtk::Gap& gap ) const
3863     {
3864 
3865         if( gap.width() <= 0 ) return;
3866 
3867         // store current rect in
3868         GdkRectangle mask( Gtk::gdk_rectangle() );
3869 
3870         switch( gap.position() )
3871         {
3872             case GTK_POS_TOP:
3873             {
3874                 mask = Gtk::gdk_rectangle( x+gap.x(), y, gap.width(), gap.height() );
3875                 break;
3876             }
3877 
3878             case GTK_POS_BOTTOM:
3879             {
3880                 mask = Gtk::gdk_rectangle( x+gap.x(), y+h-gap.height(), gap.width(), gap.height() );
3881                 break;
3882             }
3883 
3884             case GTK_POS_LEFT:
3885             {
3886                 mask = Gtk::gdk_rectangle( x, y+gap.x(), gap.height(), gap.width() );
3887                 break;
3888             }
3889 
3890             case GTK_POS_RIGHT:
3891             {
3892                 mask = Gtk::gdk_rectangle( x + w - gap.height(), y+gap.x(), gap.height(), gap.width() );
3893                 break;
3894             }
3895 
3896             default: return;
3897         }
3898 
3899         if( false )
3900         {
3901             cairo_set_source( context, ColorUtils::Rgba( 1, 0, 0, 0.3 ) );
3902             gdk_cairo_rectangle( context, &mask );
3903             cairo_fill( context );
3904         }
3905 
3906         cairo_rectangle( context, x, y, w, h );
3907         cairo_rectangle_negative( context, mask.x, mask.y, mask.width, mask.height );
3908         cairo_clip( context );
3909 
3910         return;
3911 
3912     }
3913 
3914     //_________________________________________________________
fileChanged(GFileMonitor *,GFile * file,GFile *,GFileMonitorEvent event,gpointer data)3915     void Style::fileChanged( GFileMonitor*, GFile* file, GFile*, GFileMonitorEvent event, gpointer data )
3916     {
3917 
3918         #if OXYGEN_DEBUG
3919         std::cerr << "Oxygen::Style::fileChanged -"
3920             << " file: " << g_file_get_path( file )
3921             << " event: " << Gtk::TypeNames::fileMonitorEvent( event )
3922             << std::endl;
3923         #endif
3924 
3925         Style& style( *static_cast<Style*>( data ) );
3926         if( style.initialize( QtSettings::All|QtSettings::Forced ) )
3927         { gtk_style_context_reset_widgets( gdk_screen_get_default() ); }
3928 
3929     }
3930 
3931     //_________________________________________________________
adjustMask(GtkWidget * widget,int width,int height,bool alpha)3932     void Style::adjustMask( GtkWidget* widget, int width, int height, bool alpha )
3933     {
3934 
3935         // get window and decide offset
3936         GdkWindow* window(0);
3937         int verticalMaskOffset(0);
3938         if( GTK_IS_MENU( widget ) )
3939         {
3940 
3941             window = gtk_widget_get_parent_window( widget );
3942             verticalMaskOffset=Oxygen::Menu_VerticalOffset;
3943 
3944         } else {
3945 
3946             window=gtk_widget_get_window( widget );
3947 
3948         }
3949 
3950         // adjust mask
3951         if(!alpha)
3952         {
3953 
3954             Cairo::Region mask( Style::instance().helper().roundMask( width, height - 2*verticalMaskOffset ) );
3955             gdk_window_shape_combine_region( window, mask, 0, verticalMaskOffset );
3956 
3957         } else {
3958 
3959             gdk_window_shape_combine_region( window, NULL, 0, 0 );
3960 
3961         }
3962 
3963     }
3964 
3965     //_________________________________________________________
setWindowBlur(GtkWidget * widget,bool enable)3966     void Style::setWindowBlur( GtkWidget* widget, bool enable )
3967     {
3968 
3969         #ifdef GDK_WINDOWING_X11
3970         GdkWindow* window( 0L );
3971         if( GTK_IS_MENU( widget ) )
3972         {
3973 
3974             window = gtk_widget_get_parent_window( widget );
3975 
3976         } else {
3977 
3978             window=gtk_widget_get_window( widget );
3979 
3980         }
3981 
3982         // Make whole window blurred
3983         // FIXME: should roundedness be taken into account?
3984         const int w( gdk_window_get_width( window ) );
3985         const int h( gdk_window_get_height( window ) );
3986         const unsigned long rects[4] = { 0, 0, (unsigned long)w, (unsigned long)h };
3987         const XID id( GDK_WINDOW_XID( window ) );
3988 
3989         // get display and check backend
3990         GdkDisplay *display( gdk_window_get_display( window ) );
3991         if( display && GDK_IS_X11_DISPLAY( display )  )
3992         {
3993 
3994             if(enable)
3995             {
3996 
3997                 XChangeProperty( GDK_DISPLAY_XDISPLAY( display ), id, _blurAtom, XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<const unsigned char*>(rects), 4 );
3998 
3999             } else XDeleteProperty( GDK_DISPLAY_XDISPLAY( display ), id, _blurAtom );
4000 
4001         }
4002 
4003         #endif
4004 
4005     }
4006 
4007 }
4008