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