1 /* Handle feedback about eval progress.
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your watch) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 /*
31 #define DEBUG_MEMUSE
32 #define DEBUG
33  */
34 
35 #include "ip.h"
36 
37 static iContainerClass *progress_parent_class = NULL;
38 
39 /* Our signals.
40  */
41 enum {
42 	SIG_BEGIN,		/* Switch to busy state */
43 	SIG_UPDATE,		/* Busy progress update */
44 	SIG_END,		/* End busy state */
45 	SIG_LAST
46 };
47 
48 static guint progress_signals[SIG_LAST] = { 0 };
49 
50 /* Delay before we start showing busy feedback.
51  */
52 static const double progress_busy_delay = 1.0;
53 
54 /* Delay between busy updates.
55  */
56 static const double progress_update_interval = 0.1;
57 
58 void
progress_begin(void)59 progress_begin( void )
60 {
61 	Progress *progress = progress_get();
62 
63 	g_assert( progress->count >= 0 );
64 
65 #ifdef DEBUG
66 	printf( "progress_begin: %d\n", progress->count );
67 #endif /*DEBUG*/
68 
69 	progress->count += 1;
70 
71 	if( progress->count == 1 ) {
72 		g_timer_start( progress->busy_timer );
73 		g_timer_start( progress->update_timer );
74 
75 #ifdef DEBUG_MEMUSE
76 		printf( "progress_begin:\n" );
77 		im__print_all();
78 #endif /*DEBUG_MEMUSE*/
79 	}
80 }
81 
82 static void
progress_update(Progress * progress)83 progress_update( Progress *progress )
84 {
85 	/* Don't show the process and cancel button for a bit.
86 	 */
87 	if( progress->count ) {
88 		double elapsed = g_timer_elapsed( progress->busy_timer, NULL );
89 
90 		if( !progress->busy &&
91 			elapsed > progress_busy_delay ) {
92 #ifdef DEBUG
93 			printf( "progress_update: displaying progress bar\n" );
94 #endif /*DEBUG*/
95 
96 			g_signal_emit( G_OBJECT( progress ),
97 				progress_signals[SIG_BEGIN], 0 );
98 			progress->busy = TRUE;
99 		}
100 	}
101 
102 	/* Update regularly, even if we're not inside a begin/end
103 	 * block.
104 	 */
105 	if( g_timer_elapsed( progress->update_timer, NULL ) >
106 		progress_update_interval ) {
107 		gboolean cancel;
108 
109 #ifdef DEBUG
110 		printf( "progress_update:\n" );
111 #endif /*DEBUG*/
112 
113 		g_timer_start( progress->update_timer );
114 
115 		/* Overwrite the message if we're cancelling.
116 		 */
117 		if( progress->cancel ) {
118 			vips_buf_rewind( &progress->feedback );
119 			vips_buf_appends( &progress->feedback,
120 				_( "Cancelling" ) );
121 			vips_buf_appends( &progress->feedback, " ..." );
122 		}
123 
124 		cancel = FALSE;
125 		g_signal_emit( progress,
126 			progress_signals[SIG_UPDATE], 0, &cancel );
127 		if( cancel )
128 			progress->cancel = TRUE;
129 
130 		/* Rather dangerous, but we need this to give nice updates
131 		 * for the feedback thing.
132 		 */
133 		process_events();
134 
135 #ifdef DEBUG_MEMUSE
136 		printf( "progress_update:\n" );
137 		im__print_all();
138 #endif /*DEBUG_MEMUSE*/
139 	}
140 }
141 
142 gboolean
progress_update_percent(int percent,int eta)143 progress_update_percent( int percent, int eta )
144 {
145 	Progress *progress = progress_get();
146 
147 	vips_buf_rewind( &progress->feedback );
148 	if( eta > 30 ) {
149 		int minutes = (eta + 30) / 60;
150 
151 		vips_buf_appendf( &progress->feedback, ngettext(
152 			"%d minute left", "%d minutes left",
153 			minutes ), minutes );
154 	}
155 	else if( eta > 5 )
156 		vips_buf_appendf( &progress->feedback, ngettext(
157 			"%d second left", "%d seconds left",
158 			eta ), eta );
159 	else
160 		/* The empty string changes the height of the progress bar
161 		 * argh.
162 		 */
163 		vips_buf_appendf( &progress->feedback, " " );
164 
165 	progress->percent = percent;
166 
167 	progress_update( progress );
168 
169 	return( progress->cancel );
170 }
171 
172 gboolean
progress_update_expr(Expr * expr)173 progress_update_expr( Expr *expr )
174 {
175 	Progress *progress = progress_get();
176 
177 	vips_buf_rewind( &progress->feedback );
178 	vips_buf_appends( &progress->feedback, _( "Calculating" ) );
179 	vips_buf_appends( &progress->feedback, " " );
180 	if( expr )
181 		expr_name( expr, &progress->feedback );
182 	else
183 		vips_buf_appends( &progress->feedback, symbol_get_last_calc() );
184 	vips_buf_appends( &progress->feedback, " ..." );
185 	progress->percent = 0;
186 
187 	progress_update( progress );
188 
189 	return( progress->cancel );
190 }
191 
192 gboolean
progress_update_loading(int percent,const char * filename)193 progress_update_loading( int percent, const char *filename )
194 {
195 	Progress *progress = progress_get();
196 
197 	vips_buf_rewind( &progress->feedback );
198 	vips_buf_appends( &progress->feedback, _( "Loading" ) );
199 	vips_buf_appendf( &progress->feedback, " \"%s\"", filename );
200 	progress->percent = percent;
201 
202 	progress_update( progress );
203 
204 	return( progress->cancel );
205 }
206 
207 gboolean
progress_update_tick(void)208 progress_update_tick( void )
209 {
210 	Progress *progress = progress_get();
211 
212 	progress_update( progress );
213 
214 	return( progress->cancel );
215 }
216 
217 void
progress_end(void)218 progress_end( void )
219 {
220 	Progress *progress = progress_get();
221 
222 	progress->count -= 1;
223 
224 #ifdef DEBUG
225 	printf( "progress_end: %d\n", progress->count );
226 #endif /*DEBUG*/
227 
228 	g_assert( progress->count >= 0 );
229 
230 	if( !progress->count ) {
231 		if( progress->busy )
232 			g_signal_emit( G_OBJECT( progress ),
233 				progress_signals[SIG_END], 0 );
234 
235 		progress->cancel = FALSE;
236 		progress->busy = FALSE;
237 
238 #ifdef DEBUG_MEMUSE
239 		printf( "progress_end:\n" );
240 		im__print_all();
241 #endif /*DEBUG_MEMUSE*/
242 	}
243 }
244 
245 static void
progress_class_init(ProgressClass * class)246 progress_class_init( ProgressClass *class )
247 {
248 	progress_parent_class = g_type_class_peek_parent( class );
249 
250 	progress_signals[SIG_BEGIN] = g_signal_new( "begin",
251 		G_OBJECT_CLASS_TYPE( class ),
252 		G_SIGNAL_RUN_FIRST,
253 		G_STRUCT_OFFSET( ProgressClass, begin ),
254 		NULL, NULL,
255 		g_cclosure_marshal_VOID__VOID,
256 		G_TYPE_NONE, 0 );
257 
258 	progress_signals[SIG_UPDATE] = g_signal_new( "update",
259 		G_OBJECT_CLASS_TYPE( class ),
260 		G_SIGNAL_RUN_FIRST,
261 		G_STRUCT_OFFSET( ProgressClass, update ),
262 		NULL, NULL,
263 		g_cclosure_marshal_VOID__POINTER,
264 		G_TYPE_NONE, 1,
265 		G_TYPE_POINTER );
266 
267 	progress_signals[SIG_END] = g_signal_new( "end",
268 		G_OBJECT_CLASS_TYPE( class ),
269 		G_SIGNAL_RUN_FIRST,
270 		G_STRUCT_OFFSET( ProgressClass, end ),
271 		NULL, NULL,
272 		g_cclosure_marshal_VOID__VOID,
273 		G_TYPE_NONE, 0 );
274 }
275 
276 static void
progress_init(Progress * progress)277 progress_init( Progress *progress )
278 {
279 #ifdef DEBUG
280 	printf( "progress_init\n" );
281 #endif /*DEBUG*/
282 
283 	progress->count = 0;
284 	progress->busy_timer = g_timer_new();
285 	progress->update_timer = g_timer_new();
286 	progress->cancel = FALSE;
287 	progress->busy = FALSE;
288 	vips_buf_init_static( &progress->feedback,
289 		progress->buf, PROGRESS_FEEDBACK_SIZE );
290 }
291 
292 GType
progress_get_type(void)293 progress_get_type( void )
294 {
295 	static GType type = 0;
296 
297 	if( !type ) {
298 		static const GTypeInfo info = {
299 			sizeof( ProgressClass ),
300 			NULL,           /* base_init */
301 			NULL,           /* base_finalize */
302 			(GClassInitFunc) progress_class_init,
303 			NULL,           /* class_finalize */
304 			NULL,           /* class_data */
305 			sizeof( Progress ),
306 			32,             /* n_preallocs */
307 			(GInstanceInitFunc) progress_init,
308 		};
309 
310 		type = g_type_register_static( TYPE_IOBJECT,
311 			"Progress", &info, 0 );
312 	}
313 
314 	return( type );
315 }
316 
317 static Progress *
progress_new(void)318 progress_new( void )
319 {
320 	Progress *progress = PROGRESS( g_object_new( TYPE_PROGRESS, NULL ) );
321 
322 	return( progress );
323 }
324 
325 Progress *
progress_get(void)326 progress_get( void )
327 {
328 	static Progress *progress = NULL;
329 
330 	if( !progress )
331 		progress = progress_new();
332 
333 	return( progress );
334 }
335