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 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or(at your option ) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free 17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 18 * MA 02110-1301, USA. 19 */ 20 21 #include "oxygencomboboxdata.h" 22 #include "../oxygengtkutils.h" 23 #include "../config.h" 24 25 #include <gtk/gtk.h> 26 #include <iostream> 27 #include <cassert> 28 #include <algorithm> 29 namespace Oxygen 30 { 31 32 //________________________________________________________________________________ connect(GtkWidget * widget)33 void ComboBoxData::connect( GtkWidget* widget ) 34 { 35 #if OXYGEN_DEBUG 36 std::cerr << "Oxygen::ComboBoxData::connect - widget: " << widget << std::endl; 37 #endif 38 39 // set pointers to widgets 40 _target = widget; 41 _list = 0L; 42 43 // connect signals 44 _stateChangeId.connect( G_OBJECT(widget), "state-changed", G_CALLBACK( stateChangeEvent ), this ); 45 _styleSetId.connect( G_OBJECT(widget), "style-set", G_CALLBACK( styleSetEvent ), this ); 46 47 // initialize cell view 48 initializeCellView( widget ); 49 50 /* 51 need to force the wrap-width property to 0, 52 otherwise the "appears-as-list" flag is not respected, which additionally breaks the widget rendering. 53 */ 54 gtk_combo_box_set_wrap_width( GTK_COMBO_BOX( widget ), 0 ); 55 56 } 57 58 //________________________________________________________________________________ disconnect(GtkWidget * widget)59 void ComboBoxData::disconnect( GtkWidget* widget ) 60 { 61 #if OXYGEN_DEBUG 62 std::cerr << "Oxygen::ComboBoxData::disconnect - widget: " << widget << std::endl; 63 #endif 64 65 _stateChangeId.disconnect(); 66 _styleSetId.disconnect(); 67 68 // clear pointers to widgets 69 _target = 0L; 70 _list = 0L; 71 72 _button.disconnect(); 73 _cell.disconnect(); 74 75 // disconnect all children 76 for( HoverDataMap::iterator iter = _hoverData.begin(); iter != _hoverData.end(); ++iter ) 77 { iter->second.disconnect(); } 78 79 _hoverData.clear(); 80 81 } 82 83 //________________________________________________________________________________ setButton(GtkWidget * widget)84 void ComboBoxData::setButton( GtkWidget* widget ) 85 { 86 if( _button._widget == widget ) return; 87 88 if( _button._widget ) 89 { 90 std::cerr << "Oxygen::WindowManager::wmButtonPress - warning: a button was already set for this combobox" << std::endl; 91 _button._toggledId.disconnect(); 92 _button._sizeAllocateId.disconnect(); 93 } 94 95 _button._toggledId.connect( G_OBJECT(widget), "toggled", G_CALLBACK( childToggledEvent ), this ); 96 _button._sizeAllocateId.connect( G_OBJECT(widget), "size-allocate", G_CALLBACK( childSizeAllocateEvent ), this ); 97 _button._widget = widget; 98 registerChild( widget, false ); 99 100 updateButtonEventWindow(); 101 gtk_widget_queue_draw( widget ); 102 103 } 104 105 //________________________________________________________________________________ initializeCellView(GtkWidget * widget)106 void ComboBoxData::initializeCellView( GtkWidget* widget ) 107 { 108 109 GList* children( gtk_container_get_children( GTK_CONTAINER( widget ) ) ); 110 for( GList* child = g_list_first(children); child; child = g_list_next(child) ) 111 { 112 113 if( !GTK_IS_CELL_VIEW( child->data ) ) continue; 114 115 // convert to widget and store 116 GtkWidget* widget( GTK_WIDGET( child->data ) ); 117 if( _cell._widget == widget ) return; 118 assert( !_cell._widget ); 119 120 _cell._widget = GTK_WIDGET( child->data ); 121 _cell._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 122 123 updateCellViewColor(); 124 125 } 126 127 if( children ) g_list_free( children ); 128 return; 129 130 } 131 132 //________________________________________________________________________________ updateCellViewColor(void) const133 void ComboBoxData::updateCellViewColor( void ) const 134 { 135 // change background color 136 if( _cell._widget ) 137 { gtk_cell_view_set_background_color( GTK_CELL_VIEW( _cell._widget ), 0L ); } 138 } 139 140 //________________________________________________________________________________ updateButtonEventWindow(void) const141 void ComboBoxData::updateButtonEventWindow( void ) const 142 { 143 144 // store local pointer to relevant widget 145 GtkWidget* widget( _button._widget ); 146 147 // check validity and type 148 if( !( widget && GTK_IS_BUTTON( widget ) ) ) return; 149 150 // get window 151 #if GTK_CHECK_VERSION(2, 22, 0) 152 GdkWindow* window( gtk_button_get_event_window( GTK_BUTTON( widget ) ) ); 153 #else 154 GdkWindow* window( GTK_BUTTON( widget )->event_window ) ; 155 #endif 156 157 if( !window ) return; 158 159 // offset 160 /* TODO: we should get it from the x-thickness property of the GtkFrame for this combobox */ 161 const int offset = 4; 162 163 // get allocation 164 const GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) ); 165 gdk_window_move_resize( window, allocation.x-offset, allocation.y, allocation.width+offset, allocation.height ); 166 167 } 168 169 //________________________________________________________________________________ setPressed(GtkWidget * widget,bool value)170 void ComboBoxData::setPressed( GtkWidget* widget, bool value ) 171 { 172 const bool oldPressed( pressed() ); 173 if( widget == _button._widget ) _button._pressed = value; 174 else return; 175 176 if( oldPressed != pressed() && _target ) gtk_widget_queue_draw( _target ); 177 178 } 179 180 //________________________________________________________________________________ setHovered(GtkWidget * widget,bool value)181 void ComboBoxData::setHovered( GtkWidget* widget, bool value ) 182 { 183 184 bool oldHover( hovered() ); 185 HoverDataMap::iterator iter( _hoverData.find( widget ) ); 186 if( iter != _hoverData.end() ) iter->second._hovered = value; 187 else return; 188 189 // need to schedule repaint of the whole widget 190 if( oldHover != hovered() && _target ) gtk_widget_queue_draw( _target ); 191 192 } 193 194 //________________________________________________________________________________ registerChild(GtkWidget * widget,bool recursive)195 void ComboBoxData::registerChild( GtkWidget* widget, bool recursive ) 196 { 197 198 // make sure widget is not already in map 199 if( _hoverData.find( widget ) == _hoverData.end() ) 200 { 201 202 #if OXYGEN_DEBUG 203 std::cerr 204 << "Oxygen::ComboBoxData::registerChild -" 205 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 206 << std::endl; 207 #endif 208 209 // allocate new Hover data 210 HoverData data; 211 data._widget = widget; 212 data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 213 data._enterId.connect( G_OBJECT(widget), "enter-notify-event", G_CALLBACK( enterNotifyEvent ), this ); 214 data._leaveId.connect( G_OBJECT(widget), "leave-notify-event", G_CALLBACK( leaveNotifyEvent ), this ); 215 216 // and insert in map 217 _hoverData.insert( std::make_pair( widget, data ) ); 218 219 } 220 221 /* 222 also insert widget's children, recursively. 223 that should take care of buttons in tabs and other fancy stuff that applications mght do 224 */ 225 if( recursive && GTK_IS_CONTAINER( widget ) ) 226 { 227 228 GList *children( gtk_container_get_children( GTK_CONTAINER(widget) ) ); 229 for( GList* child = g_list_first(children); child; child = g_list_next(child) ) 230 { registerChild( GTK_WIDGET( child->data ) ); } 231 232 if( children ) g_list_free( children ); 233 } 234 235 } 236 237 //________________________________________________________________________________ unregisterChild(GtkWidget * widget)238 void ComboBoxData::unregisterChild( GtkWidget* widget ) 239 { 240 241 #if OXYGEN_DEBUG 242 std::cerr 243 << "Oxygen::ComboBoxData::unregisterChild -" 244 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 245 << std::endl; 246 #endif 247 248 // see if widget is button or cell 249 if( widget == _button._widget ) _button.disconnect(); 250 if( widget == _cell._widget ) _cell.disconnect(); 251 252 // loopup in hover map 253 HoverDataMap::iterator iter( _hoverData.find( widget ) ); 254 if( iter != _hoverData.end() ) 255 { 256 iter->second.disconnect(); 257 _hoverData.erase( iter ); 258 } 259 260 } 261 262 //________________________________________________________________________________ disconnect(void)263 void ComboBoxData::ChildData::disconnect( void ) 264 { 265 266 if( !_widget ) return; 267 268 #if OXYGEN_DEBUG 269 std::cerr 270 << "Oxygen::ComboBoxData::ChildData::disconnect -" 271 << " " << _widget << " (" << G_OBJECT_TYPE_NAME( _widget ) << ")" 272 << std::endl; 273 #endif 274 275 _destroyId.disconnect(); 276 _widget = 0L; 277 } 278 279 //________________________________________________________________________________ disconnect(void)280 void ComboBoxData::ButtonData::disconnect( void ) 281 { 282 if( !_widget ) return; 283 _toggledId.disconnect(); 284 _sizeAllocateId.disconnect(); 285 _pressed = false; 286 _focus = false; 287 288 // base class 289 ChildData::disconnect(); 290 } 291 292 //________________________________________________________________________________ disconnect(void)293 void ComboBoxData::HoverData::disconnect( void ) 294 { 295 if( !_widget ) return; 296 297 #if OXYGEN_DEBUG 298 std::cerr << "Oxygen::ComboBoxData::HoverData::disconnect -" 299 << " " << _widget << " (" << G_OBJECT_TYPE_NAME( _widget ) << ")" 300 << std::endl; 301 #endif 302 303 _enterId.disconnect(); 304 _leaveId.disconnect(); 305 _hovered = false; 306 307 // base class 308 ChildData::disconnect(); 309 } 310 311 //____________________________________________________________________________________________ childDestroyNotifyEvent(GtkWidget * widget,gpointer data)312 gboolean ComboBoxData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data ) 313 { 314 #if OXYGEN_DEBUG 315 std::cerr 316 << "Oxygen::ComboBoxData::childDestroyNotifyEvent -" 317 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 318 << std::endl; 319 #endif 320 static_cast<ComboBoxData*>(data)->unregisterChild( widget ); 321 return FALSE; 322 } 323 324 //____________________________________________________________________________________________ childToggledEvent(GtkWidget * widget,gpointer data)325 void ComboBoxData::childToggledEvent( GtkWidget* widget, gpointer data) 326 { 327 if( GTK_IS_TOGGLE_BUTTON( widget ) ) 328 { static_cast<ComboBoxData*>(data)->setPressed( widget, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( widget ) ) ); } 329 return; 330 } 331 332 //____________________________________________________________________________________________ childSizeAllocateEvent(GtkWidget * widget,GtkAllocation * allocation,gpointer data)333 void ComboBoxData::childSizeAllocateEvent( GtkWidget* widget, GtkAllocation* allocation, gpointer data) 334 { 335 336 static_cast<ComboBoxData*>(data)->updateButtonEventWindow(); 337 return; 338 } 339 340 //________________________________________________________________________________ enterNotifyEvent(GtkWidget * widget,GdkEventCrossing *,gpointer data)341 gboolean ComboBoxData::enterNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 342 { 343 344 #if OXYGEN_DEBUG 345 std::cerr << "Oxygen::ComboBoxData::enterNotifyEvent -" 346 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 347 << std::endl; 348 #endif 349 350 static_cast<ComboBoxData*>( data )->setHovered( widget, true ); 351 return FALSE; 352 } 353 354 //________________________________________________________________________________ leaveNotifyEvent(GtkWidget * widget,GdkEventCrossing *,gpointer data)355 gboolean ComboBoxData::leaveNotifyEvent( GtkWidget* widget, GdkEventCrossing*, gpointer data ) 356 { 357 358 #if OXYGEN_DEBUG 359 std::cerr << "Oxygen::ComboBoxData::leaveNotifyEvent -" 360 << " " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" 361 << std::endl; 362 #endif 363 364 static_cast<ComboBoxData*>( data )->setHovered( widget, false ); 365 return FALSE; 366 } 367 368 //________________________________________________________________________________ stateChangeEvent(GtkWidget *,GtkStateType,gpointer data)369 void ComboBoxData::stateChangeEvent( GtkWidget*, GtkStateType, gpointer data ) 370 { static_cast<ComboBoxData*>( data )->updateCellViewColor(); } 371 372 //________________________________________________________________________________ styleSetEvent(GtkWidget *,GtkStyle *,gpointer data)373 void ComboBoxData::styleSetEvent( GtkWidget*, GtkStyle*, gpointer data ) 374 { static_cast<ComboBoxData*>( data )->updateCellViewColor(); } 375 376 } 377