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 <gtk/gtk.h> 23 #include "oxygeninnershadowdata.h" 24 #include "../oxygengtkutils.h" 25 #include "../config.h" 26 #include "../oxygencairocontext.h" 27 #include "../oxygencairoutils.h" 28 #include "oxygenanimations.h" 29 #include "../oxygenstyle.h" 30 #include "../oxygenmetrics.h" 31 #include <stdlib.h> 32 33 #include <cassert> 34 #include <iostream> 35 36 namespace Oxygen 37 { 38 39 //_____________________________________________ connect(GtkWidget * widget)40 void InnerShadowData::connect( GtkWidget* widget ) 41 { 42 43 assert( GTK_IS_SCROLLED_WINDOW( widget ) ); 44 assert( !_target ); 45 46 // store target 47 _target = widget; 48 49 if( gdk_display_supports_composite( gtk_widget_get_display( widget ) ) ) 50 { _exposeId.connect( G_OBJECT(_target), "draw", G_CALLBACK( targetExposeEvent ), this, true ); } 51 52 // check child 53 GtkWidget* child( gtk_bin_get_child( GTK_BIN( widget ) ) ); 54 if( !child ) return; 55 56 #if OXYGEN_DEBUG 57 std::cerr 58 << "Oxygen::InnerShadowData::connect -" 59 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 60 << " child: " << child << " (" << G_OBJECT_TYPE_NAME( child ) << ")" 61 << std::endl; 62 #endif 63 64 registerChild( child ); 65 66 } 67 68 //_____________________________________________ disconnect(GtkWidget *)69 void InnerShadowData::disconnect( GtkWidget* ) 70 { 71 _target = 0; 72 for( ChildDataMap::reverse_iterator iter = _childrenData.rbegin(); iter != _childrenData.rend(); ++iter ) 73 { iter->second.disconnect( iter->first ); } 74 75 // disconnect signals 76 _exposeId.disconnect(); 77 78 // clear child data 79 _childrenData.clear(); 80 } 81 82 //_____________________________________________ registerChild(GtkWidget * widget)83 void InnerShadowData::registerChild( GtkWidget* widget ) 84 { 85 86 #if ENABLE_INNER_SHADOWS_HACK 87 88 // check widget 89 if( !GTK_IS_WIDGET( widget ) ) return; 90 91 // make sure widget is not already in map 92 if( _childrenData.find( widget ) != _childrenData.end() ) return; 93 94 if( gtk_scrolled_window_get_shadow_type( GTK_SCROLLED_WINDOW( _target ) ) != GTK_SHADOW_IN ) 95 { return; } 96 97 #if OXYGEN_DEBUG 98 std::cerr 99 << "Oxygen::InnerShadowData::registerChild -" 100 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 101 << std::endl; 102 #endif 103 104 GdkWindow* window(gtk_widget_get_window(widget)); 105 if( 106 window && gdk_window_get_window_type( window ) == GDK_WINDOW_CHILD && 107 gdk_display_supports_composite( gtk_widget_get_display( widget ) ) ) 108 { 109 ChildData data; 110 data._unrealizeId.connect( G_OBJECT(widget), "unrealize", G_CALLBACK( childUnrealizeNotifyEvent ), this ); 111 data._initiallyComposited = gdk_window_get_composited(window); 112 gdk_window_set_composited(window,TRUE); 113 _childrenData.insert( std::make_pair( widget, data ) ); 114 } 115 116 #endif 117 118 } 119 120 //________________________________________________________________________________ unregisterChild(GtkWidget * widget)121 void InnerShadowData::unregisterChild( GtkWidget* widget ) 122 { 123 #if ENABLE_INNER_SHADOWS_HACK 124 125 ChildDataMap::iterator iter( _childrenData.find( widget ) ); 126 if( iter == _childrenData.end() ) return; 127 128 #if OXYGEN_DEBUG 129 std::cerr 130 << "Oxygen::InnerShadowData::unregisterChild -" 131 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 132 << std::endl; 133 #endif 134 135 iter->second.disconnect( widget ); 136 _childrenData.erase( iter ); 137 138 #endif 139 } 140 141 //________________________________________________________________________________ disconnect(GtkWidget * widget)142 void InnerShadowData::ChildData::disconnect( GtkWidget* widget ) 143 { 144 #if ENABLE_INNER_SHADOWS_HACK 145 146 // disconnect signals 147 _unrealizeId.disconnect(); 148 149 // remove compositing flag 150 GdkWindow* window( gtk_widget_get_window( widget ) ); 151 152 #if OXYGEN_DEBUG 153 std::cerr 154 << "Oxygen::InnerShadowData::ChildData::disconnect -" 155 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 156 << " window: " << window 157 << std::endl; 158 #endif 159 160 // restore compositing if different from initial state 161 if( GDK_IS_WINDOW( window ) && !gdk_window_is_destroyed(window) && gdk_window_get_composited( window ) != _initiallyComposited ) 162 { gdk_window_set_composited( window, _initiallyComposited ); } 163 164 #endif 165 } 166 167 //____________________________________________________________________________________________ childUnrealizeNotifyEvent(GtkWidget * widget,gpointer data)168 gboolean InnerShadowData::childUnrealizeNotifyEvent( GtkWidget* widget, gpointer data ) 169 { 170 #if OXYGEN_DEBUG 171 std::cerr 172 << "Oxygen::InnerShadowData::childUnrealizeNotifyEvent -" 173 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 174 << std::endl; 175 #endif 176 static_cast<InnerShadowData*>(data)->unregisterChild( widget ); 177 return FALSE; 178 } 179 180 //_________________________________________________________________________________________ targetExposeEvent(GtkWidget * widget,cairo_t * context,gpointer)181 gboolean InnerShadowData::targetExposeEvent( GtkWidget* widget, cairo_t *context, gpointer ) 182 { 183 184 #if ENABLE_INNER_SHADOWS_HACK 185 GtkWidget* child( gtk_bin_get_child( GTK_BIN( widget ) ) ); 186 GdkWindow* childWindow( gtk_widget_get_window( child ) ); 187 188 #if OXYGEN_DEBUG 189 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent -" 190 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME(widget) << ")" 191 << " child: " << child << " (" << G_OBJECT_TYPE_NAME(child) << ")" 192 << " path: " << Gtk::gtk_widget_path( child ) 193 << std::endl; 194 #endif 195 196 if( !gdk_window_get_composited( childWindow ) ) 197 { 198 #if OXYGEN_DEBUG 199 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent - Window isn't composite. Doing nohing\n"; 200 #endif 201 return FALSE; 202 } 203 204 // make sure the child window doesn't contain garbage 205 gdk_window_process_updates( childWindow, TRUE ); 206 207 GtkAllocation allocation; 208 gtk_widget_translate_coordinates (child, widget, 0, 0, &allocation.x, &allocation.y); 209 allocation.width = gdk_window_get_width (childWindow); 210 allocation.height = gdk_window_get_height (childWindow); 211 212 // draw child 213 gdk_cairo_rectangle( context, &allocation ); 214 cairo_clip(context); 215 gdk_cairo_set_source_window( context, childWindow, allocation.x, allocation.y ); 216 cairo_paint(context); 217 218 #if OXYGEN_DEBUG_INNERSHADOWS 219 // Show updated parts in random color 220 cairo_rectangle( context, allocation.x, allocation.y, allocation.width, allocation.height ); 221 double red= ((double)rand())/RAND_MAX; 222 double green=((double)rand())/RAND_MAX; 223 double blue=((double)rand())/RAND_MAX; 224 cairo_set_source_rgba(context,red,green,blue,0.5); 225 cairo_fill(context); 226 #endif 227 228 // Render rounded combobox list child 229 if(Gtk::gtk_combobox_is_tree_view( child )) 230 { 231 StyleOptions options( widget, gtk_widget_get_state_flags( widget ) ); 232 Corners corners(CornersAll); 233 234 // check scrollbar visibility to decide corners 235 GtkScrolledWindow* scrolledWindow( GTK_SCROLLED_WINDOW( widget ) ); 236 if( gtk_widget_get_mapped( gtk_scrolled_window_get_vscrollbar( scrolledWindow ) ) ) 237 { 238 if(Gtk::gtk_widget_layout_is_reversed( widget )) corners &= ~CornersLeft; 239 else corners &= ~CornersRight; 240 } 241 242 if( gtk_widget_get_mapped( gtk_scrolled_window_get_hscrollbar( scrolledWindow ) ) ) 243 { corners &= ~CornersBottom; } 244 245 int x(allocation.x),y(allocation.y),w(allocation.width),h(allocation.height); 246 cairo_rectangle(context,x,y,w,h); 247 if(!Gtk::gdk_default_screen_is_composited()) 248 { 249 // Take ugly shadow into account 250 x+=1; 251 y+=1; 252 w-=2; 253 h-=2; 254 } 255 cairo_rounded_rectangle_negative(context,x,y,w,h,2,corners); 256 cairo_clip(context); 257 258 Style::instance().renderMenuBackground( context, allocation.x,allocation.y,allocation.width,allocation.height, options ); 259 260 // Event handling finished, now let the event propagate 261 return FALSE; 262 } 263 264 // we only draw SHADOW_IN here 265 if( gtk_scrolled_window_get_shadow_type( GTK_SCROLLED_WINDOW( widget ) ) != GTK_SHADOW_IN ) 266 { 267 #if OXYGEN_DEBUG 268 std::cerr << "Oxygen::InnerShadowData::targetExposeEvent - Shadow type isn't GTK_SHADOW_IN, so not drawing the shadow in expose-event handler\n"; 269 #endif 270 return FALSE; 271 272 } 273 274 // draw the shadow 275 int basicOffset=2; 276 277 StyleOptions options( widget, gtk_widget_get_state_flags( widget ) ); 278 options|=NoFill; 279 options &= ~(Hover|Focus); 280 if( Style::instance().animations().scrolledWindowEngine().contains( widget ) ) 281 { 282 if( Style::instance().animations().scrolledWindowEngine().focused( widget ) ) options |= Focus; 283 if( Style::instance().animations().scrolledWindowEngine().hovered( widget ) ) options |= Hover; 284 } 285 286 const AnimationData data( Style::instance().animations().widgetStateEngine().get( widget, options, AnimationHover|AnimationFocus, AnimationFocus ) ); 287 288 int offsetX = basicOffset+Entry_SideMargin; 289 int offsetY = basicOffset; 290 291 // also need to correct from widget's margins 292 #if GTK_CHECK_VERSION( 3, 11, 0 ) 293 const int marginX = gtk_widget_get_margin_start( child ); 294 const int marginW = marginX + gtk_widget_get_margin_end( child ); 295 #else 296 const int marginX = gtk_widget_get_margin_left( child ); 297 const int marginW = marginX + gtk_widget_get_margin_right( child ); 298 #endif 299 300 const int marginY = gtk_widget_get_margin_top( child ); 301 const int marginH = marginY + gtk_widget_get_margin_bottom( child ); 302 303 // hole background 304 Style::instance().renderHoleBackground( context, 305 gtk_widget_get_window(widget), widget, 306 allocation.x - offsetX - marginX, 307 allocation.y - offsetY - marginY, 308 allocation.width + offsetX*2 + marginW, 309 allocation.height + offsetY*2 + marginH ); 310 311 // adjust offset and render hole 312 offsetX -= Entry_SideMargin; 313 Style::instance().renderHole( context, 314 allocation.x - offsetX - marginX, 315 allocation.y - offsetY - marginY, 316 allocation.width + offsetX*2 + marginW, 317 allocation.height + offsetY*2 + marginH, 318 options, data ); 319 320 #endif // enable inner shadows hack 321 322 // let the event propagate 323 return FALSE; 324 } 325 326 } 327