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