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 }