File indexing completed on 2024-05-12 05:34:37

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 treeview data code is largely inspired from the gtk redmond engine
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "oxygentreeviewdata.h"
0011 #include "../config.h"
0012 #include "../oxygengtkutils.h"
0013 
0014 #include <gtk/gtk.h>
0015 #include <iostream>
0016 
0017 namespace Oxygen
0018 {
0019 
0020     //________________________________________________________________________________
0021     void TreeViewData::connect( GtkWidget* widget )
0022     {
0023 
0024         #if OXYGEN_DEBUG
0025         std::cerr << "Oxygen::TreeViewData::connect - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl;
0026         #endif
0027 
0028         // store target
0029         _target = widget;
0030 
0031         // base class
0032         HoverData::connect( widget );
0033 
0034         // get full-width flag
0035         if( GTK_IS_TREE_VIEW( widget ) )
0036         {
0037             gtk_widget_style_get( widget, "row_ending_details", &_fullWidth, NULL );
0038 
0039             if( hovered() )
0040             {
0041                 // on connection, needs to check whether mouse pointer is in widget or not
0042                 // to have the proper initial value of the hover flag
0043                 GtkTreeView* treeView( GTK_TREE_VIEW( widget ) );
0044                 gint xPointer,yPointer;
0045                 gdk_window_get_pointer( gtk_widget_get_window( widget ), &xPointer, &yPointer, 0L );
0046                 gtk_tree_view_convert_widget_to_bin_window_coords( treeView, xPointer, yPointer, &xPointer, &yPointer );
0047                 updatePosition( widget, xPointer, yPointer );
0048             }
0049 
0050             // columns changed signal connection
0051             _columnsChangedId.connect( G_OBJECT(widget), "columns-changed", G_CALLBACK( columnsChanged ), this );
0052         }
0053 
0054         // motion notify signal connection
0055         _motionId.connect( G_OBJECT(widget), "motion-notify-event", G_CALLBACK( motionNotifyEvent ), this );
0056 
0057         // also register scrollbars from parent scrollWindow
0058         registerScrollBars( widget );
0059 
0060     }
0061 
0062     //________________________________________________________________________________
0063     void TreeViewData::disconnect( GtkWidget* widget )
0064     {
0065 
0066         #if OXYGEN_DEBUG
0067         std::cerr << "Oxygen::TreeViewData::disconnect - " << widget << " (" << (widget ? G_OBJECT_TYPE_NAME( widget ):"null") << ")" << std::endl;
0068         #endif
0069 
0070         // reset target
0071         _target = 0L;
0072 
0073         _columnsChangedId.disconnect();
0074         _motionId.disconnect();
0075 
0076         // also free path if valid
0077         _cellInfo.clear();
0078 
0079         // disconnect scrollbars
0080         _vScrollBar.disconnect();
0081         _hScrollBar.disconnect();
0082 
0083         // base class
0084         HoverData::disconnect( widget );
0085 
0086     }
0087 
0088     //________________________________________________________________________________
0089     void TreeViewData::updateColumnsCursor( void ) const
0090     {
0091         // check tree view and target
0092         if( !_cursor ) return;
0093         if( !GTK_IS_TREE_VIEW( _target ) ) return;
0094 
0095         #if OXYGEN_DEBUG
0096         std::cerr << "Oxygen::TreeViewData::updateColumnsCursor - " << _target << " (" << G_OBJECT_TYPE_NAME( _target ) << ")" << std::endl;
0097         #endif
0098 
0099         GList* children( gtk_tree_view_get_columns( GTK_TREE_VIEW( _target ) ) );
0100         for( GList *child = g_list_first( children ); child; child = g_list_next( child ) )
0101         {
0102             if( GTK_IS_TREE_VIEW_COLUMN( child->data ) )
0103             {
0104                 GdkWindow* window( GTK_TREE_VIEW_COLUMN( child->data )->window );
0105                 gdk_window_set_cursor( window, _cursor );
0106             }
0107         }
0108 
0109         if( children ) g_list_free( children );
0110     }
0111 
0112     //________________________________________________________________________________
0113     void TreeViewData::updateHoveredCell( void )
0114     {
0115         if( !( isDirty() && GTK_IS_TREE_VIEW( _target ) ) ) return;
0116         _cellInfo = Gtk::CellInfo( GTK_TREE_VIEW( _target ), _x, _y );
0117         setDirty( false );
0118     }
0119 
0120     //________________________________________________________________________________
0121     bool TreeViewData::setHovered( GtkWidget* widget, bool value )
0122     {
0123         if( !HoverData::setHovered( widget, value ) ) return false;
0124         if( !value ) clearPosition();
0125         return true;
0126     }
0127 
0128     //________________________________________________________________________________
0129     void TreeViewData::updatePosition( GtkWidget* widget, int x, int y )
0130     {
0131 
0132         // check type and cast to treeview
0133         if( !GTK_IS_TREE_VIEW( widget ) ) return;
0134         GtkTreeView* treeView( GTK_TREE_VIEW( widget ) );
0135 
0136         // store position
0137         _x = x;
0138         _y = y;
0139 
0140         // get cellInfo at x and y
0141         Gtk::CellInfo cellInfo( treeView, x, y );
0142 
0143         // do nothing if unchanged
0144         if( cellInfo == _cellInfo ) return;
0145 
0146         // prepare update area
0147         // get old rectangle
0148         const GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) );
0149         GdkRectangle oldRect( _cellInfo.backgroundRect( treeView ) );
0150         if( _fullWidth )
0151         {
0152             oldRect.x = 0;
0153             oldRect.width = allocation.width;
0154         }
0155 
0156         // get new rectangle and update position
0157         GdkRectangle newRect( cellInfo.backgroundRect( treeView ) );
0158         if( cellInfo.isValid() && _fullWidth )
0159         {
0160             newRect.x = 0;
0161             newRect.width = allocation.width;
0162         }
0163 
0164         // take the union of both rectangles
0165         GdkRectangle updateRect( Gtk::gdk_rectangle() );
0166         Gtk::gdk_rectangle_union( &oldRect, &newRect, &updateRect );
0167 
0168         // store new cell info
0169         _cellInfo = cellInfo;
0170 
0171         // convert to widget coordinates and schedule redraw
0172         gtk_tree_view_convert_bin_window_to_widget_coords( treeView, updateRect.x, updateRect.y, &updateRect.x, &updateRect.y );
0173         Gtk::gtk_widget_queue_draw( widget, &updateRect );
0174 
0175     }
0176 
0177     //________________________________________________________________________________
0178     void TreeViewData::clearPosition( GtkWidget* widget )
0179     {
0180 
0181         // check widget
0182         if( !widget ) widget = _target;
0183         if( !widget ) return;
0184 
0185         // check path and widget
0186         if( !( _cellInfo.isValid() && GTK_IS_TREE_VIEW( widget ) ) ) return;
0187         GtkTreeView* treeView( GTK_TREE_VIEW( widget ) );
0188 
0189         // prepare update area
0190         GdkRectangle updateRect( _cellInfo.backgroundRect( treeView ) );
0191         updateRect.x = 0;
0192         updateRect.width = Gtk::gtk_widget_get_allocation( widget ).width;
0193 
0194         // clear path and column
0195         _cellInfo.clear();
0196 
0197         // schedule redraw
0198         gtk_tree_view_convert_bin_window_to_widget_coords( treeView, updateRect.x, updateRect.y, &updateRect.x, &updateRect.y );
0199         Gtk::gtk_widget_queue_draw( widget, &updateRect );
0200 
0201     }
0202 
0203     //________________________________________________________________________________
0204     void TreeViewData::triggerRepaint( void )
0205     {
0206         if( !( _target && hovered() ) ) return;
0207         setDirty( true );
0208     }
0209 
0210     //________________________________________________________________________________
0211     void TreeViewData::registerScrollBars( GtkWidget* widget )
0212     {
0213 
0214         // find parent scrolled window
0215         GtkWidget* parent( Gtk::gtk_parent_scrolled_window( widget ) );
0216         if( !parent ) return;
0217 
0218         // cast and register scrollbars
0219         GtkScrolledWindow *scrolledWindow( GTK_SCROLLED_WINDOW( parent ) );
0220 
0221         if( GtkWidget* hScrollBar = gtk_scrolled_window_get_hscrollbar( scrolledWindow ) )
0222         { registerChild( hScrollBar, _hScrollBar ); }
0223 
0224         if( GtkWidget* vScrollBar = gtk_scrolled_window_get_vscrollbar( scrolledWindow ) )
0225         { registerChild( vScrollBar, _vScrollBar ); }
0226 
0227     }
0228 
0229     //________________________________________________________________________________
0230     void TreeViewData::registerChild( GtkWidget* widget, ScrollBarData& data )
0231     {
0232 
0233         if( data._widget ) data.disconnect();
0234 
0235         #if OXYGEN_DEBUG
0236         std::cerr << "Oxygen::TreeViewData::registerChild - " << widget << std::endl;
0237         #endif
0238 
0239         // make sure widget is not already in map
0240         data._widget = widget;
0241         data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this );
0242         data._valueChangedId.connect( G_OBJECT(widget), "value-changed", G_CALLBACK( childValueChanged ), this );
0243 
0244     }
0245 
0246     //________________________________________________________________________________
0247     void TreeViewData::unregisterChild( GtkWidget* widget )
0248     {
0249         if( widget == _vScrollBar._widget ) _vScrollBar.disconnect();
0250         else if( widget == _hScrollBar._widget ) _hScrollBar.disconnect();
0251     }
0252 
0253     //____________________________________________________________________________________________
0254     gboolean TreeViewData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data )
0255     {
0256         static_cast<TreeViewData*>(data)->unregisterChild( widget );
0257         return FALSE;
0258     }
0259 
0260     //________________________________________________________________________________
0261     void TreeViewData::childValueChanged( GtkRange* widget, gpointer data )
0262     {
0263         static_cast<TreeViewData*>(data)->triggerRepaint();
0264         return;
0265     }
0266 
0267     //________________________________________________________________________________
0268     void TreeViewData::columnsChanged( GtkTreeView*, gpointer data )
0269     {
0270 
0271         #if OXYGEN_DEBUG
0272         std::cerr << "Oxygen::TreeViewData::columnsChanged" << std::endl;
0273         #endif
0274 
0275         static_cast<TreeViewData*>(data)->updateColumnsCursor();
0276         return;
0277     }
0278 
0279     //________________________________________________________________________________
0280     gboolean TreeViewData::motionNotifyEvent(GtkWidget* widget, GdkEventMotion* event, gpointer data )
0281     {
0282 
0283         // check event
0284         if( !( event && event->window ) ) return FALSE;
0285 
0286         // make sure event window is treeview's bin window
0287         if( GTK_IS_TREE_VIEW( widget ) && gtk_tree_view_get_bin_window( GTK_TREE_VIEW( widget ) ) == event->window )
0288         { static_cast<TreeViewData*>( data )->updatePosition( widget, (int)event->x, (int)event->y ); }
0289 
0290         return FALSE;
0291     }
0292 
0293     //____________________________________________________________________________________________
0294     void TreeViewData::ScrollBarData::disconnect( void )
0295     {
0296 
0297         if( !_widget ) return;
0298 
0299         #if OXYGEN_DEBUG
0300         std::cerr << "Oxygen::TreeViewData::ScrollBarData::disconnect - " << _widget << std::endl;
0301         #endif
0302 
0303         _destroyId.disconnect();
0304         _valueChangedId.disconnect();
0305         _widget = 0L;
0306 
0307     }
0308 
0309 }