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 * the treeview data code is largely inspired from the gtk redmond engine 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or(at your option ) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free 19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, 20 * MA 02110-1301, USA. 21 */ 22 23 #include "oxygentreeviewdata.h" 24 #include "../config.h" 25 #include "../oxygengtkutils.h" 26 27 #include <gtk/gtk.h> 28 #include <iostream> 29 30 namespace Oxygen 31 { 32 33 //________________________________________________________________________________ connect(GtkWidget * widget)34 void TreeViewData::connect( GtkWidget* widget ) 35 { 36 37 #if OXYGEN_DEBUG 38 std::cerr << "Oxygen::TreeViewData::connect - " << widget << " (" << G_OBJECT_TYPE_NAME( widget ) << ")" << std::endl; 39 #endif 40 41 // store target 42 _target = widget; 43 44 // base class 45 HoverData::connect( widget ); 46 47 // get full-width flag 48 if( GTK_IS_TREE_VIEW( widget ) ) 49 { 50 gtk_widget_style_get( widget, "row_ending_details", &_fullWidth, NULL ); 51 52 if( hovered() ) 53 { 54 // on connection, needs to check whether mouse pointer is in widget or not 55 // to have the proper initial value of the hover flag 56 GtkTreeView* treeView( GTK_TREE_VIEW( widget ) ); 57 gint xPointer,yPointer; 58 gdk_window_get_pointer( gtk_widget_get_window( widget ), &xPointer, &yPointer, 0L ); 59 gtk_tree_view_convert_widget_to_bin_window_coords( treeView, xPointer, yPointer, &xPointer, &yPointer ); 60 updatePosition( widget, xPointer, yPointer ); 61 } 62 63 // columns changed signal connection 64 _columnsChangedId.connect( G_OBJECT(widget), "columns-changed", G_CALLBACK( columnsChanged ), this ); 65 } 66 67 // motion notify signal connection 68 _motionId.connect( G_OBJECT(widget), "motion-notify-event", G_CALLBACK( motionNotifyEvent ), this ); 69 70 // also register scrollbars from parent scrollWindow 71 registerScrollBars( widget ); 72 73 } 74 75 //________________________________________________________________________________ disconnect(GtkWidget * widget)76 void TreeViewData::disconnect( GtkWidget* widget ) 77 { 78 79 #if OXYGEN_DEBUG 80 std::cerr << "Oxygen::TreeViewData::disconnect - " << widget << " (" << (widget ? G_OBJECT_TYPE_NAME( widget ):"null") << ")" << std::endl; 81 #endif 82 83 // reset target 84 _target = 0L; 85 86 _columnsChangedId.disconnect(); 87 _motionId.disconnect(); 88 89 // also free path if valid 90 _cellInfo.clear(); 91 92 // disconnect scrollbars 93 _vScrollBar.disconnect(); 94 _hScrollBar.disconnect(); 95 96 // base class 97 HoverData::disconnect( widget ); 98 99 } 100 101 //________________________________________________________________________________ updateColumnsCursor(void) const102 void TreeViewData::updateColumnsCursor( void ) const 103 { 104 // check tree view and target 105 if( !_cursor ) return; 106 if( !GTK_IS_TREE_VIEW( _target ) ) return; 107 108 #if OXYGEN_DEBUG 109 std::cerr << "Oxygen::TreeViewData::updateColumnsCursor - " << _target << " (" << G_OBJECT_TYPE_NAME( _target ) << ")" << std::endl; 110 #endif 111 112 GList* children( gtk_tree_view_get_columns( GTK_TREE_VIEW( _target ) ) ); 113 for( GList *child = g_list_first( children ); child; child = g_list_next( child ) ) 114 { 115 if( GTK_IS_TREE_VIEW_COLUMN( child->data ) ) 116 { 117 GdkWindow* window( GTK_TREE_VIEW_COLUMN( child->data )->window ); 118 gdk_window_set_cursor( window, _cursor ); 119 } 120 } 121 122 if( children ) g_list_free( children ); 123 } 124 125 //________________________________________________________________________________ updateHoveredCell(void)126 void TreeViewData::updateHoveredCell( void ) 127 { 128 if( !( isDirty() && GTK_IS_TREE_VIEW( _target ) ) ) return; 129 _cellInfo = Gtk::CellInfo( GTK_TREE_VIEW( _target ), _x, _y ); 130 setDirty( false ); 131 } 132 133 //________________________________________________________________________________ setHovered(GtkWidget * widget,bool value)134 bool TreeViewData::setHovered( GtkWidget* widget, bool value ) 135 { 136 if( !HoverData::setHovered( widget, value ) ) return false; 137 if( !value ) clearPosition(); 138 return true; 139 } 140 141 //________________________________________________________________________________ updatePosition(GtkWidget * widget,int x,int y)142 void TreeViewData::updatePosition( GtkWidget* widget, int x, int y ) 143 { 144 145 // check type and cast to treeview 146 if( !GTK_IS_TREE_VIEW( widget ) ) return; 147 GtkTreeView* treeView( GTK_TREE_VIEW( widget ) ); 148 149 // store position 150 _x = x; 151 _y = y; 152 153 // get cellInfo at x and y 154 Gtk::CellInfo cellInfo( treeView, x, y ); 155 156 // do nothing if unchanged 157 if( cellInfo == _cellInfo ) return; 158 159 // prepare update area 160 // get old rectangle 161 const GtkAllocation allocation( Gtk::gtk_widget_get_allocation( widget ) ); 162 GdkRectangle oldRect( _cellInfo.backgroundRect( treeView ) ); 163 if( _fullWidth ) 164 { 165 oldRect.x = 0; 166 oldRect.width = allocation.width; 167 } 168 169 // get new rectangle and update position 170 GdkRectangle newRect( cellInfo.backgroundRect( treeView ) ); 171 if( cellInfo.isValid() && _fullWidth ) 172 { 173 newRect.x = 0; 174 newRect.width = allocation.width; 175 } 176 177 // take the union of both rectangles 178 GdkRectangle updateRect( Gtk::gdk_rectangle() ); 179 Gtk::gdk_rectangle_union( &oldRect, &newRect, &updateRect ); 180 181 // store new cell info 182 _cellInfo = cellInfo; 183 184 // convert to widget coordinates and schedule redraw 185 gtk_tree_view_convert_bin_window_to_widget_coords( treeView, updateRect.x, updateRect.y, &updateRect.x, &updateRect.y ); 186 Gtk::gtk_widget_queue_draw( widget, &updateRect ); 187 188 } 189 190 //________________________________________________________________________________ clearPosition(GtkWidget * widget)191 void TreeViewData::clearPosition( GtkWidget* widget ) 192 { 193 194 // check widget 195 if( !widget ) widget = _target; 196 if( !widget ) return; 197 198 // check path and widget 199 if( !( _cellInfo.isValid() && GTK_IS_TREE_VIEW( widget ) ) ) return; 200 GtkTreeView* treeView( GTK_TREE_VIEW( widget ) ); 201 202 // prepare update area 203 GdkRectangle updateRect( _cellInfo.backgroundRect( treeView ) ); 204 updateRect.x = 0; 205 updateRect.width = Gtk::gtk_widget_get_allocation( widget ).width; 206 207 // clear path and column 208 _cellInfo.clear(); 209 210 // schedule redraw 211 gtk_tree_view_convert_bin_window_to_widget_coords( treeView, updateRect.x, updateRect.y, &updateRect.x, &updateRect.y ); 212 Gtk::gtk_widget_queue_draw( widget, &updateRect ); 213 214 } 215 216 //________________________________________________________________________________ triggerRepaint(void)217 void TreeViewData::triggerRepaint( void ) 218 { 219 if( !( _target && hovered() ) ) return; 220 setDirty( true ); 221 } 222 223 //________________________________________________________________________________ registerScrollBars(GtkWidget * widget)224 void TreeViewData::registerScrollBars( GtkWidget* widget ) 225 { 226 227 // find parent scrolled window 228 GtkWidget* parent( Gtk::gtk_parent_scrolled_window( widget ) ); 229 if( !parent ) return; 230 231 // cast and register scrollbars 232 GtkScrolledWindow *scrolledWindow( GTK_SCROLLED_WINDOW( parent ) ); 233 234 if( GtkWidget* hScrollBar = gtk_scrolled_window_get_hscrollbar( scrolledWindow ) ) 235 { registerChild( hScrollBar, _hScrollBar ); } 236 237 if( GtkWidget* vScrollBar = gtk_scrolled_window_get_vscrollbar( scrolledWindow ) ) 238 { registerChild( vScrollBar, _vScrollBar ); } 239 240 } 241 242 //________________________________________________________________________________ registerChild(GtkWidget * widget,ScrollBarData & data)243 void TreeViewData::registerChild( GtkWidget* widget, ScrollBarData& data ) 244 { 245 246 if( data._widget ) data.disconnect(); 247 248 #if OXYGEN_DEBUG 249 std::cerr << "Oxygen::TreeViewData::registerChild - " << widget << std::endl; 250 #endif 251 252 // make sure widget is not already in map 253 data._widget = widget; 254 data._destroyId.connect( G_OBJECT(widget), "destroy", G_CALLBACK( childDestroyNotifyEvent ), this ); 255 data._valueChangedId.connect( G_OBJECT(widget), "value-changed", G_CALLBACK( childValueChanged ), this ); 256 257 } 258 259 //________________________________________________________________________________ unregisterChild(GtkWidget * widget)260 void TreeViewData::unregisterChild( GtkWidget* widget ) 261 { 262 if( widget == _vScrollBar._widget ) _vScrollBar.disconnect(); 263 else if( widget == _hScrollBar._widget ) _hScrollBar.disconnect(); 264 } 265 266 //____________________________________________________________________________________________ childDestroyNotifyEvent(GtkWidget * widget,gpointer data)267 gboolean TreeViewData::childDestroyNotifyEvent( GtkWidget* widget, gpointer data ) 268 { 269 static_cast<TreeViewData*>(data)->unregisterChild( widget ); 270 return FALSE; 271 } 272 273 //________________________________________________________________________________ childValueChanged(GtkRange * widget,gpointer data)274 void TreeViewData::childValueChanged( GtkRange* widget, gpointer data ) 275 { 276 static_cast<TreeViewData*>(data)->triggerRepaint(); 277 return; 278 } 279 280 //________________________________________________________________________________ columnsChanged(GtkTreeView *,gpointer data)281 void TreeViewData::columnsChanged( GtkTreeView*, gpointer data ) 282 { 283 284 #if OXYGEN_DEBUG 285 std::cerr << "Oxygen::TreeViewData::columnsChanged" << std::endl; 286 #endif 287 288 static_cast<TreeViewData*>(data)->updateColumnsCursor(); 289 return; 290 } 291 292 //________________________________________________________________________________ motionNotifyEvent(GtkWidget * widget,GdkEventMotion * event,gpointer data)293 gboolean TreeViewData::motionNotifyEvent(GtkWidget* widget, GdkEventMotion* event, gpointer data ) 294 { 295 296 // check event 297 if( !( event && event->window ) ) return FALSE; 298 299 // make sure event window is treeview's bin window 300 if( GTK_IS_TREE_VIEW( widget ) && gtk_tree_view_get_bin_window( GTK_TREE_VIEW( widget ) ) == event->window ) 301 { static_cast<TreeViewData*>( data )->updatePosition( widget, (int)event->x, (int)event->y ); } 302 303 return FALSE; 304 } 305 306 //____________________________________________________________________________________________ disconnect(void)307 void TreeViewData::ScrollBarData::disconnect( void ) 308 { 309 310 if( !_widget ) return; 311 312 #if OXYGEN_DEBUG 313 std::cerr << "Oxygen::TreeViewData::ScrollBarData::disconnect - " << _widget << std::endl; 314 #endif 315 316 _destroyId.disconnect(); 317 _valueChangedId.disconnect(); 318 _widget = 0L; 319 320 } 321 322 } 323