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