1 /*
2 * this file is part of the oxygen gtk engine
3 * Copyright (c) 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
4 *
5 * the tabwidget data code is largely inspired from the gtk redmond engine
6 *
7 * This  library is free  software; you can  redistribute it and/or
8 * modify it  under  the terms  of the  GNU Lesser  General  Public
9 * License  as published  by the Free  Software  Foundation; either
10 * version 2 of the License, or(at your option ) any later version.
11 *
12 * This library is distributed  in the hope that it will be useful,
13 * but  WITHOUT ANY WARRANTY; without even  the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License  along  with  this library;  if not,  write to  the Free
19 * Software Foundation, Inc., 51  Franklin St, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
21 */
22 
23 #include "oxygentabwidgetdata.h"
24 #include "../oxygengtkutils.h"
25 #include "../config.h"
26 
27 #include <gtk/gtk.h>
28 #include <cassert>
29 #include <iostream>
30 
31 namespace Oxygen
32 {
33 
34     //________________________________________________________________________________
connect(GtkWidget * widget)35     void TabWidgetData::connect( GtkWidget* widget )
36     {
37 
38         #if OXYGEN_DEBUG
39         std::cerr << "Oxygen::TabWidgetData::connect - " << widget << std::endl;
40         #endif
41 
42         _target = widget;
43         _motionId.connect( G_OBJECT(widget), "motion-notify-event", G_CALLBACK( motionNotifyEvent ), this );
44         _leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this );
45         _pageAddedId.connect( G_OBJECT(widget), "page-added", G_CALLBACK( pageAddedEvent ), this );
46 
47         updateRegisteredChildren( widget );
48 
49     }
50 
51     //________________________________________________________________________________
disconnect(GtkWidget * widget)52     void TabWidgetData::disconnect( GtkWidget* widget )
53     {
54 
55         #if OXYGEN_DEBUG
56         std::cerr << "Oxygen::TabWidgetData::disconnect - " << widget << std::endl;
57         #endif
58 
59         _target = 0L;
60         _motionId.disconnect();
61         _leaveId.disconnect();
62         _pageAddedId.disconnect();
63 
64         // disconnect all children
65         for( ChildDataMap::iterator iter = _childrenData.begin(); iter != _childrenData.end(); ++iter )
66         { iter->second.disconnect(); }
67         _childrenData.clear();
68 
69     }
70 
71     //________________________________________________________________________________
updateHoveredTab(GtkWidget * widget)72     void TabWidgetData::updateHoveredTab(GtkWidget* widget )
73     {
74 
75         if( !widget ) widget = _target;
76         if( !widget ) return;
77 
78         // get pointer position
79         int xPointer,yPointer;
80         gdk_window_get_pointer( gtk_widget_get_window( widget ), &xPointer, &yPointer, 0L );
81 
82         // loop over tabs and check matching
83         for( unsigned int i = (unsigned int)Gtk::gtk_notebook_find_first_tab( widget ); i < _tabRects.size(); i++ )
84         {
85             if( Gtk::gdk_rectangle_contains( &_tabRects[i], xPointer, yPointer ) )
86             { setHoveredTab( widget, i ); return; }
87         }
88 
89         // reset hovered tab
90         setHoveredTab( widget, -1 );
91         return;
92 
93     }
94 
95     //________________________________________________________________________________
updateTabRect(GtkWidget * widget,int index,const GdkRectangle & r)96     void TabWidgetData::updateTabRect( GtkWidget* widget, int index, const GdkRectangle& r )
97     {
98         // make sure the vector has the right size
99         if( !GTK_IS_NOTEBOOK( widget ) ) return;
100         GtkNotebook* notebook = GTK_NOTEBOOK( widget );
101         _tabRects.resize( gtk_notebook_get_n_pages( notebook ), defaultRect() );
102 
103         // check index against number of tabs
104         if( index < 0 || index >= (int)_tabRects.size() )
105         { return; }
106 
107         // store rectangle
108         _tabRects[index]=r;
109     }
110 
111     //________________________________________________________________________________
setDirty(bool value)112     void TabWidgetData::setDirty( bool value )
113     {
114         if( _dirty == value ) return;
115         _dirty = value;
116         if( _dirty && _target )
117         {
118 
119             // we should only update the tabbar rect here
120             GdkRectangle updateRect;
121             Gtk::gtk_notebook_get_tabbar_rect( GTK_NOTEBOOK( _target ), &updateRect );
122             Gtk::gtk_widget_queue_draw( _target, &updateRect );
123 
124             #if OXYGEN_DEBUG
125             std::cerr << "Oxygen::TabWidgetData::setDirty - update: " << updateRect << std::endl;
126             #endif
127 
128         }
129 
130     }
131 
132     //________________________________________________________________________________
isInTab(int x,int y) const133     bool TabWidgetData::isInTab( int x, int y ) const
134     {
135 
136         // loop over tab rectangles and check.
137         for( RectangleList::const_iterator iter = _tabRects.begin(); iter != _tabRects.end(); ++iter )
138         { if( Gtk::gdk_rectangle_contains( &(*iter), x, y ) ) return true; }
139 
140         return false;
141 
142     }
143 
144     //________________________________________________________________________________
setHoveredTab(GtkWidget * widget,int index)145     void TabWidgetData::setHoveredTab( GtkWidget* widget, int index )
146     {
147 
148         if( _hoveredTab == index ) return;
149 
150         _hoveredTab = index;
151 
152         GdkRectangle updateRect( Gtk::gdk_rectangle() );
153         for( RectangleList::const_iterator iter = _tabRects.begin(); iter != _tabRects.end(); ++iter )
154         { gdk_rectangle_union( &(*iter), &updateRect, &updateRect ); }
155 
156         gtk_widget_queue_draw_area( widget, updateRect.x-4, updateRect.y-4, updateRect.width+8, updateRect.height+8 );
157 
158         return;
159     }
160 
161     //________________________________________________________________________________
motionNotifyEvent(GtkWidget * widget,GdkEventMotion *,gpointer data)162     gboolean TabWidgetData::motionNotifyEvent(GtkWidget* widget, GdkEventMotion*, gpointer data )
163     {
164 
165         static_cast<TabWidgetData*>( data )->updateHoveredTab( widget );
166         return FALSE;
167 
168     }
169 
170     //________________________________________________________________________________
leaveNotifyEvent(GtkWidget * widget,GdkEventCrossing *,gpointer data)171     gboolean TabWidgetData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
172     {
173         // reset hovered tab
174         static_cast<TabWidgetData*>( data )->setHoveredTab( widget, -1 );
175         return FALSE;
176     }
177 
178     //________________________________________________________________________________
pageAddedEvent(GtkNotebook * parent,GtkWidget * child,guint,gpointer data)179     void TabWidgetData::pageAddedEvent( GtkNotebook* parent, GtkWidget* child, guint, gpointer data)
180     {
181         #if OXYGEN_DEBUG
182         std::cerr << "Oxygen::TabWidgetData::pageAddedEvent - " << child << std::endl;
183         #endif
184         static_cast<TabWidgetData*>(data)->updateRegisteredChildren( GTK_WIDGET( parent ) );
185     }
186 
187     //________________________________________________________________________________
updateRegisteredChildren(GtkWidget * widget)188     void TabWidgetData::updateRegisteredChildren( GtkWidget* widget )
189     {
190 
191         if( !widget ) widget = _target;
192         if( !widget ) return;
193 
194         // cast to notebook and check against number of pages
195         if( GTK_IS_NOTEBOOK( widget ) )
196         {
197             GtkNotebook* notebook( GTK_NOTEBOOK( widget ) );
198             for( int i = 0; i <  gtk_notebook_get_n_pages( notebook ); ++i )
199             {
200 
201                 // retrieve page and tab label
202                 GtkWidget* page( gtk_notebook_get_nth_page( notebook, i ) );
203                 registerChild( gtk_notebook_get_tab_label( notebook, page ) );
204             }
205         }
206     }
207 
208     //________________________________________________________________________________
registerChild(GtkWidget * widget)209     void TabWidgetData::registerChild( GtkWidget* widget )
210     {
211 
212         // do nothing if child is invalid (might happen: not checked at calling stage)
213         if( !widget ) return;
214 
215         // make sure widget is not already in map
216         if( _childrenData.find( widget ) == _childrenData.end() )
217         {
218 
219             #if OXYGEN_DEBUG
220             std::cerr << "Oxygen::TabWidgetData::registerChild - " << widget << std::endl;
221             #endif
222 
223             // allocate new ChildData
224             ChildData data;
225             data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
226             data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( childCrossingNotifyEvent ), this );
227             data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( childCrossingNotifyEvent ), this );
228 
229             if( GTK_IS_CONTAINER( widget ) )
230             { data._addId.connect( G_OBJECT(widget), "add", G_CALLBACK( childAddedEvent ), this ); }
231 
232             // and insert in map
233             _childrenData.insert( std::make_pair( widget, data ) );
234 
235         }
236 
237         /*
238         also insert widget's children, recursively.
239         that should take care of buttons in tabs and other fancy stuff that applications mght do
240         */
241         if( GTK_IS_CONTAINER( widget ) )
242         {
243 
244             GList *children( gtk_container_get_children( GTK_CONTAINER(widget) ) );
245             for( GList* child = g_list_first(children); child; child = g_list_next(child) )
246             { registerChild( GTK_WIDGET( child->data ) ); }
247 
248             if( children ) g_list_free( children );
249         }
250 
251     }
252 
253     //________________________________________________________________________________
unregisterChild(GtkWidget * widget)254     void TabWidgetData::unregisterChild( GtkWidget* widget )
255     {
256 
257         ChildDataMap::iterator iter( _childrenData.find( widget ) );
258         if( iter == _childrenData.end() ) return;
259 
260         #if OXYGEN_DEBUG
261         std::cerr << "Oxygen::TabWidgetData::unregisterChild - " << widget << std::endl;
262         #endif
263 
264         iter->second.disconnect();
265         _childrenData.erase( iter );
266 
267     }
268 
269     //____________________________________________________________________________________________
childDestroyNotifyEvent(GtkWidget * widget,gpointer data)270     gboolean TabWidgetData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
271     {
272         static_cast<TabWidgetData*>(data)->unregisterChild( widget );
273         return FALSE;
274     }
275 
276     //____________________________________________________________________________________________
childAddedEvent(GtkContainer * parent,GtkWidget *,gpointer data)277     void TabWidgetData::childAddedEvent( GtkContainer* parent, GtkWidget*, gpointer data )
278     {
279         static_cast<TabWidgetData*>(data)->updateRegisteredChildren();
280         return;
281     }
282 
283 
284     //____________________________________________________________________________________________
childCrossingNotifyEvent(GtkWidget * widget,GdkEventCrossing *,gpointer data)285     gboolean TabWidgetData::childCrossingNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data )
286     {
287 
288         // retrieve widget's parent and check type
289         static_cast<TabWidgetData*>(data)->updateHoveredTab();
290         return FALSE;
291 
292     }
293 
294     //____________________________________________________________________________________________
disconnect(void)295     void TabWidgetData::ChildData::disconnect( void )
296     {
297 
298         _destroyId.disconnect();
299         _enterId.disconnect();
300         _leaveId.disconnect();
301         _addId.disconnect();
302 
303     }
304 
305 }
306