1 /* abstract base class for a vobject object ... watch an iobject and call
2  * _refresh in idle if it changes.
3  */
4 
5 /*
6 
7     Copyright (C) 1991-2003 The National Gallery
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License along
20     with this program; if not, write to the Free Software Foundation, Inc.,
21     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 
23  */
24 
25 /*
26 
27     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
28 
29  */
30 
31 /*
32 #define DEBUG
33  */
34 
35 /* Time each refresh
36 #define DEBUG_TIME
37  */
38 
39 #include "ip.h"
40 
41 static GtkVBoxClass *parent_class = NULL;
42 
43 static Queue *vobject_dirty = NULL;
44 
45 static gint vobject_refresh_timeout = 0;
46 
47 /* Remove from refresh queue.
48  */
49 static void
vobject_refresh_dequeue(vObject * vobject)50 vobject_refresh_dequeue( vObject *vobject )
51 {
52 	if( vobject->dirty ) {
53 #ifdef DEBUG
54 		printf( "vobject_refresh_dequeue: \"%s\"\n",
55 			G_OBJECT_TYPE_NAME( vobject ) );
56 #endif /*DEBUG*/
57 
58 		vobject->dirty = FALSE;
59 		queue_remove( vobject_dirty, vobject );
60 	}
61 }
62 
63 #ifdef DEBUG_TIME
64 /* Refresh all vobjects at once and time them.
65  */
66 static gboolean
vobject_refresh_timeout_cb(gpointer user_data)67 vobject_refresh_timeout_cb( gpointer user_data )
68 {
69 	static GTimer *refresh_timer = NULL;
70 	double last_elapsed;
71 	double worst_time = 0.0;
72 	int worst_index = -1;
73 	void *data;
74 	int n;
75 
76 	vobject_refresh_timeout = 0;
77 
78 	if( !refresh_timer )
79 		refresh_timer = g_timer_new();
80 
81 	g_timer_reset( refresh_timer );
82 
83 	printf( "vobject_idle_refresh: starting ...\n" );
84 
85 	for( n = 0; (data = queue_head( vobject_dirty )); n++ ) {
86 		vObject *vobject = VOBJECT( data );
87 		double elapsed;
88 
89 		vobject->dirty = FALSE;
90 		vobject_refresh( vobject );
91 		elapsed = g_timer_elapsed( refresh_timer, NULL );
92 
93 		if( elapsed - last_elapsed > worst_time ) {
94 			worst_time = elapsed - last_elapsed;
95 			worst_index = n;
96 		}
97 
98 		last_elapsed = elapsed;
99 	}
100 
101 	printf( "vobject_idle_refresh: done after %gs (%d refreshes)\n",
102 		g_timer_elapsed( refresh_timer, NULL ), n );
103 
104 	printf( "vobject_idle_refresh: worst %gs (refresh %d)\n",
105 		worst_time, worst_index );
106 
107 	return( FALSE );
108 }
109 #else /*DEBUG_TIME*/
110 /* Refresh stuff off the dirty list.
111  */
112 static gboolean
vobject_refresh_timeout_cb(gpointer user_data)113 vobject_refresh_timeout_cb( gpointer user_data )
114 {
115 	void *data;
116 
117 #ifdef DEBUG
118 	printf( "vobject_refresh_timeout_cb:\n" );
119 #endif /*DEBUG*/
120 
121 	vobject_refresh_timeout = 0;
122 
123 	while( (data = queue_head( vobject_dirty ) ) ) {
124 		vObject *vobject = VOBJECT( data );
125 
126 #ifdef DEBUG
127 		printf( "vobject_refresh_timeout_cb: starting \"%s\" (%p)\n",
128 			G_OBJECT_TYPE_NAME( vobject ), vobject );
129 #endif /*DEBUG*/
130 
131 		/* We must clear dirty before we _refresh() so that if the
132 		 * _refresh() indirectly triggers another update, we will
133 		 * _refresh() again.
134 		 */
135 		vobject->dirty = FALSE;
136 		vobject_refresh( vobject );
137 	}
138 
139 	return( FALSE );
140 }
141 #endif /*DEBUG_TIME*/
142 
143 /* Mark something for refresh. Seldom call this directly ... just change the
144  * iobject and all vobjects will have a refresh queued.
145  */
146 void *
vobject_refresh_queue(vObject * vobject)147 vobject_refresh_queue( vObject *vobject )
148 {
149 	if( !vobject->dirty ) {
150 #ifdef DEBUG
151 		printf( "vobject_refresh_queue: %s (%p)",
152 			G_OBJECT_TYPE_NAME( vobject ), vobject );
153 		if( vobject->iobject )
154 			printf( ", iobject %s \"%s\"",
155 				G_OBJECT_TYPE_NAME( vobject->iobject ),
156 				NN( vobject->iobject->name ) );
157 		printf( "\n" );
158 #endif /*DEBUG*/
159 
160 		vobject->dirty = TRUE;
161 		queue_add( vobject_dirty, vobject );
162 
163 		IM_FREEF( g_source_remove, vobject_refresh_timeout );
164 		vobject_refresh_timeout = g_timeout_add( 20,
165 			(GSourceFunc) vobject_refresh_timeout_cb, NULL );
166 	}
167 
168 	return( NULL );
169 }
170 
171 /* Called for iobject changed signal ... queue a refresh.
172  */
173 static void
vobject_iobject_changed(iObject * iobject,vObject * vobject)174 vobject_iobject_changed( iObject *iobject, vObject *vobject )
175 {
176 #ifdef DEBUG
177 	printf( "vobject_iobject_changed: %s %s \"%s\"\n",
178 		G_OBJECT_TYPE_NAME( vobject ),
179 		G_OBJECT_TYPE_NAME( iobject ),
180 		NN( iobject->name ) );
181 #endif /*DEBUG*/
182 
183 	vobject_refresh_queue( vobject );
184 }
185 
186 /* Called for iobject destroy signal ... kill the vobject too.
187  */
188 static void
vobject_iobject_destroy(iObject * iobject,vObject * vobject)189 vobject_iobject_destroy( iObject *iobject, vObject *vobject )
190 {
191 #ifdef DEBUG
192 	printf( "vobject_iobject_destroy: iobject %s \"%s\"\n",
193 		G_OBJECT_TYPE_NAME( iobject ), NN( iobject->name ) );
194 #endif /*DEBUG*/
195 
196 	gtk_widget_destroy( GTK_WIDGET( vobject ) );
197 }
198 
199 /* Link to iobject.
200  */
201 void
vobject_link(vObject * vobject,iObject * iobject)202 vobject_link( vObject *vobject, iObject *iobject )
203 {
204 	vObjectClass *vobject_class = VOBJECT_GET_CLASS( vobject );
205 
206 	g_assert( !vobject->iobject );
207 
208 	if( vobject_class->link )
209 		vobject_class->link( vobject, iobject );
210 
211 	/* Queue a refresh ... we always need at least one.
212 	 */
213 	vobject_refresh_queue( vobject );
214 }
215 
216 static void
vobject_destroy(GtkObject * object)217 vobject_destroy( GtkObject *object )
218 {
219 	vObject *vobject;
220 
221 	g_return_if_fail( object != NULL );
222 	g_return_if_fail( IS_VOBJECT( object ) );
223 
224 	vobject = VOBJECT( object );
225 
226 #ifdef DEBUG
227 	printf( "vobject_destroy: \"%s\"\n", G_OBJECT_TYPE_NAME( object ) );
228 #endif /*DEBUG*/
229 
230 	if( vobject->iobject ) {
231 		FREESID( vobject->changed_sid, vobject->iobject );
232 		FREESID( vobject->destroy_sid, vobject->iobject );
233 		vobject->iobject = NULL;
234 	}
235 	vobject_refresh_dequeue( vobject );
236 
237 	GTK_OBJECT_CLASS( parent_class )->destroy( object );
238 }
239 
240 static void
vobject_finalize(GObject * gobject)241 vobject_finalize( GObject *gobject )
242 {
243 #ifdef DEBUG
244 	printf( "vobject_finalize: \"%s\"\n", G_OBJECT_TYPE_NAME( gobject ) );
245 #endif /*DEBUG*/
246 
247 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
248 }
249 
250 static void
vobject_real_refresh(vObject * vobject)251 vobject_real_refresh( vObject *vobject )
252 {
253 #ifdef DEBUG
254 	printf( "vobject_real_refresh: %p %s\n",
255 		vobject, G_OBJECT_TYPE_NAME( vobject ) );
256 #endif /*DEBUG*/
257 }
258 
259 static void
vobject_real_link(vObject * vobject,iObject * iobject)260 vobject_real_link( vObject *vobject, iObject *iobject )
261 {
262 	vobject->iobject = iobject;
263 
264 	vobject->changed_sid = g_signal_connect( iobject, "changed",
265 		G_CALLBACK( vobject_iobject_changed ), vobject );
266 	vobject->destroy_sid = g_signal_connect( iobject, "destroy",
267 		G_CALLBACK( vobject_iobject_destroy ), vobject );
268 }
269 
270 static void
vobject_class_init(vObjectClass * class)271 vobject_class_init( vObjectClass *class )
272 {
273 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
274 	GtkObjectClass *object_class = (GtkObjectClass*) class;
275 
276 	parent_class = g_type_class_peek_parent( class );
277 
278 	gobject_class->finalize = vobject_finalize;
279 
280 	object_class->destroy = vobject_destroy;
281 
282 	/* Create signals.
283 	 */
284 
285 	/* Init default methods.
286 	 */
287 	class->refresh = vobject_real_refresh;
288 	class->link = vobject_real_link;
289 
290 	/* Static init.
291 	 */
292 	vobject_dirty = queue_new();
293 }
294 
295 static void
vobject_init(vObject * vobject)296 vobject_init( vObject *vobject )
297 {
298 	/* Init our instance fields.
299 	 */
300 	vobject->iobject = NULL;
301 	vobject->changed_sid = 0;
302 	vobject->destroy_sid = 0;
303 
304 	vobject->dirty = FALSE;
305 
306 	/* All new vobjects will need refreshing.
307 	 */
308 	vobject_refresh_queue( vobject );
309 }
310 
311 GtkType
vobject_get_type(void)312 vobject_get_type( void )
313 {
314 	static GtkType vobject_type = 0;
315 
316 	if( !vobject_type ) {
317 		static const GtkTypeInfo vobject_info = {
318 			"vObject",
319 			sizeof( vObject ),
320 			sizeof( vObjectClass ),
321 			(GtkClassInitFunc) vobject_class_init,
322 			(GtkObjectInitFunc) vobject_init,
323 			/* reserved_1 */ NULL,
324 			/* reserved_2 */ NULL,
325 			(GtkClassInitFunc) NULL,
326 		};
327 
328 		vobject_type = gtk_type_unique( GTK_TYPE_VBOX, &vobject_info );
329 	}
330 
331 	return( vobject_type );
332 }
333 
334 /* Trigger the refresh method for a vobject immediately ... we usually queue
335  * and wait for idle, but this can be better for interactive stuff.
336  */
337 void *
vobject_refresh(vObject * vobject)338 vobject_refresh( vObject *vobject )
339 {
340 	vObjectClass *vobject_class = VOBJECT_GET_CLASS( vobject );
341 
342 	if( vobject_class->refresh )
343 		vobject_class->refresh( vobject );
344 
345 	return( NULL );
346 }
347