1 /* main processing window
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 #include "ip.h"
31 
32 /*
33 #define DEBUG
34  */
35 
36 /* Load and save recent items here.
37  */
38 #define RECENT_WORKSPACE "recent_workspace"
39 #define RECENT_IMAGE "recent_image"
40 #define RECENT_MATRIX "recent_matrix"
41 
42 /* Recently loaded/saved workspaces, images and matricies.
43  */
44 GSList *mainw_recent_workspace = NULL;
45 GSList *mainw_recent_image = NULL;
46 GSList *mainw_recent_matrix = NULL;
47 
48 /* Auto-recalc state. Don't do this as a preference, since preferences are
49  * workspaces and need to have recalc working to operate.
50  */
51 gboolean mainw_auto_recalc = TRUE;
52 
53 static gint mainw_layout_timeout = 0;
54 
55 static iWindowClass *parent_class = NULL;
56 
57 /* All the mainw.
58  */
59 static GSList *mainw_all = NULL;
60 
61 void
mainw_startup(void)62 mainw_startup( void )
63 {
64 	IM_FREEF( recent_free, mainw_recent_workspace );
65 	IM_FREEF( recent_free, mainw_recent_image );
66 	IM_FREEF( recent_free, mainw_recent_matrix );
67 
68 	mainw_recent_workspace = recent_load( RECENT_WORKSPACE );
69 	mainw_recent_image = recent_load( RECENT_IMAGE );
70 	mainw_recent_matrix = recent_load( RECENT_MATRIX );
71 }
72 
73 void
mainw_shutdown(void)74 mainw_shutdown( void )
75 {
76 	recent_save( mainw_recent_workspace, RECENT_WORKSPACE );
77 	recent_save( mainw_recent_image, RECENT_IMAGE );
78 	recent_save( mainw_recent_matrix, RECENT_MATRIX );
79 
80 	IM_FREEF( recent_free, mainw_recent_workspace );
81 	IM_FREEF( recent_free, mainw_recent_image );
82 	IM_FREEF( recent_free, mainw_recent_matrix );
83 }
84 
85 static int mainw_recent_freeze_count = 0;
86 
87 void
mainw_recent_freeze(void)88 mainw_recent_freeze( void )
89 {
90 	mainw_recent_freeze_count += 1;
91 }
92 
93 void
mainw_recent_thaw(void)94 mainw_recent_thaw( void )
95 {
96 	g_assert( mainw_recent_freeze_count > 0 );
97 
98 	mainw_recent_freeze_count -= 1;
99 }
100 
101 void
mainw_recent_add(GSList ** recent,const char * filename)102 mainw_recent_add( GSList **recent, const char *filename )
103 {
104 	if( !mainw_recent_freeze_count ) {
105 		char buf[FILENAME_MAX];
106 
107 		im_strncpy( buf, PATH_TMP, FILENAME_MAX );
108 		path_expand( buf );
109 
110 		if( filename &&
111 			strcmp( filename, "" ) != 0 &&
112 			!is_prefix( buf, filename ) )
113 			*recent = recent_add( *recent, filename );
114 	}
115 }
116 
117 /* Pick a mainw at random. Used if we need a window for a dialog, and we're
118  * not sure which to pick.
119  */
120 Mainw *
mainw_pick_one(void)121 mainw_pick_one( void )
122 {
123 	if( !mainw_all )
124 		/* Must be a cast here, since iwindow_pick_one() can return
125 		 * NULL during shutdown.
126 		 */
127 		return( (Mainw *) iwindow_pick_one() );
128 
129 	return( MAINW( mainw_all->data ) );
130 }
131 
132 static void
mainw_finalize(GObject * gobject)133 mainw_finalize( GObject *gobject )
134 {
135 #ifdef DEBUG
136 	printf( "mainw_finalize: %p\n", gobject );
137 #endif /*DEBUG*/
138 
139 	g_return_if_fail( gobject != NULL );
140 	g_return_if_fail( IS_MAINW( gobject ) );
141 
142 	G_OBJECT_CLASS( parent_class )->finalize( gobject );
143 }
144 
145 static void
mainw_dispose(GObject * object)146 mainw_dispose( GObject *object )
147 {
148 	Mainw *mainw;
149 
150 	g_return_if_fail( object != NULL );
151 	g_return_if_fail( IS_MAINW( object ) );
152 
153 	mainw = MAINW( object );
154 
155 #ifdef DEBUG
156 	printf( "mainw_dispose\n" );
157 #endif /*DEBUG*/
158 
159 	IM_FREEF( g_source_remove, mainw->refresh_timeout );
160 
161 	FREESID( mainw->changed_sid, mainw->wsg );
162 
163 	FREESID( mainw->imageinfo_changed_sid, main_imageinfogroup );
164 	FREESID( mainw->heap_changed_sid, reduce_context->heap );
165 	FREESID( mainw->watch_changed_sid, main_watchgroup );
166 
167 	FREESID( mainw->begin_sid, progress_get() );
168 	FREESID( mainw->update_sid, progress_get() );
169 	FREESID( mainw->end_sid, progress_get() );
170 
171 	UNREF( mainw->kitgview );
172 
173 	/* We don't unref wsg: it's destroyed by mainw_popdown() with
174 	 * filemodel_inter_savenclose_cb().
175 	 */
176 
177 	mainw_all = g_slist_remove( mainw_all, mainw );
178 
179 	G_OBJECT_CLASS( parent_class )->dispose( object );
180 }
181 
182 static void *
mainw_configure_event_sub(Workspace * ws,GdkEventConfigure * event)183 mainw_configure_event_sub( Workspace *ws, GdkEventConfigure *event )
184 {
185 	MODEL( ws )->window_x = event->x;
186 	MODEL( ws )->window_y = event->y;
187 	MODEL( ws )->window_width = event->width;
188 	MODEL( ws )->window_height = event->height;
189 
190 	return( NULL );
191 }
192 
193 static gboolean
mainw_configure_event(GtkWidget * widget,GdkEventConfigure * event)194 mainw_configure_event( GtkWidget *widget, GdkEventConfigure *event )
195 {
196 	Mainw *mainw = MAINW( widget );
197 
198 	/* We have to record on all wses, since we don't know which will be
199 	 * first on reload.
200 	 */
201 	workspacegroup_map( mainw->wsg,
202 		(workspace_map_fn) mainw_configure_event_sub, event, NULL );
203 
204 	return( GTK_WIDGET_CLASS( parent_class )->
205 		configure_event( widget, event ) );
206 }
207 
208 static void
mainw_class_init(MainwClass * class)209 mainw_class_init( MainwClass *class )
210 {
211 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
212 	GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
213 
214 	parent_class = g_type_class_peek_parent( class );
215 
216 	gobject_class->finalize = mainw_finalize;
217 	gobject_class->dispose = mainw_dispose;
218 
219 	widget_class->configure_event = mainw_configure_event;
220 }
221 
222 static void
mainw_progress_begin(Progress * progress,Mainw * mainw)223 mainw_progress_begin( Progress *progress, Mainw *mainw )
224 {
225 	mainw->cancel = FALSE;
226         gtk_widget_show( mainw->progress_box );
227 }
228 
229 static void
mainw_progress_update(Progress * progress,gboolean * cancel,Mainw * mainw)230 mainw_progress_update( Progress *progress, gboolean *cancel, Mainw *mainw )
231 {
232 	gtk_progress_bar_set_text( GTK_PROGRESS_BAR( mainw->progress ),
233 		vips_buf_all( &progress->feedback ) );
234 	gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR( mainw->progress ),
235 		IM_CLIP( 0.0, (double) progress->percent / 100.0, 1.0 ) );
236 
237 	if( mainw->cancel )
238 		*cancel = TRUE;
239 }
240 
241 static void
mainw_progress_end(Progress * progress,Mainw * mainw)242 mainw_progress_end( Progress *progress, Mainw *mainw )
243 {
244         gtk_widget_hide( mainw->progress_box );
245 	mainw->cancel = FALSE;
246 }
247 
248 static void
mainw_init(Mainw * mainw)249 mainw_init( Mainw *mainw )
250 {
251 	mainw->wsg = NULL;
252 
253 	mainw->changed_sid = 0;
254 
255 	mainw->imageinfo_changed_sid = 0;
256 	mainw->heap_changed_sid = 0;
257 	mainw->watch_changed_sid = 0;
258 
259 	mainw->begin_sid = g_signal_connect( progress_get(), "begin",
260 		G_CALLBACK( mainw_progress_begin ), mainw );
261 	mainw->update_sid = g_signal_connect( progress_get(), "update",
262 		G_CALLBACK( mainw_progress_update ), mainw );
263 	mainw->end_sid = g_signal_connect( progress_get(), "end",
264 		G_CALLBACK( mainw_progress_end ), mainw );
265 	mainw->cancel = FALSE;
266 
267 	mainw->free_type = FALSE;
268 
269 	mainw->toolbar_visible = MAINW_TOOLBAR;
270 	mainw->statusbar_visible = MAINW_STATUSBAR;
271 
272 	mainw->kitgview = NULL;
273 	mainw->toolbar = NULL;
274 
275 	mainw->statusbar_main = NULL;
276 	mainw->statusbar = NULL;
277 	mainw->space_free = NULL;
278 	mainw->space_free_eb = NULL;
279 	mainw->progress_box = NULL;
280 	mainw->progress = NULL;
281 
282 	mainw_all = g_slist_prepend( mainw_all, mainw );
283 }
284 
285 GType
mainw_get_type(void)286 mainw_get_type( void )
287 {
288 	static GType type = 0;
289 
290 	if( !type ) {
291 		static const GTypeInfo info = {
292 			sizeof( MainwClass ),
293 			NULL,           /* base_init */
294 			NULL,           /* base_finalize */
295 			(GClassInitFunc) mainw_class_init,
296 			NULL,           /* class_finalize */
297 			NULL,           /* class_data */
298 			sizeof( Mainw ),
299 			32,             /* n_preallocs */
300 			(GInstanceInitFunc) mainw_init,
301 		};
302 
303 		type = g_type_register_static( TYPE_IWINDOW,
304 			"Mainw", &info, 0 );
305 	}
306 
307 	return( type );
308 }
309 
310 static void
mainw_cancel_cb(GtkWidget * wid,Mainw * mainw)311 mainw_cancel_cb( GtkWidget *wid, Mainw *mainw )
312 {
313 	mainw->cancel = TRUE;
314 }
315 
316 void
mainw_find_disc(VipsBuf * buf)317 mainw_find_disc( VipsBuf *buf )
318 {
319 	double sz = find_space( PATH_TMP );
320 
321 	if( sz < 0 )
322 		vips_buf_appendf( buf, _( "No temp area" ) );
323 	else {
324 		char txt[MAX_STRSIZE];
325 		VipsBuf buf2 = VIPS_BUF_STATIC( txt );
326 
327 		vips_buf_append_size( &buf2, sz );
328 		vips_buf_appendf( buf, _( "%s free" ), vips_buf_all( &buf2 ) );
329 	}
330 }
331 
332 void
mainw_find_heap(VipsBuf * buf,Heap * heap)333 mainw_find_heap( VipsBuf *buf, Heap *heap )
334 {
335 	/* How much we can still expand the heap by ... this
336 	 * can be -ve if we've closed a workspace, or changed
337 	 * the upper limit.
338 	 */
339 	int togo = IM_MAX( 0, (heap->mxb - heap->nb) * heap->rsz );
340 
341 	vips_buf_appendf( buf, _( "%d cells free" ), heap->nfree + togo );
342 }
343 
344 Workspace *
mainw_get_workspace(Mainw * mainw)345 mainw_get_workspace( Mainw *mainw )
346 {
347 	Workspace *ws;
348 
349 	if( mainw->wsg &&
350 		(ws = WORKSPACE( ICONTAINER( mainw->wsg )->current )) )
351 		return( ws );
352 
353 	return( NULL );
354 }
355 
356 Workspace *
mainw_next_workspace(Mainw * mainw)357 mainw_next_workspace( Mainw *mainw )
358 {
359 	Workspace *ws;
360 
361 	if( mainw->wsg &&
362 		(ws = WORKSPACE(
363 			icontainer_next( ICONTAINER( mainw->wsg ) ) )) )
364 		return( ws );
365 
366 	return( NULL );
367 }
368 
369 /* Update the space remaining indicator.
370  */
371 static void
mainw_free_update(Mainw * mainw)372 mainw_free_update( Mainw *mainw )
373 {
374 	Heap *heap = reduce_context->heap;
375 	char txt[80];
376 	VipsBuf buf = VIPS_BUF_STATIC( txt );
377 	Workspace *ws;
378 
379 	if( (ws = mainw_get_workspace( mainw )) &&
380 		workspace_selected_any( ws ) ) {
381 		vips_buf_appends( &buf, _( "Selected:" ) );
382 		vips_buf_appends( &buf, " " );
383 		workspace_selected_names( ws, &buf, ", " );
384 	}
385 	else {
386 		/* Out of space? Make sure we swap to cell display.
387 		 */
388 		if( !heap->free )
389 			mainw->free_type = FALSE;
390 
391 		if( mainw->free_type )
392 			mainw_find_heap( &buf, heap );
393 		else
394 			mainw_find_disc( &buf );
395 	}
396 
397 	set_glabel( mainw->space_free, "%s", vips_buf_all( &buf ) );
398 }
399 
400 static void
mainw_title_update(Mainw * mainw)401 mainw_title_update( Mainw *mainw )
402 {
403 	Workspace *ws;
404 	char txt[512];
405 	VipsBuf buf = VIPS_BUF_STATIC( txt );
406 	char *filename;
407 
408 	if( mainw->wsg &&
409 		FILEMODEL( mainw->wsg )->modified )
410 		vips_buf_appendf( &buf, "*" );
411 
412 	if( mainw->wsg &&
413 		(filename = FILEMODEL( mainw->wsg )->filename) ) {
414 		char *base = g_path_get_basename( filename );
415 		char *dir = g_path_get_dirname( filename );
416 
417 		vips_buf_appendf( &buf, "%s (%s)", base, dir );
418 
419 		g_free( base );
420 		g_free( dir );
421 	}
422 	else
423 		vips_buf_appends( &buf, _( "unsaved workspace" ) );
424 
425 	if( (ws = mainw_get_workspace( mainw )) ) {
426 		vips_buf_appends( &buf, " - " );
427 		vips_buf_appendf( &buf, "%s", NN( IOBJECT( ws->sym )->name ) );
428 		if( ws->compat_major ) {
429 			vips_buf_appends( &buf, " - " );
430 			vips_buf_appends( &buf, _( "compatibility mode" ) );
431 			vips_buf_appendf( &buf, " %d.%d",
432 				ws->compat_major,
433 				ws->compat_minor );
434 		}
435 	}
436 
437 	vips_buf_appendf( &buf, " - %s", PACKAGE );
438 
439 	iwindow_set_title( IWINDOW( mainw ), "%s", vips_buf_all( &buf ) );
440 }
441 
442 static void
mainw_status_update(Mainw * mainw)443 mainw_status_update( Mainw *mainw )
444 {
445 	Workspace *ws;
446 
447 	if( (ws = mainw_get_workspace( mainw )) &&
448 		ws->status )
449 		gtk_label_set_text( GTK_LABEL( mainw->statusbar ), ws->status );
450 	else {
451 		char txt[256];
452 
453 		im_snprintf( txt, 256, _( NIP_COPYRIGHT ), PACKAGE );
454 		gtk_label_set_markup( GTK_LABEL( mainw->statusbar ), txt );
455 	}
456 }
457 
458 static gboolean
mainw_refresh_timeout_cb(gpointer user_data)459 mainw_refresh_timeout_cb( gpointer user_data )
460 {
461 	static GtkToolbarStyle styles[] = {
462 		0,			/* Overwrite with system default */
463 		GTK_TOOLBAR_ICONS,
464 		GTK_TOOLBAR_TEXT,
465 		GTK_TOOLBAR_BOTH,
466 		GTK_TOOLBAR_BOTH_HORIZ
467 	};
468 	static gboolean inited_default_style = FALSE;
469 
470 	/* Keep in step with the WorkspaceMode enum.
471 	 */
472 	const static char *view_mode[] = {
473 		"Normal",
474 		"ShowFormula",
475 		"NoEdit"
476 	};
477 
478 	Mainw *mainw = MAINW( user_data );
479 	iWindow *iwnd = IWINDOW( mainw );
480 	int pref = IM_CLIP( 0, MAINW_TOOLBAR_STYLE, IM_NUMBER( styles ) - 1 );
481 
482         GtkAction *action;
483 	Workspace *ws;
484 
485 #ifdef DEBUG
486 	printf( "mainw_refresh_timeout_cb: %p\n", mainw );
487 #endif /*DEBUG*/
488 
489 	mainw->refresh_timeout = 0;
490 
491 	mainw_status_update( mainw );
492 	mainw_free_update( mainw );
493 	mainw_title_update( mainw );
494 
495 	if( !inited_default_style ) {
496 		styles[0] =
497 			gtk_toolbar_get_style( GTK_TOOLBAR( mainw->toolbar ) );
498 		inited_default_style = TRUE;
499 	}
500 
501 	gtk_toolbar_set_style( GTK_TOOLBAR( mainw->toolbar ), styles[pref] );
502 
503 	action = gtk_action_group_get_action( iwnd->action_group,
504 		"AutoRecalculate" );
505 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
506 		mainw_auto_recalc );
507 
508 	action = gtk_action_group_get_action( iwnd->action_group,
509 		"Toolbar" );
510 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
511 		mainw->toolbar_visible );
512         widget_visible( mainw->toolbar, mainw->toolbar_visible );
513 
514 	action = gtk_action_group_get_action( iwnd->action_group,
515 		"Statusbar" );
516 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
517 		mainw->statusbar_visible );
518         widget_visible( mainw->statusbar_main, mainw->statusbar_visible );
519 
520 	if( (ws = mainw_get_workspace( mainw )) ) {
521 		action = gtk_action_group_get_action( iwnd->action_group,
522 			"Lock" );
523 		gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
524 			ws->locked );
525 
526 		action = gtk_action_group_get_action( iwnd->action_group,
527 			"Tabdefs" );
528 		gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
529 			ws->lpane_open );
530 
531 		action = gtk_action_group_get_action( iwnd->action_group,
532 			"ToolkitBrowser" );
533 		gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
534 			ws->rpane_open );
535 
536 		action = gtk_action_group_get_action( iwnd->action_group,
537 			view_mode[ws->mode] );
538 		gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
539 			TRUE );
540 
541 		workspace_jump_update( ws, mainw->jump_to_column_menu );
542 
543 		if( mainw->kitg != ws->kitg ) {
544 			UNREF( mainw->kitgview );
545 
546 			mainw->kitgview = TOOLKITGROUPVIEW(
547 				model_view_new( MODEL( ws->kitg ), NULL ) );
548 			g_object_ref( G_OBJECT( mainw->kitgview ) );
549 			gtk_object_sink( GTK_OBJECT( mainw->kitgview ) );
550 			toolkitgroupview_set_mainw( mainw->kitgview, mainw );
551 			gtk_menu_set_accel_group(
552 				GTK_MENU( mainw->kitgview->menu ),
553 				iwnd->accel_group );
554 
555 			mainw->kitg = ws->kitg;
556 		}
557 	}
558 
559 	return( FALSE );
560 }
561 
562 static void
mainw_refresh(Mainw * mainw)563 mainw_refresh( Mainw *mainw )
564 {
565 	IM_FREEF( g_source_remove, mainw->refresh_timeout );
566 
567 	mainw->refresh_timeout = g_timeout_add( 100,
568 		(GSourceFunc) mainw_refresh_timeout_cb, mainw );
569 }
570 
571 static void
mainw_duplicate_action_cb(GtkAction * action,Mainw * mainw)572 mainw_duplicate_action_cb( GtkAction *action, Mainw *mainw )
573 {
574 	Workspacegroup *new_wsg;
575 	Mainw *new_mainw;
576 
577 	progress_begin();
578 
579 	if( !(new_wsg = workspacegroup_duplicate( mainw->wsg )) ) {
580 		progress_end();
581 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
582 		return;
583 	}
584 
585 	new_mainw = mainw_new( new_wsg );
586 	gtk_widget_show( GTK_WIDGET( new_mainw ) );
587 
588 	symbol_recalculate_all();
589 
590 	progress_end();
591 }
592 
593 static void
mainw_save_action_cb(GtkAction * action,Mainw * mainw)594 mainw_save_action_cb( GtkAction *action, Mainw *mainw )
595 {
596 	workspacegroup_set_save_type( mainw->wsg, WORKSPACEGROUP_SAVE_ALL );
597 	filemodel_inter_save( IWINDOW( mainw ), FILEMODEL( mainw->wsg ) );
598 }
599 
600 static void
mainw_save_as_action_cb(GtkAction * action,Mainw * mainw)601 mainw_save_as_action_cb( GtkAction *action, Mainw *mainw )
602 {
603 	workspacegroup_set_save_type( mainw->wsg, WORKSPACEGROUP_SAVE_ALL );
604 	filemodel_inter_saveas( IWINDOW( mainw ), FILEMODEL( mainw->wsg ) );
605 }
606 
607 /* Event in the "space free" display ... toggle mode on left click.
608  */
609 static gint
mainw_space_free_event(GtkWidget * widget,GdkEvent * ev,Mainw * mainw)610 mainw_space_free_event( GtkWidget *widget, GdkEvent *ev, Mainw *mainw )
611 {
612 	if( ev->type == GDK_BUTTON_RELEASE ) {
613 		mainw->free_type = !mainw->free_type;
614 		mainw_free_update( mainw );
615 	}
616 
617 	return( FALSE );
618 }
619 
620 /* Count number and sizes of all image objects.
621  */
622 
623 static void *
mainw_count_images(VipsObject * object,int * n)624 mainw_count_images( VipsObject *object, int *n )
625 {
626 	if( VIPS_IS_IMAGE( object ) )
627 		*n += 1;
628 
629 	return( NULL );
630 }
631 
632 static void *
mainw_size_images(VipsObject * object,size_t * size)633 mainw_size_images( VipsObject *object, size_t *size )
634 {
635 	if( VIPS_IS_IMAGE( object ) )
636 		*size += VIPS_IMAGE_SIZEOF_IMAGE( VIPS_IMAGE( object ) );
637 
638 	return( NULL );
639 }
640 
641 static void
mainw_space_free_tooltip_generate(GtkWidget * widget,VipsBuf * buf,Mainw * mainw)642 mainw_space_free_tooltip_generate( GtkWidget *widget, VipsBuf *buf,
643 	Mainw *mainw )
644 {
645 	Heap *heap = reduce_context->heap;
646 
647 	size_t size;
648 	int n;
649 
650 	mainw_find_disc( buf );
651 	/* Expands to (eg.) "14GB free in /pics/tmp" */
652         vips_buf_appendf( buf, _( " in \"%s\"" ), PATH_TMP );
653         vips_buf_appends( buf, ", " );
654 
655         vips_buf_appendf( buf,
656 		_( "%d cells in heap, %d cells free, %d cells maximum" ),
657                 heap->ncells, heap->nfree, heap->max_fn( heap ) );
658         vips_buf_appends( buf, ", " );
659 
660 	if( mainw->wsg ) {
661 		vips_buf_appendf( buf, _( "%d objects in workspace" ),
662 			workspacegroup_get_n_objects( mainw->wsg ) );
663 		vips_buf_appends( buf, ", " );
664 	}
665 
666         vips_buf_appendf( buf, _( "%d vips calls cached" ),
667 		cache_history_size );
668         vips_buf_appends( buf, ", " );
669 
670         vips_buf_appendf( buf, _( "using %d threads" ), im_concurrency_get() );
671         vips_buf_appends( buf, ", " );
672 
673 	size = 0;
674 	vips_object_map( (VipsSListMap2Fn) mainw_size_images, &size, NULL );
675 	n = 0;
676 	vips_object_map( (VipsSListMap2Fn) mainw_count_images, &n, NULL );
677 
678 	vips_buf_append_size( buf, size );
679         vips_buf_appendf( buf, _( " in %d images" ), n );
680 }
681 
682 static void
mainw_free_changed_cb(gpointer * dummy,Mainw * mainw)683 mainw_free_changed_cb( gpointer *dummy, Mainw *mainw )
684 {
685 	mainw_free_update( mainw );
686 	mainw_status_update( mainw );
687 }
688 
689 /* Go to home page.
690  */
691 void
mainw_homepage_action_cb(GtkAction * action,iWindow * iwnd)692 mainw_homepage_action_cb( GtkAction *action, iWindow *iwnd )
693 {
694 	box_url( GTK_WIDGET( iwnd ), VIPS_HOMEPAGE );
695 }
696 
697 /* About... box.
698  */
699 void
mainw_about_action_cb(GtkAction * action,iWindow * iwnd)700 mainw_about_action_cb( GtkAction *action, iWindow *iwnd )
701 {
702 	box_about( GTK_WIDGET( iwnd ) );
703 }
704 
705 /* User's guide.
706  */
707 void
mainw_guide_action_cb(GtkAction * action,iWindow * iwnd)708 mainw_guide_action_cb( GtkAction *action, iWindow *iwnd )
709 {
710 	box_url( GTK_WIDGET( iwnd ), "file://" NIP_DOCPATH "/nipguide.html" );
711 }
712 
713 static void
mainw_selected_duplicate_action_cb(GtkAction * action,Mainw * mainw)714 mainw_selected_duplicate_action_cb( GtkAction *action, Mainw *mainw )
715 {
716 	Workspace *ws;
717 
718 	progress_begin();
719 	if( (ws = mainw_get_workspace( mainw )) &&
720 		!workspace_selected_duplicate( ws ) )
721 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
722 	progress_end();
723 }
724 
725 /* Ungroup the selected object(s), or the bottom object.
726  */
727 static void
mainw_ungroup_action_cb(GtkAction * action,Mainw * mainw)728 mainw_ungroup_action_cb( GtkAction *action, Mainw *mainw )
729 {
730 	Workspace *ws;
731 
732 	progress_begin();
733 	if( (ws = mainw_get_workspace( mainw )) &&
734 		!workspace_selected_ungroup( ws ) )
735 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
736 	progress_end();
737 }
738 
739 /* Group the selected object(s).
740  */
741 void
mainw_group_action_cb(GtkAction * action,Mainw * mainw)742 mainw_group_action_cb( GtkAction *action, Mainw *mainw )
743 {
744 	Workspace *ws;
745 
746 	if( (ws = mainw_get_workspace( mainw )) )
747 		workspace_selected_group( ws );
748 }
749 
750 /* Select all objects.
751  */
752 static void
mainw_select_all_action_cb(GtkAction * action,Mainw * mainw)753 mainw_select_all_action_cb( GtkAction *action, Mainw *mainw )
754 {
755 	Workspace *ws;
756 
757 	if( (ws = mainw_get_workspace( mainw )) )
758 		workspace_select_all( ws );
759 }
760 
761 static void
mainw_find_action_cb(GtkAction * action,Mainw * mainw)762 mainw_find_action_cb( GtkAction *action, Mainw *mainw )
763 {
764 	error_top( _( "Not implemented." ) );
765 	error_sub( _( "Find in workspace not implemented yet." ) );
766 	iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO );
767 }
768 
769 static void
mainw_find_again_action_cb(GtkAction * action,Mainw * mainw)770 mainw_find_again_action_cb( GtkAction *action, Mainw *mainw )
771 {
772 	error_top( _( "Not implemented." ) );
773 	error_sub( _( "Find again in workspace not implemented yet." ) );
774 	iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO );
775 }
776 
777 void
mainw_next_error_action_cb(GtkAction * action,Mainw * mainw)778 mainw_next_error_action_cb( GtkAction *action, Mainw *mainw )
779 {
780 	Workspace *first_ws;
781 	Workspace *ws;
782 
783 	if( !(first_ws = mainw_get_workspace( mainw )) )
784 		return;
785 
786 	do {
787 		ws = mainw_get_workspace( mainw );
788 
789 		if( workspace_next_error( ws ) )
790 			return;
791 
792 		ws = mainw_next_workspace( mainw );
793 	} while( ws != first_ws );
794 
795 	error_top( _( "No errors in workspace." ) );
796 	error_sub( "%s", _( "There are no errors (that I can see) "
797 		"in this workspace." ) );
798 	iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO );
799 }
800 
801 static void
mainw_force_calc_action_cb(GtkAction * action,Mainw * mainw)802 mainw_force_calc_action_cb( GtkAction *action, Mainw *mainw )
803 {
804 	Workspace *ws;
805 
806 	if( (ws = mainw_get_workspace( mainw )) &&
807 		workspace_selected_any( ws ) ) {
808 		if( !workspace_selected_recalc( ws ) )
809 			iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
810 	}
811 	else
812 		symbol_recalculate_all_force( TRUE );
813 }
814 
815 Workspacegroup *
mainw_open_workspace(Workspaceroot * wsr,const char * filename)816 mainw_open_workspace( Workspaceroot *wsr, const char *filename )
817 {
818 	Workspacegroup *wsg;
819 	Mainw *mainw;
820 
821 	if( !(wsg = workspacegroup_new_from_file( wsr, filename, filename )) )
822 		return( NULL );
823 	mainw = mainw_new( wsg );
824 	gtk_widget_show( GTK_WIDGET( mainw ) );
825 
826 	return( wsg );
827 }
828 
829 /* Track these during a load.
830  */
831 typedef struct {
832 	Mainw *mainw;
833 	VipsBuf *buf;
834 	int nitems;
835 } MainwLoad;
836 
837 /* Try to open a file. Workspace files we load immediately, other ones we
838  * add the load to a buffer.
839  */
840 static void *
mainw_open_fn(Filesel * filesel,const char * filename,MainwLoad * load)841 mainw_open_fn( Filesel *filesel, const char *filename, MainwLoad *load )
842 {
843 	Workspaceroot *wsr = load->mainw->wsg->wsr;
844 
845 	if( is_file_type( &filesel_wfile_type, filename ) ) {
846 		if( !mainw_open_workspace( wsr, filename ) )
847 			return( filesel );
848 		mainw_recent_add( &mainw_recent_workspace, filename );
849 	}
850 	else {
851 		if( load->nitems )
852 			vips_buf_appends( load->buf, ", " );
853 		if( !workspace_load_file_buf( load->buf, filename ) )
854 			return( filesel );
855 		mainw_recent_add( &mainw_recent_image, filename );
856 		load->nitems += 1;
857 	}
858 
859 	return( NULL );
860 }
861 
862 /* Callback from load browser.
863  */
864 static void
mainw_open_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)865 mainw_open_done_cb( iWindow *iwnd, void *client,
866 	iWindowNotifyFn nfn, void *sys )
867 {
868 	Mainw *mainw = MAINW( client );
869 	Filesel *filesel = FILESEL( iwnd );
870 	char txt[MAX_STRSIZE];
871 	VipsBuf buf = VIPS_BUF_STATIC( txt );
872 	MainwLoad load;
873 	Workspace *ws;
874 
875 	load.mainw = mainw;
876 	load.buf = &buf;
877 	load.nitems = 0;
878 
879 	if( filesel_map_filename_multi( filesel,
880 		(FileselMapFn) mainw_open_fn, &load, NULL ) ) {
881 		nfn( sys, IWINDOW_ERROR );
882 		return;
883 	}
884 
885 	/* Some actual files (image, matrix) were selected. Load into
886 	 * the current workspace.
887 	 */
888 	if( load.nitems &&
889 		(ws = mainw_get_workspace( mainw )) ) {
890 		char txt2[MAX_STRSIZE];
891 		VipsBuf buf2 = VIPS_BUF_STATIC( txt2 );
892 
893 		if( load.nitems > 1 )
894 			vips_buf_appendf( &buf2, "Group [%s]",
895 				vips_buf_all( &buf ) );
896 		else
897 			vips_buf_appends( &buf2, vips_buf_all( &buf ) );
898 
899 		if( !workspace_add_def_recalc( ws, vips_buf_all( &buf2 ) ) ) {
900 			error_top( _( "Load failed." ) );
901 			error_sub( _( "Unable to execute:\n   %s" ),
902 				vips_buf_all( &buf2 ) );
903 			nfn( sys, IWINDOW_ERROR );
904 			return;
905 		}
906 	}
907 
908 	/* Wses will need a recalc.
909 	 */
910 	symbol_recalculate_all();
911 
912 	/* If we had an empty wsg, perhaps we've just started up,
913 	 * kill it.
914 	 */
915 	if( workspacegroup_is_empty( mainw->wsg ) ) {
916 		filemodel_set_modified( FILEMODEL( mainw->wsg ), FALSE );
917 		iwindow_kill( IWINDOW( mainw ) );
918 	}
919 
920 	nfn( sys, IWINDOW_YES );
921 }
922 
923 /* Show an open file dialog ... any type, but default to one of the image
924  * ones.
925  */
926 static void
mainw_open(Mainw * mainw)927 mainw_open( Mainw *mainw )
928 {
929 	GtkWidget *filesel = filesel_new();
930 
931 	iwindow_set_title( IWINDOW( filesel ), _( "Open File" ) );
932 	filesel_set_flags( FILESEL( filesel ), TRUE, FALSE );
933 	filesel_set_filetype( FILESEL( filesel ),
934 		filesel_type_mainw, IMAGE_FILE_TYPE );
935 	filesel_set_filetype_pref( FILESEL( filesel ), "IMAGE_FILE_TYPE" );
936 	iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) );
937 	filesel_set_done( FILESEL( filesel ),
938 		mainw_open_done_cb, mainw );
939 	filesel_set_multi( FILESEL( filesel ), TRUE );
940 	iwindow_build( IWINDOW( filesel ) );
941 
942 	gtk_widget_show( GTK_WIDGET( filesel ) );
943 }
944 
945 void
mainw_open_action_cb(GtkAction * action,Mainw * mainw)946 mainw_open_action_cb( GtkAction *action, Mainw *mainw )
947 {
948 	mainw_open( mainw );
949 }
950 
951 /* Open one of the example workspaces ... just a shortcut.
952  */
953 static void
mainw_open_examples(Mainw * mainw)954 mainw_open_examples( Mainw *mainw )
955 {
956 	GtkWidget *filesel = filesel_new();
957 
958 	iwindow_set_title( IWINDOW( filesel ), _( "Open File" ) );
959 	filesel_set_flags( FILESEL( filesel ), TRUE, FALSE );
960 	filesel_set_filetype( FILESEL( filesel ),
961 		filesel_type_workspace, 0 );
962 	iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) );
963 	filesel_set_done( FILESEL( filesel ),
964 		mainw_open_done_cb, mainw );
965 	filesel_set_multi( FILESEL( filesel ), TRUE );
966 	iwindow_build( IWINDOW( filesel ) );
967 	filesel_set_filename( FILESEL( filesel ),
968 		"$VIPSHOME" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S
969 		PACKAGE G_DIR_SEPARATOR_S "data" G_DIR_SEPARATOR_S
970 		"examples" G_DIR_SEPARATOR_S "1_point_mosaic" );
971 
972 	/* Reset the filetype ... setting the filename will have changed it to
973 	 * 'all', since there's no suffix.
974 	 */
975 	filesel_set_filetype( FILESEL( filesel ),
976 		filesel_type_workspace, 0 );
977 
978 	gtk_widget_show( GTK_WIDGET( filesel ) );
979 }
980 
981 static void
mainw_open_examples_action_cb(GtkAction * action,Mainw * mainw)982 mainw_open_examples_action_cb( GtkAction *action, Mainw *mainw )
983 {
984 	mainw_open_examples( mainw );
985 }
986 
987 static gboolean
mainw_recent_open(Mainw * mainw,const char * filename)988 mainw_recent_open( Mainw *mainw, const char *filename )
989 {
990 	Workspacegroup *wsg = mainw->wsg;
991 
992 	if( is_file_type( &filesel_wfile_type, filename ) ) {
993 		if( !mainw_open_workspace( wsg->wsr, filename ) )
994 			return( FALSE );
995 
996 		/* If we had an empty wsg, perhaps we've just started up,
997 		 * kill it.
998 		 */
999 		if( workspacegroup_is_empty( wsg ) ) {
1000 			filemodel_set_modified( FILEMODEL( wsg ), FALSE );
1001 			iwindow_kill( IWINDOW( mainw ) );
1002 		}
1003 	}
1004 	else {
1005 		Workspace *ws;
1006 
1007 		if( (ws = mainw_get_workspace( mainw )) &&
1008 			!workspace_load_file( ws, filename ) )
1009 			return( FALSE );
1010 	}
1011 
1012 	return( TRUE );
1013 }
1014 
1015 static void
mainw_recent_open_cb(GtkWidget * widget,const char * filename)1016 mainw_recent_open_cb( GtkWidget *widget, const char *filename )
1017 {
1018 	Mainw *mainw = MAINW( iwindow_get_root( widget ) );
1019 
1020 	progress_begin();
1021 	if( !mainw_recent_open( mainw, filename ) ) {
1022 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
1023 		progress_end();
1024 		return;
1025 	}
1026 	progress_end();
1027 
1028 	symbol_recalculate_all();
1029 }
1030 
1031 static void
mainw_recent_build(GtkWidget * menu,GSList * recent)1032 mainw_recent_build( GtkWidget *menu, GSList *recent )
1033 {
1034 	GSList *p;
1035 
1036 	for( p = recent; p; p = p->next ) {
1037 		const char *filename = (const char *) p->data;
1038 		GtkWidget *item;
1039 		char txt[80];
1040 		VipsBuf buf = VIPS_BUF_STATIC( txt );
1041 		char *utf8;
1042 
1043 		vips_buf_appendf( &buf, "    %s", im_skip_dir( filename ) );
1044 		utf8 = f2utf8( vips_buf_all( &buf ) );
1045 		item = gtk_menu_item_new_with_label( utf8 );
1046 		g_free( utf8 );
1047 		utf8 = f2utf8( filename );
1048 		set_tooltip( item, "%s", utf8 );
1049 		g_free( utf8 );
1050 		gtk_menu_append( GTK_MENU( menu ), item );
1051 		gtk_widget_show( item );
1052 		gtk_signal_connect( GTK_OBJECT( item ), "activate",
1053 			GTK_SIGNAL_FUNC( mainw_recent_open_cb ),
1054 			(char *) filename );
1055 	}
1056 }
1057 
1058 static void
mainw_recent_clear_cb(GtkWidget * widget,const char * filename)1059 mainw_recent_clear_cb( GtkWidget *widget, const char *filename )
1060 {
1061 	IM_FREEF( recent_free, mainw_recent_workspace );
1062 	IM_FREEF( recent_free, mainw_recent_image );
1063 	IM_FREEF( recent_free, mainw_recent_matrix );
1064 
1065 	/* Need to remove files too to prevent merge on quit.
1066 	 */
1067 	(void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s",
1068 		get_savedir(), RECENT_WORKSPACE );
1069 	(void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s",
1070 		get_savedir(), RECENT_IMAGE );
1071 	(void) unlinkf( "%s" G_DIR_SEPARATOR_S "%s",
1072 		get_savedir(), RECENT_MATRIX );
1073 }
1074 
1075 static void
mainw_recent_map_cb(GtkWidget * widget,Mainw * mainw)1076 mainw_recent_map_cb( GtkWidget *widget, Mainw *mainw )
1077 {
1078 	GtkWidget *menu = mainw->recent_menu;
1079 	GtkWidget *item;
1080 
1081 	gtk_container_foreach( GTK_CONTAINER( menu ),
1082 		(GtkCallback) gtk_widget_destroy, NULL );
1083 
1084 	if( mainw_recent_image ) {
1085 		item = gtk_menu_item_new_with_label( _( "Recent Images" ) );
1086 		gtk_menu_append( GTK_MENU( menu ), item );
1087 		gtk_widget_show( item );
1088 		gtk_widget_set_sensitive( item, FALSE );
1089 		mainw_recent_build( menu, mainw_recent_image );
1090 	}
1091 
1092 	if( mainw_recent_workspace ) {
1093 		item = gtk_menu_item_new_with_label( _( "Recent Workspaces" ) );
1094 		gtk_menu_append( GTK_MENU( menu ), item );
1095 		gtk_widget_show( item );
1096 		gtk_widget_set_sensitive( item, FALSE );
1097 		mainw_recent_build( menu, mainw_recent_workspace );
1098 	}
1099 
1100 	if( mainw_recent_matrix ) {
1101 		item = gtk_menu_item_new_with_label( _( "Recent Matricies" ) );
1102 		gtk_menu_append( GTK_MENU( menu ), item );
1103 		gtk_widget_show( item );
1104 		gtk_widget_set_sensitive( item, FALSE );
1105 		mainw_recent_build( menu, mainw_recent_matrix );
1106 	}
1107 
1108 	if( !mainw_recent_workspace && !mainw_recent_image &&
1109 		!mainw_recent_matrix ) {
1110 		item = gtk_menu_item_new_with_label( _( "No recent items" ) );
1111 		gtk_menu_append( GTK_MENU( menu ), item );
1112 		gtk_widget_show( item );
1113 		gtk_widget_set_sensitive( item, FALSE );
1114 	}
1115 	else {
1116 		item = gtk_menu_item_new_with_label( _( "Clear Recent Menu" ) );
1117 		gtk_menu_append( GTK_MENU( menu ), item );
1118 		gtk_widget_show( item );
1119 		gtk_signal_connect( GTK_OBJECT( item ), "activate",
1120 			GTK_SIGNAL_FUNC( mainw_recent_clear_cb ), NULL );
1121 	}
1122 }
1123 
1124 /* Merge a .ws into this wsg.
1125  */
1126 static void *
mainw_workspace_merge_fn(Filesel * filesel,const char * filename,void * a,void * b)1127 mainw_workspace_merge_fn( Filesel *filesel,
1128 	const char *filename, void *a, void *b )
1129 {
1130 	Mainw *mainw = MAINW( a );
1131 
1132 	if( !workspacegroup_merge_workspaces( mainw->wsg, filename ) )
1133 		return( filesel );
1134 
1135 	/* Process some events to make sure we rethink the layout and
1136 	 * are able to get the append-at-RHS offsets.
1137 	 */
1138 	process_events();
1139 
1140 	symbol_recalculate_all();
1141 	mainw_recent_add( &mainw_recent_workspace, filename );
1142 
1143 	return( NULL );
1144 }
1145 
1146 /* Callback from load browser.
1147  */
1148 static void
mainw_workspace_merge_done_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1149 mainw_workspace_merge_done_cb( iWindow *iwnd,
1150 	void *client, iWindowNotifyFn nfn, void *sys )
1151 {
1152 	Filesel *filesel = FILESEL( iwnd );
1153 	Mainw *mainw = MAINW( client );
1154 
1155 	if( filesel_map_filename_multi( filesel,
1156 		mainw_workspace_merge_fn, mainw, NULL ) ) {
1157 		symbol_recalculate_all();
1158 		nfn( sys, IWINDOW_ERROR );
1159 		return;
1160 	}
1161 
1162 	symbol_recalculate_all();
1163 
1164 	nfn( sys, IWINDOW_YES );
1165 }
1166 
1167 /* Merge ws file into current ws.
1168  */
1169 void
mainw_workspace_merge(Mainw * mainw)1170 mainw_workspace_merge( Mainw *mainw )
1171 {
1172 	GtkWidget *filesel = filesel_new();
1173 
1174 	iwindow_set_title( IWINDOW( filesel ),
1175 		_( "Merge Workspace from File" ) );
1176 	filesel_set_flags( FILESEL( filesel ), FALSE, FALSE );
1177 	filesel_set_filetype( FILESEL( filesel ), filesel_type_workspace, 0 );
1178 	iwindow_set_parent( IWINDOW( filesel ), GTK_WIDGET( mainw ) );
1179 	filesel_set_done( FILESEL( filesel ),
1180 		mainw_workspace_merge_done_cb, mainw );
1181 	filesel_set_multi( FILESEL( filesel ), TRUE );
1182 	iwindow_build( IWINDOW( filesel ) );
1183 
1184 	gtk_widget_show( GTK_WIDGET( filesel ) );
1185 }
1186 
1187 void
mainw_workspace_merge_action_cb(GtkAction * action,Mainw * mainw)1188 mainw_workspace_merge_action_cb( GtkAction *action, Mainw *mainw )
1189 {
1190 	mainw_workspace_merge( mainw );
1191 }
1192 
1193 /* Load a workspace, called from a yesno dialog.
1194  */
1195 static void
mainw_auto_recover_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1196 mainw_auto_recover_cb( iWindow *iwnd,
1197 	void *client, iWindowNotifyFn nfn, void *sys )
1198 {
1199 	char *filename = (char *) client;
1200 
1201 	Workspacegroup *new_wsg;
1202 	Mainw *new_mainw;
1203 
1204         progress_begin();
1205 
1206 	if( !(new_wsg = workspacegroup_new_from_file( main_workspaceroot,
1207 		filename, filename )) ) {
1208 		progress_end();
1209 		nfn( sys, IWINDOW_ERROR );
1210 		return;
1211 	}
1212 
1213 	filemodel_set_filename( FILEMODEL( new_wsg ), NULL );
1214 
1215 	new_mainw = mainw_new( new_wsg );
1216 	gtk_widget_show( GTK_WIDGET( new_mainw ) );
1217 
1218 	symbol_recalculate_all();
1219 
1220 	progress_end();
1221 
1222 	nfn( sys, IWINDOW_YES );
1223 }
1224 
1225 /* Auto recover.
1226  */
1227 static void
mainw_recover_action_cb(GtkAction * action,Mainw * mainw)1228 mainw_recover_action_cb( GtkAction *action, Mainw *mainw )
1229 {
1230 	char *filename;
1231 
1232 	if( !(filename = workspacegroup_autosave_recover()) ) {
1233 		if( !AUTO_WS_SAVE ) {
1234 			error_top( _( "No backup workspaces found." ) );
1235 			error_sub( "%s",
1236 				_( "You need to enable \"Auto workspace "
1237 				"save\" in Preferences "
1238 				"before automatic recovery works." ) );
1239 		}
1240 		else {
1241 			error_top( _( "No backup workspaces found." ) );
1242 			error_sub( _( "No suitable workspace save files found "
1243 				"in \"%s\"" ), PATH_TMP );
1244 		}
1245 
1246 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_INFO );
1247 		return;
1248 	}
1249 
1250 	/* Tricksy ... free str with notify callack from yesno.
1251 	 */
1252 
1253 	box_yesno( GTK_WIDGET( mainw ),
1254 		mainw_auto_recover_cb, iwindow_true_cb, filename,
1255 		(iWindowNotifyFn) im_free, filename,
1256 		GTK_STOCK_OPEN,
1257 		_( "Open workspace backup?" ),
1258 		_( "Found workspace backup:\n\n\t%s\n\n"
1259 		"Do you want to recover this workspace?" ),
1260 		filename );
1261 }
1262 
1263 /* Callback from make new column.
1264  */
1265 void
mainw_column_new_action_cb(GtkAction * action,Mainw * mainw)1266 mainw_column_new_action_cb( GtkAction *action, Mainw *mainw )
1267 {
1268 	Workspace *ws;
1269 
1270 	if( (ws = mainw_get_workspace( mainw )) )
1271 		(void) workspace_column_new( ws );
1272 }
1273 
1274 /* Done button hit.
1275  */
1276 static void
mainw_column_new_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1277 mainw_column_new_cb( iWindow *iwnd, void *client,
1278 	iWindowNotifyFn nfn, void *sys )
1279 {
1280 	Mainw *mainw = MAINW( client );
1281 	Stringset *ss = STRINGSET( iwnd );
1282 	StringsetChild *name = stringset_child_get( ss, _( "Name" ) );
1283 	StringsetChild *caption = stringset_child_get( ss, _( "Caption" ) );
1284 
1285 	Workspace *ws;
1286 	Column *col;
1287 
1288 	char name_text[1024];
1289 	char caption_text[1024];
1290 
1291 	if( !(ws = mainw_get_workspace( mainw )) ) {
1292 		nfn( sys, IWINDOW_YES );
1293 		return;
1294 	}
1295 
1296 	if( !get_geditable_name( name->entry, name_text, 1024 ) ||
1297 		!get_geditable_string( caption->entry, caption_text, 1024 ) ) {
1298 		nfn( sys, IWINDOW_ERROR );
1299 		return;
1300 	}
1301 
1302 	if( !(col = column_new( ws, name_text )) ) {
1303 		nfn( sys, IWINDOW_ERROR );
1304 		return;
1305 	}
1306 
1307 	if( strcmp( caption_text, "" ) != 0 )
1308 		iobject_set( IOBJECT( col ), NULL, caption_text );
1309 
1310 	workspace_column_select( ws, col );
1311 
1312 	column_scrollto( col, MODEL_SCROLL_TOP );
1313 
1314 	nfn( sys, IWINDOW_YES );
1315 }
1316 
1317 /* Make a new column with a specified name.
1318  */
1319 static void
mainw_column_new_named_action_cb(GtkAction * action,Mainw * mainw)1320 mainw_column_new_named_action_cb( GtkAction *action, Mainw *mainw )
1321 {
1322 	GtkWidget *ss = stringset_new();
1323 	char new_name[MAX_STRSIZE];
1324 	Workspace *ws;
1325 
1326 	if( !(ws = mainw_get_workspace( mainw )) )
1327 		return;
1328 
1329 	workspace_column_name_new( ws, new_name );
1330 
1331 	stringset_child_new( STRINGSET( ss ),
1332 		_( "Name" ), new_name, _( "Set column name here" ) );
1333 	stringset_child_new( STRINGSET( ss ),
1334 		_( "Caption" ), "", _( "Set column caption here" ) );
1335 
1336 	iwindow_set_title( IWINDOW( ss ), _( "New Column" ) );
1337 	idialog_set_callbacks( IDIALOG( ss ),
1338 		iwindow_true_cb, NULL, NULL, mainw );
1339 	idialog_add_ok( IDIALOG( ss ),
1340 		mainw_column_new_cb, _( "Create Column" ) );
1341 	iwindow_set_parent( IWINDOW( ss ), GTK_WIDGET( mainw ) );
1342 	iwindow_build( IWINDOW( ss ) );
1343 
1344 	gtk_widget_show( ss );
1345 }
1346 
1347 /* Callback from program.
1348  */
1349 static void
mainw_program_new_action_cb(GtkAction * action,Mainw * mainw)1350 mainw_program_new_action_cb( GtkAction *action, Mainw *mainw )
1351 {
1352 	Workspace *ws;
1353 
1354 	if( (ws = mainw_get_workspace( mainw )) ) {
1355 		Program *program;
1356 
1357 		program = program_new( ws->kitg );
1358 		gtk_widget_show( GTK_WIDGET( program ) );
1359 	}
1360 }
1361 
1362 static void
mainw_workspace_new_action_cb(GtkAction * action,Mainw * mainw)1363 mainw_workspace_new_action_cb( GtkAction *action, Mainw *mainw )
1364 {
1365 	workspace_new_blank( mainw->wsg );
1366 }
1367 
1368 /* New workbook.
1369  */
1370 static void
mainw_workbook_new_action_cb(GtkAction * action,Mainw * mainw)1371 mainw_workbook_new_action_cb( GtkAction *action, Mainw *mainw )
1372 {
1373 	Mainw *new_mainw;
1374 	Workspacegroup *new_wsg;
1375 	char name[256];
1376 
1377 	workspaceroot_name_new( mainw->wsg->wsr, name );
1378 	new_wsg = workspacegroup_new_blank( mainw->wsg->wsr, name );
1379 	new_mainw = mainw_new( new_wsg );
1380 	gtk_widget_show( GTK_WIDGET( new_mainw ) );
1381 }
1382 
1383 /* Callback from auto-recalc toggle.
1384  */
1385 static void
mainw_autorecalc_action_cb(GtkToggleAction * action,Mainw * mainw)1386 mainw_autorecalc_action_cb( GtkToggleAction *action, Mainw *mainw )
1387 {
1388 	GSList *i;
1389 
1390 	mainw_auto_recalc = gtk_toggle_action_get_active( action );
1391 
1392 	/* Yuk! We have to ask all mainw to refresh by hand, since we're not
1393 	 * using the prefs system for auto_recalc for reasons noted at top.
1394 	 */
1395 	for( i = mainw_all; i; i = i->next )
1396 		mainw_refresh( MAINW( i->data ) );
1397 
1398 	if( mainw_auto_recalc )
1399 		symbol_recalculate_all();
1400 }
1401 
1402 /* Callback from lock toggle.
1403  */
1404 static void
mainw_lock_action_cb(GtkToggleAction * action,Mainw * mainw)1405 mainw_lock_action_cb( GtkToggleAction *action, Mainw *mainw )
1406 {
1407 	Workspace *ws;
1408 
1409 	if( (ws = mainw_get_workspace( mainw )) )
1410 		workspace_set_locked( ws,
1411 			gtk_toggle_action_get_active( action ) );
1412 }
1413 
1414 /* Callback from show toolbar toggle.
1415  */
1416 static void
mainw_toolbar_action_cb(GtkToggleAction * action,Mainw * mainw)1417 mainw_toolbar_action_cb( GtkToggleAction *action, Mainw *mainw )
1418 {
1419 	mainw->toolbar_visible = gtk_toggle_action_get_active( action );
1420 	prefs_set( "MAINW_TOOLBAR",
1421 		"%s", bool_to_char( mainw->toolbar_visible ) );
1422 	mainw_refresh( mainw );
1423 }
1424 
1425 /* Callback from show statusbar toggle.
1426  */
1427 static void
mainw_statusbar_action_cb(GtkToggleAction * action,Mainw * mainw)1428 mainw_statusbar_action_cb( GtkToggleAction *action, Mainw *mainw )
1429 {
1430 	mainw->statusbar_visible = gtk_toggle_action_get_active( action );
1431 	prefs_set( "MAINW_STATUSBAR",
1432 		"%s", bool_to_char( mainw->statusbar_visible ) );
1433 	mainw_refresh( mainw );
1434 }
1435 
1436 /* Expose/hide the toolkit browser.
1437  */
1438 static void
mainw_toolkitbrowser_action_cb(GtkToggleAction * action,Mainw * mainw)1439 mainw_toolkitbrowser_action_cb( GtkToggleAction *action, Mainw *mainw )
1440 {
1441 	Workspace *ws;
1442 
1443 	if( (ws = mainw_get_workspace( mainw )) ) {
1444 		ws->rpane_open = gtk_toggle_action_get_active( action );
1445 		iobject_changed( IOBJECT( ws ) );
1446 	}
1447 }
1448 
1449 /* Expose/hide the workspace defs.
1450  */
1451 static void
mainw_tabdefs_action_cb(GtkToggleAction * action,Mainw * mainw)1452 mainw_tabdefs_action_cb( GtkToggleAction *action, Mainw *mainw )
1453 {
1454 	Workspace *ws;
1455 
1456 	if( (ws = mainw_get_workspace( mainw )) ) {
1457 		ws->lpane_open = gtk_toggle_action_get_active( action );
1458 		iobject_changed( IOBJECT( ws ) );
1459 	}
1460 }
1461 
1462 /* Remove selected items.
1463  */
1464 static void
mainw_selected_remove_action_cb(GtkAction * action,Mainw * mainw)1465 mainw_selected_remove_action_cb( GtkAction *action, Mainw *mainw )
1466 {
1467 	Workspace *ws;
1468 
1469 	if( (ws = mainw_get_workspace( mainw )) )
1470 		workspace_selected_remove_yesno( ws, GTK_WIDGET( mainw ) );
1471 }
1472 
1473 void
mainw_revert_ok_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1474 mainw_revert_ok_cb( iWindow *iwnd, void *client,
1475 	iWindowNotifyFn nfn, void *sys )
1476 {
1477 	Prefs *prefs = PREFS( client );
1478 	Workspacegroup *wsg = workspace_get_workspacegroup( prefs->ws );
1479 
1480 	if( FILEMODEL( wsg )->filename ) {
1481 		(void) unlinkf( "%s", FILEMODEL( wsg )->filename );
1482 		main_reload();
1483 		symbol_recalculate_all();
1484 	}
1485 
1486 	nfn( sys, IWINDOW_YES );
1487 }
1488 
1489 void
mainw_revert_cb(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)1490 mainw_revert_cb( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
1491 {
1492 	Prefs *prefs = PREFS( client );
1493 
1494 	box_yesno( GTK_WIDGET( iwnd ),
1495 		mainw_revert_ok_cb, iwindow_true_cb, prefs,
1496 		nfn, sys,
1497 		_( "Revert to Defaults" ),
1498 		_( "Revert to installation defaults?" ),
1499 		_( "Would you like to reset all preferences to their factory "
1500 		"settings? This will delete any changes you have ever made "
1501 		"to your preferences and may take a few seconds." ) );
1502 }
1503 
1504 static void
mainw_preferences_action_cb(GtkAction * action,Mainw * mainw)1505 mainw_preferences_action_cb( GtkAction *action, Mainw *mainw )
1506 {
1507 	Prefs *prefs;
1508 
1509 	/* Can fail if there's no prefs ws, or an error on load.
1510 	 */
1511 	if( !(prefs = prefs_new( NULL )) ) {
1512 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
1513 		return;
1514 	}
1515 
1516 	iwindow_set_title( IWINDOW( prefs ), _( "Preferences" ) );
1517 	iwindow_set_parent( IWINDOW( prefs ), GTK_WIDGET( mainw ) );
1518 	idialog_set_callbacks( IDIALOG( prefs ),
1519 		NULL, NULL, NULL, prefs );
1520 	idialog_add_ok( IDIALOG( prefs ),
1521 		mainw_revert_cb, _( "Revert to Defaults ..." ) );
1522 	idialog_add_ok( IDIALOG( prefs ), iwindow_true_cb, GTK_STOCK_CLOSE );
1523 	iwindow_build( IWINDOW( prefs ) );
1524 
1525 	/* Just big enough to avoid a horizontal scrollbar on my machine.
1526 
1527 		FIXME ... Yuk! There must be a better way to do this!
1528 		Maybe a setting in prefs to suppress the h scrollbar?
1529 
1530 	 */
1531 	gtk_window_set_default_size( GTK_WINDOW( prefs ), 780, 480 );
1532 
1533 	gtk_widget_show( GTK_WIDGET( prefs ) );
1534 }
1535 
1536 /* Make a magic definition for the selected symbol.
1537 
1538 	FIXME .. paste this back when magic is reinstated
1539 
1540 static void
1541 mainw_magic_cb( gpointer callback_data, guint callback_action,
1542         GtkWidget *widget )
1543 {
1544 	Workspace *ws = main_workspaceroot->current;
1545 	Row *row = workspace_selected_one( ws );
1546 
1547 	if( !row )
1548 		box_alert( mainw,
1549 			"Select a single object with left-click, "
1550 			"select Magic Definition." );
1551 	else if( !magic_sym( row->sym ) )
1552 		iwindow_alert( GTK_WIDGET( mainw ), GTK_MESSAGE_ERROR );
1553 	else
1554 		workspace_deselect_all( ws );
1555 }
1556  */
1557 
1558 #ifdef HAVE_LIBGVC
1559 static void
mainw_graph_action_cb(GtkAction * action,Mainw * mainw)1560 mainw_graph_action_cb( GtkAction *action, Mainw *mainw )
1561 {
1562 	Workspace *ws;
1563 
1564 	if( (ws = mainw_get_workspace( mainw )) ) {
1565 		Graphwindow *graphwindow;
1566 
1567 		graphwindow = graphwindow_new( ws, GTK_WIDGET( mainw ) );
1568 		gtk_widget_show( GTK_WIDGET( graphwindow ) );
1569 	}
1570 }
1571 #endif /*HAVE_LIBGVC*/
1572 
1573 /* Set display mode.
1574  */
1575 static void
mainw_mode_action_cb(GtkRadioAction * action,GtkRadioAction * current,Mainw * mainw)1576 mainw_mode_action_cb( GtkRadioAction *action, GtkRadioAction *current,
1577 	Mainw *mainw )
1578 {
1579 	Workspace *ws;
1580 
1581 	if( (ws = mainw_get_workspace( mainw )) )
1582 		workspace_set_mode( ws,
1583 			gtk_radio_action_get_current_value( action ) );
1584 }
1585 
1586 /* Our actions.
1587  */
1588 static GtkActionEntry mainw_actions[] = {
1589 	/* Menu items.
1590 	 */
1591 	{ "RecentMenu", NULL, N_( "Open _Recent" ) },
1592 	{ "JumpToColumnMenu", NULL, N_( "Jump to _Column" ) },
1593 	{ "ToolkitsMenu", NULL, N_( "_Toolkits" ) },
1594 
1595 	/* Dummy action ... replaced at runtime.
1596 	 */
1597 	{ "Stub",
1598 		NULL, "", NULL, "", NULL },
1599 
1600 	/* Actions.
1601 	 */
1602 	{ "NewColumn",
1603 		GTK_STOCK_NEW, N_( "C_olumn" ), NULL,
1604 		N_( "Create a new column" ),
1605 		G_CALLBACK( mainw_column_new_action_cb ) },
1606 
1607 	{ "NewColumnName",
1608 		GTK_STOCK_NEW, N_( "C_olumn" ), NULL,
1609 		N_( "Create a new column with a specified name" ),
1610 		G_CALLBACK( mainw_column_new_named_action_cb ) },
1611 
1612 	{ "NewTab",
1613 		GTK_STOCK_NEW, N_( "_Tab" ), "<control>T",
1614 		N_( "Create a new tab" ),
1615 		G_CALLBACK( mainw_workspace_new_action_cb ) },
1616 
1617 	{ "NewWorkspace",
1618 		GTK_STOCK_NEW, N_( "_Workspace" ), NULL,
1619 		N_( "Create a new workspace" ),
1620 		G_CALLBACK( mainw_workbook_new_action_cb ) },
1621 
1622 	{ "Open",
1623 		GTK_STOCK_OPEN, N_( "_Open" ), NULL,
1624 		N_( "Open a file" ),
1625 		G_CALLBACK( mainw_open_action_cb ) },
1626 
1627 	{ "OpenExamples",
1628 		NULL, N_( "Open _Examples" ), NULL,
1629 		N_( "Open example workspaces" ),
1630 		G_CALLBACK( mainw_open_examples_action_cb ) },
1631 
1632 	{ "DuplicateWorkspace",
1633 		STOCK_DUPLICATE, N_( "_Duplicate Workspace" ), NULL,
1634 		N_( "Duplicate workspace" ),
1635 		G_CALLBACK( mainw_duplicate_action_cb ) },
1636 
1637 	{ "Merge",
1638 		NULL, N_( "_Merge Into Workspace" ), NULL,
1639 		N_( "Merge workspace into this workspace" ),
1640 		G_CALLBACK( mainw_workspace_merge_action_cb ) },
1641 
1642 	{ "Save",
1643 		GTK_STOCK_SAVE, N_( "_Save Workspace" ), NULL,
1644 		N_( "Save workspace" ),
1645 		G_CALLBACK( mainw_save_action_cb ) },
1646 
1647 	{ "SaveAs",
1648 		GTK_STOCK_SAVE_AS, N_( "_Save Workspace As" ), NULL,
1649 		N_( "Save workspace as" ),
1650 		G_CALLBACK( mainw_save_as_action_cb ) },
1651 
1652 	{ "Recover",
1653 		NULL, N_( "Search for Workspace _Backups" ), NULL,
1654 		N_( "Load last automatically backed-up workspace" ),
1655 		G_CALLBACK( mainw_recover_action_cb ) },
1656 
1657 	{ "Delete",
1658 		GTK_STOCK_DELETE, N_( "_Delete" ), "<control>BackSpace",
1659 		N_( "Delete selected items" ),
1660 		G_CALLBACK( mainw_selected_remove_action_cb ) },
1661 
1662 	{ "SelectAll",
1663 		NULL, N_( "Select _All" ), "<control>A",
1664 		N_( "Select all items" ),
1665 		G_CALLBACK( mainw_select_all_action_cb ) },
1666 
1667 	{ "Duplicate",
1668 		STOCK_DUPLICATE, N_( "D_uplicate Selected" ), "<control>U",
1669 		N_( "Duplicate selected items" ),
1670 		G_CALLBACK( mainw_selected_duplicate_action_cb ) },
1671 
1672 	{ "Recalculate",
1673 		NULL, N_( "_Recalculate" ), NULL,
1674 		N_( "Recalculate selected items" ),
1675 		G_CALLBACK( mainw_force_calc_action_cb ) },
1676 
1677 	{ "Find",
1678 		GTK_STOCK_FIND, N_( "_Find" ), NULL,
1679 		N_( "Find in workspace" ),
1680 		G_CALLBACK( mainw_find_action_cb ) },
1681 
1682 	{ "FindNext",
1683 		NULL, N_( "Find _Next" ), NULL,
1684 		N_( "Find again in workspace" ),
1685 		G_CALLBACK( mainw_find_again_action_cb ) },
1686 
1687 	{ "NextError",
1688 		STOCK_NEXT_ERROR, NULL, NULL,
1689 		N_( "Jump to next error" ),
1690 		G_CALLBACK( mainw_next_error_action_cb ) },
1691 
1692 	{ "Group",
1693 		NULL, N_( "_Group" ), NULL,
1694 		N_( "Group selected items" ),
1695 		G_CALLBACK( mainw_group_action_cb ) },
1696 
1697 	{ "Ungroup",
1698 		NULL, N_( "U_ngroup" ), NULL,
1699 		N_( "Ungroup selected items" ),
1700 		G_CALLBACK( mainw_ungroup_action_cb ) },
1701 
1702 	{ "Preferences",
1703 		GTK_STOCK_PREFERENCES, N_( "_Preferences" ), NULL,
1704 		N_( "Edit preferences" ),
1705 		G_CALLBACK( mainw_preferences_action_cb ) },
1706 
1707 #ifdef HAVE_LIBGVC
1708 	{ "Graph",
1709 		NULL, N_( "Workspace as Grap_h" ), NULL,
1710 		N_( "Show a graph of workspace dependencies" ),
1711 		G_CALLBACK( mainw_graph_action_cb ) },
1712 #endif /*HAVE_LIBGVC*/
1713 
1714 	{ "EditToolkits",
1715 		NULL, N_( "_Edit" ), NULL,
1716 		N_( "Edit toolkits" ),
1717 		G_CALLBACK( mainw_program_new_action_cb ) }
1718 };
1719 
1720 static GtkToggleActionEntry mainw_toggle_actions[] = {
1721 	{ "AutoRecalculate",
1722 		NULL, N_( "Au_to Recalculate" ), NULL,
1723 		N_( "Recalculate automatically on change" ),
1724 		G_CALLBACK( mainw_autorecalc_action_cb ), TRUE },
1725 
1726 	{ "Lock",
1727 		NULL, N_( "_Lock tab" ), NULL,
1728 		N_( "Lock tab" ),
1729 		G_CALLBACK( mainw_lock_action_cb ), TRUE },
1730 
1731 	{ "Toolbar",
1732 		NULL, N_( "_Toolbar" ), NULL,
1733 		N_( "Show window toolbar" ),
1734 		G_CALLBACK( mainw_toolbar_action_cb ), TRUE },
1735 
1736 	{ "Statusbar",
1737 		NULL, N_( "_Statusbar" ), NULL,
1738 		N_( "Show window statusbar" ),
1739 		G_CALLBACK( mainw_statusbar_action_cb ), TRUE },
1740 
1741 	{ "ToolkitBrowser",
1742 		NULL, N_( "Toolkit _Browser" ), NULL,
1743 		N_( "Show toolkit browser" ),
1744 		G_CALLBACK( mainw_toolkitbrowser_action_cb ), FALSE },
1745 
1746 	{ "Tabdefs",
1747 		NULL, N_( "Tab _Definitions" ), NULL,
1748 		N_( "Show tab definitions" ),
1749 		G_CALLBACK( mainw_tabdefs_action_cb ), FALSE },
1750 };
1751 
1752 static GtkRadioActionEntry mainw_radio_actions[] = {
1753 	{ "Normal",
1754 		NULL, N_( "_Normal" ), NULL,
1755 		N_( "Normal view mode" ),
1756 		WORKSPACE_MODE_REGULAR },
1757 
1758 	{ "ShowFormula",
1759 		NULL, N_( "Show _Formula" ), NULL,
1760 		N_( "Show formula view mode" ),
1761 		WORKSPACE_MODE_FORMULA },
1762 
1763 	{ "NoEdit",
1764 		NULL, N_( "No _Edits" ), NULL,
1765 		N_( "No edits view mode" ),
1766 		WORKSPACE_MODE_NOEDIT },
1767 };
1768 
1769 static const char *mainw_menubar_ui_description =
1770 "<ui>"
1771 "  <menubar name='MainwMenubar'>"
1772 "    <menu action='FileMenu'>"
1773 "      <menu action='NewMenu'>"
1774 "        <menuitem action='NewColumnName'/>"
1775 "        <menuitem action='NewTab'/>"
1776 "        <menuitem action='NewWorkspace'/>"
1777 "      </menu>"
1778 "      <menuitem action='Open'/>"
1779 "      <menu action='RecentMenu'>"
1780 "        <menuitem action='Stub'/>"	/* Dummy ... replaced on map */
1781 "      </menu>"
1782 "      <menuitem action='OpenExamples'/>"
1783 "      <separator/>"
1784 "      <menuitem action='DuplicateWorkspace'/>"
1785 "      <menuitem action='Merge'/>"
1786 "      <menuitem action='Save'/>"
1787 "      <menuitem action='SaveAs'/>"
1788 "      <separator/>"
1789 "      <menuitem action='Recover'/>"
1790 "      <separator/>"
1791 "      <menuitem action='Close'/>"
1792 "      <menuitem action='Quit'/>"
1793 "    </menu>"
1794 "    <menu action='EditMenu'>"
1795 "      <menuitem action='Delete'/>"
1796 "      <menuitem action='SelectAll'/>"
1797 "      <menuitem action='Duplicate'/>"
1798 "      <menuitem action='Recalculate'/>"
1799 "      <menuitem action='AutoRecalculate'/>"
1800 "      <menuitem action='Lock'/>"
1801 "      <separator/>"
1802 "      <menuitem action='Find'/>"
1803 "      <menuitem action='FindNext'/>"
1804 "      <menuitem action='NextError'/>"
1805 "      <menu action='JumpToColumnMenu'>"
1806 "        <menuitem action='Stub'/>"	/* Dummy ... replaced on map */
1807 "      </menu>"
1808 "      <separator/>"
1809 "      <menuitem action='Group'/>"
1810 "      <menuitem action='Ungroup'/>"
1811 "      <separator/>"
1812 "      <menuitem action='Preferences'/>"
1813 "    </menu>"
1814 "    <menu action='ViewMenu'>"
1815 "      <menuitem action='Toolbar'/>"
1816 "      <menuitem action='Statusbar'/>"
1817 "      <menuitem action='ToolkitBrowser'/>"
1818 "      <menuitem action='Tabdefs'/>"
1819 "      <separator/>"
1820 #ifdef HAVE_LIBGVC
1821 "      <menuitem action='Graph'/>"
1822 "      <separator/>"
1823 #endif /*HAVE_LIBGVC*/
1824 "      <menuitem action='Normal'/>"
1825 "      <menuitem action='ShowFormula'/>"
1826 "      <menuitem action='NoEdit'/>"
1827 "    </menu>"
1828 "    <menu action='ToolkitsMenu'>"
1829 "      <menuitem action='EditToolkits'/>"
1830 "      <separator/>"
1831 "      <menuitem action='Stub'/>"	/* Toolkits pasted here at runtime */
1832 "    </menu>"
1833 "    <menu action='HelpMenu'>"
1834 "      <menuitem action='Guide'/>"
1835 "      <menuitem action='About'/>"
1836 "      <separator/>"
1837 "      <menuitem action='Homepage'/>"
1838 "    </menu>"
1839 "  </menubar>"
1840 "</ui>";
1841 
1842 static const char *mainw_toolbar_ui_description =
1843 "<ui>"
1844 "  <toolbar name='MainwToolbar'>"
1845 "    <toolitem action='Open'/>"
1846 "    <toolitem action='SaveAs'/>"
1847 "    <toolitem action='NewWorkspace'/>"
1848 "    <toolitem action='DuplicateWorkspace'/>"
1849 "    <separator/>"
1850 "    <toolitem action='NewColumn'/>"
1851 "    <toolitem action='Duplicate'/>"
1852 "  </toolbar>"
1853 "</ui>";
1854 
1855 static void
mainw_watch_changed_cb(Watchgroup * watchgroup,Watch * watch,Mainw * mainw)1856 mainw_watch_changed_cb( Watchgroup *watchgroup, Watch *watch, Mainw *mainw )
1857 {
1858 	if( strcmp( IOBJECT( watch )->name, "MAINW_TOOLBAR_STYLE" ) == 0 )
1859 		mainw->toolbar_visible = MAINW_TOOLBAR;
1860 
1861 	mainw_refresh( mainw );
1862 }
1863 
1864 /* Make the insides of the panel.
1865  */
1866 static void
mainw_build(iWindow * iwnd,GtkWidget * vbox)1867 mainw_build( iWindow *iwnd, GtkWidget *vbox )
1868 {
1869 	Mainw *mainw = MAINW( iwnd );
1870 
1871         GtkWidget *mbar;
1872 	GtkWidget *frame;
1873 	GError *error;
1874 	GtkWidget *cancel;
1875 	GtkWidget *item;
1876 
1877 #ifdef DEBUG
1878 	printf( "mainw_build: %p\n", mainw );
1879 #endif /*DEBUG*/
1880 
1881         /* Make main menu bar
1882          */
1883 	gtk_action_group_add_actions( iwnd->action_group,
1884 		mainw_actions, G_N_ELEMENTS( mainw_actions ),
1885 		GTK_WINDOW( mainw ) );
1886 	gtk_action_group_add_toggle_actions( iwnd->action_group,
1887 		mainw_toggle_actions, G_N_ELEMENTS( mainw_toggle_actions ),
1888 		GTK_WINDOW( mainw ) );
1889 	gtk_action_group_add_radio_actions( iwnd->action_group,
1890 		mainw_radio_actions, G_N_ELEMENTS( mainw_radio_actions ),
1891 		WORKSPACE_MODE_REGULAR,
1892 		G_CALLBACK( mainw_mode_action_cb ),
1893 		GTK_WINDOW( mainw ) );
1894 
1895 	error = NULL;
1896 	if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager,
1897 			mainw_menubar_ui_description, -1, &error ) ||
1898 		!gtk_ui_manager_add_ui_from_string( iwnd->ui_manager,
1899 			mainw_toolbar_ui_description, -1, &error ) ) {
1900 		g_message( "building menus failed: %s", error->message );
1901 		g_error_free( error );
1902 		exit( EXIT_FAILURE );
1903 	}
1904 
1905 	mbar = gtk_ui_manager_get_widget( iwnd->ui_manager, "/MainwMenubar" );
1906 	gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 );
1907         gtk_widget_show( mbar );
1908 
1909 	/* Get the dummy item on the recent menu, then get the enclosing menu
1910 	 * for that dummy item.
1911 	 */
1912         item = gtk_ui_manager_get_widget( iwnd->ui_manager,
1913 		"/MainwMenubar/FileMenu/RecentMenu/Stub" );
1914 	mainw->recent_menu = gtk_widget_get_parent( GTK_WIDGET( item ) );
1915         gtk_signal_connect( GTK_OBJECT( mainw->recent_menu ), "map",
1916                 GTK_SIGNAL_FUNC( mainw_recent_map_cb ), mainw );
1917 
1918 	/* Same for the column jump menu.
1919 	 */
1920         item = gtk_ui_manager_get_widget( iwnd->ui_manager,
1921 		"/MainwMenubar/EditMenu/JumpToColumnMenu/Stub" );
1922 	mainw->jump_to_column_menu =
1923 		gtk_widget_get_parent( GTK_WIDGET( item ) );
1924 
1925 	/* Same for the tk menu.
1926 	 */
1927         item = gtk_ui_manager_get_widget( iwnd->ui_manager,
1928 		"/MainwMenubar/ToolkitsMenu/Stub" );
1929         mainw->toolkit_menu = gtk_widget_get_parent( GTK_WIDGET( item ) );
1930 	gtk_widget_destroy( item );
1931 
1932 	/* Attach toolbar.
1933   	 */
1934 	mainw->toolbar = gtk_ui_manager_get_widget(
1935 		iwnd->ui_manager, "/MainwToolbar" );
1936 	gtk_box_pack_start( GTK_BOX( vbox ), mainw->toolbar, FALSE, FALSE, 0 );
1937         widget_visible( mainw->toolbar, MAINW_TOOLBAR );
1938 
1939 	/* This will set to NULL if we don't have infobar support.
1940 	 */
1941 	if( (IWINDOW( mainw )->infobar = infobar_new()) )
1942 		gtk_box_pack_start( GTK_BOX( vbox ),
1943 			GTK_WIDGET( IWINDOW( mainw )->infobar ),
1944 			FALSE, FALSE, 0 );
1945 
1946 	/* hbox for status bar etc.
1947 	 */
1948         mainw->statusbar_main = gtk_hbox_new( FALSE, 2 );
1949         gtk_box_pack_end( GTK_BOX( vbox ),
1950 		mainw->statusbar_main, FALSE, FALSE, 2 );
1951         widget_visible( mainw->statusbar_main, MAINW_STATUSBAR );
1952 
1953 	/* Make space free label.
1954 	 */
1955 	mainw->space_free_eb = gtk_event_box_new();
1956         gtk_box_pack_start( GTK_BOX( mainw->statusbar_main ),
1957 		mainw->space_free_eb, FALSE, FALSE, 0 );
1958 	gtk_widget_show( mainw->space_free_eb );
1959 	frame = gtk_frame_new( NULL );
1960 	gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_IN );
1961 	gtk_container_add( GTK_CONTAINER( mainw->space_free_eb ), frame );
1962 	gtk_widget_show( frame );
1963 	mainw->space_free = gtk_label_new( "space_free" );
1964 	gtk_misc_set_padding( GTK_MISC( mainw->space_free ), 2, 2 );
1965         gtk_container_add( GTK_CONTAINER( frame ), mainw->space_free );
1966         gtk_signal_connect( GTK_OBJECT( mainw->space_free_eb ), "event",
1967                 GTK_SIGNAL_FUNC( mainw_space_free_event ), mainw );
1968 	set_tooltip_generate( mainw->space_free_eb,
1969 		(TooltipGenerateFn) mainw_space_free_tooltip_generate,
1970 		mainw, NULL );
1971         gtk_widget_show( mainw->space_free );
1972 	mainw->imageinfo_changed_sid = g_signal_connect( main_imageinfogroup,
1973 		"changed",
1974 		G_CALLBACK( mainw_free_changed_cb ), mainw );
1975 	mainw->heap_changed_sid = g_signal_connect( reduce_context->heap,
1976 		"changed",
1977 		G_CALLBACK( mainw_free_changed_cb ), mainw );
1978 
1979 	/* Make message label.
1980 	 */
1981 	mainw->statusbar = gtk_label_new( "" );
1982 	gtk_label_set_ellipsize( GTK_LABEL( mainw->statusbar ),
1983 		PANGO_ELLIPSIZE_MIDDLE );
1984 	/* 6 is enough to stop the statusbar changing height when the progress
1985 	 * indicator changes visibility.
1986 	 */
1987 	gtk_misc_set_padding( GTK_MISC( mainw->statusbar ), 2, 6 );
1988 	gtk_misc_set_alignment( GTK_MISC( mainw->statusbar ), 0.0, 0.5 );
1989         gtk_box_pack_start( GTK_BOX( mainw->statusbar_main ),
1990 		mainw->statusbar, TRUE, TRUE, 0 );
1991         gtk_widget_show( mainw->statusbar );
1992 
1993         mainw->progress_box = gtk_hbox_new( FALSE, 2 );
1994 
1995 	mainw->progress = gtk_progress_bar_new();
1996 	gtk_widget_set_size_request( GTK_WIDGET( mainw->progress ), 200, -1 );
1997         gtk_box_pack_end( GTK_BOX( mainw->progress_box ), mainw->progress,
1998 		FALSE, TRUE, 0 );
1999         gtk_widget_show( mainw->progress );
2000 
2001         cancel = gtk_button_new_with_label( "Cancel" );
2002         g_signal_connect( cancel, "clicked",
2003                 G_CALLBACK( mainw_cancel_cb ), mainw );
2004         gtk_box_pack_end( GTK_BOX( mainw->progress_box ), cancel,
2005 		FALSE, TRUE, 0 );
2006         gtk_widget_show( cancel );
2007 
2008         gtk_box_pack_end( GTK_BOX( mainw->statusbar_main ),
2009 		mainw->progress_box, FALSE, TRUE, 0 );
2010 
2011 	mainw->wsgview = WORKSPACEGROUPVIEW( workspacegroupview_new() );
2012 	gtk_box_pack_start( GTK_BOX( vbox ),
2013 		GTK_WIDGET( mainw->wsgview ), TRUE, TRUE, 0 );
2014 	view_link( VIEW( mainw->wsgview ), MODEL( mainw->wsg ), NULL );
2015 	gtk_widget_show( GTK_WIDGET( mainw->wsgview ) );
2016 
2017 	/* Any changes to prefs, refresh (yuk!).
2018 	 */
2019 	mainw->watch_changed_sid = g_signal_connect( main_watchgroup,
2020 		"watch_changed",
2021 		G_CALLBACK( mainw_watch_changed_cb ), mainw );
2022 }
2023 
2024 static void
mainw_popdown(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)2025 mainw_popdown( iWindow *iwnd, void *client, iWindowNotifyFn nfn, void *sys )
2026 {
2027 	Mainw *mainw = MAINW( iwnd );
2028 
2029 	/* We can be destroyed in two ways: either our iwnd tells us to go, or
2030 	 * our model is destroyed under us. If the model has gone, we just go.
2031 	 * If the model is still there, we need to ask about saving and
2032 	 * quitting.
2033 	 */
2034 
2035 	if( mainw->wsg )
2036 		filemodel_inter_savenclose_cb( IWINDOW( mainw ),
2037 			FILEMODEL( mainw->wsg ), nfn, sys );
2038 	else
2039 		nfn( sys, IWINDOW_YES );
2040 }
2041 
2042 static void
mainw_wsg_changed_cb(Workspacegroup * wsg,Mainw * mainw)2043 mainw_wsg_changed_cb( Workspacegroup *wsg, Mainw *mainw )
2044 {
2045 	mainw_refresh( mainw );
2046 }
2047 
2048 static void
mainw_wsg_destroy_cb(Workspacegroup * wsg,Mainw * mainw)2049 mainw_wsg_destroy_cb( Workspacegroup *wsg, Mainw *mainw )
2050 {
2051 	mainw->wsg = NULL;
2052 }
2053 
2054 static void
mainw_link(Mainw * mainw,Workspacegroup * wsg)2055 mainw_link( Mainw *mainw, Workspacegroup *wsg )
2056 {
2057 	Workspace *ws = workspacegroup_get_workspace( wsg );
2058 
2059 	/* Take ownership of the wsg.
2060 	 */
2061 	mainw->wsg = wsg;
2062 	g_object_ref( G_OBJECT( mainw->wsg ) );
2063 	iobject_sink( IOBJECT( mainw->wsg ) );
2064 
2065 	wsg->iwnd = IWINDOW( mainw );
2066 
2067 	iwindow_set_build( IWINDOW( mainw ),
2068 		(iWindowBuildFn) mainw_build, wsg, NULL, NULL );
2069 	iwindow_set_popdown( IWINDOW( mainw ), mainw_popdown, NULL );
2070 	iwindow_set_size_prefs( IWINDOW( mainw ),
2071 		"MAINW_WINDOW_WIDTH", "MAINW_WINDOW_HEIGHT" );
2072 	iwindow_build( IWINDOW( mainw ) );
2073 
2074 	if( ws &&
2075 		MODEL( ws )->window_width != - 1 )
2076 		gtk_window_set_default_size( GTK_WINDOW( mainw ),
2077 			MODEL( ws )->window_width,
2078 			MODEL( ws )->window_height );
2079 
2080 	/* Set start state.
2081 	 */
2082 	(void) mainw_refresh( mainw );
2083 
2084 	mainw->changed_sid = g_signal_connect( mainw->wsg,
2085 		"changed",
2086 		G_CALLBACK( mainw_wsg_changed_cb ), mainw );
2087 	mainw->destroy_sid = g_signal_connect( mainw->wsg,
2088 		"destroy",
2089 		G_CALLBACK( mainw_wsg_destroy_cb ), mainw );
2090 }
2091 
2092 Mainw *
mainw_new(Workspacegroup * wsg)2093 mainw_new( Workspacegroup *wsg )
2094 {
2095 	Mainw *mainw;
2096 
2097 	mainw = MAINW( g_object_new( TYPE_MAINW, NULL ) );
2098 
2099 	mainw_link( mainw, wsg );
2100 
2101 	return( mainw );
2102 }
2103 
2104 static void *
mainw_cull_sub(Mainw * mainw)2105 mainw_cull_sub( Mainw *mainw )
2106 {
2107 	if( !ICONTAINER( mainw->wsg )->children ) {
2108 		filemodel_set_modified( FILEMODEL( mainw->wsg ), FALSE );
2109 		iwindow_kill( IWINDOW( mainw ) );
2110 	}
2111 
2112 	return( NULL );
2113 }
2114 
2115 void
mainw_cull(void)2116 mainw_cull( void )
2117 {
2118 	slist_map( mainw_all,
2119 		(SListMapFn) mainw_cull_sub, NULL );
2120 }
2121 
2122 static void *
mainw_layout_sub(Workspace * ws)2123 mainw_layout_sub( Workspace *ws )
2124 {
2125 	model_layout( MODEL( ws ) );
2126 	workspace_set_needs_layout( ws, FALSE );
2127 
2128 	return( NULL );
2129 }
2130 
2131 static gboolean
mainw_layout_timeout_cb(gpointer user_data)2132 mainw_layout_timeout_cb( gpointer user_data )
2133 {
2134 	mainw_layout_timeout = 0;
2135 
2136 	slist_map( workspace_get_needs_layout(),
2137 		(SListMapFn) mainw_layout_sub, NULL );
2138 
2139 	return( FALSE );
2140 }
2141 
2142 void
mainw_layout(void)2143 mainw_layout( void )
2144 {
2145 	IM_FREEF( g_source_remove, mainw_layout_timeout );
2146 	mainw_layout_timeout = g_timeout_add( 300,
2147 		(GSourceFunc) mainw_layout_timeout_cb, NULL );
2148 }
2149