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