1 /* A rowview in a workspace ... not a widget, part of column
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 ModelClass *parent_class = NULL;
37 
38 enum {
39 	ROWVIEW_TARGET_STRING,
40 };
41 
42 static GtkTargetEntry rowview_target_table[] = {
43 	{ "STRING", 0, ROWVIEW_TARGET_STRING },
44 	{ "text/plain", 0, ROWVIEW_TARGET_STRING }
45 };
46 
47 /* Just one popup for all tally buttons.
48  */
49 static GtkWidget *rowview_popup_menu = NULL;
50 
51 static void
rowview_destroy(GtkObject * object)52 rowview_destroy( GtkObject *object )
53 {
54 	Rowview *rview;
55 
56 	g_return_if_fail( object != NULL );
57 	g_return_if_fail( IS_ROWVIEW( object ) );
58 
59 	rview = ROWVIEW( object );
60 
61 #ifdef DEBUG
62 	printf( "rowview_destroy: " );
63 	row_name_print( ROW( VOBJECT( rview )->iobject ) );
64 	printf( "\n" );
65 #endif /*DEBUG*/
66 
67 	IM_FREE( rview->last_tooltip );
68 
69 	/* Kill children ... must do this ourselves, since we are not a
70 	 * self-contained widget.
71 	 */
72 	DESTROY_GTK( rview->but );
73 	DESTROY_GTK( rview->spin );
74 	DESTROY_GTK( rview->led );
75 
76 	GTK_OBJECT_CLASS( parent_class )->destroy( object );
77 }
78 
79 static void
rowview_attach(Rowview * rview,GtkWidget * child,int x,GtkAttachOptions xoptions,GtkAttachOptions yoptions)80 rowview_attach( Rowview *rview, GtkWidget *child, int x,
81 	GtkAttachOptions xoptions, GtkAttachOptions yoptions )
82 {
83 	Subcolumnview *sview = rview->sview;
84 
85 	gtk_widget_ref( child );
86 
87 	if( child->parent )
88 		gtk_container_remove( GTK_CONTAINER( sview->table ), child );
89 	gtk_table_attach( GTK_TABLE( sview->table ), child,
90 		x, x + 1, rview->rnum, rview->rnum + 1,
91 		xoptions, yoptions, 0, 0 );
92 
93 	gtk_widget_unref( child );
94 }
95 
96 static void
rowview_update_widgets(Rowview * rview)97 rowview_update_widgets( Rowview *rview )
98 {
99 	Row *row = ROW( VOBJECT( rview )->iobject );
100 	int pos = ICONTAINER( row )->pos;
101 	gboolean editable = row->ws->mode != WORKSPACE_MODE_NOEDIT;
102 
103 #ifdef DEBUG
104 	printf( "rowview_refresh: " );
105 	row_name_print( row );
106 	printf( "\n" );
107 	printf( "\teditable == %d\n", editable );
108 #endif /*DEBUG*/
109 
110 	/* Attach widgets to parent in new place.
111 	 */
112         if( rview->rnum != pos ) {
113 #ifdef DEBUG
114 		printf( "rowview_refresh: move from row %d to row %d\n",
115 			rview->rnum, pos );
116 #endif /*DEBUG*/
117 
118 		rview->rnum = pos;
119 
120 		rowview_attach( rview, rview->spin,
121 			0, GTK_FILL, GTK_FILL );
122 		rowview_attach( rview, rview->but,
123 			1, GTK_FILL, GTK_EXPAND | GTK_FILL );
124 		rowview_attach( rview, rview->led,
125 			2, GTK_FILL, GTK_EXPAND | GTK_FILL );
126 		if( rview->rhsview )
127 			rowview_attach( rview, GTK_WIDGET( rview->rhsview ),
128 				3,
129 				GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL );
130 	}
131 
132         /* Set colours.
133          */
134 	if( CALC_DISPLAY_LED ) {
135 		char *stock_id;
136 
137 		stock_id = STOCK_LED_OFF;
138 		if( row->selected )
139 			stock_id = STOCK_LED_GREEN;
140 		else if( row->show == ROW_SHOW_PARENT )
141 			stock_id = STOCK_LED_CYAN;
142 		else if( row->show == ROW_SHOW_CHILD )
143 			stock_id = STOCK_LED_BLUE;
144 		else if( row->err )
145 			stock_id = STOCK_LED_RED;
146 		else if( row->dirty )
147 			stock_id = STOCK_LED_YELLOW;
148 
149 		gtk_image_set_from_stock( GTK_IMAGE( rview->led ),
150 			stock_id, GTK_ICON_SIZE_MENU );
151 	}
152 	else {
153 		gchar *name = "";
154 
155 		if( row->selected )
156 			name = "selected_widget";
157 		else if( row->show == ROW_SHOW_PARENT )
158 			name = "parent_widget";
159 		else if( row->show == ROW_SHOW_CHILD )
160 			name = "child_widget";
161 		else if( row->err )
162 			name = "error_widget";
163 		else if( row->dirty )
164 			name = "dirty_widget";
165 
166 		gtk_widget_set_name( rview->but, name );
167 	}
168 	widget_visible( rview->led,
169 		rview->visible && CALC_DISPLAY_LED && editable );
170 
171 	/* Update button.
172 	 */
173         set_glabel( rview->label, "%s", row_name( row ) );
174 	widget_visible( rview->but, rview->visible && editable );
175 
176 	/* Spin visible only if this is a class.
177 	 */
178 	widget_visible( rview->spin,
179 		rview->visible && row->is_class && editable );
180 
181 	if( rview->rhsview )
182 		widget_visible( GTK_WIDGET( rview->rhsview ), rview->visible );
183 }
184 
185 static void
rowview_reset(View * view)186 rowview_reset( View *view )
187 {
188 	Rowview *rview = ROWVIEW( view );
189 
190 	rowview_update_widgets( rview );
191 
192 	VIEW_CLASS( parent_class )->reset( view );
193 }
194 
195 static void
rowview_refresh(vObject * vobject)196 rowview_refresh( vObject *vobject )
197 {
198 	Rowview *rview = ROWVIEW( vobject );
199 
200 	rowview_update_widgets( rview );
201 
202 	VOBJECT_CLASS( parent_class )->refresh( vobject );
203 }
204 
205 /* Single click on button callback.
206  */
207 static void
rowview_single_cb(GtkWidget * wid,GdkEvent * event,Rowview * rview)208 rowview_single_cb( GtkWidget *wid, GdkEvent *event, Rowview *rview )
209 {
210 	Row *row = ROW( VOBJECT( rview )->iobject );
211 
212 	row_select_modifier( row, event->button.state );
213 }
214 
215 /* Edit our object.
216  */
217 static gboolean
rowview_edit(Rowview * rview)218 rowview_edit( Rowview *rview )
219 {
220 	Row *row = ROW( VOBJECT( rview )->iobject );
221 	Model *graphic = row->child_rhs->graphic;
222 
223 	if( graphic )
224 		model_edit( GTK_WIDGET( rview->sview ), graphic );
225 
226 	return( TRUE );
227 }
228 
229 /* Double click on button callback.
230  */
231 static void
rowview_double_cb(GtkWidget * button,GdkEvent * event,Rowview * rview)232 rowview_double_cb( GtkWidget *button, GdkEvent *event, Rowview *rview )
233 {
234 	if( !rowview_edit( rview ) )
235 		iwindow_alert( button, GTK_MESSAGE_ERROR );
236 }
237 
238 /* Edit in menu.
239  */
240 static void
rowview_edit_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)241 rowview_edit_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
242 {
243 	if( !rowview_edit( rview ) )
244 		iwindow_alert( button, GTK_MESSAGE_ERROR );
245 }
246 
247 /* Show info.
248  */
249 static gboolean
rowview_header(Rowview * rview)250 rowview_header( Rowview *rview )
251 {
252 	Row *row = ROW( VOBJECT( rview )->iobject );
253 	Model *graphic = row->child_rhs->graphic;
254 
255 	if( graphic )
256 		model_header( GTK_WIDGET( rview->sview ), graphic );
257 
258 	return( TRUE );
259 }
260 
261 /* Info in menu.
262  */
263 static void
rowview_header_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)264 rowview_header_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
265 {
266 	if( !rowview_header( rview ) )
267 		iwindow_alert( button, GTK_MESSAGE_ERROR );
268 }
269 
270 /* Clone the current item.
271  */
272 static void
rowview_clone_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)273 rowview_clone_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
274 {
275 	Row *row = ROW( VOBJECT( rview )->iobject );
276 	Workspace *ws = row->top_col->ws;
277 
278 	/* Only allow clone of top level rows.
279 	 */
280 	if( row->top_row != row ) {
281 		error_top( _( "Can't duplicate." ) );
282 		error_sub( "%s",
283 			_( "You can only duplicate top level rows." ) );
284 		iwindow_alert( button, GTK_MESSAGE_INFO );
285 		return;
286 	}
287 
288         workspace_deselect_all( ws );
289         row_select( row );
290         if( !workspace_selected_duplicate( ws ) )
291 		iwindow_alert( button, GTK_MESSAGE_ERROR );
292         workspace_deselect_all( ws );
293 
294         symbol_recalculate_all();
295 }
296 
297 /* Ungroup the current item.
298  */
299 static void
rowview_ungroup_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)300 rowview_ungroup_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
301 {
302 	Row *row = ROW( VOBJECT( rview )->iobject );
303 
304         workspace_deselect_all( row->ws );
305         row_select( row );
306         if( !workspace_selected_ungroup( row->ws ) )
307 		iwindow_alert( button, GTK_MESSAGE_ERROR );
308         symbol_recalculate_all();
309 }
310 
311 /* Save the current item.
312  */
313 static void
rowview_save_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)314 rowview_save_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
315 {
316 	iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( rview ) ) );
317 	Row *row = ROW( VOBJECT( rview )->iobject );
318 	Model *graphic = row->child_rhs->graphic;
319 
320 	if( graphic )
321 		classmodel_graphic_save( CLASSMODEL( graphic ),
322 			GTK_WIDGET( iwnd ) );
323 }
324 
325 /* Replace the current item.
326  */
327 static void
rowview_replace_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)328 rowview_replace_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
329 {
330 	iWindow *iwnd = IWINDOW( view_get_toplevel( VIEW( rview ) ) );
331 	Row *row = ROW( VOBJECT( rview )->iobject );
332 	Model *graphic = row->child_rhs->graphic;
333 
334 	if( graphic )
335 		classmodel_graphic_replace( CLASSMODEL( graphic ),
336 			GTK_WIDGET( iwnd ) );
337 }
338 
339 /* Recalculate the current item.
340  */
341 static void
rowview_recalc_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)342 rowview_recalc_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
343 {
344 	Row *row = ROW( VOBJECT( rview )->iobject );
345 	Workspace *ws = row->top_col->ws;
346 
347 	/* Mark dirty from this sym on, and force a recalc even if recalc is
348 	 * off.
349 	 */
350         workspace_deselect_all( ws );
351         row_select( row );
352         if( !workspace_selected_recalc( ws ) )
353 		iwindow_alert( button, GTK_MESSAGE_ERROR );
354         workspace_deselect_all( ws );
355 
356 	/* Now ... pick up any errors.
357 	 */
358 	if( row->sym &&
359 		!symbol_recalculate_check( row->sym ) )
360 		iwindow_alert( button, GTK_MESSAGE_ERROR );
361 }
362 
363 /* Reset the current item.
364  */
365 static void
rowview_clear_edited_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)366 rowview_clear_edited_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
367 {
368 	Row *row = ROW( VOBJECT( rview )->iobject );
369 
370 	(void) icontainer_map_all( ICONTAINER( row ),
371 		(icontainer_map_fn) model_clear_edited, NULL );
372 	symbol_recalculate_all();
373 }
374 
375 /* Remove the current item.
376  */
377 static void
rowview_remove_cb(GtkWidget * menu,GtkWidget * button,Rowview * rview)378 rowview_remove_cb( GtkWidget *menu, GtkWidget *button, Rowview *rview )
379 {
380 	Row *row = ROW( VOBJECT( rview )->iobject );
381 	Workspace *ws = row->top_col->ws;
382 
383 	workspace_deselect_all( ws );
384 	row_select( row );
385 	workspace_selected_remove_yesno( ws, button );
386 }
387 
388 /* Callback for up/down spin button.
389  */
390 static void
rowview_spin_up_cb(GtkWidget * widget,gpointer client)391 rowview_spin_up_cb( GtkWidget *widget, gpointer client )
392 {
393 	Rowview *rview = ROWVIEW( client );
394 	Row *row = ROW( VOBJECT( rview )->iobject );
395 	Rhs *rhs = row->child_rhs;
396 
397 	rhs_vislevel_down( rhs );
398 	workspace_set_modified( row->ws, TRUE );
399 }
400 
401 static void
rowview_spin_down_cb(GtkWidget * widget,gpointer client)402 rowview_spin_down_cb( GtkWidget *widget, gpointer client )
403 {
404 	Rowview *rview = ROWVIEW( client );
405 	Row *row = ROW( VOBJECT( rview )->iobject );
406 	Rhs *rhs = row->child_rhs;
407 
408 	rhs_vislevel_up( rhs );
409 	workspace_set_modified( row->ws, TRUE );
410 }
411 
412 /* Scroll to make tally entry visible.
413  */
414 static void
rowview_scrollto(View * view,ModelScrollPosition position)415 rowview_scrollto( View *view, ModelScrollPosition position )
416 {
417 	Rowview *rview = ROWVIEW( view );
418 	Columnview *cview = view_get_columnview( VIEW( rview ) );
419 	Workspaceview *wview = cview->wview;
420 
421         int x, y, w, h;
422 
423         /* Extract position of tally row in RC widget.
424          */
425         rowview_get_position( rview, &x, &y, &w, &h );
426         workspaceview_scroll( wview, x, y, w, h );
427 }
428 
429 static void
rowview_drag(Rowview * rview_from,Rowview * rview_to)430 rowview_drag( Rowview *rview_from, Rowview *rview_to )
431 {
432 	Row *row_from = ROW( VOBJECT( rview_from )->iobject );
433 	Row *row_to = ROW( VOBJECT( rview_to )->iobject );
434 
435 	if( row_from->top_col != row_to->top_col ) {
436 		error_top( _( "Not implemented." ) );
437 		error_sub( _( "Drag between columns not yet implemented." ) );
438 		iwindow_alert( GTK_WIDGET( rview_from ), GTK_MESSAGE_ERROR );
439 		return;
440 	}
441 
442 	icontainer_child_move( ICONTAINER( row_from ),
443 		ICONTAINER( row_to )->pos );
444 
445 	/* Refresh all the rows, to make sure we move all rows to their new
446 	 * slots.
447 	 */
448 	icontainer_map( ICONTAINER( row_from->scol ),
449 		(icontainer_map_fn) iobject_changed, NULL, NULL );
450 
451         workspace_deselect_all( row_from->ws );
452 }
453 
454 static void
rowview_drag_data_get(GtkWidget * but,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time,Rowview * rview)455 rowview_drag_data_get( GtkWidget *but,
456 	GdkDragContext *context, GtkSelectionData *selection_data,
457 	guint info, guint time, Rowview *rview )
458 {
459 	if( info == ROWVIEW_TARGET_STRING ) {
460 		/* Send a pointer to us.
461  		 */
462 		gtk_selection_data_set( selection_data,
463 			selection_data->target,
464 			8, (const guchar *) &rview, sizeof( Rowview * ) );
465 	}
466 }
467 
468 static void
rowview_drag_data_received(GtkWidget * but,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,Rowview * rview_to)469 rowview_drag_data_received( GtkWidget *but,
470 	GdkDragContext *context, gint x, gint y,
471 	GtkSelectionData *data, guint info, guint time, Rowview *rview_to )
472 {
473 	if( data->length == sizeof( Rowview * ) && data->format == 8 &&
474 		info == ROWVIEW_TARGET_STRING ) {
475 		Rowview *rview_from = *((Rowview **) data->data);
476 
477 		if( IS_ROWVIEW( rview_from ) ) {
478 			rowview_drag( rview_from, rview_to );
479 			gtk_drag_finish( context, TRUE, FALSE, time );
480 			return;
481 		}
482 	}
483 
484 	gtk_drag_finish( context, FALSE, FALSE, time );
485 }
486 
487 /* Attach the rowview menu to a widget ... also used by iimageview
488  */
489 guint
rowview_menu_attach(Rowview * rview,GtkWidget * widget)490 rowview_menu_attach( Rowview *rview, GtkWidget *widget )
491 {
492 	return( popup_attach( widget, rowview_popup_menu, rview ) );
493 }
494 
495 static void
rowview_link(View * view,Model * model,View * parent)496 rowview_link( View *view, Model *model, View *parent )
497 {
498 	Row *row = ROW( model );
499 	Rowview *rview = ROWVIEW( view );
500 	Subcolumnview *sview = SUBCOLUMNVIEW( parent );
501 
502 	VIEW_CLASS( parent_class )->link( view, model, parent );
503 
504 	rview->sview = sview;
505 
506 	/* Only drag n drop top level rows.
507 	 */
508 	if( row->top_row == row ) {
509 		gtk_drag_source_set( rview->but, GDK_BUTTON1_MASK,
510 			rowview_target_table, IM_NUMBER( rowview_target_table ),
511 			GDK_ACTION_COPY );
512 		gtk_signal_connect( GTK_OBJECT( rview->but ), "drag_data_get",
513 			GTK_SIGNAL_FUNC( rowview_drag_data_get ), rview );
514 
515 		gtk_drag_dest_set( rview->but, GTK_DEST_DEFAULT_ALL,
516 			rowview_target_table, IM_NUMBER( rowview_target_table ),
517 			GDK_ACTION_COPY );
518 		gtk_signal_connect( GTK_OBJECT( rview->but ),
519 			"drag_data_received",
520 			GTK_SIGNAL_FUNC( rowview_drag_data_received ), rview );
521 	}
522 
523 	rowview_menu_attach( rview, rview->but );
524 }
525 
526 static void
rowview_child_add(View * parent,View * child)527 rowview_child_add( View *parent, View *child )
528 {
529 	Rowview *rowview = ROWVIEW( parent );
530 
531 	g_assert( IS_RHSVIEW( child ) );
532 	g_assert( !rowview->rhsview );
533 
534 	rowview->rhsview = RHSVIEW( child );
535 
536 	VIEW_CLASS( parent_class )->child_add( parent, child );
537 }
538 
539 static void
rowview_child_remove(View * parent,View * child)540 rowview_child_remove( View *parent, View *child )
541 {
542 	Rowview *rowview = ROWVIEW( parent );
543 
544 	g_assert( IS_RHSVIEW( child ) );
545 	g_assert( rowview->rhsview );
546 
547 	rowview->rhsview = NULL;
548 
549 	VIEW_CLASS( parent_class )->child_remove( parent, child );
550 }
551 
552 static void
rowview_class_init(RowviewClass * class)553 rowview_class_init( RowviewClass *class )
554 {
555 	GtkObjectClass *object_class = (GtkObjectClass *) class;
556 	vObjectClass *vobject_class = (vObjectClass *) class;
557 	ViewClass *view_class = (ViewClass *) class;
558 
559 	GtkWidget *pane;
560 
561 	parent_class = g_type_class_peek_parent( class );
562 
563 	/* Create signals.
564 	 */
565 
566 	/* Init methods.
567 	 */
568 	object_class->destroy = rowview_destroy;
569 
570 	vobject_class->refresh = rowview_refresh;
571 
572 	view_class->link = rowview_link;
573 	view_class->child_add = rowview_child_add;
574 	view_class->child_remove = rowview_child_remove;
575 	view_class->reset = rowview_reset;
576 	view_class->scrollto = rowview_scrollto;
577 
578         /* Other init.
579          */
580 	pane = rowview_popup_menu = popup_build( _( "Row menu" ) );
581 	popup_add_but( pane, _( "_Edit" ),
582 		POPUP_FUNC( rowview_edit_cb ) );
583 	popup_add_but( pane, _( "_Header" ),
584 		POPUP_FUNC( rowview_header_cb ) );
585 	popup_add_but( pane, STOCK_DUPLICATE,
586 		POPUP_FUNC( rowview_clone_cb ) );
587 	popup_add_but( pane, _( "U_ngroup" ),
588 		POPUP_FUNC( rowview_ungroup_cb ) );
589 	popup_add_but( pane, GTK_STOCK_SAVE_AS,
590 		POPUP_FUNC( rowview_save_cb ) );
591 	popup_add_but( pane, _( "Replace From _File" ),
592 		POPUP_FUNC( rowview_replace_cb ) );
593 	popup_add_but( pane, _( "_Recalculate" ),
594 		POPUP_FUNC( rowview_recalc_cb ) );
595 	popup_add_but( pane, _( "Re_set" ),
596 		POPUP_FUNC( rowview_clear_edited_cb ) );
597 	menu_add_sep( pane );
598 	popup_add_but( pane, GTK_STOCK_DELETE,
599 		POPUP_FUNC( rowview_remove_cb ) );
600 }
601 
602 static void
rowview_enter_cb(GtkWidget * widget,Rowview * rview)603 rowview_enter_cb( GtkWidget *widget, Rowview *rview )
604 {
605 	Row *row = ROW( VOBJECT( rview )->iobject );
606 
607 	row_set_status( row );
608 	row_show_dependents( row );
609 }
610 
611 static void
rowview_leave_cb(GtkWidget * widget,Rowview * rview)612 rowview_leave_cb( GtkWidget *widget, Rowview *rview )
613 {
614 	Row *row = ROW( VOBJECT( rview )->iobject );
615 
616 	row_hide_dependents( row );
617 }
618 
619 static gboolean
rowview_focus_cb(GtkWidget * widget,GtkDirectionType dir,Rowview * rview)620 rowview_focus_cb( GtkWidget *widget, GtkDirectionType dir, Rowview *rview )
621 {
622         view_scrollto( VIEW( rview ), MODEL_SCROLL_TOP );
623 
624         return( FALSE );
625 }
626 
627 static void
rowview_tooltip_generate(GtkWidget * widget,VipsBuf * buf,Rowview * rview)628 rowview_tooltip_generate( GtkWidget *widget, VipsBuf *buf, Rowview *rview )
629 {
630 	Row *row = ROW( VOBJECT( rview )->iobject );
631 
632 	iobject_info( IOBJECT( row ), buf );
633 	vips_buf_removec( buf, '\n' );
634 }
635 
636 static void
rowview_init(Rowview * rview)637 rowview_init( Rowview *rview )
638 {
639         rview->visible = TRUE;
640 	rview->rnum = -1;
641         rview->last_tooltip = NULL;
642 
643 	/* Make leds.
644 	 */
645 	rview->led = gtk_image_new_from_stock( STOCK_LED_OFF,
646 		GTK_ICON_SIZE_MENU );
647 	gtk_misc_set_alignment( GTK_MISC( rview->led ), 0.5, 0.0 );
648 	gtk_misc_set_padding( GTK_MISC( rview->led ), 2, 2 );
649 
650         /* Make fold/unfold button.
651          */
652 	rview->spin = spin_new();
653         gtk_signal_connect( GTK_OBJECT( rview->spin ), "up_click",
654                 GTK_SIGNAL_FUNC( rowview_spin_up_cb ), rview );
655         gtk_signal_connect( GTK_OBJECT( rview->spin ), "down_click",
656                 GTK_SIGNAL_FUNC( rowview_spin_down_cb ), rview );
657         gtk_widget_show( rview->spin );
658 	set_tooltip( rview->spin, _( "Click to open or close class" ) );
659 
660         /* Make name button.
661          */
662         rview->but = gtk_button_new();
663         gtk_widget_show( rview->but );
664         doubleclick_add( rview->but, FALSE,
665                 DOUBLECLICK_FUNC( rowview_single_cb ), rview,
666 		DOUBLECLICK_FUNC( rowview_double_cb ), rview );
667         rview->label = gtk_label_new( "" );
668         gtk_misc_set_alignment( GTK_MISC( rview->label ), 1, 0 );
669         gtk_misc_set_padding( GTK_MISC( rview->label ), 2, 0 );
670         gtk_container_add( GTK_CONTAINER( rview->but ), rview->label );
671         gtk_widget_show( rview->label );
672         gtk_signal_connect( GTK_OBJECT( rview->but ), "enter",
673                 GTK_SIGNAL_FUNC( rowview_enter_cb ), rview );
674         gtk_signal_connect( GTK_OBJECT( rview->but ), "leave",
675                 GTK_SIGNAL_FUNC( rowview_leave_cb ), rview );
676         gtk_signal_connect( GTK_OBJECT( rview->but ), "focus",
677                 GTK_SIGNAL_FUNC( rowview_focus_cb ), rview );
678 	set_tooltip_generate( rview->but,
679 		(TooltipGenerateFn) rowview_tooltip_generate, rview, NULL );
680 }
681 
682 GtkType
rowview_get_type(void)683 rowview_get_type( void )
684 {
685 	static GtkType rowview_type = 0;
686 
687 	if( !rowview_type ) {
688 		static const GtkTypeInfo rview_info = {
689 			"Rowview",
690 			sizeof( Rowview ),
691 			sizeof( RowviewClass ),
692 			(GtkClassInitFunc) rowview_class_init,
693 			(GtkObjectInitFunc) rowview_init,
694 			/* reserved_1 */ NULL,
695 			/* reserved_2 */ NULL,
696 			(GtkClassInitFunc) NULL,
697 		};
698 
699 		rowview_type = gtk_type_unique( TYPE_VIEW, &rview_info );
700 	}
701 
702 	return( rowview_type );
703 }
704 
705 View *
rowview_new(void)706 rowview_new( void )
707 {
708 	Rowview *rview = gtk_type_new( TYPE_ROWVIEW );
709 
710 	return( VIEW( rview ) );
711 }
712 
713 /* Find the position and size of a row in the enclosing GtkFixed.
714  */
715 void
rowview_get_position(Rowview * rview,int * x,int * y,int * w,int * h)716 rowview_get_position( Rowview *rview, int *x, int *y, int *w, int *h )
717 {
718         Columnview *cview = view_get_columnview( VIEW( rview ) );
719 
720         if( GTK_WIDGET_VISIBLE( rview->spin ) ) {
721                 *x = rview->spin->allocation.x;
722                 *y = rview->spin->allocation.y;
723                 *w = rview->spin->allocation.width;
724                 *h = rview->spin->allocation.height;
725         }
726         else {
727                 *x = rview->but->allocation.x;
728                 *y = rview->but->allocation.y;
729                 *w = 0;
730                 *h = 0;
731         }
732 
733         *w += rview->but->allocation.width;
734         *h = IM_MAX( rview->but->allocation.height, *h );
735 
736         if( GTK_WIDGET_VISIBLE( rview->led ) ) {
737                 *w += rview->led->allocation.width;
738                 *h = IM_MAX( rview->led->allocation.height, *h );
739         }
740 
741         *w += GTK_WIDGET( rview->rhsview )->allocation.width;
742         *h = IM_MAX( GTK_WIDGET( rview->rhsview )->allocation.height, *h );
743 
744         /* Title bar, plus separator.
745          */
746         *y += cview->title->allocation.height + 2;
747 
748         *x += cview->main->allocation.x;
749         *y += cview->main->allocation.y;
750 
751 #ifdef DEBUG
752         printf( "rowview_get_position: " );
753         row_name_print( ROW( VOBJECT( rview )->iobject ) );
754         printf( ": x = %d, y = %d, w = %d, h = %d\n", *x, *y, *w, *h );
755 #endif /*DEBUG*/
756 }
757 
758 /* Hide/show a row.
759  */
760 void
rowview_set_visible(Rowview * rview,gboolean visible)761 rowview_set_visible( Rowview *rview, gboolean visible )
762 {
763 	if( rview->visible != visible ) {
764 		rview->visible = visible;
765 		rowview_update_widgets( rview );
766 	}
767 }
768 
769 gboolean
rowview_get_visible(Rowview * rview)770 rowview_get_visible( Rowview *rview )
771 {
772 	return( rview->visible );
773 }
774