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