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 * Hook-setup code provided by Ruslan 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or( at your option ) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free 20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 21 * MA 02110-1301, USA. 22 */ 23 24 #include "oxygenwidgetlookup.h" 25 #include "config.h" 26 27 #include <iostream> 28 #include <cairo/cairo-gobject.h> 29 30 namespace Oxygen 31 { 32 33 //__________________________________________________________________ WidgetLookup(void)34 WidgetLookup::WidgetLookup( void ): 35 _hooksInitialized( false ), 36 _context( 0L ), 37 _widget( 0L ) 38 {} 39 40 41 //_____________________________________________________ ~WidgetLookup(void)42 WidgetLookup::~WidgetLookup( void ) 43 { 44 45 // disconnect hooks 46 _drawHook.disconnect(); 47 48 } 49 50 //_____________________________________________________ initializeHooks(void)51 void WidgetLookup::initializeHooks( void ) 52 { 53 54 #if OXYGEN_DEBUG 55 std::cerr << "Oxygen::WidgetLookup::initializeHooks" << std::endl; 56 #endif 57 58 if( _hooksInitialized ) return; 59 60 // install hook and test 61 if( !_drawHook.connect( "draw", (GSignalEmissionHook)drawHook, this ) ) return; 62 63 // set initialization flag 64 _hooksInitialized = true; 65 66 return; 67 68 } 69 70 //_____________________________________________________ find(cairo_t * context,const GtkWidgetPath * path) const71 GtkWidget* WidgetLookup::find( cairo_t* context, const GtkWidgetPath* path ) const 72 { 73 74 // check path 75 if( !path ) return 0L; 76 77 // get length and check 78 const gint length( gtk_widget_path_length( path ) ); 79 if( length < 1 ) return 0L; 80 81 // lookup last type 82 return find( context, gtk_widget_path_iter_get_object_type( path, length-1 ) ); 83 84 } 85 86 //_____________________________________________________ find(cairo_t * context,GType type) const87 GtkWidget* WidgetLookup::find( cairo_t* context, GType type ) const 88 { 89 // check context 90 if( context != _context ) 91 { 92 93 if( GTK_IS_WIDGET( _widget ) && G_OBJECT_TYPE( _widget ) == type && GTK_IS_SCROLLED_WINDOW( gtk_widget_get_parent( _widget ) ) ) 94 { 95 96 // FIXME: this is very fragile 97 // in latest gtk3 versions, scrolled window children do not get the right context passed when drawn, with respect to what is 98 // sent by the "draw" signal. Hence the context does not match. 99 // Assuming this is the case here, we return the latest widget nonetheless 100 return _widget; 101 102 } else { 103 104 #if OXYGEN_DEBUG 105 std::cerr 106 << "Oxygen::WidgetLookup::find -" 107 << " context: " << context 108 << " type: " << g_type_name( type ) 109 << " invalid context." 110 << std::endl; 111 #endif 112 return 0L; 113 114 } 115 116 } 117 118 #if OXYGEN_DEBUG 119 std::cerr 120 << "Oxygen::WidgetLookup::find -" 121 << " context: " << context 122 << " type: " << g_type_name( type ) 123 << std::endl; 124 #endif 125 126 // look for type in stored widgets 127 /* we loop backward, since last added widgets are more likely to be looked up */ 128 for( WidgetList::const_reverse_iterator iter = _widgets.rbegin(); iter != _widgets.rend(); ++iter ) 129 { 130 // compare types and return if matched 131 if( G_OBJECT_TYPE( *iter ) == type ) 132 { 133 #if OXYGEN_DEBUG 134 std::cerr 135 << "Oxygen::WidgetLookup::find -" 136 << " context: " << context 137 << " type: " << g_type_name( type ) 138 << " found: " << *iter 139 << std::endl; 140 #endif 141 return *iter; 142 } 143 } 144 145 #if OXYGEN_DEBUG 146 std::cerr 147 << "Oxygen::WidgetLookup::find -" 148 << " context: " << context 149 << " type: " << g_type_name( type ) 150 << " - no match found" 151 << std::endl; 152 #endif 153 154 return 0L; 155 156 } 157 158 //_____________________________________________________ bind(GtkWidget * widget,cairo_t * context)159 void WidgetLookup::bind( GtkWidget* widget, cairo_t* context ) 160 { 161 // check if context has changed and clear widgets if yes 162 if( context != _context ) 163 { 164 165 #if OXYGEN_DEBUG 166 std::cerr 167 << "Oxygen::WidgetLookup::bind -" 168 << " context: " << _context 169 << " widgets: " << _widgets.size() 170 << std::endl; 171 #endif 172 173 _context = context; 174 _widgets.clear(); 175 } 176 177 _widgets.push_back( widget ); 178 _widget = widget; 179 180 // add to all widgets map 181 if( _allWidgets.find( widget ) == _allWidgets.end() ) 182 { 183 Signal destroyId; 184 destroyId.connect( G_OBJECT( widget ), "destroy", G_CALLBACK( destroyNotifyEvent ), this ); 185 _allWidgets.insert( std::make_pair( widget, destroyId ) ); 186 } 187 188 } 189 190 //____________________________________________________________________________________________ unregisterWidget(GtkWidget * widget)191 void WidgetLookup::unregisterWidget( GtkWidget* widget ) 192 { 193 194 #if OXYGEN_DEBUG 195 std::cerr << "Oxygen::WidgetLookup::unregisterWidget - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl; 196 #endif 197 198 // find in map 199 WidgetMap::iterator iter( _allWidgets.find( widget ) ); 200 assert( iter != _allWidgets.end() ); 201 202 // disconnect signal 203 iter->second.disconnect(); 204 205 // erase from lists 206 _allWidgets.erase( widget ); 207 _widgets.remove( widget ); 208 209 // also clean direct widget 210 if( _widget == widget ) { _widget = 0L; } 211 212 } 213 214 //_____________________________________________________ drawHook(GSignalInvocationHint *,guint numParams,const GValue * params,gpointer data)215 gboolean WidgetLookup::drawHook( GSignalInvocationHint*, guint numParams, const GValue* params, gpointer data ) 216 { 217 218 // check number of parameters 219 if( numParams < 2 ) return FALSE; 220 221 // get widget from params 222 GtkWidget* widget( GTK_WIDGET( g_value_get_object( params ) ) ); 223 224 // check type 225 if( !GTK_IS_WIDGET( widget ) ) return FALSE; 226 227 // check second parameter type. 228 if( !G_VALUE_HOLDS( params+1, CAIRO_GOBJECT_TYPE_CONTEXT ) ) 229 { return FALSE; } 230 231 // retrieve context and cast 232 cairo_t* context( static_cast<cairo_t*>( g_value_get_boxed(params+1) ) ); 233 234 #if OXYGEN_DEBUG 235 std::cerr 236 << "Oxygen::WidgetLookup::drawHook -" 237 << " widget: " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 238 << " context: " << context 239 << std::endl; 240 #endif 241 242 // bind widget and context 243 static_cast<WidgetLookup*>( data )->bind( widget, context ); 244 245 return TRUE; 246 247 } 248 249 250 //____________________________________________________________________________________________ destroyNotifyEvent(GtkWidget * widget,gpointer data)251 gboolean WidgetLookup::destroyNotifyEvent( GtkWidget* widget, gpointer data ) 252 { 253 static_cast<WidgetLookup*>(data)->unregisterWidget( widget ); 254 return FALSE; 255 } 256 257 } 258