1 /* Make various little popup dialogs ... error, info, question.
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 option) 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
32  */
33 
34 #include "ip.h"
35 
36 /* Max amount of text in a info/error/question dialog.
37  */
38 #define MAX_DIALOG_TEXT (2000)
39 
40 /* Find a window to use as dialog parent.
41  */
42 static GtkWidget *
box_pick_parent(GtkWidget * par)43 box_pick_parent( GtkWidget *par )
44 {
45 	if( !par )
46 		return( GTK_WIDGET( mainw_pick_one() ) );
47 	else
48 		return( par );
49 }
50 
51 /* Make the insides of a error, info or question dialog.
52  */
53 static void
box_build(iDialog * idlg,GtkWidget * work,char * s,const char * stock_id)54 box_build( iDialog *idlg,
55 	GtkWidget *work, char *s, const char *stock_id )
56 {
57 	GtkWidget *icon;
58 	GtkWidget *hb;
59 	GtkWidget *lab;
60 
61 	hb = gtk_hbox_new( FALSE, 12 );
62 	gtk_container_border_width( GTK_CONTAINER( hb ), 0 );
63 	gtk_container_add( GTK_CONTAINER( work ), hb );
64 	gtk_widget_show( hb );
65 
66 	icon = gtk_image_new_from_stock( stock_id, GTK_ICON_SIZE_DIALOG );
67 	gtk_misc_set_alignment( GTK_MISC( icon ), 0.0, 0.0 );
68         gtk_box_pack_start( GTK_BOX( hb ), icon, FALSE, FALSE, 0 );
69 	gtk_widget_show( icon );
70 
71 	lab = gtk_label_new( NULL );
72 	gtk_label_set_markup( GTK_LABEL( lab ), s );
73         gtk_label_set_justify( GTK_LABEL( lab ), GTK_JUSTIFY_LEFT );
74         gtk_label_set_selectable( GTK_LABEL( lab ), TRUE );
75 	gtk_label_set_line_wrap( GTK_LABEL( lab ), TRUE );
76         gtk_box_pack_start( GTK_BOX( hb ), lab, FALSE, FALSE, 0 );
77 	gtk_widget_show( lab );
78 }
79 
80 /* Make an error dialog.
81  */
82 /*VARARGS2*/
83 static void
box_error(GtkWidget * par,const char * fmt,...)84 box_error( GtkWidget *par, const char *fmt, ... )
85 {
86 	va_list ap;
87 	char buf[MAX_DIALOG_TEXT];
88 	GtkWidget *idlg;
89 
90         va_start( ap, fmt );
91 	(void) im_vsnprintf( buf, MAX_DIALOG_TEXT, fmt, ap );
92         va_end( ap );
93 
94 	idlg = idialog_new();
95 	idialog_set_build( IDIALOG( idlg ),
96 		(iWindowBuildFn) box_build, buf, GTK_STOCK_DIALOG_ERROR, NULL );
97 	idialog_set_callbacks( IDIALOG( idlg ), NULL, NULL, NULL, NULL );
98 	idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK );
99 	iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) );
100 	iwindow_build( IWINDOW( idlg ) );
101 
102 	gtk_widget_show( GTK_WIDGET( idlg ) );
103 }
104 
105 /* Mark up a top/sub pair for a dialog box.
106  */
107 static void
box_vmarkup(char * out,const char * top,const char * sub,va_list ap)108 box_vmarkup( char *out, const char *top, const char *sub, va_list ap )
109 {
110 	char buf1[MAX_DIALOG_TEXT];
111 	char buf2[MAX_DIALOG_TEXT];
112 	char buf3[MAX_DIALOG_TEXT];
113 
114 	escape_markup( top, buf1, MAX_DIALOG_TEXT );
115 	(void) im_vsnprintf( buf2, MAX_DIALOG_TEXT, sub, ap );
116 	escape_markup( buf2, buf3, MAX_DIALOG_TEXT );
117 
118 	(void) im_snprintf( out, MAX_DIALOG_TEXT,
119 		"<b><big>%s</big></b>", buf1 );
120 	if( strcmp( buf3, "" ) != 0 ) {
121 		int len = strlen( out );
122 
123 		(void) im_snprintf( out + len, MAX_DIALOG_TEXT - len,
124 			"\n\n%s", buf3 );
125 	}
126 }
127 
128 static void
box_markup(char * out,const char * top,const char * sub,...)129 box_markup( char *out, const char *top, const char *sub, ... )
130 {
131 	va_list ap;
132 
133         va_start( ap, sub );
134 	box_vmarkup( out, top, sub, ap );
135         va_end( ap );
136 }
137 
138 /* Display buffered errors in an error dialog.
139  */
140 void
box_alert(GtkWidget * par)141 box_alert( GtkWidget *par )
142 {
143 	char buf[MAX_DIALOG_TEXT];
144 
145 	if( main_option_batch ) {
146 		/* No X, just print.
147 		 */
148 		fprintf( stderr, "%s\n", error_get_top() );
149 		fprintf( stderr, "%s\n", error_get_sub() );
150 		return;
151 	}
152 
153 	box_markup( buf, error_get_top(), "%s", error_get_sub() );
154 	box_error( par, "%s", buf );
155 }
156 
157 /* Make an information dialog.
158  */
159 void
box_vinfo(GtkWidget * par,const char * top,const char * sub,va_list ap)160 box_vinfo( GtkWidget *par, const char *top, const char *sub, va_list ap )
161 {
162 	char buf[MAX_DIALOG_TEXT];
163 	GtkWidget *idlg;
164 
165 	box_vmarkup( buf, top, sub, ap );
166 
167 	idlg = idialog_new();
168 	idialog_set_build( IDIALOG( idlg ),
169 		(iWindowBuildFn) box_build,
170 			buf, GTK_STOCK_DIALOG_INFO, NULL );
171 	idialog_set_callbacks( IDIALOG( idlg ), NULL, NULL, NULL, NULL );
172 	idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK );
173 	iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) );
174 	iwindow_build( IWINDOW( idlg ) );
175 
176 	gtk_widget_show( GTK_WIDGET( idlg ) );
177 }
178 
179 /* Make an information dialog.
180  */
181 void
box_info(GtkWidget * par,const char * top,const char * sub,...)182 box_info( GtkWidget *par, const char *top, const char *sub, ... )
183 {
184 	va_list ap;
185 
186         va_start( ap, sub );
187 	box_vinfo( par, top, sub, ap );
188         va_end( ap );
189 }
190 
191 /* Pop up an 'Are you sure?' window.
192  */
193 iDialog *
box_yesno(GtkWidget * par,iWindowFn okcb,iWindowFn cancelcb,void * client,iWindowNotifyFn nfn,void * sys,const char * yes_label,const char * top,const char * sub,...)194 box_yesno( GtkWidget *par,
195 	iWindowFn okcb, iWindowFn cancelcb, void *client, /* Call client */
196 	iWindowNotifyFn nfn, void *sys,			  /* Call parent */
197 	const char *yes_label,
198 	const char *top, const char *sub, ... )
199 {
200 	va_list ap;
201 	char buf[MAX_DIALOG_TEXT];
202 	GtkWidget *idlg;
203 
204 	va_start( ap, sub );
205 	box_vmarkup( buf, top, sub, ap );
206 	va_end( ap );
207 
208 	idlg = idialog_new();
209 	idialog_set_build( IDIALOG( idlg ),
210 		(iWindowBuildFn) box_build,
211 			buf, GTK_STOCK_DIALOG_QUESTION, NULL );
212 	idialog_set_callbacks( IDIALOG( idlg ), cancelcb, NULL, NULL, client );
213 	idialog_add_ok( IDIALOG( idlg ), okcb, "%s", yes_label );
214 	idialog_set_notify( IDIALOG( idlg ), nfn, sys );
215 	iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) );
216 	iwindow_build( IWINDOW( idlg ) );
217 
218 	gtk_widget_show( GTK_WIDGET( idlg ) );
219 
220 	return( IDIALOG( idlg ) );
221 }
222 
223 /* Pop up a `save'/`don't save'/`cancel' dialog.
224  */
225 void
box_savenosave(GtkWidget * par,iWindowFn save,iWindowFn nosave,void * client,iWindowNotifyFn nfn,void * sys,const char * top,const char * sub,...)226 box_savenosave( GtkWidget *par,
227 	iWindowFn save, iWindowFn nosave, void *client,   /* Call client */
228 	iWindowNotifyFn nfn, void *sys,			  /* Call parent */
229 	const char *top, const char *sub, ... )
230 {
231 	va_list ap;
232 	char buf[MAX_DIALOG_TEXT];
233 	GtkWidget *idlg;
234 
235 	va_start( ap, sub );
236 	box_vmarkup( buf, top, sub, ap );
237 	va_end( ap );
238 
239 	idlg = idialog_new();
240 	idialog_set_build( IDIALOG( idlg ),
241 		(iWindowBuildFn) box_build,
242 			buf, GTK_STOCK_DIALOG_QUESTION, NULL );
243 	idialog_set_callbacks( IDIALOG( idlg ),
244 		iwindow_true_cb, NULL, NULL, client );
245 	idialog_add_ok( IDIALOG( idlg ), nosave, _( "Close _without Saving" ) );
246 	idialog_add_ok( IDIALOG( idlg ), save, GTK_STOCK_SAVE );
247 	idialog_set_notify( IDIALOG( idlg ), nfn, sys );
248 	iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) );
249 	iwindow_build( IWINDOW( idlg ) );
250 
251 	gtk_widget_show( GTK_WIDGET( idlg ) );
252 }
253 
254 #define ABOUT(A) ((About *) (A))
255 
256 /* Make the insides of an about box.
257  */
258 static void
about_build(iDialog * idlg,GtkWidget * work)259 about_build( iDialog *idlg, GtkWidget *work )
260 {
261 	/* Translators: translate this to a credit for you, and it'll appear in
262 	 * the About box.
263 	 */
264 	char *translator_credits = _( "translator_credits" );
265 
266 	GtkWidget *hb;
267 	GtkWidget *lab;
268 	char txt[MAX_DIALOG_TEXT];
269 	char txt2[MAX_DIALOG_TEXT];
270 	VipsBuf buf = VIPS_BUF_STATIC( txt );
271 	GtkWidget *image;
272 
273 	im_snprintf( txt2, MAX_DIALOG_TEXT, _( "About %s." ), PACKAGE );
274 	vips_buf_appendf( &buf, "<b><big>%s</big></b>\n\n", txt2 );
275 	im_snprintf( txt2, MAX_DIALOG_TEXT,
276 		_( "%s is an image processing package." ), PACKAGE );
277 	vips_buf_appendf( &buf, "%s\n\n", txt2 );
278 
279 	im_snprintf( txt2, MAX_DIALOG_TEXT,
280 		_( "%s comes with ABSOLUTELY NO WARRANTY. This is "
281 		"free software and you are welcome to redistribute "
282 		"it under certain conditions, see http://www.gnu.org." ),
283 		PACKAGE );
284 	vips_buf_appendf( &buf, "%s\n\n", txt2 );
285 
286 	im_snprintf( txt2, MAX_DIALOG_TEXT, _( NIP_COPYRIGHT ), PACKAGE );
287 	vips_buf_appendf( &buf, "%s\n\n", txt2 );
288 
289 {
290 	char buf1[FILENAME_MAX];
291 	char buf2[FILENAME_MAX];
292 
293 	im_snprintf( buf1, FILENAME_MAX, "%s" G_DIR_SEPARATOR_S "start",
294 		get_savedir() );
295 	expand_variables( buf1, buf2 );
296         nativeize_path( buf2 );
297 	escape_markup( buf2, buf1, FILENAME_MAX );
298 	vips_buf_appendf( &buf, "<b>%s:</b> %s\n",
299 		_( "Personal start folder" ), buf1 );
300 }
301 
302 	vips_buf_appendf( &buf, "<b>%s:</b> %s\n",
303 		_( "Homepage" ), VIPS_HOMEPAGE );
304 	escape_markup( im_version_string(), txt2, MAX_DIALOG_TEXT );
305 	vips_buf_appendf( &buf, "<b>%s:</b> %s\n",
306 		_( "Linked to VIPS" ), txt2 );
307 	escape_markup( IM_VERSION_STRING, txt2, MAX_DIALOG_TEXT );
308 	vips_buf_appendf( &buf, "<b>%s:</b> %s\n",
309 		_( "Built against VIPS" ), txt2 );
310 	escape_markup( PACKAGE, txt2, MAX_DIALOG_TEXT );
311 	vips_buf_appendf( &buf, "<b>$PACKAGE:</b> %s\n", txt2 );
312 	escape_markup( VERSION, txt2, MAX_DIALOG_TEXT );
313 	vips_buf_appendf( &buf, "<b>$VERSION:</b> %s\n", txt2 );
314 	escape_markup( NN( g_getenv( "VIPSHOME" ) ), txt2, MAX_DIALOG_TEXT );
315 	vips_buf_appendf( &buf, "<b>$VIPSHOME:</b> %s\n", txt2 );
316 	escape_markup( NN( g_getenv( "HOME" ) ), txt2, MAX_DIALOG_TEXT );
317 	vips_buf_appendf( &buf, "<b>$HOME:</b> %s\n", txt2 );
318 	escape_markup( NN( g_getenv( "SAVEDIR" ) ), txt2, MAX_DIALOG_TEXT );
319 	vips_buf_appendf( &buf, "<b>$SAVEDIR:</b> %s\n", txt2 );
320 	escape_markup( PATH_TMP, txt2, MAX_DIALOG_TEXT );
321 	vips_buf_appendf( &buf, "<b>%s:</b> %s\n",
322 		_( "Temp files in" ), txt2 );
323 	if( strcmp( translator_credits, "translator_credits" ) != 0 ) {
324 		vips_buf_appendf( &buf, "\n" );
325 		vips_buf_appends( &buf, translator_credits );
326 	}
327 
328 	vips_buf_appendf( &buf, "\n" );
329 
330 	mainw_find_disc( &buf );
331 	/* Expands to (eg.) "14GB free in /pics/tmp" */
332         vips_buf_appendf( &buf, _( " in \"%s\"" ), PATH_TMP );
333         vips_buf_appends( &buf, "\n" );
334 
335         vips_buf_appendf( &buf,
336 		_( "%d cells in heap, %d cells free, %d cells maximum" ),
337                 reduce_context->heap->ncells,
338 		reduce_context->heap->nfree,
339 		reduce_context->heap->max_fn( reduce_context->heap ) );
340         vips_buf_appends( &buf, "\n" );
341 
342         vips_buf_appendf( &buf, _( "%d vips calls cached by nip2" ),
343 		cache_history_size );
344         vips_buf_appends( &buf, "\n" );
345 
346         vips_buf_appendf( &buf, _( "%d vips operations cached by libvips" ),
347 		vips_cache_get_size() );
348         vips_buf_appends( &buf, "\n" );
349 
350         vips_buf_appendf( &buf, _( "using %d threads" ), im_concurrency_get() );
351         vips_buf_appends( &buf, "\n" );
352 
353         vips_buf_appendf( &buf, _( "%d pixel buffers in vips" ),
354 		vips_tracked_get_allocs() );
355         vips_buf_appends( &buf, "\n" );
356 
357 	vips_buf_append_size( &buf, vips_tracked_get_mem() );
358         vips_buf_appendf( &buf, _( " of ram in pixel buffers" ) );
359         vips_buf_appends( &buf, "\n" );
360 
361 	vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
362         vips_buf_appendf( &buf, _( " of ram highwater mark" ) );
363         vips_buf_appends( &buf, "\n" );
364 
365 	hb = gtk_hbox_new( FALSE, 0 );
366 	gtk_container_border_width( GTK_CONTAINER( hb ), 10 );
367 	gtk_container_add( GTK_CONTAINER( work ), hb );
368 	gtk_widget_show( hb );
369 
370 	image = image_new_from_file(
371 		"$VIPSHOME/share/$PACKAGE/data/vips-128.png" );
372         gtk_box_pack_start( GTK_BOX( hb ), image, FALSE, FALSE, 2 );
373 	gtk_widget_show( image );
374 
375 	lab = gtk_label_new( "" );
376 	gtk_label_set_markup( GTK_LABEL( lab ), vips_buf_all( &buf ) );
377         gtk_label_set_justify( GTK_LABEL( lab ), GTK_JUSTIFY_LEFT );
378         gtk_label_set_selectable( GTK_LABEL( lab ), TRUE );
379 	gtk_label_set_line_wrap( GTK_LABEL( lab ), TRUE );
380         gtk_box_pack_start( GTK_BOX( hb ), lab, FALSE, FALSE, 2 );
381 	gtk_widget_show( lab );
382 }
383 
384 /* Pop up an "about" window.
385  */
386 void
box_about(GtkWidget * par)387 box_about( GtkWidget *par )
388 {
389 	GtkWidget *idlg;
390 
391 	idlg = idialog_new();
392 	idialog_set_build( IDIALOG( idlg ),
393 		(iWindowBuildFn) about_build, NULL, NULL, NULL );
394 	idialog_add_ok( IDIALOG( idlg ), iwindow_true_cb, GTK_STOCK_OK );
395 	iwindow_set_parent( IWINDOW( idlg ), box_pick_parent( par ) );
396 	iwindow_build( IWINDOW( idlg ) );
397 
398 	gtk_widget_show( GTK_WIDGET( idlg ) );
399 }
400 
401 /* A big list of all the help tags, plus the file and anchor they are defined
402  * in. See makehelpindex.pl.
403  */
404 static const char *box_helpindex[][2] = {
405 #include "helpindex.h"
406 };
407 
408 /* Pop up a help window for a tag.
409  */
410 void
box_help(GtkWidget * par,const char * name)411 box_help( GtkWidget *par, const char *name )
412 {
413 	int i;
414 
415 	for( i = 0; i < IM_NUMBER( box_helpindex ); i++ )
416 		if( strcmp( name, box_helpindex[i][0] ) == 0 ) {
417 			char url[512];
418 
419 			im_snprintf( url, 512, "file://%s/%s",
420 				NIP_DOCPATH, box_helpindex[i][1] );
421 			box_url( par, url );
422 			return;
423 		}
424 
425 	error_top( _( "Help page not found." ) );
426 	error_sub( _( "No indexed help page found for tag \"%s\"" ), name );
427 	iwindow_alert( par, GTK_MESSAGE_ERROR );
428 }
429 
430 /* Name + caption dialog ... for new workspace / new column.
431  */
432 
433 static iDialogClass *stringset_parent_class = NULL;
434 
435 void *
stringset_child_destroy(StringsetChild * ssc)436 stringset_child_destroy( StringsetChild *ssc )
437 {
438 	ssc->ss->children = g_slist_remove( ssc->ss->children, ssc );
439 
440 	IM_FREE( ssc->label );
441 	IM_FREE( ssc->text );
442 	IM_FREE( ssc->tooltip );
443 	IM_FREE( ssc );
444 
445 	return( NULL );
446 }
447 
448 StringsetChild *
stringset_child_new(Stringset * ss,const char * label,const char * text,const char * tooltip)449 stringset_child_new( Stringset *ss,
450 	const char *label, const char *text, const char *tooltip )
451 {
452 	StringsetChild *ssc = INEW( NULL, StringsetChild );
453 
454 	ssc->ss = ss;
455 	ssc->label = im_strdup( NULL, label );
456 	ssc->text = im_strdup( NULL, text );
457 	ssc->tooltip = im_strdup( NULL, tooltip );
458 
459 	ss->children = g_slist_append( ss->children, ssc );
460 
461 	return( ssc );
462 }
463 
464 static void
stringset_destroy(GtkObject * object)465 stringset_destroy( GtkObject *object )
466 {
467 	Stringset *ss;
468 
469 	g_return_if_fail( object != NULL );
470 	g_return_if_fail( IS_STRINGSET( object ) );
471 
472 	ss = STRINGSET( object );
473 
474 	slist_map( ss->children,
475 		(SListMapFn) stringset_child_destroy, NULL );
476 	UNREF( ss->group );
477 
478 	if( GTK_OBJECT_CLASS( stringset_parent_class )->destroy )
479 		GTK_OBJECT_CLASS( stringset_parent_class )->destroy( object );
480 }
481 
482 static void *
stringset_build_set_default(StringsetChild * ssc,iDialog * idlg)483 stringset_build_set_default( StringsetChild *ssc, iDialog *idlg )
484 {
485 	idialog_set_default_entry( idlg, GTK_ENTRY( ssc->entry ) );
486 
487 	return( NULL );
488 }
489 
490 static void
stringset_build(GtkWidget * widget)491 stringset_build( GtkWidget *widget )
492 {
493 	Stringset *ss = STRINGSET( widget );
494 	iDialog *idlg = IDIALOG( widget );
495 	GSList *p;
496 
497 #ifdef DEBUG
498 	printf( "stringset_build: %s\n", IWINDOW( ss )->title );
499 #endif /*DEBUG*/
500 
501 	/* Call all builds in superclasses.
502 	 */
503 	if( IWINDOW_CLASS( stringset_parent_class )->build )
504 		IWINDOW_CLASS( stringset_parent_class )->build( widget );
505 
506 	ss->group = gtk_size_group_new( GTK_SIZE_GROUP_HORIZONTAL );
507 
508 	for( p = ss->children; p; p = p->next ) {
509 		StringsetChild *ssc = (StringsetChild *) p->data;
510 
511 		ssc->entry =
512 			build_glabeltext4( idlg->work, ss->group, ssc->label );
513 		if( ssc->text )
514 			set_gentry( ssc->entry, "%s", ssc->text );
515 		if( ssc->tooltip )
516 			set_tooltip( ssc->entry, "%s", ssc->tooltip );
517 	}
518 
519 	/* Set defaults in reverse, so we get top item with focus.
520 	 */
521 	(void) slist_map_rev( ss->children,
522 		(SListMapFn) stringset_build_set_default, idlg );
523 
524 	gtk_widget_show_all( idlg->work );
525 }
526 
527 static void
stringset_class_init(StringsetClass * class)528 stringset_class_init( StringsetClass *class )
529 {
530 	GtkObjectClass *object_class;
531 	iWindowClass *iwindow_class;
532 
533 	object_class = (GtkObjectClass *) class;
534 	iwindow_class = (iWindowClass *) class;
535 
536 	object_class->destroy = stringset_destroy;
537 	iwindow_class->build = stringset_build;
538 
539 	stringset_parent_class = g_type_class_peek_parent( class );
540 }
541 
542 static void
stringset_init(Stringset * ss)543 stringset_init( Stringset *ss )
544 {
545 #ifdef DEBUG
546 	printf( "stringset_init: %s\n", IWINDOW( ss )->title );
547 #endif /*DEBUG*/
548 
549 	ss->children = NULL;
550 }
551 
552 GtkType
stringset_get_type(void)553 stringset_get_type( void )
554 {
555 	static GtkType stringset_type = 0;
556 
557 	if( !stringset_type ) {
558 		static const GtkTypeInfo info = {
559 			"Stringset",
560 			sizeof( Stringset ),
561 			sizeof( StringsetClass ),
562 			(GtkClassInitFunc) stringset_class_init,
563 			(GtkObjectInitFunc) stringset_init,
564 			/* reserved_1 */ NULL,
565 			/* reserved_2 */ NULL,
566 			(GtkClassInitFunc) NULL,
567 		};
568 
569 		stringset_type = gtk_type_unique( TYPE_IDIALOG, &info );
570 	}
571 
572 	return( stringset_type );
573 }
574 
575 GtkWidget *
stringset_new(void)576 stringset_new( void )
577 {
578 	Stringset *ss = gtk_type_new( TYPE_STRINGSET );
579 
580 	return( GTK_WIDGET( ss ) );
581 }
582 
583 StringsetChild *
stringset_child_get(Stringset * ss,const char * label)584 stringset_child_get( Stringset *ss, const char *label )
585 {
586 	GSList *p;
587 
588 	for( p = ss->children; p; p = p->next ) {
589 		StringsetChild *ssc = (StringsetChild *) p->data;
590 
591 		if( strcmp( label, ssc->label ) == 0 )
592 			return( ssc );
593 	}
594 
595 	return( NULL );
596 }
597 
598 /* Find dialog.
599  */
600 
601 static iDialogClass *find_parent_class = NULL;
602 
603 static void
find_build(GtkWidget * widget)604 find_build( GtkWidget *widget )
605 {
606 	Find *find = FIND( widget );
607 	iDialog *idlg = IDIALOG( widget );
608 
609 #ifdef DEBUG
610 	printf( "find_build: %s\n", IWINDOW( find )->title );
611 #endif /*DEBUG*/
612 
613 	/* Call all builds in superclasses.
614 	 */
615 	if( IWINDOW_CLASS( find_parent_class )->build )
616 		(*IWINDOW_CLASS( find_parent_class )->build)( widget );
617 
618 	find->search = build_glabeltext4( idlg->work, NULL, _( "Search for" ) );
619 	find->csens = build_gtoggle( idlg->work, _( "Case sensitive" ) );
620 #ifdef HAVE_GREGEX
621 	find->regexp = build_gtoggle( idlg->work, _( "Regular expression" ) );
622 #endif /*HAVE_GREGEX*/
623 	find->fromtop = build_gtoggle( idlg->work, _( "Search from start" ) );
624 	idialog_set_default_entry( idlg, GTK_ENTRY( find->search ) );
625 	gtk_widget_show_all( idlg->work );
626 }
627 
628 static void
find_class_init(FindClass * class)629 find_class_init( FindClass *class )
630 {
631 	iWindowClass *iwindow_class = (iWindowClass *) class;
632 
633 	iwindow_class->build = find_build;
634 
635 	find_parent_class = g_type_class_peek_parent( class );
636 }
637 
638 static void
find_init(Find * find)639 find_init( Find *find )
640 {
641 #ifdef DEBUG
642 	printf( "find_init: %s\n", IWINDOW( find )->title );
643 #endif /*DEBUG*/
644 
645 	idialog_set_pinup( IDIALOG( find ), TRUE );
646 }
647 
648 GtkType
find_get_type(void)649 find_get_type( void )
650 {
651 	static GtkType find_type = 0;
652 
653 	if( !find_type ) {
654 		static const GtkTypeInfo info = {
655 			"Find",
656 			sizeof( Find ),
657 			sizeof( FindClass ),
658 			(GtkClassInitFunc) find_class_init,
659 			(GtkObjectInitFunc) find_init,
660 			/* reserved_1 */ NULL,
661 			/* reserved_2 */ NULL,
662 			(GtkClassInitFunc) NULL,
663 		};
664 
665 		find_type = gtk_type_unique( TYPE_IDIALOG, &info );
666 	}
667 
668 	return( find_type );
669 }
670 
671 GtkWidget *
find_new(void)672 find_new( void )
673 {
674 	Find *find = gtk_type_new( TYPE_FIND );
675 
676 	return( GTK_WIDGET( find ) );
677 }
678 
679 /* Launch a viewer on a URL.
680  */
681 void
box_url(GtkWidget * par,const char * url)682 box_url( GtkWidget *par, const char *url )
683 {
684 #ifdef OS_WIN32
685 	char url2[FILENAME_MAX];
686 	int v;
687 
688 	expand_variables( url, url2 );
689 	v = (int) ShellExecute( NULL, "open", url2, NULL, NULL, SW_SHOWNORMAL );
690 	if( v <= 32 ) {
691 		error_top( _( "Unable to view help file." ) );
692 		error_sub( _( "Unable to open URL \"%s\", "
693 			"windows error code = %d." ), url, v );
694 		iwindow_alert( par, GTK_MESSAGE_ERROR );
695 	}
696 #elif defined OS_DARWIN
697 	(void) systemf( "open %s", url );
698 #elif defined HAVE_XDG_OPEN
699 	static gboolean shown = FALSE;
700 
701 	if( systemf( "%s %s", XDG_OPEN, url ) ) {
702 		error_top( _( "Unable to view help file." ) );
703 		error_sub( _( "Attempt to view URL with xdg-open failed\n%s" ),
704 			url );
705 		iwindow_alert( par, GTK_MESSAGE_ERROR );
706 	}
707 	else if( !shown ) {
708 		error_top( _( "Browser window opened." ) );
709 		error_sub( "%s",
710 			_( "You may need to switch desktops to see the "
711 			"new window." ) );
712 		iwindow_alert( par, GTK_MESSAGE_INFO );
713 		shown = TRUE;
714 	}
715 #else /*default unix-y*/
716 	static gboolean shown = FALSE;
717 
718 	char txt[512];
719 	VipsBuf buf = VIPS_BUF_STATIC( txt );
720 	char txt2[512];
721 	VipsBuf buf2 = VIPS_BUF_STATIC( txt2 );
722 
723 	char url2[FILENAME_MAX];
724 
725 	expand_variables( url, url2 );
726 
727 	vips_buf_appendf( &buf, "%s %s", BOX_BROWSER, BOX_BROWSER_REMOTE );
728 	vips_buf_appendf( &buf2, vips_buf_all( &buf ), url2 );
729 
730 	if( systemf( "%s", vips_buf_all( &buf2 ) ) ) {
731 		error_top( _( "Unable to view help file." ) );
732 		error_sub( _(
733 			"Attempted to launch browser with command:\n"
734 			"  %s\n"
735 			"You can change this command in Preferences." ),
736 			vips_buf_all( &buf2 ) );
737 		iwindow_alert( par, GTK_MESSAGE_ERROR );
738 	}
739 	else if( !shown ) {
740 		error_top( _( "Browser window opened." ) );
741 		error_sub( "%s",
742 			_( "You may need to switch desktops to see the "
743 			"new window." ) );
744 		iwindow_alert( par, GTK_MESSAGE_INFO );
745 		shown = TRUE;
746 	}
747 #endif /*lots*/
748 }
749 
750 /* Fontchooser dialog.
751  */
752 
753 static iDialogClass *fontchooser_parent_class = NULL;
754 
755 static void
fontchooser_build(GtkWidget * widget)756 fontchooser_build( GtkWidget *widget )
757 {
758 	Fontchooser *fontchooser = FONTCHOOSER( widget );
759 	iDialog *idlg = IDIALOG( widget );
760 
761 #ifdef DEBUG
762 	printf( "fontchooser_build: %s\n", IWINDOW( fontchooser )->title );
763 #endif /*DEBUG*/
764 
765 	/* Call all builds in superclasses.
766 	 */
767 	if( IWINDOW_CLASS( fontchooser_parent_class )->build )
768 		(*IWINDOW_CLASS( fontchooser_parent_class )->build)( widget );
769 
770 	fontchooser->fontchooser = gtk_font_selection_new();
771         gtk_box_pack_start( GTK_BOX( idlg->work ),
772 		fontchooser->fontchooser, TRUE, TRUE, 2 );
773 
774 	iwindow_set_title( IWINDOW( idlg ), _( "Select Font" ) );
775 
776 	gtk_widget_show_all( idlg->work );
777 }
778 
779 static void
fontchooser_class_init(FontchooserClass * class)780 fontchooser_class_init( FontchooserClass *class )
781 {
782 	iWindowClass *iwindow_class;
783 
784 	fontchooser_parent_class = g_type_class_peek_parent( class );
785 
786 	iwindow_class = (iWindowClass *) class;
787 
788 	iwindow_class->build = fontchooser_build;
789 }
790 
791 static void
fontchooser_init(Fontchooser * fontchooser)792 fontchooser_init( Fontchooser *fontchooser )
793 {
794 }
795 
796 GtkType
fontchooser_get_type(void)797 fontchooser_get_type( void )
798 {
799 	static GtkType fontchooser_type = 0;
800 
801 	if( !fontchooser_type ) {
802 		static const GtkTypeInfo info = {
803 			"Fontchooser",
804 			sizeof( Fontchooser ),
805 			sizeof( FontchooserClass ),
806 			(GtkClassInitFunc) fontchooser_class_init,
807 			(GtkObjectInitFunc) fontchooser_init,
808 			/* reserved_1 */ NULL,
809 			/* reserved_2 */ NULL,
810 			(GtkClassInitFunc) NULL,
811 		};
812 
813 		fontchooser_type = gtk_type_unique( TYPE_IDIALOG, &info );
814 	}
815 
816 	return( fontchooser_type );
817 }
818 
819 Fontchooser *
fontchooser_new(void)820 fontchooser_new( void )
821 {
822 	Fontchooser *fontchooser = gtk_type_new( TYPE_FONTCHOOSER );
823 
824 	return( fontchooser );
825 }
826 
827 gboolean
fontchooser_set_font_name(Fontchooser * fontchooser,const char * font_name)828 fontchooser_set_font_name( Fontchooser *fontchooser, const char *font_name )
829 {
830 	if( !gtk_font_selection_set_font_name(
831 		GTK_FONT_SELECTION( fontchooser->fontchooser ), font_name ) ) {
832 		error_top( _( "Font not found." ) );
833 		error_sub( _( "Font \"%s\" not found on system." ),
834 			font_name );
835 		return( FALSE );
836 	}
837 
838 	return( TRUE );
839 }
840 
841 char *
fontchooser_get_font_name(Fontchooser * fontchooser)842 fontchooser_get_font_name( Fontchooser *fontchooser )
843 {
844 	return( gtk_font_selection_get_font_name(
845 		GTK_FONT_SELECTION( fontchooser->fontchooser ) ) );
846 }
847 
848 /* Fontbutton.
849  */
850 
851 /* Our signals.
852  */
853 enum {
854 	SIG_CHANGED,	/* New font selected */
855 	SIG_LAST
856 };
857 
858 static GtkButtonClass *fontbutton_parent_class = NULL;
859 
860 static guint fontbutton_signals[SIG_LAST] = { 0 };
861 
862 static void
fontbutton_finalize(GObject * gobject)863 fontbutton_finalize( GObject *gobject )
864 {
865 	Fontbutton *fontbutton;
866 
867 	g_return_if_fail( gobject != NULL );
868 	g_return_if_fail( IS_FONTBUTTON( gobject ) );
869 
870 	fontbutton = FONTBUTTON( gobject );
871 
872 	IM_FREE( fontbutton->font_name );
873 
874 	G_OBJECT_CLASS( fontbutton_parent_class )->finalize( gobject );
875 }
876 
877 static void
fontbutton_ok_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)878 fontbutton_ok_cb( iWindow *iwnd, void *client,
879 	iWindowNotifyFn nfn, void *sys )
880 {
881 	Fontchooser *fontchooser = FONTCHOOSER( iwnd );
882 	Fontbutton *fontbutton = FONTBUTTON( client );
883 	char *font_name;
884 
885 	font_name = fontchooser_get_font_name( fontchooser );
886 	fontbutton_set_font_name( fontbutton, font_name );
887 	g_free( font_name );
888 
889 	nfn( sys, IWINDOW_YES );
890 }
891 
892 static void
fontbutton_popdown_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)893 fontbutton_popdown_cb( iWindow *iwnd, void *client,
894 	iWindowNotifyFn nfn, void *sys )
895 {
896 	Fontbutton *fontbutton = FONTBUTTON( client );
897 
898 	fontbutton->fontchooser = NULL;
899 
900 	nfn( sys, IWINDOW_YES );
901 }
902 
903 static void
fontbutton_clicked(GtkButton * button)904 fontbutton_clicked( GtkButton *button )
905 {
906 	Fontbutton *fontbutton = FONTBUTTON( button );
907 
908 	if( fontbutton->fontchooser )
909 		gtk_window_present( GTK_WINDOW( fontbutton->fontchooser ) );
910 	else {
911 		fontbutton->fontchooser = fontchooser_new();
912 		iwindow_set_title( IWINDOW( fontbutton->fontchooser ),
913 			_( "Pick a font" ) );
914 		idialog_set_callbacks( IDIALOG( fontbutton->fontchooser ),
915 			iwindow_true_cb, fontbutton_popdown_cb, NULL,
916 			fontbutton );
917 		idialog_add_ok( IDIALOG( fontbutton->fontchooser ),
918 			fontbutton_ok_cb, _( "Set Font" ) );
919 		iwindow_set_parent( IWINDOW( fontbutton->fontchooser ),
920 			GTK_WIDGET( button ) );
921 		idialog_set_pinup( IDIALOG( fontbutton->fontchooser ), TRUE );
922 		iwindow_build( IWINDOW( fontbutton->fontchooser ) );
923 		fontchooser_set_font_name( fontbutton->fontchooser,
924 			fontbutton->font_name );
925 
926 		gtk_widget_show( GTK_WIDGET( fontbutton->fontchooser ) );
927 	}
928 }
929 
930 static void
fontbutton_real_changed(Fontbutton * fontbutton)931 fontbutton_real_changed( Fontbutton *fontbutton )
932 {
933 }
934 
935 static void
fontbutton_class_init(FontbuttonClass * class)936 fontbutton_class_init( FontbuttonClass *class )
937 {
938 	GObjectClass *gobject_class = (GObjectClass *) class;
939 	GtkButtonClass *bobject_class = (GtkButtonClass *) class;
940 
941 	fontbutton_parent_class = g_type_class_peek_parent( class );
942 
943 	gobject_class->finalize = fontbutton_finalize;
944 
945 	bobject_class->clicked = fontbutton_clicked;
946 
947 	class->changed = fontbutton_real_changed;
948 
949 	fontbutton_signals[SIG_CHANGED] = g_signal_new( "changed",
950 		G_OBJECT_CLASS_TYPE( gobject_class ),
951 		G_SIGNAL_RUN_FIRST,
952 		G_STRUCT_OFFSET( FontbuttonClass, changed ),
953 		NULL, NULL,
954 		g_cclosure_marshal_VOID__VOID,
955 		G_TYPE_NONE, 0 );
956 }
957 
958 static void
fontbutton_init(Fontbutton * fontbutton)959 fontbutton_init( Fontbutton *fontbutton )
960 {
961 	fontbutton->font_name = NULL;
962 	fontbutton->fontchooser = NULL;
963 
964 	set_tooltip( GTK_WIDGET( fontbutton ), _( "Click to select font" ) );
965 }
966 
967 GtkType
fontbutton_get_type(void)968 fontbutton_get_type( void )
969 {
970 	static GtkType fontbutton_type = 0;
971 
972 	if( !fontbutton_type ) {
973 		static const GtkTypeInfo info = {
974 			"Fontbutton",
975 			sizeof( Fontbutton ),
976 			sizeof( FontbuttonClass ),
977 			(GtkClassInitFunc) fontbutton_class_init,
978 			(GtkObjectInitFunc) fontbutton_init,
979 			/* reserved_1 */ NULL,
980 			/* reserved_2 */ NULL,
981 			(GtkClassInitFunc) NULL,
982 		};
983 
984 		fontbutton_type = gtk_type_unique( GTK_TYPE_BUTTON, &info );
985 	}
986 
987 	return( fontbutton_type );
988 }
989 
990 Fontbutton *
fontbutton_new(void)991 fontbutton_new( void )
992 {
993 	Fontbutton *fontbutton = g_object_new( TYPE_FONTBUTTON,
994 		"label", "Sans 12", NULL );
995 
996 	return( fontbutton );
997 }
998 
999 void
fontbutton_set_font_name(Fontbutton * fontbutton,const char * font_name)1000 fontbutton_set_font_name( Fontbutton *fontbutton, const char *font_name )
1001 {
1002 	char font[256];
1003 	char button_text[256];
1004 	int i;
1005 
1006 	if( !fontbutton->font_name ||
1007 		strcmp( fontbutton->font_name, font_name ) != 0 ) {
1008 		IM_SETSTR( fontbutton->font_name, font_name );
1009 
1010 		im_strncpy( font, font_name, 256 );
1011 		for( i = strlen( font ) - 1; i > 0 && isdigit( font[i] ); i-- )
1012 			font[i] = '\0';
1013 		im_snprintf( button_text, 256,
1014 			"<span font_desc=\"%s\" size=\"medium\">%s</span>",
1015 			font, font_name );
1016 		gtk_label_set_markup(
1017 			GTK_LABEL( gtk_bin_get_child( GTK_BIN( fontbutton ) ) ),
1018 			button_text );
1019 
1020 		if( fontbutton->fontchooser )
1021 			fontchooser_set_font_name( fontbutton->fontchooser,
1022 				font_name );
1023 
1024 		g_signal_emit( G_OBJECT( fontbutton ),
1025 			fontbutton_signals[SIG_CHANGED], 0 );
1026 	}
1027 }
1028 
1029 const char *
fontbutton_get_font_name(Fontbutton * fontbutton)1030 fontbutton_get_font_name( Fontbutton *fontbutton )
1031 {
1032 	return( fontbutton->font_name );
1033 }
1034 
1035 /* Infobar. Optional: it's only in quite recent gtk.
1036  */
1037 #ifdef USE_INFOBAR
1038 
1039 static GtkInfoBarClass *infobar_parent_class = NULL;
1040 
1041 static void
infobar_destroy(GtkObject * object)1042 infobar_destroy( GtkObject *object )
1043 {
1044 	Infobar *infobar;
1045 
1046 	g_return_if_fail( object != NULL );
1047 	g_return_if_fail( IS_INFOBAR( object ) );
1048 
1049 	infobar = INFOBAR( object );
1050 
1051 	IM_FREEF( g_source_remove, infobar->close_timeout );
1052 	IM_FREEF( g_source_remove, infobar->close_animation_timeout );
1053 
1054 	GTK_OBJECT_CLASS( infobar_parent_class )->destroy( object );
1055 }
1056 
1057 static void
infobar_class_init(InfobarClass * class)1058 infobar_class_init( InfobarClass *class )
1059 {
1060 	GtkObjectClass *object_class = (GtkObjectClass *) class;
1061 
1062 	infobar_parent_class = g_type_class_peek_parent( class );
1063 
1064 	object_class->destroy = infobar_destroy;
1065 }
1066 
1067 static void
infobar_init(Infobar * infobar)1068 infobar_init( Infobar *infobar )
1069 {
1070 	infobar->top = NULL;
1071 	infobar->sub = NULL;
1072 	infobar->close_timeout = 0;
1073 	infobar->close_animation_timeout = 0;
1074 	infobar->height = 0;
1075 }
1076 
1077 GType
infobar_get_type(void)1078 infobar_get_type( void )
1079 {
1080 	static GType type = 0;
1081 
1082 	if( !type ) {
1083 		static const GTypeInfo info = {
1084 			sizeof( InfobarClass ),
1085 			NULL,           /* base_init */
1086 			NULL,           /* base_finalize */
1087 			(GClassInitFunc) infobar_class_init,
1088 			NULL,           /* class_finalize */
1089 			NULL,           /* class_data */
1090 			sizeof( Infobar ),
1091 			32,             /* n_preallocs */
1092 			(GInstanceInitFunc) infobar_init,
1093 		};
1094 
1095 		type = g_type_register_static( GTK_TYPE_INFO_BAR,
1096 			"Infobar", &info, 0 );
1097 	}
1098 
1099 	return( type );
1100 }
1101 
1102 static void
infobar_cancel_close(Infobar * infobar)1103 infobar_cancel_close( Infobar *infobar )
1104 {
1105 	IM_FREEF( g_source_remove, infobar->close_timeout );
1106 	IM_FREEF( g_source_remove, infobar->close_animation_timeout );
1107 	gtk_widget_set_size_request( GTK_WIDGET( infobar ), -1, -1 );
1108 }
1109 
1110 static void
infobar_hide(Infobar * infobar)1111 infobar_hide( Infobar *infobar )
1112 {
1113 	infobar_cancel_close( infobar );
1114 	gtk_widget_hide( GTK_WIDGET( infobar ) );
1115 	gtk_widget_hide( GTK_WIDGET( infobar->sub ) );
1116 	gtk_widget_set_sensitive( GTK_WIDGET( infobar->info ), TRUE );
1117 }
1118 
1119 static gboolean
infobar_close_animation_timeout(Infobar * infobar)1120 infobar_close_animation_timeout( Infobar *infobar )
1121 {
1122 	infobar->height -= 20;
1123 	if( infobar->height <= 0 ) {
1124 		infobar_hide( infobar );
1125 		return( FALSE );
1126 	}
1127 	gtk_widget_set_size_request( GTK_WIDGET( infobar ),
1128 		-1, infobar->height );
1129 
1130 	return( TRUE );
1131 }
1132 
1133 static void
infobar_start_close(Infobar * infobar)1134 infobar_start_close( Infobar *infobar )
1135 {
1136 	infobar_cancel_close( infobar );
1137 
1138 	infobar->height = GTK_WIDGET( infobar )->allocation.height;
1139 	infobar->close_animation_timeout = g_timeout_add( 50,
1140 		(GSourceFunc) infobar_close_animation_timeout, infobar );
1141 }
1142 
1143 static gboolean
infobar_close_timeout(Infobar * infobar)1144 infobar_close_timeout( Infobar *infobar )
1145 {
1146 	infobar_start_close( infobar );
1147 
1148 	return( FALSE );
1149 }
1150 
1151 static void
infobar_show(Infobar * infobar)1152 infobar_show( Infobar *infobar )
1153 {
1154 	infobar_cancel_close( infobar );
1155 
1156 	infobar->close_timeout = g_timeout_add( 5000,
1157 		(GSourceFunc) infobar_close_timeout, infobar );
1158 
1159 	gtk_widget_show( GTK_WIDGET( infobar ) );
1160 }
1161 
1162 static void
infobar_info_cb(GtkWidget * button,Infobar * infobar)1163 infobar_info_cb( GtkWidget *button, Infobar *infobar )
1164 {
1165 	infobar_cancel_close( infobar );
1166 	gtk_widget_show( GTK_WIDGET( infobar->sub ) );
1167 	gtk_widget_set_sensitive( GTK_WIDGET( infobar->info ), FALSE );
1168 }
1169 
1170 static void
infobar_close_cb(GtkWidget * button,Infobar * infobar)1171 infobar_close_cb( GtkWidget *button, Infobar *infobar )
1172 {
1173 	infobar_start_close( infobar );
1174 }
1175 
1176 Infobar *
infobar_new(void)1177 infobar_new( void )
1178 {
1179 	Infobar *infobar;
1180 	GtkWidget *vbox;
1181 	GtkWidget *content_area;
1182 	GtkWidget *hbox;
1183 	GtkWidget *action_area;
1184 	GtkWidget *button;
1185 
1186 	infobar = g_object_new( TYPE_INFOBAR, NULL );
1187 
1188 	vbox = gtk_vbox_new( FALSE, 10 );
1189 	content_area = gtk_info_bar_get_content_area( GTK_INFO_BAR( infobar ) );
1190 	gtk_container_add( GTK_CONTAINER( content_area ), vbox );
1191 	gtk_widget_show( vbox );
1192 
1193 	infobar->top = gtk_label_new( "" );
1194         gtk_label_set_justify( GTK_LABEL( infobar->top ), GTK_JUSTIFY_LEFT );
1195         gtk_label_set_selectable( GTK_LABEL( infobar->top ), TRUE );
1196 	gtk_label_set_line_wrap( GTK_LABEL( infobar->top ), TRUE );
1197 	gtk_container_add( GTK_CONTAINER( vbox ), infobar->top );
1198 	gtk_widget_show( infobar->top );
1199 
1200 	infobar->sub = gtk_label_new( "" );
1201         gtk_label_set_justify( GTK_LABEL( infobar->sub ), GTK_JUSTIFY_LEFT );
1202         gtk_label_set_selectable( GTK_LABEL( infobar->sub ), TRUE );
1203 	gtk_label_set_line_wrap( GTK_LABEL( infobar->sub ), TRUE );
1204 	gtk_container_add( GTK_CONTAINER( vbox ), infobar->sub );
1205 
1206 	/* We can't use gtk_info_bar_add_button(), we need the buttons
1207 	 * horizontally.
1208 	 */
1209 
1210 	hbox = gtk_hbox_new( FALSE, 2 );
1211 	action_area = gtk_info_bar_get_action_area( GTK_INFO_BAR( infobar ) );
1212 	gtk_container_add( GTK_CONTAINER( action_area ), hbox );
1213 	gtk_widget_show( hbox );
1214 
1215 	button = gtk_button_new_from_stock( GTK_STOCK_CLOSE );
1216         gtk_box_pack_end( GTK_BOX( hbox ), button, TRUE, TRUE, 2 );
1217 	g_signal_connect( button, "clicked",
1218 		G_CALLBACK( infobar_close_cb ), infobar );
1219 	gtk_widget_show( button );
1220 
1221 	infobar->info = gtk_button_new_from_stock( GTK_STOCK_INFO );
1222         gtk_box_pack_end( GTK_BOX( hbox ), infobar->info, TRUE, TRUE, 2 );
1223 	g_signal_connect( infobar->info, "clicked",
1224 		G_CALLBACK( infobar_info_cb ), infobar );
1225 	gtk_widget_show( infobar->info );
1226 
1227 	return( infobar );
1228 }
1229 
1230 #else /*!USE_INFOBAR*/
1231 
1232 Infobar *
infobar_new(void)1233 infobar_new( void )
1234 {
1235 	return( NULL );
1236 }
1237 
1238 #endif /*USE_INFOBAR*/
1239 
1240 /* Set the label on an infobar to some marked-up text.
1241  */
1242 void
infobar_vset(Infobar * infobar,GtkMessageType type,const char * top,const char * sub,va_list ap)1243 infobar_vset( Infobar *infobar, GtkMessageType type,
1244 	const char *top, const char *sub, va_list ap )
1245 {
1246 #ifdef USE_INFOBAR
1247 	char buf1[MAX_DIALOG_TEXT];
1248 	char buf2[MAX_DIALOG_TEXT];
1249 	char *p;
1250 
1251 	escape_markup( top, buf1, MAX_DIALOG_TEXT );
1252 	im_snprintf( buf2, MAX_DIALOG_TEXT, "<b>%s</b>", buf1 );
1253 	gtk_label_set_markup( GTK_LABEL( infobar->top ), buf2 );
1254 
1255 	(void) im_vsnprintf( buf1, MAX_DIALOG_TEXT, sub, ap );
1256 	escape_markup( buf1, buf2, MAX_DIALOG_TEXT );
1257 
1258 	/* Remove any trailing newlines, they make infobars rather large.
1259 	 */
1260 	while( (p = buf2 + strlen( buf2 )) > buf2 && p[-1] == '\n' )
1261 		p[-1] = '\0';
1262 
1263 	gtk_label_set_markup( GTK_LABEL( infobar->sub ), buf2 );
1264 
1265 	gtk_info_bar_set_message_type( GTK_INFO_BAR( infobar ), type );
1266 
1267 	infobar_show( infobar );
1268 #endif /*USE_INFOBAR*/
1269 }
1270 
1271 /* Set the label on an infobar to some marked-up text.
1272  */
1273 void
infobar_set(Infobar * infobar,GtkMessageType type,const char * top,const char * sub,...)1274 infobar_set( Infobar *infobar, GtkMessageType type,
1275 	const char *top, const char *sub, ... )
1276 {
1277 	va_list ap;
1278 
1279         va_start( ap, sub );
1280 	infobar_vset( infobar, type, top, sub, ap );
1281         va_end( ap );
1282 }
1283