File indexing completed on 2024-05-12 05:34:35
0001 /* 0002 this file is part of the oxygen gtk engine 0003 SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr> 0004 0005 the tabwidget data code is largely inspired from the gtk redmond engine 0006 0007 SPDX-License-Identifier: LGPL-2.0-or-later 0008 */ 0009 0010 #include "oxygentabwidgetdata.h" 0011 #include "../oxygengtkutils.h" 0012 #include "../config.h" 0013 0014 #include <gtk/gtk.h> 0015 #include <cassert> 0016 #include <iostream> 0017 0018 namespace Oxygen 0019 { 0020 0021 //________________________________________________________________________________ 0022 void TabWidgetData::connect( GtkWidget* widget ) 0023 { 0024 0025 #if OXYGEN_DEBUG 0026 std::cerr << "Oxygen::TabWidgetData::connect - " << widget << std::endl; 0027 #endif 0028 0029 _target = widget; 0030 _motionId.connect( G_OBJECT(widget), "motion-notify-event", G_CALLBACK( motionNotifyEvent ), this ); 0031 _leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this ); 0032 _pageAddedId.connect( G_OBJECT(widget), "page-added", G_CALLBACK( pageAddedEvent ), this ); 0033 0034 updateRegisteredChildren( widget ); 0035 0036 } 0037 0038 //________________________________________________________________________________ 0039 void TabWidgetData::disconnect( GtkWidget* widget ) 0040 { 0041 0042 #if OXYGEN_DEBUG 0043 std::cerr << "Oxygen::TabWidgetData::disconnect - " << widget << std::endl; 0044 #endif 0045 0046 _target = 0L; 0047 _motionId.disconnect(); 0048 _leaveId.disconnect(); 0049 _pageAddedId.disconnect(); 0050 0051 // disconnect all children 0052 for( ChildDataMap::iterator iter = _childrenData.begin(); iter != _childrenData.end(); ++iter ) 0053 { iter->second.disconnect(); } 0054 _childrenData.clear(); 0055 0056 } 0057 0058 //________________________________________________________________________________ 0059 void TabWidgetData::updateHoveredTab(GtkWidget* widget ) 0060 { 0061 0062 if( !widget ) widget = _target; 0063 if( !widget ) return; 0064 0065 // get pointer position 0066 int xPointer,yPointer; 0067 gdk_window_get_pointer( gtk_widget_get_window( widget ), &xPointer, &yPointer, 0L ); 0068 0069 // loop over tabs and check matching 0070 for( unsigned int i = (unsigned int)Gtk::gtk_notebook_find_first_tab( widget ); i < _tabRects.size(); i++ ) 0071 { 0072 if( Gtk::gdk_rectangle_contains( &_tabRects[i], xPointer, yPointer ) ) 0073 { setHoveredTab( widget, i ); return; } 0074 } 0075 0076 // reset hovered tab 0077 setHoveredTab( widget, -1 ); 0078 return; 0079 0080 } 0081 0082 //________________________________________________________________________________ 0083 void TabWidgetData::updateTabRect( GtkWidget* widget, int index, const GdkRectangle& r ) 0084 { 0085 // make sure the vector has the right size 0086 if( !GTK_IS_NOTEBOOK( widget ) ) return; 0087 GtkNotebook* notebook = GTK_NOTEBOOK( widget ); 0088 _tabRects.resize( gtk_notebook_get_n_pages( notebook ), defaultRect() ); 0089 0090 // check index against number of tabs 0091 if( index < 0 || index >= (int)_tabRects.size() ) 0092 { return; } 0093 0094 // store rectangle 0095 _tabRects[index]=r; 0096 } 0097 0098 //________________________________________________________________________________ 0099 void TabWidgetData::setDirty( bool value ) 0100 { 0101 if( _dirty == value ) return; 0102 _dirty = value; 0103 if( _dirty && _target ) 0104 { 0105 0106 // we should only update the tabbar rect here 0107 GdkRectangle updateRect; 0108 Gtk::gtk_notebook_get_tabbar_rect( GTK_NOTEBOOK( _target ), &updateRect ); 0109 Gtk::gtk_widget_queue_draw( _target, &updateRect ); 0110 0111 #if OXYGEN_DEBUG 0112 std::cerr << "Oxygen::TabWidgetData::setDirty - update: " << updateRect << std::endl; 0113 #endif 0114 0115 } 0116 0117 } 0118 0119 //________________________________________________________________________________ 0120 bool TabWidgetData::isInTab( int x, int y ) const 0121 { 0122 0123 // loop over tab rectangles and check. 0124 for( RectangleList::const_iterator iter = _tabRects.begin(); iter != _tabRects.end(); ++iter ) 0125 { if( Gtk::gdk_rectangle_contains( &(*iter), x, y ) ) return true; } 0126 0127 return false; 0128 0129 } 0130 0131 //________________________________________________________________________________ 0132 void TabWidgetData::setHoveredTab( GtkWidget* widget, int index ) 0133 { 0134 0135 if( _hoveredTab == index ) return; 0136 0137 _hoveredTab = index; 0138 0139 GdkRectangle updateRect( Gtk::gdk_rectangle() ); 0140 for( RectangleList::const_iterator iter = _tabRects.begin(); iter != _tabRects.end(); ++iter ) 0141 { gdk_rectangle_union( &(*iter), &updateRect, &updateRect ); } 0142 0143 gtk_widget_queue_draw_area( widget, updateRect.x-4, updateRect.y-4, updateRect.width+8, updateRect.height+8 ); 0144 0145 return; 0146 } 0147 0148 //________________________________________________________________________________ 0149 gboolean TabWidgetData::motionNotifyEvent(GtkWidget* widget, GdkEventMotion*, gpointer data ) 0150 { 0151 0152 static_cast<TabWidgetData*>( data )->updateHoveredTab( widget ); 0153 return FALSE; 0154 0155 } 0156 0157 //________________________________________________________________________________ 0158 gboolean TabWidgetData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0159 { 0160 // reset hovered tab 0161 static_cast<TabWidgetData*>( data )->setHoveredTab( widget, -1 ); 0162 return FALSE; 0163 } 0164 0165 //________________________________________________________________________________ 0166 void TabWidgetData::pageAddedEvent( GtkNotebook* parent, GtkWidget* child, guint, gpointer data) 0167 { 0168 #if OXYGEN_DEBUG 0169 std::cerr << "Oxygen::TabWidgetData::pageAddedEvent - " << child << std::endl; 0170 #endif 0171 static_cast<TabWidgetData*>(data)->updateRegisteredChildren( GTK_WIDGET( parent ) ); 0172 } 0173 0174 //________________________________________________________________________________ 0175 void TabWidgetData::updateRegisteredChildren( GtkWidget* widget ) 0176 { 0177 0178 if( !widget ) widget = _target; 0179 if( !widget ) return; 0180 0181 // cast to notebook and check against number of pages 0182 if( GTK_IS_NOTEBOOK( widget ) ) 0183 { 0184 GtkNotebook* notebook( GTK_NOTEBOOK( widget ) ); 0185 for( int i = 0; i < gtk_notebook_get_n_pages( notebook ); ++i ) 0186 { 0187 0188 // retrieve page and tab label 0189 GtkWidget* page( gtk_notebook_get_nth_page( notebook, i ) ); 0190 registerChild( gtk_notebook_get_tab_label( notebook, page ) ); 0191 } 0192 } 0193 } 0194 0195 //________________________________________________________________________________ 0196 void TabWidgetData::registerChild( GtkWidget* widget ) 0197 { 0198 0199 // do nothing if child is invalid (might happen: not checked at calling stage) 0200 if( !widget ) return; 0201 0202 // make sure widget is not already in map 0203 if( _childrenData.find( widget ) == _childrenData.end() ) 0204 { 0205 0206 #if OXYGEN_DEBUG 0207 std::cerr << "Oxygen::TabWidgetData::registerChild - " << widget << std::endl; 0208 #endif 0209 0210 // allocate new ChildData 0211 ChildData data; 0212 data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 0213 data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( childCrossingNotifyEvent ), this ); 0214 data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( childCrossingNotifyEvent ), this ); 0215 0216 if( GTK_IS_CONTAINER( widget ) ) 0217 { data._addId.connect( G_OBJECT(widget), "add", G_CALLBACK( childAddedEvent ), this ); } 0218 0219 // and insert in map 0220 _childrenData.insert( std::make_pair( widget, data ) ); 0221 0222 } 0223 0224 /* 0225 also insert widget's children, recursively. 0226 that should take care of buttons in tabs and other fancy stuff that applications mght do 0227 */ 0228 if( GTK_IS_CONTAINER( widget ) ) 0229 { 0230 0231 GList *children( gtk_container_get_children( GTK_CONTAINER(widget) ) ); 0232 for( GList* child = g_list_first(children); child; child = g_list_next(child) ) 0233 { registerChild( GTK_WIDGET( child->data ) ); } 0234 0235 if( children ) g_list_free( children ); 0236 } 0237 0238 } 0239 0240 //________________________________________________________________________________ 0241 void TabWidgetData::unregisterChild( GtkWidget* widget ) 0242 { 0243 0244 ChildDataMap::iterator iter( _childrenData.find( widget ) ); 0245 if( iter == _childrenData.end() ) return; 0246 0247 #if OXYGEN_DEBUG 0248 std::cerr << "Oxygen::TabWidgetData::unregisterChild - " << widget << std::endl; 0249 #endif 0250 0251 iter->second.disconnect(); 0252 _childrenData.erase( iter ); 0253 0254 } 0255 0256 //____________________________________________________________________________________________ 0257 gboolean TabWidgetData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data ) 0258 { 0259 static_cast<TabWidgetData*>(data)->unregisterChild( widget ); 0260 return FALSE; 0261 } 0262 0263 //____________________________________________________________________________________________ 0264 void TabWidgetData::childAddedEvent( GtkContainer* parent, GtkWidget*, gpointer data ) 0265 { 0266 static_cast<TabWidgetData*>(data)->updateRegisteredChildren(); 0267 return; 0268 } 0269 0270 0271 //____________________________________________________________________________________________ 0272 gboolean TabWidgetData::childCrossingNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0273 { 0274 0275 // retrieve widget's parent and check type 0276 static_cast<TabWidgetData*>(data)->updateHoveredTab(); 0277 return FALSE; 0278 0279 } 0280 0281 //____________________________________________________________________________________________ 0282 void TabWidgetData::ChildData::disconnect( void ) 0283 { 0284 0285 _destroyId.disconnect(); 0286 _enterId.disconnect(); 0287 _leaveId.disconnect(); 0288 _addId.disconnect(); 0289 0290 } 0291 0292 }