File indexing completed on 2024-05-12 05:34:32
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 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "oxygencomboboxdata.h" 0009 #include "../oxygengtkutils.h" 0010 #include "../config.h" 0011 0012 #include <gtk/gtk.h> 0013 #include <iostream> 0014 #include <cassert> 0015 #include <algorithm> 0016 namespace Oxygen 0017 { 0018 0019 //________________________________________________________________________________ 0020 void ComboBoxData::connect( GtkWidget* widget ) 0021 { 0022 #if OXYGEN_DEBUG 0023 std::cerr << "Oxygen::ComboBoxData::connect - widget: " << widget << std::endl; 0024 #endif 0025 0026 // set pointers to widgets 0027 _target = widget; 0028 _list = 0L; 0029 0030 // connect signals 0031 _stateChangeId.connect( G_OBJECT(widget), "state-changed", G_CALLBACK( stateChangeEvent ), this ); 0032 _styleSetId.connect( G_OBJECT(widget), "style-set", G_CALLBACK( styleSetEvent ), this ); 0033 0034 // initialize cell view 0035 initializeCellView( widget ); 0036 0037 /* 0038 need to force the wrap-width property to 0, 0039 otherwise the "appears-as-list" flag is not respected, which additionally breaks the widget rendering. 0040 */ 0041 gtk_combo_box_set_wrap_width( GTK_COMBO_BOX( widget ), 0 ); 0042 0043 } 0044 0045 //________________________________________________________________________________ 0046 void ComboBoxData::disconnect( GtkWidget* widget ) 0047 { 0048 #if OXYGEN_DEBUG 0049 std::cerr << "Oxygen::ComboBoxData::disconnect - widget: " << widget << std::endl; 0050 #endif 0051 0052 _stateChangeId.disconnect(); 0053 _styleSetId.disconnect(); 0054 0055 // clear pointers to widgets 0056 _target = 0L; 0057 _list = 0L; 0058 0059 _button.disconnect(); 0060 _cell.disconnect(); 0061 0062 // disconnect all children 0063 for( HoverDataMap::iterator iter = _hoverData.begin(); iter != _hoverData.end(); ++iter ) 0064 { iter->second.disconnect(); } 0065 0066 _hoverData.clear(); 0067 0068 } 0069 0070 //________________________________________________________________________________ 0071 void ComboBoxData::setButton( GtkWidget* widget ) 0072 { 0073 if( _button._widget == widget ) return; 0074 0075 if( _button._widget ) 0076 { 0077 std::cerr << "Oxygen::WindowManager::wmButtonPress - warning: a button was already set for this combobox" << std::endl; 0078 _button._toggledId.disconnect(); 0079 _button._sizeAllocateId.disconnect(); 0080 } 0081 0082 _button._toggledId.connect( G_OBJECT(widget), "toggled", G_CALLBACK( childToggledEvent ), this ); 0083 _button._sizeAllocateId.connect( G_OBJECT(widget), "size-allocate", G_CALLBACK( childSizeAllocateEvent ), this ); 0084 _button._widget = widget; 0085 registerChild( widget, false ); 0086 0087 updateButtonEventWindow(); 0088 gtk_widget_queue_draw( widget ); 0089 0090 } 0091 0092 //________________________________________________________________________________ 0093 void ComboBoxData::initializeCellView( GtkWidget* widget ) 0094 { 0095 0096 GList* children( gtk_container_get_children( GTK_CONTAINER( widget ) ) ); 0097 for( GList* child = g_list_first(children); child; child = g_list_next(child) ) 0098 { 0099 0100 if( !GTK_IS_CELL_VIEW( child->data ) ) continue; 0101 0102 // convert to widget and store 0103 GtkWidget* widget( GTK_WIDGET( child->data ) ); 0104 if( _cell._widget == widget ) return; 0105 assert( !_cell._widget ); 0106 0107 _cell._widget = GTK_WIDGET( child->data ); 0108 _cell._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 0109 0110 updateCellViewColor(); 0111 0112 } 0113 0114 if( children ) g_list_free( children ); 0115 return; 0116 0117 } 0118 0119 //________________________________________________________________________________ 0120 void ComboBoxData::updateCellViewColor( void ) const 0121 { 0122 // change background color 0123 if( _cell._widget ) 0124 { gtk_cell_view_set_background_color( GTK_CELL_VIEW( _cell._widget ), 0L ); } 0125 } 0126 0127 //________________________________________________________________________________ 0128 void ComboBoxData::updateButtonEventWindow( void ) const 0129 { 0130 0131 // store local pointer to relevant widget 0132 GtkWidget* widget( _button._widget ); 0133 0134 // check validity and type 0135 if( !( widget && GTK_IS_BUTTON( widget ) ) ) return; 0136 0137 // get window 0138 #if GTK_CHECK_VERSION(2, 22, 0) 0139 GdkWindow* window( gtk_button_get_event_window( GTK_BUTTON( widget ) ) ); 0140 #else 0141 GdkWindow* window( GTK_BUTTON( widget )->event_window ) ; 0142 #endif 0143 0144 if( !window ) return; 0145 0146 // offset 0147 /* TODO: we should get it from the x-thickness property of the GtkFrame for this combobox */ 0148 const int offset = 4; 0149 0150 // get allocation 0151 const GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) ); 0152 gdk_window_move_resize( window, allocation.x-offset, allocation.y, allocation.width+offset, allocation.height ); 0153 0154 } 0155 0156 //________________________________________________________________________________ 0157 void ComboBoxData::setPressed( GtkWidget* widget, bool value ) 0158 { 0159 const bool oldPressed( pressed() ); 0160 if( widget == _button._widget ) _button._pressed = value; 0161 else return; 0162 0163 if( oldPressed != pressed() && _target ) gtk_widget_queue_draw( _target ); 0164 0165 } 0166 0167 //________________________________________________________________________________ 0168 void ComboBoxData::setHovered( GtkWidget* widget, bool value ) 0169 { 0170 0171 bool oldHover( hovered() ); 0172 HoverDataMap::iterator iter( _hoverData.find( widget ) ); 0173 if( iter != _hoverData.end() ) iter->second._hovered = value; 0174 else return; 0175 0176 // need to schedule repaint of the whole widget 0177 if( oldHover != hovered() && _target ) gtk_widget_queue_draw( _target ); 0178 0179 } 0180 0181 //________________________________________________________________________________ 0182 void ComboBoxData::registerChild( GtkWidget* widget, bool recursive ) 0183 { 0184 0185 // make sure widget is not already in map 0186 if( _hoverData.find( widget ) == _hoverData.end() ) 0187 { 0188 0189 #if OXYGEN_DEBUG 0190 std::cerr 0191 << "Oxygen::ComboBoxData::registerChild -" 0192 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0193 << std::endl; 0194 #endif 0195 0196 // allocate new Hover data 0197 HoverData data; 0198 data._widget = widget; 0199 data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 0200 data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( enterNotifyEvent ), this ); 0201 data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this ); 0202 0203 // and insert in map 0204 _hoverData.insert( std::make_pair( widget, data ) ); 0205 0206 } 0207 0208 /* 0209 also insert widget's children, recursively. 0210 that should take care of buttons in tabs and other fancy stuff that applications mght do 0211 */ 0212 if( recursive && GTK_IS_CONTAINER( widget ) ) 0213 { 0214 0215 GList *children( gtk_container_get_children( GTK_CONTAINER(widget) ) ); 0216 for( GList* child = g_list_first(children); child; child = g_list_next(child) ) 0217 { registerChild( GTK_WIDGET( child->data ) ); } 0218 0219 if( children ) g_list_free( children ); 0220 } 0221 0222 } 0223 0224 //________________________________________________________________________________ 0225 void ComboBoxData::unregisterChild( GtkWidget* widget ) 0226 { 0227 0228 #if OXYGEN_DEBUG 0229 std::cerr 0230 << "Oxygen::ComboBoxData::unregisterChild -" 0231 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0232 << std::endl; 0233 #endif 0234 0235 // see if widget is button or cell 0236 if( widget == _button._widget ) _button.disconnect(); 0237 if( widget == _cell._widget ) _cell.disconnect(); 0238 0239 // loopup in hover map 0240 HoverDataMap::iterator iter( _hoverData.find( widget ) ); 0241 if( iter != _hoverData.end() ) 0242 { 0243 iter->second.disconnect(); 0244 _hoverData.erase( iter ); 0245 } 0246 0247 } 0248 0249 //________________________________________________________________________________ 0250 void ComboBoxData::ChildData::disconnect( void ) 0251 { 0252 0253 if( !_widget ) return; 0254 0255 #if OXYGEN_DEBUG 0256 std::cerr 0257 << "Oxygen::ComboBoxData::ChildData::disconnect -" 0258 << " " << _widget << " (" << G_OBJECT_TYPE_NAME( _widget ) << ")" 0259 << std::endl; 0260 #endif 0261 0262 _destroyId.disconnect(); 0263 _widget = 0L; 0264 } 0265 0266 //________________________________________________________________________________ 0267 void ComboBoxData::ButtonData::disconnect( void ) 0268 { 0269 if( !_widget ) return; 0270 _toggledId.disconnect(); 0271 _sizeAllocateId.disconnect(); 0272 _pressed = false; 0273 _focus = false; 0274 0275 // base class 0276 ChildData::disconnect(); 0277 } 0278 0279 //________________________________________________________________________________ 0280 void ComboBoxData::HoverData::disconnect( void ) 0281 { 0282 if( !_widget ) return; 0283 0284 #if OXYGEN_DEBUG 0285 std::cerr << "Oxygen::ComboBoxData::HoverData::disconnect -" 0286 << " " << _widget << " (" << G_OBJECT_TYPE_NAME( _widget ) << ")" 0287 << std::endl; 0288 #endif 0289 0290 _enterId.disconnect(); 0291 _leaveId.disconnect(); 0292 _hovered = false; 0293 0294 // base class 0295 ChildData::disconnect(); 0296 } 0297 0298 //____________________________________________________________________________________________ 0299 gboolean ComboBoxData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data ) 0300 { 0301 #if OXYGEN_DEBUG 0302 std::cerr 0303 << "Oxygen::ComboBoxData::childDestroyNotifyEvent -" 0304 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0305 << std::endl; 0306 #endif 0307 static_cast<ComboBoxData*>(data)->unregisterChild( widget ); 0308 return FALSE; 0309 } 0310 0311 //____________________________________________________________________________________________ 0312 void ComboBoxData::childToggledEvent( GtkWidget* widget, gpointer data) 0313 { 0314 if( GTK_IS_TOGGLE_BUTTON( widget ) ) 0315 { static_cast<ComboBoxData*>(data)->setPressed( widget, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( widget ) ) ); } 0316 return; 0317 } 0318 0319 //____________________________________________________________________________________________ 0320 void ComboBoxData::childSizeAllocateEvent( GtkWidget* widget, GtkAllocation* allocation, gpointer data) 0321 { 0322 0323 static_cast<ComboBoxData*>(data)->updateButtonEventWindow(); 0324 return; 0325 } 0326 0327 //________________________________________________________________________________ 0328 gboolean ComboBoxData::enterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0329 { 0330 0331 #if OXYGEN_DEBUG 0332 std::cerr << "Oxygen::ComboBoxData::enterNotifyEvent -" 0333 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0334 << std::endl; 0335 #endif 0336 0337 static_cast<ComboBoxData*>( data )->setHovered( widget, true ); 0338 return FALSE; 0339 } 0340 0341 //________________________________________________________________________________ 0342 gboolean ComboBoxData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 0343 { 0344 0345 #if OXYGEN_DEBUG 0346 std::cerr << "Oxygen::ComboBoxData::leaveNotifyEvent -" 0347 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 0348 << std::endl; 0349 #endif 0350 0351 static_cast<ComboBoxData*>( data )->setHovered( widget, false ); 0352 return FALSE; 0353 } 0354 0355 //________________________________________________________________________________ 0356 void ComboBoxData::stateChangeEvent( GtkWidget*, GtkStateType, gpointer data ) 0357 { static_cast<ComboBoxData*>( data )->updateCellViewColor(); } 0358 0359 //________________________________________________________________________________ 0360 void ComboBoxData::styleSetEvent( GtkWidget*, GtkStyle*, gpointer data ) 0361 { static_cast<ComboBoxData*>( data )->updateCellViewColor(); } 0362 0363 }