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 "oxygenscrolledwindowdata.h"
23 #include "../oxygengtkutils.h"
24 #include "../config.h"
25 #include "../oxygencairocontext.h"
26 #include "oxygenanimations.h"
27 #include "../oxygenstyle.h"
28 
29 #include <cassert>
30 #include <iostream>
31 
32 namespace Oxygen
33 {
34 
35     //_____________________________________________
connect(GtkWidget * widget)36     void ScrolledWindowData::connect( GtkWidget* widget )
37     {
38         assert( GTK_IS_SCROLLED_WINDOW( widget ) );
39         assert( !_target );
40 
41         // store target
42         _target = widget;
43 
44         // register scrollbars
45         GtkScrolledWindow* scrolledWindow( GTK_SCROLLED_WINDOW( widget ) );
46 
47         if( GtkWidget* hScrollBar = gtk_scrolled_window_get_hscrollbar( scrolledWindow ) )
48         { registerChild( hScrollBar ); }
49 
50         if( GtkWidget* vScrollBar = gtk_scrolled_window_get_vscrollbar( scrolledWindow ) )
51         { registerChild( vScrollBar ); }
52 
53         // check child
54         GtkWidget* child( gtk_bin_get_child( GTK_BIN( widget ) ) );
55         if( !child ) return;
56 
57         #if OXYGEN_DEBUG
58         std::cerr
59             << "Oxygen::ScrolledWindowData::connect -"
60             << " child: " << child << " (" << G_OBJECT_TYPE_NAME( child ) << ")"
61             << std::endl;
62         #endif
63 
64         if( GTK_IS_TREE_VIEW( child ) || GTK_IS_TEXT_VIEW( child ) || GTK_IS_ICON_VIEW( child ) )
65         {
66 
67             registerChild( child );
68 
69         } else {
70 
71             // list widget types for which scrolled window needs register
72             static const char* widgetTypes[] = { "ExoIconView", "FMIconContainer", 0L };
73             for( unsigned int i = 0; widgetTypes[i]; i++ )
74             {
75                 if( Gtk::g_object_is_a( G_OBJECT( child ), widgetTypes[i] ) )
76                 {
77                     registerChild( child );
78                     break;
79                 }
80             }
81 
82         }
83 
84     }
85 
86     //_____________________________________________
disconnect(GtkWidget *)87     void ScrolledWindowData::disconnect( GtkWidget* )
88     {
89         _target = 0;
90         for( ChildDataMap::iterator iter = _childrenData.begin(); iter != _childrenData.end(); ++iter )
91         { iter->second.disconnect( iter->first ); }
92 
93         _childrenData.clear();
94     }
95 
96     //________________________________________________________________________________
setHovered(GtkWidget * widget,bool value)97     void ScrolledWindowData::setHovered( GtkWidget* widget, bool value )
98     {
99 
100         bool oldHover( hovered() );
101         ChildDataMap::iterator iter( _childrenData.find( widget ) );
102         if( iter != _childrenData.end() ) iter->second._hovered = value;
103         else return;
104 
105         // need to schedule repaint of the whole widget
106         if( oldHover != hovered() && _target ) gtk_widget_queue_draw( _target );
107 
108     }
109 
110     //________________________________________________________________________________
setFocused(GtkWidget * widget,bool value)111     void ScrolledWindowData::setFocused( GtkWidget* widget, bool value )
112     {
113 
114         bool oldFocus( focused() );
115         ChildDataMap::iterator iter( _childrenData.find( widget ) );
116         if( iter != _childrenData.end() ) iter->second._focused = value;
117         else return;
118 
119         // need to schedule repaint of the whole widget
120         if( oldFocus != focused() && _target ) gtk_widget_queue_draw( _target );
121 
122     }
123 
124     //_____________________________________________
registerChild(GtkWidget * widget)125     void ScrolledWindowData::registerChild( GtkWidget* widget )
126     {
127         // make sure widget is not already in map
128         if( _childrenData.find( widget ) == _childrenData.end() )
129         {
130 
131             #if OXYGEN_DEBUG
132             std::cerr
133                 << "Oxygen::ScrolledWindowData::registerChild -"
134                 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
135                 << std::endl;
136             #endif
137 
138             // adjust event mask
139             gtk_widget_add_events( widget, GDK_ENTER_NOTIFY_MASK|GDK_LEAVE_NOTIFY_MASK|GDK_FOCUS_CHANGE_MASK );
140 
141             // allocate new Hover data
142             ChildData data;
143             data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
144             data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( enterNotifyEvent ), this );
145             data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this );
146             data._focusInId.connect( G_OBJECT(widget), "focus-in-event", G_CALLBACK( focusInNotifyEvent ), this );
147             data._focusOutId.connect( G_OBJECT(widget), "focus-out-event", G_CALLBACK( focusOutNotifyEvent ), this );
148 
149             // and insert in map
150             _childrenData.insert( std::make_pair( widget, data ) );
151 
152             // set initial focus
153             setFocused( widget, gtk_widget_has_focus( widget ) );
154 
155             // set initial hover
156             const bool enabled( gtk_widget_get_state( widget ) != GTK_STATE_INSENSITIVE );
157 
158             // on connection, needs to check whether mouse pointer is in widget or not
159             // to have the proper initial value of the hover flag
160             if( enabled && gtk_widget_get_window( widget ) )
161             {
162 
163                 gint xPointer,yPointer;
164                 gdk_window_get_pointer( gtk_widget_get_window( widget ), &xPointer, &yPointer, 0L );
165                 const GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) );
166                 const GdkRectangle rect( Gtk::gdk_rectangle( 0, 0, allocation.width, allocation.height ) );
167                 setHovered( widget, Gtk::gdk_rectangle_contains( &rect, xPointer, yPointer ) );
168 
169             } else setHovered( widget, false );
170 
171         }
172 
173     }
174 
175     //________________________________________________________________________________
unregisterChild(GtkWidget * widget)176     void ScrolledWindowData::unregisterChild( GtkWidget* widget )
177     {
178 
179         // loopup in hover map
180         ChildDataMap::iterator iter( _childrenData.find( widget ) );
181         if( iter == _childrenData.end() ) return;
182 
183         #if OXYGEN_DEBUG
184         std::cerr
185             << "Oxygen::ScrolledWindowData::unregisterChild -"
186             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
187             << std::endl;
188         #endif
189 
190         iter->second.disconnect( widget );
191         _childrenData.erase( iter );
192 
193     }
194 
195     //________________________________________________________________________________
196     #if OXYGEN_DEBUG
disconnect(GtkWidget * widget)197     void ScrolledWindowData::ChildData::disconnect( GtkWidget* widget )
198     #else
199     void ScrolledWindowData::ChildData::disconnect( GtkWidget* )
200     #endif
201     {
202 
203         #if OXYGEN_DEBUG
204         std::cerr
205             << "Oxygen::ScrolledWindowData::ChildData::disconnect -"
206             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
207             << std::endl;
208         #endif
209 
210         _destroyId.disconnect();
211         _enterId.disconnect();
212         _leaveId.disconnect();
213         _focusInId.disconnect();
214         _focusOutId.disconnect();
215         _hovered = false;
216         _focused = false;
217 
218     }
219 
220     //____________________________________________________________________________________________
childDestroyNotifyEvent(GtkWidget * widget,gpointer data)221     gboolean ScrolledWindowData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
222     {
223         #if OXYGEN_DEBUG
224         std::cerr
225             << "Oxygen::ScrolledWindowData::childDestroyNotifyEvent -"
226             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
227             << std::endl;
228         #endif
229         static_cast<ScrolledWindowData*>(data)->unregisterChild( widget );
230         return FALSE;
231     }
232 
233     //________________________________________________________________________________
enterNotifyEvent(GtkWidget * widget,GdkEventCrossing * event,gpointer data)234     gboolean ScrolledWindowData::enterNotifyEvent( GtkWidget* widget, GdkEventCrossing* event, gpointer data )
235     {
236 
237         #if OXYGEN_DEBUG
238         std::cerr << "Oxygen::ScrolledWindowData::enterNotifyEvent -"
239             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
240             << std::endl;
241         #endif
242 
243         if( !(event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK) ) )
244         { static_cast<ScrolledWindowData*>( data )->setHovered( widget, true ); }
245 
246         return FALSE;
247     }
248 
249     //________________________________________________________________________________
leaveNotifyEvent(GtkWidget * widget,GdkEventCrossing * event,gpointer data)250     gboolean ScrolledWindowData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing* event, gpointer data )
251     {
252 
253         #if OXYGEN_DEBUG
254         std::cerr << "Oxygen::ScrolledWindowData::leaveNotifyEvent -"
255             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
256             << std::endl;
257         #endif
258 
259         if( !(event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK) ) )
260         { static_cast<ScrolledWindowData*>( data )->setHovered( widget, false ); }
261 
262         return FALSE;
263     }
264 
265     //________________________________________________________________________________
focusInNotifyEvent(GtkWidget * widget,GdkEvent *,gpointer data)266     gboolean ScrolledWindowData::focusInNotifyEvent( GtkWidget* widget, GdkEvent*, gpointer data )
267     {
268 
269         #if OXYGEN_DEBUG
270         std::cerr << "Oxygen::ScrolledWindowData::focusInNotifyEvent -"
271             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
272             << std::endl;
273         #endif
274 
275         static_cast<ScrolledWindowData*>( data )->setFocused( widget, true );
276         return FALSE;
277     }
278 
279     //________________________________________________________________________________
focusOutNotifyEvent(GtkWidget * widget,GdkEvent *,gpointer data)280     gboolean ScrolledWindowData::focusOutNotifyEvent( GtkWidget* widget, GdkEvent*, gpointer data )
281     {
282 
283         #if OXYGEN_DEBUG
284         std::cerr << "Oxygen::ScrolledWindowData::focusOutNotifyEvent -"
285             << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")"
286             << std::endl;
287         #endif
288 
289         static_cast<ScrolledWindowData*>( data )->setFocused( widget, false );
290         return FALSE;
291     }
292 
293 }
294