1 /* run the display for an image in a workspace
2  */
3 
4 /*
5 
6     Copyright (C) 1991-2003 The National Gallery
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12 
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17 
18     You should have received a copy of the GNU General Public License along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21 
22  */
23 
24 /*
25 
26     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
27 
28  */
29 
30 /*
31 #define DEBUG
32  */
33 
34 #include "ip.h"
35 
36 static GraphicviewClass *parent_class = NULL;
37 
38 static void
iimageview_realize(GtkWidget * widget)39 iimageview_realize( GtkWidget *widget )
40 {
41 	GTK_WIDGET_CLASS( parent_class )->realize( widget );
42 
43 	/* Mark us as a symbol drag-to widget.
44 	 */
45 	set_symbol_drag_type( widget );
46 }
47 
48 GtkWidget *
iimageview_drag_window_new(int width,int height)49 iimageview_drag_window_new( int width, int height )
50 {
51 	GtkWidget *window;
52 
53 	window = gtk_window_new( GTK_WINDOW_POPUP );
54 	gtk_widget_set_app_paintable( GTK_WIDGET( window ), TRUE );
55 	gtk_widget_set_size_request( window, width, height );
56 	gtk_widget_realize( window );
57 #ifdef HAVE_SET_OPACITY
58 	gdk_window_set_opacity( window->window, 0.5 );
59 #endif /*HAVE_SET_OPACITY*/
60 
61 	return( window );
62 }
63 
64 static void
iimageview_drag_begin(GtkWidget * widget,GdkDragContext * context)65 iimageview_drag_begin( GtkWidget *widget, GdkDragContext *context )
66 {
67 	iImageview *iimageview = IIMAGEVIEW( widget );
68 	Conversion *conv = iimageview->conv;
69 	GtkWidget *window;
70 	Imagedisplay *id;
71 
72 #ifdef DEBUG
73 	printf( "iimageview_drag_begin: \n" );
74 #endif /*DEBUG*/
75 
76 	window = iimageview_drag_window_new(
77 		conv->canvas.width, conv->canvas.height );
78 	gtk_object_set_data_full( GTK_OBJECT( widget ),
79 		"nip2-drag-window", window,
80 		(GtkDestroyNotify) gtk_widget_destroy );
81 	id = imagedisplay_new( conv );
82 	gtk_container_add( GTK_CONTAINER( window ), GTK_WIDGET( id ) );
83 	gtk_widget_show( GTK_WIDGET( id ) );
84 	gtk_drag_set_icon_widget( context, window, -2, -2 );
85 }
86 
87 static void
iimageview_drag_end(GtkWidget * widget,GdkDragContext * context)88 iimageview_drag_end( GtkWidget *widget, GdkDragContext *context )
89 {
90 #ifdef DEBUG
91 	printf( "iimageview_drag_end:\n" );
92 #endif /*DEBUG*/
93 
94 	gtk_object_set_data( GTK_OBJECT( widget ),
95 		"nip2-drag-window", NULL );
96 }
97 
98 static void
iimageview_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)99 iimageview_drag_data_get( GtkWidget *widget, GdkDragContext *context,
100 	GtkSelectionData *selection_data, guint info, guint time )
101 {
102 #ifdef DEBUG
103 	printf( "iimageview_drag_data_get:\n" );
104 #endif /*DEBUG*/
105 
106 	if( info == TARGET_SYMBOL ) {
107 		iImageview *iimageview = IIMAGEVIEW( widget );
108 		iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject );
109 		Row *row = HEAPMODEL( iimage )->row;
110 		char txt[256];
111 		VipsBuf buf = VIPS_BUF_STATIC( txt );
112 
113 		/* Drag the fully-qualified row name.
114 		 */
115 		row_qualified_name_relative( main_workspaceroot->sym,
116 			row, &buf );
117 		gtk_selection_data_set( selection_data,
118 			gdk_atom_intern( "text/symbol", FALSE ), 8,
119 			(guchar *) vips_buf_all( &buf ),
120 			strlen( vips_buf_all( &buf ) ) );
121 	}
122 }
123 
124 static void
iimageview_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time)125 iimageview_drag_data_received( GtkWidget *widget, GdkDragContext *context,
126 	gint x, gint y, GtkSelectionData *selection_data,
127 	guint info, guint time )
128 {
129 
130 #ifdef DEBUG
131 	printf( "iimageview_drag_data_received:\n" );
132 #endif /*DEBUG*/
133 
134 	if( info == TARGET_SYMBOL && selection_data->length > 0 &&
135 		selection_data->format == 8 ) {
136 		const char *from_row_path = (const char *) selection_data->data;
137 		iImageview *iimageview = IIMAGEVIEW( widget );
138 		iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject );
139 		Row *row = HEAPMODEL( iimage )->row;
140 		Row *from_row;
141 
142 #ifdef DEBUG
143 		printf( " seen TARGET_SYMBOL \"%s\"\n",
144 			from_row_path );
145 #endif /*DEBUG*/
146 
147 		/* Block drags to ourselves ... pointless.
148 		 */
149 		if( (from_row = row_parse_name( main_workspaceroot->sym,
150 			from_row_path )) &&
151 			from_row != row ) {
152 			iText *itext = ITEXT( HEAPMODEL( iimage )->rhs->itext );
153 			char txt[256];
154 			VipsBuf buf = VIPS_BUF_STATIC( txt );
155 
156 			/* Qualify relative to us. We don't want to embed
157 			 * workspace names unless we have to.
158 			 */
159 			if( row->top_row->sym )
160 				row_qualified_name_relative( row->top_row->sym,
161 					from_row, &buf );
162 
163 			if( itext_set_formula( itext, vips_buf_all( &buf ) ) ) {
164 				itext_set_edited( itext, TRUE );
165 				(void) expr_dirty( row->expr,
166 					link_serial_new() );
167 				workspace_set_modified( row->ws, TRUE );
168 				symbol_recalculate_all();
169 			}
170 
171 			/* Usually the drag-from row will be selected, very
172 			 * annoying. Select the drag-to row.
173 			 */
174 			row_select( row );
175 		}
176 	}
177 }
178 
179 /* Not the same as model->edit :-( if this is a region, don't pop the region
180  * edit box, pop a viewer on the image.
181  */
182 static void
iimageview_edit(GtkWidget * parent,iImageview * iimageview)183 iimageview_edit( GtkWidget *parent, iImageview *iimageview )
184 {
185 	iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject );
186 
187 	if( IS_IREGION( iimage ) && iimage->value.ii )
188 		imageview_new( iimage, parent );
189 	else
190 		model_edit( parent, MODEL( iimage ) );
191 }
192 
193 static void
iimageview_link(View * view,Model * model,View * parent)194 iimageview_link( View *view, Model *model, View *parent )
195 {
196 	iImageview *iimageview = IIMAGEVIEW( view );
197 
198 	Rowview *rview;
199 
200 	VIEW_CLASS( parent_class )->link( view, model, parent );
201 
202 	if( (rview = ROWVIEW( parent->parent )) ) {
203 		Row *row = ROW( VOBJECT( rview )->iobject );
204 
205 		rowview_menu_attach( rview, GTK_WIDGET( iimageview->id ) );
206 
207 		if( row->popup && row->top_row == row ) {
208 			row->popup = FALSE;
209 			iimageview_edit( GTK_WIDGET( view ), iimageview );
210 		}
211 	}
212 }
213 
214 static void
iimageview_refresh(vObject * vobject)215 iimageview_refresh( vObject *vobject )
216 {
217 	iImageview *iimageview = IIMAGEVIEW( vobject );
218 	iImage *iimage = IIMAGE( vobject->iobject );
219 	Row *row = HEAPMODEL( iimage )->row;
220 
221 	int w, h;
222 	gboolean enabled;
223 	double scale, offset;
224 	gboolean falsecolour, type;
225 
226 #ifdef DEBUG
227 	printf( "iimageview_refresh\n" );
228 #endif /*DEBUG*/
229 
230 	w = IM_MAX( GTK_WIDGET( iimageview->id )->requisition.width,
231 		DISPLAY_THUMBNAIL );
232 	h = DISPLAY_THUMBNAIL;
233 	conversion_set_image( iimageview->conv, iimage->value.ii );
234 	gtk_widget_set_size_request( GTK_WIDGET( iimageview->id ), w, h );
235 	gtk_widget_queue_draw( GTK_WIDGET( iimageview->id ) );
236 
237 	set_gcaption( iimageview->label, "%s",
238 		NN( IOBJECT( iimage )->caption ) );
239 
240 	/* Set scale/offset for the thumbnail. Use the prefs setting, or if
241 	 * there's a setting for this image, override with that.
242 	 */
243 	enabled = DISPLAY_CONVERSION;
244 	scale = row->ws->scale;
245 	offset = row->ws->offset;
246 	falsecolour = FALSE;
247 	type = TRUE;
248 
249 	/* If the image_width has been set, a viewer must have popped down and
250 	 * set it, so the recorded settings must be valid.
251 	 */
252 	if( MODEL( iimage )->window_width != -1 ) {
253 		enabled = iimage->show_convert;
254 		scale = iimage->scale;
255 		offset = iimage->offset;
256 		falsecolour = iimage->falsecolour;
257 		type = iimage->type;
258 	}
259 
260 	conversion_set_params( iimageview->conv,
261 		enabled, scale, offset, falsecolour, type );
262 
263 	VOBJECT_CLASS( parent_class )->refresh( vobject );
264 }
265 
266 static void
iimageview_class_init(iImageviewClass * class)267 iimageview_class_init( iImageviewClass *class )
268 {
269 	GtkWidgetClass *widget_class = (GtkWidgetClass *) class;
270 	vObjectClass *vobject_class = (vObjectClass *) class;
271 	ViewClass *view_class = (ViewClass *) class;
272 
273 	parent_class = g_type_class_peek_parent( class );
274 
275 	/* Create signals.
276 	 */
277 
278 	/* Init methods.
279 	 */
280 	widget_class->realize = iimageview_realize;
281 	widget_class->drag_begin = iimageview_drag_begin;
282 	widget_class->drag_end = iimageview_drag_end;
283 	widget_class->drag_data_get = iimageview_drag_data_get;
284 	widget_class->drag_data_received = iimageview_drag_data_received;
285 
286 	vobject_class->refresh = iimageview_refresh;
287 
288 	view_class->link = iimageview_link;
289 }
290 
291 static void
iimageview_doubleclick_one_cb(GtkWidget * widget,GdkEvent * event,iImageview * iimageview)292 iimageview_doubleclick_one_cb( GtkWidget *widget, GdkEvent *event,
293 	iImageview *iimageview )
294 {
295 	Heapmodel *heapmodel = HEAPMODEL( VOBJECT( iimageview )->iobject );
296 	Row *row = heapmodel->row;
297 
298 	row_select_modifier( row, event->button.state );
299 }
300 
301 static void
iimageview_doubleclick_two_cb(GtkWidget * widget,GdkEvent * event,iImageview * iimageview)302 iimageview_doubleclick_two_cb( GtkWidget *widget, GdkEvent *event,
303 	iImageview *iimageview )
304 {
305 	iimageview_edit( widget, iimageview );
306 }
307 
308 static gboolean
iimageview_filedrop(iImageview * iimageview,const char * file)309 iimageview_filedrop( iImageview *iimageview, const char *file )
310 {
311 	iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject );
312 	gboolean result;
313 
314 	if( (result = iimage_replace( iimage, file )) )
315 		symbol_recalculate_all();
316 
317 	return( result );
318 }
319 
320 static void
iimageview_tooltip_generate(GtkWidget * widget,VipsBuf * buf,iImageview * iimageview)321 iimageview_tooltip_generate( GtkWidget *widget,
322 	VipsBuf *buf, iImageview *iimageview )
323 {
324 	iImage *iimage = IIMAGE( VOBJECT( iimageview )->iobject );
325 	Imageinfo *ii = iimage->value.ii;
326 	IMAGE *im = imageinfo_get( FALSE, ii );
327 
328 	vips_buf_rewind( buf );
329 	vips_buf_appends( buf, vips_buf_all( &iimage->caption_buffer ) );
330 	if( im ) {
331 		double size = (double) im->Ysize * IM_IMAGE_SIZEOF_LINE( im );
332 
333 		vips_buf_appends( buf, ", " );
334 		vips_buf_append_size( buf, size );
335 		vips_buf_appendf( buf, ", %.3gx%.3g p/mm", im->Xres, im->Yres );
336 	}
337 }
338 
339 static void
iimageview_init(iImageview * iimageview)340 iimageview_init( iImageview *iimageview )
341 {
342 	GtkWidget *eb;
343 	GtkWidget *vbox;
344 
345 #ifdef DEBUG
346 	printf( "iimageview_init\n" );
347 #endif /*DEBUG*/
348 
349         eb = gtk_event_box_new();
350         gtk_box_pack_start( GTK_BOX( iimageview ), eb, FALSE, FALSE, 0 );
351 	vbox = gtk_vbox_new( FALSE, 0 );
352         gtk_container_add( GTK_CONTAINER( eb ), vbox );
353         gtk_widget_show( vbox );
354 
355 	iimageview->conv = conversion_new( NULL );
356 	iimageview->conv->tile_size = 16;
357         iimageview->id = imagedisplay_new( iimageview->conv );
358 	imagedisplay_set_shrink_to_fit( iimageview->id, TRUE );
359         gtk_box_pack_start( GTK_BOX( vbox ),
360 		GTK_WIDGET( iimageview->id ), FALSE, FALSE, 0 );
361 	gtk_widget_show( GTK_WIDGET( iimageview->id ) );
362 
363 	/* Need these events in the enclosing workspaceview.
364 	 */
365 	gtk_widget_add_events( GTK_WIDGET( iimageview->id ),
366 		GDK_POINTER_MOTION_MASK |
367 		GDK_POINTER_MOTION_HINT_MASK |
368 		GDK_BUTTON_PRESS_MASK |
369 		GDK_BUTTON_RELEASE_MASK );
370 
371 	iimageview->label = gtk_label_new( "" );
372         gtk_misc_set_alignment( GTK_MISC( iimageview->label ), 0, 0.5 );
373         gtk_misc_set_padding( GTK_MISC( iimageview->label ), 2, 0 );
374         gtk_box_pack_start( GTK_BOX( vbox ),
375 		GTK_WIDGET( iimageview->label ), FALSE, FALSE, 0 );
376 	gtk_widget_show( GTK_WIDGET( iimageview->label ) );
377 
378 	/* Set as file drop destination
379 	 */
380 	filedrop_register( GTK_WIDGET( iimageview ),
381 		(FiledropFunc) iimageview_filedrop, iimageview );
382 
383 	doubleclick_add( GTK_WIDGET( iimageview ), FALSE,
384 		DOUBLECLICK_FUNC( iimageview_doubleclick_one_cb ), iimageview,
385 		DOUBLECLICK_FUNC( iimageview_doubleclick_two_cb ), iimageview );
386 
387         set_tooltip_generate( eb,
388 		(TooltipGenerateFn) iimageview_tooltip_generate,
389 		iimageview, NULL );
390 
391 	gtk_widget_set_name( eb, "caption_widget" );
392         gtk_widget_show( GTK_WIDGET( eb ) );
393 }
394 
395 GtkType
iimageview_get_type(void)396 iimageview_get_type( void )
397 {
398 	static GtkType iimageview_type = 0;
399 
400 	if( !iimageview_type ) {
401 		static const GtkTypeInfo info = {
402 			"iImageview",
403 			sizeof( iImageview ),
404 			sizeof( iImageviewClass ),
405 			(GtkClassInitFunc) iimageview_class_init,
406 			(GtkObjectInitFunc) iimageview_init,
407 			/* reserved_1 */ NULL,
408 			/* reserved_2 */ NULL,
409 			(GtkClassInitFunc) NULL,
410 		};
411 
412 		iimageview_type = gtk_type_unique( TYPE_GRAPHICVIEW, &info );
413 	}
414 
415 	return( iimageview_type );
416 }
417 
418 View *
iimageview_new(void)419 iimageview_new( void )
420 {
421 	iImageview *iimageview = gtk_type_new( TYPE_IIMAGEVIEW );
422 
423 	return( VIEW( iimageview ) );
424 }
425