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