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 }