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