1 /* display an image in a window ... watching an iImage model.
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 /* Define to trace button press events.
35 #define EVENT
36  */
37 
38 #include "ip.h"
39 
40 static FloatwindowClass *parent_class = NULL;
41 
42 /* All the magnification menus we have.
43  */
44 typedef struct _ImageviewMagmenu {
45 	const char *name;
46 	int mag;
47 } ImageviewMagmenu;
48 
49 static const ImageviewMagmenu imageview_mags[] = {
50 	{ "Zoom6Mode", -16 },
51 	{ "Zoom12Mode", -8 },
52 	{ "Zoom25Mode", -4 },
53 	{ "Zoom50Mode", -2 },
54 	{ "Zoom100Mode", 1 },
55 	{ "Zoom200Mode", 2 },
56 	{ "Zoom400Mode", 4 },
57 	{ "Zoom800Mode", 8 },
58 	{ "Zoom1600Mode", 16 }
59 };
60 
61 static void
imageview_destroy(GtkObject * object)62 imageview_destroy( GtkObject *object )
63 {
64 	Imageview *iv;
65 
66 	g_return_if_fail( object != NULL );
67 	g_return_if_fail( IS_IMAGEVIEW( object ) );
68 
69 	iv = IMAGEVIEW( object );
70 
71 #ifdef DEBUG
72 	printf( "imageview_destroy\n" );
73 #endif /*DEBUG*/
74 
75 	/* My instance destroy stuff.
76 	 */
77 	UNREF( iv->imagemodel );
78 
79 	GTK_OBJECT_CLASS( parent_class )->destroy( object );
80 }
81 
82 static void
imageview_class_init(ImageviewClass * class)83 imageview_class_init( ImageviewClass *class )
84 {
85 	GtkObjectClass *object_class = (GtkObjectClass *) class;
86 
87 	parent_class = g_type_class_peek_parent( class );
88 
89 	object_class->destroy = imageview_destroy;
90 
91 	/* Create signals.
92 	 */
93 
94 	/* Init methods.
95 	 */
96 }
97 
98 static void
imageview_init(Imageview * iv)99 imageview_init( Imageview *iv )
100 {
101 	iv->imagemodel = NULL;
102 }
103 
104 GtkType
imageview_get_type(void)105 imageview_get_type( void )
106 {
107 	static GtkType type = 0;
108 
109 	if( !type ) {
110 		static const GtkTypeInfo info = {
111 			"Imageview",
112 			sizeof( Imageview ),
113 			sizeof( ImageviewClass ),
114 			(GtkClassInitFunc) imageview_class_init,
115 			(GtkObjectInitFunc) imageview_init,
116 			/* reserved_1 */ NULL,
117 			/* reserved_2 */ NULL,
118 			(GtkClassInitFunc) NULL,
119 		};
120 
121 		type = gtk_type_unique( TYPE_FLOATWINDOW, &info );
122 	}
123 
124 	return( type );
125 }
126 
127 static void
imageview_refresh_title(Imageview * iv)128 imageview_refresh_title( Imageview *iv )
129 {
130 	Imagemodel *imagemodel = iv->imagemodel;
131 	iImage *iimage = imagemodel->iimage;
132 	Row *row = HEAPMODEL( iimage )->row;
133 	Workspace *ws = row_get_workspace( row );
134 
135 	/* Can come here during ws destroy.
136 	 */
137 	if( ws ) {
138 		Conversion *conv = imagemodel->conv;
139 		Imageinfo *ii = iimage->value.ii;
140 
141 		char txt[512];
142 		VipsBuf buf = VIPS_BUF_STATIC( txt );
143 
144 		row_qualified_name_relative( ws->sym, row, &buf );
145 
146 		if( ii && imageinfo_is_from_file( ii ) )
147 			vips_buf_appendf( &buf, " - %s", IOBJECT( ii )->name );
148 
149 		vips_buf_appendf( &buf, " - %.0f%%",
150 			100.0 * conversion_dmag( conv->mag ) );
151 
152 		iwindow_set_title( IWINDOW( iv ), "%s", vips_buf_all( &buf ) );
153 	}
154 }
155 
156 /* The model has changed ... update our menus and titlebar.
157  */
158 static void
imageview_imagemodel_changed_cb(Imagemodel * imagemodel,Imageview * iv)159 imageview_imagemodel_changed_cb( Imagemodel *imagemodel, Imageview *iv )
160 {
161 	iWindow *iwnd = IWINDOW( iv );
162 	Conversion *conv = imagemodel->conv;
163 
164 	GtkAction *action;
165 	int i;
166 
167 	action = gtk_action_group_get_action( iwnd->action_group,
168 		"Status" );
169 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
170 		imagemodel->show_status );
171 
172 	action = gtk_action_group_get_action( iwnd->action_group,
173 		"Control" );
174 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
175 		imagemodel->show_convert );
176 
177 	action = gtk_action_group_get_action( iwnd->action_group,
178 		"Paint" );
179 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
180 		imagemodel->show_paintbox );
181 
182 	action = gtk_action_group_get_action( iwnd->action_group,
183 		"Rulers" );
184 	gtk_toggle_action_set_active( GTK_TOGGLE_ACTION( action ),
185 		imagemodel->show_rulers );
186 
187 	for( i = 0; i < IM_NUMBER( imageview_mags ); i++ )
188 		if( conv->mag == imageview_mags[i].mag ) {
189 			action = gtk_action_group_get_action(
190 				iwnd->action_group,
191 				imageview_mags[i].name );
192 			gtk_toggle_action_set_active(
193 				GTK_TOGGLE_ACTION( action ),
194 				TRUE );
195 			break;
196 		}
197 
198 	imageview_refresh_title( iv );
199 }
200 
201 /* Region class names indexed by iRegionType.
202  */
203 static const char *imageview_region_name[] = {
204 	CLASS_MARK,
205 	CLASS_HGUIDE,
206 	CLASS_VGUIDE,
207 	CLASS_ARROW,
208 	CLASS_REGION,
209 	CLASS_AREA
210 };
211 
212 /* Look up a iRegionType from an action name.
213  */
214 static iRegionType
imageview_get_region_type(GtkAction * action)215 imageview_get_region_type( GtkAction *action )
216 {
217 	/* Action names indexed by iRegionType.
218 	 */
219 	static const char *action_names[] = {
220 		"NewMark",
221 		"NewHGuide",
222 		"NewVGuide",
223 		"NewArrow",
224 		"NewRegion"
225 	};
226 
227 	const char *name = gtk_action_get_name( action );
228 
229 	int i;
230 
231 	for( i = 0; i < IM_NUMBER( action_names ); i++ )
232 		if( strcmp( name, action_names[i] ) == 0 )
233 			return( (iRegionType) i );
234 
235 	g_assert( FALSE );
236 
237 	/* Keep gcc happy.
238 	 */
239 	return( FALSE );
240 }
241 
242 static void
imageview_new_arrow2_action_cb(GtkAction * action,Imageview * iv)243 imageview_new_arrow2_action_cb( GtkAction *action, Imageview *iv )
244 {
245 	iRegionType rt = imageview_get_region_type( action );
246 	Imagemodel *imagemodel = iv->imagemodel;
247 	iImage *iimage = imagemodel->iimage;
248 	Row *row = HEAPMODEL( iimage )->row;
249 	Workspace *ws = row_get_workspace( row );
250 	Conversion *conv = imagemodel->conv;
251 	int dx = imagemodel->visible.left + imagemodel->visible.width / 2;
252 	int dy = imagemodel->visible.top + imagemodel->visible.height / 2;
253 
254 	char txt[MAX_STRSIZE];
255 	VipsBuf buf = VIPS_BUF_STATIC( txt );
256 	Symbol *sym;
257 	int ix, iy;
258 
259 	conversion_disp_to_im( conv, dx, dy, &ix, &iy );
260 
261 	vips_buf_appendf( &buf, "%s ", imageview_region_name[rt] );
262 	row_qualified_name_relative( ws->sym, row, &buf );
263 	switch( rt ) {
264 	case IREGION_MARK:
265 		vips_buf_appendf( &buf, " (%d) (%d)", ix, iy );
266 		break;
267 
268 	case IREGION_HGUIDE:
269 		vips_buf_appendf( &buf, " (%d)", iy );
270 		break;
271 
272 	case IREGION_VGUIDE:
273 		vips_buf_appendf( &buf, " (%d)", ix );
274 		break;
275 
276 	default:
277 		g_assert( FALSE );
278 	}
279 
280 	if( !(sym = workspace_add_def_recalc( ws, vips_buf_all( &buf ) )) ) {
281 		iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR );
282 		return;
283 	}
284 
285 	workspace_deselect_all( ws );
286 }
287 
288 static void
imageview_new_arrow4_action_cb(GtkAction * action,Imageview * iv)289 imageview_new_arrow4_action_cb( GtkAction *action, Imageview *iv )
290 {
291 	iRegionType rt = imageview_get_region_type( action );
292 	Imagemodel *imagemodel = iv->imagemodel;
293 	iImage *iimage = imagemodel->iimage;
294 	Row *row = HEAPMODEL( iimage )->row;
295 	Workspace *ws = row_get_workspace( row );
296 	Conversion *conv = imagemodel->conv;
297 
298 	Rect dr, ir;
299 	char txt[MAX_STRSIZE];
300 	VipsBuf buf = VIPS_BUF_STATIC( txt );
301 	Symbol *sym;
302 	Column *col;
303 
304 	dr.left = imagemodel->visible.left + imagemodel->visible.width / 4;
305 	dr.top = imagemodel->visible.top + imagemodel->visible.height / 4;
306 	dr.width = imagemodel->visible.width / 2;
307 	dr.height = imagemodel->visible.height / 2;
308 	conversion_disp_to_im_rect( conv, &dr, &ir );
309 
310 	vips_buf_appendf( &buf, "%s ", imageview_region_name[rt] );
311 	row_qualified_name_relative( ws->sym, row, &buf );
312 	vips_buf_appendf( &buf, " (%d) (%d) %d %d",
313 		ir.left, ir.top, ir.width, ir.height );
314 
315 	if( !(sym = workspace_add_def_recalc( ws, vips_buf_all( &buf ) )) ) {
316 		iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR );
317 		return;
318 	}
319 
320 	col = sym->expr->row->top_col;
321 	column_scrollto( col, MODEL_SCROLL_BOTTOM );
322 }
323 
324 static void
imageview_replace_action_cb(GtkAction * action,Imageview * iv)325 imageview_replace_action_cb( GtkAction *action, Imageview *iv )
326 {
327 	Imagemodel *imagemodel = iv->imagemodel;
328 	iImage *iimage = imagemodel->iimage;
329 
330 	classmodel_graphic_replace( CLASSMODEL( iimage ), GTK_WIDGET( iv ) );
331 }
332 
333 static void
imageview_save_action_cb(GtkAction * action,Imageview * iv)334 imageview_save_action_cb( GtkAction *action, Imageview *iv )
335 {
336 	Imagemodel *imagemodel = iv->imagemodel;
337 	iImage *iimage = imagemodel->iimage;
338 
339 	classmodel_graphic_save( CLASSMODEL( iimage ), GTK_WIDGET( iv ) );
340 }
341 
342 static void
imageview_recalc_action_cb(GtkAction * action,Imageview * iv)343 imageview_recalc_action_cb( GtkAction *action, Imageview *iv )
344 {
345 	Imagemodel *imagemodel = iv->imagemodel;
346 	iImage *iimage = imagemodel->iimage;
347 	Row *row = HEAPMODEL( iimage )->row;
348 
349         workspace_deselect_all( row->ws );
350         row_select( row );
351         if( !workspace_selected_recalc( row->ws ) )
352 		iwindow_alert( GTK_WIDGET( iv ), GTK_MESSAGE_ERROR );
353         workspace_deselect_all( row->ws );
354 }
355 
356 static void
imageview_header_action_cb(GtkAction * action,Imageview * iv)357 imageview_header_action_cb( GtkAction *action, Imageview *iv )
358 {
359 	Imagemodel *imagemodel = iv->imagemodel;
360 	iImage *iimage = imagemodel->iimage;
361 
362 	iimage_header( GTK_WIDGET( iv ), MODEL( iimage ) );
363 }
364 
365 static void
imageview_zoom_in_action_cb(GtkAction * action,Imageview * iv)366 imageview_zoom_in_action_cb( GtkAction *action, Imageview *iv )
367 {
368 	Imagemodel *imagemodel = iv->imagemodel;
369 	Conversion *conv = imagemodel->conv;
370 
371 	conversion_set_mag( conv, conversion_double( conv->mag ) );
372 }
373 
374 static void
imageview_zoom_out_action_cb(GtkAction * action,Imageview * iv)375 imageview_zoom_out_action_cb( GtkAction *action, Imageview *iv )
376 {
377 	Imagemodel *imagemodel = iv->imagemodel;
378 	Conversion *conv = imagemodel->conv;
379 
380 	conversion_set_mag( conv, conversion_halve( conv->mag ) );
381 }
382 
383 static void
imageview_zoom_100_action_cb(GtkAction * action,Imageview * iv)384 imageview_zoom_100_action_cb( GtkAction *action, Imageview *iv )
385 {
386 	if( iv->ip )
387 		imagepresent_zoom_to( iv->ip, 1 );
388 }
389 
390 static void
imageview_zoom_fit_action_cb(GtkAction * action,Imageview * iv)391 imageview_zoom_fit_action_cb( GtkAction *action, Imageview *iv )
392 {
393 	imagepresent_zoom_to( iv->ip, 0 );
394 }
395 
396 static void
imageview_show_status_action_cb(GtkToggleAction * action,Imageview * iv)397 imageview_show_status_action_cb( GtkToggleAction *action, Imageview *iv )
398 {
399 	imagemodel_set_status( iv->imagemodel,
400 		gtk_toggle_action_get_active( action ) );
401 }
402 
403 static void
imageview_show_convert_action_cb(GtkToggleAction * action,Imageview * iv)404 imageview_show_convert_action_cb( GtkToggleAction *action, Imageview *iv )
405 {
406 	imagemodel_set_convert( iv->imagemodel,
407 		gtk_toggle_action_get_active( action ) );
408 }
409 
410 static void
imageview_show_paintbox_action_cb(GtkToggleAction * action,Imageview * iv)411 imageview_show_paintbox_action_cb( GtkToggleAction *action, Imageview *iv )
412 {
413 	imagemodel_set_paintbox( iv->imagemodel,
414 		gtk_toggle_action_get_active( action ) );
415 }
416 
417 static void
imageview_show_rulers_action_cb(GtkToggleAction * action,Imageview * iv)418 imageview_show_rulers_action_cb( GtkToggleAction *action, Imageview *iv )
419 {
420 	imagemodel_set_rulers( iv->imagemodel,
421 		gtk_toggle_action_get_active( action ) );
422 }
423 
424 static void
imageview_mode_action_cb(GtkRadioAction * action,GtkRadioAction * current,Imageview * iv)425 imageview_mode_action_cb( GtkRadioAction *action, GtkRadioAction *current,
426 	Imageview *iv )
427 {
428 	ImagemodelState state = (ImagemodelState)
429 		gtk_radio_action_get_current_value( action );
430 
431 	imagemodel_set_state( iv->imagemodel, state, GTK_WIDGET( iv ) );
432 }
433 
434 static void
imageview_mag_action_cb(GtkRadioAction * action,GtkRadioAction * current,Imageview * iv)435 imageview_mag_action_cb( GtkRadioAction *action, GtkRadioAction *current,
436 	Imageview *iv )
437 {
438 	if( iv->ip )
439 		imagepresent_zoom_to( iv->ip,
440 			gtk_radio_action_get_current_value( action ) );
441 }
442 
443 /* Our actions.
444  */
445 static GtkActionEntry imageview_actions[] = {
446 	/* Menu items.
447 	 */
448 	{ "ViewToolbarMenu", NULL, "_Toolbar" },
449 	{ "ViewModeMenu", NULL, "M_ode" },
450 	{ "ViewZoomMenu", NULL, "_Zoom" },
451 
452 	/* Actions.
453 	 */
454 	{ "NewMark",
455 		NULL, N_( "_Mark" ), NULL,
456 		N_( "Create a new mark" ),
457 		G_CALLBACK( imageview_new_arrow2_action_cb ) },
458 
459 	{ "NewHGuide",
460 		NULL, N_( "_Horizontal Guide" ), NULL,
461 		N_( "Create a new horizontal guide" ),
462 		G_CALLBACK( imageview_new_arrow2_action_cb ) },
463 
464 	{ "NewVGuide",
465 		NULL, N_( "_Vertical Guide" ), NULL,
466 		N_( "Create a new vertical guide" ),
467 		G_CALLBACK( imageview_new_arrow2_action_cb ) },
468 
469 	{ "NewArrow",
470 		NULL, N_( "_Arrow" ), NULL,
471 		N_( "Create a new arrow" ),
472 		G_CALLBACK( imageview_new_arrow4_action_cb ) },
473 
474 	{ "NewRegion",
475 		NULL, N_( "_Region" ), NULL,
476 		N_( "Create a new region" ),
477 		G_CALLBACK( imageview_new_arrow4_action_cb ) },
478 
479 	{ "Replace",
480 		NULL, N_( "Replace Image" ), NULL,
481 		N_( "Replace image from file" ),
482 		G_CALLBACK( imageview_replace_action_cb ) },
483 
484 	{ "SaveAs",
485 		GTK_STOCK_SAVE_AS, N_( "Save Image As" ), NULL,
486 		N_( "Save image to file" ),
487 		G_CALLBACK( imageview_save_action_cb ) },
488 
489 	{ "Recalculate",
490 		NULL, N_( "Recalculate" ), "<control>C",
491 		N_( "Recalculate image" ),
492 		G_CALLBACK( imageview_recalc_action_cb ) },
493 
494 	{ "Header",
495 		NULL, N_( "_Header" ), NULL,
496 		N_( "View image header" ),
497 		G_CALLBACK( imageview_header_action_cb ) },
498 
499 	{ "ZoomIn",
500 		GTK_STOCK_ZOOM_IN, N_( "Zoom _In" ), "<control>plus",
501 		N_( "Zoom in on mouse cursor" ),
502 		G_CALLBACK( imageview_zoom_in_action_cb ) },
503 
504 	{ "ZoomOut",
505 		GTK_STOCK_ZOOM_OUT, N_( "Zoom _Out" ), "<control>minus",
506 		N_( "Zoom out" ),
507 		G_CALLBACK( imageview_zoom_out_action_cb ) },
508 
509 	{ "Zoom100",
510 		GTK_STOCK_ZOOM_100, N_( "Zoom _100%" ), "<control>equal",
511 		N_( "Zoom to 100%" ),
512 		G_CALLBACK( imageview_zoom_100_action_cb ) },
513 
514 	{ "ZoomFit",
515 		GTK_STOCK_ZOOM_FIT, N_( "Zoom to _Fit" ), NULL,
516 		N_( "Zoom to fit image to window" ),
517 		G_CALLBACK( imageview_zoom_fit_action_cb ) }
518 };
519 
520 static GtkToggleActionEntry imageview_toggle_actions[] = {
521 	{ "Status",
522 		NULL, N_( "_Status" ), NULL,
523 		N_( "Show status bar" ),
524 		G_CALLBACK( imageview_show_status_action_cb ), TRUE },
525 
526 	{ "Control",
527 		NULL, N_( "_Display Control" ), NULL,
528 		N_( "Show display control bar" ),
529 		G_CALLBACK( imageview_show_convert_action_cb ), TRUE },
530 
531 	{ "Paint",
532 		NULL, N_( "_Paint" ), NULL,
533 		N_( "Show paint bar" ),
534 		G_CALLBACK( imageview_show_paintbox_action_cb ), FALSE },
535 
536 	{ "Rulers",
537 		NULL, N_( "_Rulers" ), NULL,
538 		N_( "Show rulers" ),
539 		G_CALLBACK( imageview_show_rulers_action_cb ), FALSE }
540 };
541 
542 static GtkRadioActionEntry imageview_mode_radio_actions[] = {
543 	{ "SelectMode",
544 		NULL, N_( "_Select" ), NULL,
545 		N_( "Select and modify selections" ),
546 		IMAGEMODEL_SELECT },
547 
548 	{ "PanMode",
549 		NULL, N_( "_Pan" ), NULL,
550 		N_( "Pan image" ),
551 		IMAGEMODEL_PAN },
552 
553 	{ "ZoomInMode",
554 		NULL, N_( "Zoom _In" ), NULL,
555 		N_( "Zoom in on mouse cursor" ),
556 		IMAGEMODEL_MAGIN },
557 
558 	{ "ZoomOutMode",
559 		NULL, N_( "Zoom _Out" ), NULL,
560 		N_( "Zoom out" ),
561 		IMAGEMODEL_MAGOUT }
562 };
563 
564 static GtkRadioActionEntry imageview_zoom_radio_actions[] = {
565 	{ "Zoom6Mode",
566 		NULL, N_( "6%" ), NULL, N_( "Zoom to 6%" ), -16 },
567 	{ "Zoom12Mode",
568 		NULL, N_( "12%" ), NULL, N_( "Zoom to 12%" ), -8 },
569 	{ "Zoom25Mode",
570 		NULL, N_( "25%" ), NULL, N_( "Zoom to 25%" ), -4 },
571 	{ "Zoom50Mode",
572 		NULL, N_( "50%" ), NULL, N_( "Zoom to 50%" ), -2 },
573 	{ "Zoom100Mode",
574 		NULL, N_( "100%" ), NULL, N_( "Zoom to 100%" ), 1 },
575 	{ "Zoom200Mode",
576 		NULL, N_( "200%" ), NULL, N_( "Zoom to 200%" ), 2 },
577 	{ "Zoom400Mode",
578 		NULL, N_( "400%" ), NULL, N_( "Zoom to 400%" ), 4 },
579 	{ "Zoom800Mode",
580 		NULL, N_( "800%" ), NULL, N_( "Zoom to 800%" ), 8 },
581 	{ "Zoom1600Mode",
582 		NULL, N_( "1600%" ), NULL, N_( "Zoom to 1600%" ), 16 }
583 };
584 
585 static const char *imageview_menubar_ui_description =
586 "<ui>"
587 
588 "  <menubar name='ImageviewMenubar'>"
589 "    <menu action='FileMenu'>"
590 "      <menu action='NewMenu'>"
591 "        <menuitem action='NewMark'/>"
592 "        <menuitem action='NewHGuide'/>"
593 "        <menuitem action='NewVGuide'/>"
594 "        <menuitem action='NewArrow'/>"
595 "        <menuitem action='NewRegion'/>"
596 "      </menu>"
597 "      <separator/>"
598 "      <menuitem action='Replace'/>"
599 "      <menuitem action='SaveAs'/>"
600 "      <separator/>"
601 "      <menuitem action='Recalculate'/>"
602 "      <separator/>"
603 "      <menuitem action='Close'/>"
604 "      <menuitem action='Quit'/>"
605 "    </menu>"
606 "    <menu action='ViewMenu'>"
607 "      <menu action='ViewToolbarMenu'>"
608 "        <menuitem action='Status'/>"
609 "        <menuitem action='Control'/>"
610 "        <menuitem action='Paint'/>"
611 "        <menuitem action='Rulers'/>"
612 "      </menu>"
613 "      <menu action='ViewModeMenu'>"
614 "        <menuitem action='SelectMode'/>"
615 "        <menuitem action='PanMode'/>"
616 "        <menuitem action='ZoomInMode'/>"
617 "        <menuitem action='ZoomOutMode'/>"
618 "      </menu>"
619 "      <menuitem action='Header'/>"
620 "      <separator/>"
621 "      <menuitem action='ZoomIn'/>"
622 "      <menuitem action='ZoomOut'/>"
623 "      <menuitem action='Zoom100'/>"
624 "      <menuitem action='ZoomFit'/>"
625 "      <menu action='ViewZoomMenu'>"
626 "        <menuitem action='Zoom6Mode'/>"
627 "        <menuitem action='Zoom12Mode'/>"
628 "        <menuitem action='Zoom25Mode'/>"
629 "        <menuitem action='Zoom50Mode'/>"
630 "        <menuitem action='Zoom100Mode'/>"
631 "        <menuitem action='Zoom200Mode'/>"
632 "        <menuitem action='Zoom400Mode'/>"
633 "        <menuitem action='Zoom800Mode'/>"
634 "        <menuitem action='Zoom1600Mode'/>"
635 "      </menu>"
636 "    </menu>"
637 "    <menu action='HelpMenu'>"
638 "      <menuitem action='Guide'/>"
639 "      <menuitem action='About'/>"
640 "      <separator/>"
641 "      <menuitem action='Homepage'/>"
642 "    </menu>"
643 "  </menubar>"
644 
645 "  <popup name='ImageviewPopup'>"
646 "    <menu action='ViewToolbarMenu'>"
647 "      <menuitem action='Status'/>"
648 "      <menuitem action='Control'/>"
649 "      <menuitem action='Paint'/>"
650 "      <menuitem action='Rulers'/>"
651 "    </menu>"
652 "    <menuitem action='Zoom100'/>"
653 "    <menuitem action='ZoomFit'/>"
654 "    <menuitem action='Header'/>"
655 "    <separator/>"
656 "    <menuitem action='Replace'/>"
657 "    <menuitem action='SaveAs'/>"
658 "    <menuitem action='Recalculate'/>"
659 "    <separator/>"
660 "    <menuitem action='Close'/>"
661 "  </popup>"
662 
663 "</ui>";
664 
665 static gint
imageview_event(GtkWidget * widget,GdkEvent * event,Imageview * iv)666 imageview_event( GtkWidget *widget, GdkEvent *event, Imageview *iv )
667 {
668 	gboolean handled = FALSE;
669 
670 #ifdef EVENT
671 	if( event->type == GDK_BUTTON_PRESS )
672 		printf( "imageview_event: GDK_BUTTON_PRESS\n" );
673 #endif /*EVENT*/
674 
675 	switch( event->type ) {
676 	case GDK_MOTION_NOTIFY:
677 {
678 		Imagemodel *imagemodel = iv->imagemodel;
679 		Conversion *conv = imagemodel->conv;
680 		int ix, iy;
681 
682 		conversion_disp_to_im( conv,
683 			event->button.x, event->button.y, &ix, &iy );
684 
685 		statusview_mouse( iv->sv, ix, iy );
686 }
687 
688 		break;
689 
690 	case GDK_BUTTON_PRESS:
691 		switch( event->button.button ) {
692 		case 3:
693 {
694 			iWindow *iwnd = IWINDOW( iv );
695 			GtkWidget *popup;
696 
697 			popup = gtk_ui_manager_get_widget( iwnd->ui_manager,
698 				"/ImageviewPopup" );
699 			gtk_menu_popup( GTK_MENU( popup ), NULL, NULL,
700 				(GtkMenuPositionFunc) NULL, NULL, 3,
701 				event->button.time );
702 			handled = TRUE;
703 }
704 			break;
705 
706 		default:
707 			break;
708 		}
709 
710 		break;
711 
712 	default:
713 		break;
714 	}
715 
716 	return( handled );
717 }
718 
719 static gboolean
imageview_filedrop(Imageview * iv,const char * file)720 imageview_filedrop( Imageview *iv, const char *file )
721 {
722 	gboolean result;
723 
724 	if( (result = iimage_replace( iv->imagemodel->iimage, file )) )
725 		symbol_recalculate_all();
726 
727 	return( result );
728 }
729 
730 static void
imageview_build(Imageview * iv,GtkWidget * vbox,iImage * iimage)731 imageview_build( Imageview *iv, GtkWidget *vbox, iImage *iimage )
732 {
733 	iWindow *iwnd = IWINDOW( iv );
734 
735 	GError *error;
736 	GtkWidget *mbar;
737 	GtkWidget *frame;
738 	GList *focus_chain;
739 
740 	/* All the model parts for our set of views.
741 	 */
742 	iv->imagemodel = imagemodel_new( iimage );
743 	g_object_ref( G_OBJECT( iv->imagemodel ) );
744 	iobject_sink( IOBJECT( iv->imagemodel ) );
745 	iv->imagemodel_changed_sid = g_signal_connect(
746 		G_OBJECT( iv->imagemodel ), "changed",
747 		G_CALLBACK( imageview_imagemodel_changed_cb ), iv );
748 
749         /* Make main menu bar
750          */
751 	gtk_action_group_add_actions( iwnd->action_group,
752 		imageview_actions, G_N_ELEMENTS( imageview_actions ),
753 		GTK_WINDOW( iv ) );
754 	gtk_action_group_add_toggle_actions( iwnd->action_group,
755 		imageview_toggle_actions,
756 			G_N_ELEMENTS( imageview_toggle_actions ),
757 		GTK_WINDOW( iv ) );
758 	gtk_action_group_add_radio_actions( iwnd->action_group,
759 		imageview_mode_radio_actions,
760 			G_N_ELEMENTS( imageview_mode_radio_actions ),
761 		IMAGEMODEL_SELECT,
762 		G_CALLBACK( imageview_mode_action_cb ),
763 		GTK_WINDOW( iv ) );
764 	gtk_action_group_add_radio_actions( iwnd->action_group,
765 		imageview_zoom_radio_actions,
766 			G_N_ELEMENTS( imageview_zoom_radio_actions ),
767 		1,
768 		G_CALLBACK( imageview_mag_action_cb ),
769 		GTK_WINDOW( iv ) );
770 
771 	error = NULL;
772 	if( !gtk_ui_manager_add_ui_from_string( iwnd->ui_manager,
773 		imageview_menubar_ui_description, -1, &error ) ) {
774 		g_message( "building menus failed: %s", error->message );
775 		g_error_free( error );
776 		exit( EXIT_FAILURE );
777 	}
778 
779 	mbar = gtk_ui_manager_get_widget( iwnd->ui_manager,
780 		"/ImageviewMenubar" );
781 	gtk_box_pack_start( GTK_BOX( vbox ), mbar, FALSE, FALSE, 0 );
782         gtk_widget_show( mbar );
783 
784 	/* This will set to NULL if we don't have infobar support.
785 	 */
786 	if( (IWINDOW( iv )->infobar = infobar_new()) )
787 		gtk_box_pack_start( GTK_BOX( vbox ),
788 			GTK_WIDGET( IWINDOW( iv )->infobar ), FALSE, FALSE, 0 );
789 
790 	/* Status bar.
791 	 */
792 	iv->sv = statusview_new( iv->imagemodel );
793 	gtk_box_pack_start( GTK_BOX( vbox ),
794 		GTK_WIDGET( iv->sv ), FALSE, FALSE, 0 );
795 
796 	/* Conversion bar.
797 	 */
798 	iv->cv = conversionview_new( iv->imagemodel );
799 	gtk_box_pack_start( GTK_BOX( vbox ),
800 		GTK_WIDGET( iv->cv ), FALSE, FALSE, 0 );
801 
802 	/* Paintbox bar.
803 	 */
804 	iv->pbv = paintboxview_new( iv->imagemodel );
805 	gtk_box_pack_start( GTK_BOX( vbox ),
806 		GTK_WIDGET( iv->pbv ), FALSE, FALSE, 0 );
807 
808 	/* Image area.
809 	 */
810 	frame = gtk_frame_new( NULL );
811 	gtk_frame_set_shadow_type( GTK_FRAME( frame ), GTK_SHADOW_OUT );
812 	gtk_widget_show( frame );
813 	gtk_box_pack_start( GTK_BOX( vbox ),
814 		GTK_WIDGET( frame ), TRUE, TRUE, 0 );
815 	iv->ip = imagepresent_new( iv->imagemodel );
816 	gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( iv->ip ) );
817 	gtk_widget_show( GTK_WIDGET( iv->ip ) );
818 	gtk_signal_connect_after( GTK_OBJECT( iv->ip->id ), "event",
819 		GTK_SIGNAL_FUNC( imageview_event ), iv );
820 
821 	/* Position and size to restore?
822 	 */
823 	if( MODEL( iimage )->window_width != -1 ) {
824 		/* Floatwindow will set pos/size.
825 		 */
826 		iv->imagemodel->show_status = iimage->show_status;
827 		iv->imagemodel->show_paintbox = iimage->show_paintbox;
828 		iv->imagemodel->show_convert = iimage->show_convert;
829 		iv->imagemodel->show_rulers = iimage->show_rulers;
830 		iv->imagemodel->scale = iimage->scale;
831 		iv->imagemodel->offset = iimage->offset;
832 		iv->imagemodel->falsecolour = iimage->falsecolour;
833 		iv->imagemodel->type = iimage->type;
834 
835 		/* Our caller must call imagepresent_set_mag_pos() after
836 		 * _show(). Not accurate if we set it here.
837 		 */
838 	}
839 	else {
840 		int w, h;
841 
842 		/* Set initial size. This is really hard to do right :-( These
843 		 * magic numbers will break with different themes.
844 
845 		 	FIXME ... maybe realize the window but don't map it,
846 			calculate border size, then set default size and map?
847 			yuk!
848 
849 			the magic numbers here are hard to derive, there are
850 			many, many widgets piled up together to make this
851 			window
852 
853 			last set correctly for clearlooks
854 
855 		 */
856 		w = IM_MIN( IMAGE_WINDOW_WIDTH,
857 			iv->imagemodel->conv->image.width + 14 );
858 		h = IM_MIN( IMAGE_WINDOW_HEIGHT,
859 			iv->imagemodel->conv->image.height + 39 );
860 		gtk_window_set_default_size( GTK_WINDOW( iv ), w, h );
861 		conversion_set_mag( iv->imagemodel->conv, 1 );
862 	}
863 
864 	/* Set as file drop destination
865 	 */
866 	filedrop_register( GTK_WIDGET( iv ),
867 		(FiledropFunc) imageview_filedrop, iv );
868 
869 	/* Override the focus_chain ... we want the imagedisplay first.
870 	 */
871 	focus_chain = NULL;
872 	focus_chain = g_list_append( focus_chain, iv->ip );
873 	focus_chain = g_list_append( focus_chain, iv->cv );
874 	focus_chain = g_list_append( focus_chain, iv->pbv );
875 	gtk_container_set_focus_chain( GTK_CONTAINER( vbox ), focus_chain );
876 	g_list_free( focus_chain );
877 
878 	gtk_widget_grab_focus( GTK_WIDGET( iv->ip->id ) );
879 }
880 
881 static void *
imageview_add_region(Classmodel * classmodel,Imageview * iv)882 imageview_add_region( Classmodel *classmodel, Imageview *iv )
883 {
884 	iRegionInstance *instance;
885 
886 	if( MODEL( classmodel )->display &&
887 		(instance = classmodel_get_instance( classmodel )) ) {
888 		Regionview *regionview = regionview_new( classmodel,
889 			&instance->area, iv->ip );
890 		PElement *root = &HEAPMODEL( classmodel )->row->expr->root;
891 
892 		/* Look at the class we are drawing, set the display type.
893 		 */
894 		regionview_set_type( regionview, root );
895 	}
896 
897 	return( NULL );
898 }
899 
900 static void
imageview_popdown(iWindow * iwnd,void * client,iWindowNotifyFn nfn,void * sys)901 imageview_popdown( iWindow *iwnd, void *client,
902 	iWindowNotifyFn nfn, void *sys )
903 {
904 	Imageview *iv = IMAGEVIEW( iwnd );
905 	Imagemodel *imagemodel = iv->imagemodel;
906 	iImage *iimage = imagemodel->iimage;
907 	Conversion *conv = imagemodel->conv;
908 
909 	/* We have to note position/size in popdown rather than destroy, since
910 	 * the widgets have to all still be extant.
911 	 */
912 
913 	/* Save the centre of the window in image cods.
914 	 */
915 	conversion_disp_to_im( conv,
916 		imagemodel->visible.left + imagemodel->visible.width / 2,
917 		imagemodel->visible.top + imagemodel->visible.height / 2,
918 		&iimage->image_left, &iimage->image_top );
919 	iimage->image_mag = conv->mag;
920 
921 	iimage->show_status = imagemodel->show_status;
922 	iimage->show_paintbox = imagemodel->show_paintbox;
923 	iimage->show_rulers = imagemodel->show_rulers;
924 
925 	/* Signal changed on iimage if we save the convert settings. This will
926 	 * make the thumbnail update.
927 	 */
928 	if( iimage->show_convert != imagemodel->show_convert ||
929 		iimage->scale != imagemodel->scale ||
930 		iimage->offset != imagemodel->offset ||
931 		iimage->falsecolour != imagemodel->falsecolour ||
932 		iimage->type != imagemodel->type ) {
933 		iimage->show_convert = imagemodel->show_convert;
934 		iimage->scale = imagemodel->scale;
935 		iimage->offset = imagemodel->offset;
936 		iimage->falsecolour = imagemodel->falsecolour;
937 		iimage->type = imagemodel->type;
938 		iobject_changed( IOBJECT( iimage ) );
939 	}
940 
941 	nfn( sys, IWINDOW_YES );
942 }
943 
944 static void
imageview_link(Imageview * iv,iImage * iimage,GtkWidget * parent)945 imageview_link( Imageview *iv, iImage *iimage, GtkWidget *parent )
946 {
947 	iwindow_set_build( IWINDOW( iv ),
948 		(iWindowBuildFn) imageview_build, iimage, NULL, NULL );
949 	iwindow_set_popdown( IWINDOW( iv ), imageview_popdown, NULL );
950 	iwindow_set_parent( IWINDOW( iv ), parent );
951 	floatwindow_link( FLOATWINDOW( iv ), MODEL( iimage ) );
952 	iwindow_build( IWINDOW( iv ) );
953 	slist_map( iimage->classmodels,
954 		(SListMapFn) imageview_add_region, iv );
955 
956 	/* Initial "changed" on the model to get all views to init.
957 	 */
958 	iobject_changed( IOBJECT( iv->imagemodel ) );
959 }
960 
961 Imageview *
imageview_new(iImage * iimage,GtkWidget * parent)962 imageview_new( iImage *iimage, GtkWidget *parent )
963 {
964 	Imageview *iv = gtk_type_new( TYPE_IMAGEVIEW );
965 
966 	imageview_link( iv, iimage, parent );
967 
968 	/* This is odd ... we wouldn't normally _show() the widget in _new(),
969 	 * but restoring the scroll position doesn't work unless the window is
970 	 * visible. We have to show here.
971 	 */
972 	gtk_widget_show( GTK_WIDGET( iv ) );
973 
974 	if( MODEL( iimage )->window_width != -1 )
975 		imagepresent_set_mag_pos( iv->ip,
976 			iimage->image_mag,
977 			iimage->image_left, iimage->image_top );
978 
979 	return( iv );
980 }
981 
982 /* Make an imageview, and try to make area (image cods) visible. width/height
983  * can be -ve
984  */
985 Imageview *
imageview_new_area(iImage * iimage,Rect * area,GtkWidget * parent)986 imageview_new_area( iImage *iimage, Rect *area, GtkWidget *parent )
987 {
988 	Imageview *iv = imageview_new( iimage, parent );
989 	Imagemodel *imagemodel = iv->imagemodel;
990 	Conversion *conv = imagemodel->conv;
991 	int shrink_x, shrink_y, shrink;
992 
993 	/* Calculate a shrink factor which should make all the region
994 	 * visible ... don't zoom.
995 	 */
996 	shrink_x = (abs( area->width ) + conv->canvas.width) /
997 		conv->canvas.width;
998 	shrink_y = (abs( area->height ) + conv->canvas.height) /
999 		conv->canvas.height;
1000 	shrink = -IM_MAX( 1, IM_MAX( shrink_x, shrink_y ) );
1001 	if( shrink == -1 )
1002 		shrink = 1;
1003 
1004 	imagepresent_set_mag_pos( iv->ip, shrink,
1005 		area->left + area->width / 2,
1006 		area->top + area->height / 2 );
1007 
1008 	return( iv );
1009 }
1010