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 }