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