[ncview] 12/31: Imported Upstream version 2.1.2

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Apr 18 00:43:41 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository ncview.

commit 6954e14ed6ea728aead0a778cb636f6d68b12d63
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Apr 18 01:40:38 2015 +0200

    Imported Upstream version 2.1.2
---
 src/Makefile.am                       |   2 +-
 src/{Makefile.am => Makefile.am.orig} |   0
 src/Makefile.in                       |   2 +-
 src/do_buttons.c                      |  18 +-
 src/file_netcdf.c                     | 548 ++++++++++++++++++++++++++++------
 src/interface/{x_interface.c => '}    | 184 +++++++++++-
 src/interface/filesel.c               |  12 +-
 src/interface/x_interface.c           | 170 +++++++++--
 src/ncview.c                          |  50 +++-
 src/ncview.defines.h                  |  10 +-
 src/ncview.protos.h                   |   4 +-
 src/util.c                            | 158 +++++++++-
 src/view.c                            |  99 +++++-
 x_interface.c                         |   0
 14 files changed, 1091 insertions(+), 166 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index f27ba2f..de8ca20 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -33,5 +33,5 @@ sources = ncview.c file.c util.c do_buttons.c             \
 	  stringlist.c handle_rc_file.c
 
 AM_CPPFLAGS=-DNCVIEW_LIB_DIR=\"$(pkgdatadir)\" $(PNG_CPPFLAGS) $(UDUNITS2_CPPFLAGS) $(NETCDF_CPPFLAGS)
-AM_CFLAGS=-Wall $(X_CFLAGS)
+AM_CFLAGS=$(X_CFLAGS)
 AM_LDFLAGS=$(PNG_LDFLAGS) $(UDUNITS2_LDFLAGS) $(NETCDF_LDFLAGS) $(X_PRE_LIBS) $(X_LIBS) $(X11_LIBS) $(X_EXTRA_LIBS) $(RPATH_FLAGS)
diff --git a/src/Makefile.am b/src/Makefile.am.orig
similarity index 100%
copy from src/Makefile.am
copy to src/Makefile.am.orig
diff --git a/src/Makefile.in b/src/Makefile.in
index bc95987..d1efbfd 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -227,7 +227,7 @@ sources = ncview.c file.c util.c do_buttons.c             \
 	  stringlist.c handle_rc_file.c
 
 AM_CPPFLAGS = -DNCVIEW_LIB_DIR=\"$(pkgdatadir)\" $(PNG_CPPFLAGS) $(UDUNITS2_CPPFLAGS) $(NETCDF_CPPFLAGS)
-AM_CFLAGS = -Wall $(X_CFLAGS)
+AM_CFLAGS = $(X_CFLAGS)
 AM_LDFLAGS = $(PNG_LDFLAGS) $(UDUNITS2_LDFLAGS) $(NETCDF_LDFLAGS) $(X_PRE_LIBS) $(X_LIBS) $(X11_LIBS) $(X_EXTRA_LIBS) $(RPATH_FLAGS)
 all: all-am
 
diff --git a/src/do_buttons.c b/src/do_buttons.c
index ab7cdf6..219bf9d 100644
--- a/src/do_buttons.c
+++ b/src/do_buttons.c
@@ -92,7 +92,7 @@ do_rewind( int modifier )
 	in_timer_clear();
 
 	if( modifier == MOD_2 ) {
-		change_view( -10, PERCENT );
+		change_view( -10, FRAMES );
 		in_timer_set( (XtTimerCallbackProc)do_rewind, (XtPointer)(MOD_2), delay_millisec );
 		}
 	else
@@ -157,7 +157,7 @@ do_fastforward( int modifier )
 	delay_millisec = (long)(DELAY_DELTA * options.frame_delay) + DELAY_OFFSET;
 
 	if( modifier == MOD_2 ) {
-		if( change_view( 10, PERCENT ) == 0 )
+		if( change_view( 10, FRAMES ) == 0 )
 			in_timer_set( (XtTimerCallbackProc)do_fastforward, (XtPointer)(MOD_2), delay_millisec );
 		}
 	else
@@ -229,27 +229,29 @@ do_set_maximum( int modifier )
 	void
 do_blowup( int modifier )
 {
+	int view_var_is_valid = TRUE;
+
 	if( modifier == MOD_3 )
-		view_change_blowup( -1, TRUE );
+		view_change_blowup( -1, TRUE, view_var_is_valid );
 
 	else if( modifier == MOD_2 ) {
 		/* Double the current blowup -- make image BIGGER */
 		if( options.blowup > 0 )
-			view_change_blowup( options.blowup, TRUE );
+			view_change_blowup( options.blowup, TRUE, view_var_is_valid );
 		else	
-			view_change_blowup( -(options.blowup)/2, TRUE );
+			view_change_blowup( -(options.blowup)/2, TRUE, view_var_is_valid );
 		}
 
 	else if( modifier == MOD_4 ) {
 		/* Halve the current blowup -- make image SMALLER */
 		if( options.blowup > 0 ) 
-			view_change_blowup( -(options.blowup/2), TRUE );
+			view_change_blowup( -(options.blowup/2), TRUE, view_var_is_valid );
 		else
-			view_change_blowup( options.blowup, TRUE );
+			view_change_blowup( options.blowup, TRUE, view_var_is_valid );
 		}
 		
 	else
-		view_change_blowup( 1, TRUE );
+		view_change_blowup( 1, TRUE, view_var_is_valid );
 	
 	/* If we are shrinking magnification, then try re-saving
 	 * the frames because now there might be enough room.
diff --git a/src/file_netcdf.c b/src/file_netcdf.c
index 7b20ada..6c29ea4 100644
--- a/src/file_netcdf.c
+++ b/src/file_netcdf.c
@@ -40,6 +40,10 @@ void warn_about_char_dims();
 int safe_ncdimid( int fileid, char *dim_name1 );
 int netcdf_dimvar_id( int fileid, char *dim_name );
 int netcdf_get_att_util( int id, int varid, char *var_name, char *att_name, int expected_len, void *value );
+int nc_inq_varid_grp( int ncid, char *varname, int *varid, int *groupid );
+void varname_no_groups( char *varname, char *varname_sans_groups );
+char *ncview_groupname( int gid );
+char *ncview_varname( int gid, int varid );
 
 char *nc_type_to_string( nc_type type );
 
@@ -108,26 +112,62 @@ int netcdf_fi_recdim_id( int fileid )
 
 	err = nc_inq( fileid, &n_dims, &n_vars, &n_gatts, &rec_dim );
 	if( err != NC_NOERR ) {
-		fprintf( stderr, "netcdf_fi_list_vars: error on nc_inq, cdfid=%d\n", fileid );
+		fprintf( stderr, "netcdf_fi_recdim_id: error on nc_inq, cdfid=%d\n", fileid );
 		exit( -1 );
 		}
 	return( rec_dim );
 }
 
-/*******************************************************************************************/
-Stringlist *netcdf_fi_list_vars( int fileid )
+/*******************************************************************************************
+ * NOTE that netcdf returns names starting with slashes, while I do not. So, I strip 
+ * the leading slash from returned names.
+ */
+void ncdf_fi_name_of_group( int ncid, char **name, int full_path ) 
 {
-	int	n_vars, err, i, jj, n_dims, n_var_dims, eff_ndims;
-	char	*var_name;
-	Stringlist *ret_val, *dimlist;
-	int	n_gatts, rec_dim;
-	size_t	*size, total_size;
+        int     ierr;
+	size_t  nchar, dummy;
+
+	ierr = nc_inq_grpname_len( ncid, &nchar );	/* According to docs, ALWAYS returns len of full path */
+	if( ierr != NC_NOERR ) {
+		fprintf( stderr, "Error getting grpname length from file for ncid=%d: %s\n", 
+			ncid, nc_strerror(ierr) );
+		exit(-1);
+		}
 
-	ret_val = NULL;
+	*name = (char *)malloc( sizeof(char) * (nchar+2) );     /* add space for trailing NULL */
 
-	err = nc_inq( fileid, &n_dims, &n_vars, &n_gatts, &rec_dim );
+	if( full_path == 0 ) 
+		ierr = nc_inq_grpname( ncid, *name );
+	else
+		ierr = nc_inq_grpname_full( ncid, &dummy, *name );
+
+	/* Get rid of leading slash */
+	if( (*name)[0] == '/' ) 
+		(*name)++;
+
+	if( ierr != NC_NOERR ) {
+		fprintf( stderr, "Error getting grpname from file for ncid=%d: %s\n", 
+			ncid, nc_strerror(ierr) );
+		exit(-1);
+		}
+}
+
+/*******************************************************************************************
+ * Returns list of ONLY displayable vars in the passed group. The list of displayable vars
+ * is appended to ret_val, which might already have some displayable vars from different
+ * groups in it.
+ */
+void netcdf_fi_list_vars_inner( Stringlist **ret_val, int gid, char *groupname )
+{
+	int	n_vars, err, i, jj, kk, n_dims, n_var_dims, eff_ndims;
+	char	*var_name, *grp_var_name;
+	Stringlist *dimlist;
+	int	n_gatts, rec_dim, n_groups;
+	size_t	*size, total_size;
+
+	err = nc_inq( gid, &n_dims, &n_vars, &n_gatts, &rec_dim );
 	if( err != NC_NOERR ) {
-		fprintf( stderr, "netcdf_fi_list_vars: error on ncinqire, cdfid=%d\n", fileid );
+		fprintf( stderr, "netcdf_fi_list_vars: error on ncinqire, cdfid=%d\n", gid );
 		exit( -1 );
 		}
 
@@ -135,15 +175,31 @@ Stringlist *netcdf_fi_list_vars( int fileid )
 	 * as a displayable variable.  At present, we require: 1) that the
 	 * variable have at least 1 scannable dimensions. 2) It shouldn't
 	 * be a "dimension variable"; i.e., there should be no dimension
-	 * with the same name as this variable. 3) It's total size should
+	 * with the same name as this variable. 3) Its total size should
 	 * be > 1.
 	 */
 	for( i=0; i<n_vars; i++ ) {
-		var_name = netcdf_varindex_to_name( fileid, i );
-		if( netcdf_dim_name_to_id( fileid, var_name, var_name ) == -1 ){
+		var_name = netcdf_varindex_to_name( gid, i );
+
+		/* Prepend group name */
+		grp_var_name = (char *)malloc( sizeof(char) * (strlen(var_name) + strlen(groupname) + 10) );
+		grp_var_name[0] = '\0';
+		if( (strlen(groupname) == 0) || ((strlen(groupname) == 1) && (groupname[0] == '/' )))
+			strcpy( grp_var_name, var_name );
+		else
+			{
+			strcat( grp_var_name, groupname );
+			strcat( grp_var_name, "/" );
+			strcat( grp_var_name, var_name );
+			}
+
+		if( options.debug ) printf( "netcdf_fi_list_vars_inner: checking to see if a displayable var: >%s<\n", 
+			grp_var_name );
+
+		if( netcdf_dim_name_to_id( gid, var_name, var_name ) == -1 ){
 			/* then it's NOT a dimension variable */
-			size = netcdf_fi_var_size( fileid, var_name );
-			n_var_dims = netcdf_fi_n_dims( fileid, var_name );
+			size = netcdf_fi_var_size( gid, var_name );
+			n_var_dims = netcdf_fi_n_dims( gid, var_name );
 			total_size = 1L;
 			eff_ndims  = 0;
 			for(jj=0; jj<n_var_dims; jj++ ) {
@@ -151,45 +207,118 @@ Stringlist *netcdf_fi_list_vars( int fileid )
 				if( *(size+jj) > 1 ) 
 					eff_ndims++;
 				}
-			dimlist  = fi_scannable_dims( fileid, var_name );
-			if( (total_size > 1L) && (stringlist_len( dimlist ) >= 1))
+			dimlist  = fi_scannable_dims( gid, var_name );
+			if( (total_size > 1L) && (stringlist_len( dimlist ) >= 1)) {
 				/* Hack to make version 1.70+ emulate older versions
 				 * that did not display 1-d vars.
 				 */
-				if( ! (options.no_1d_vars && (eff_ndims == 1) ))
-					stringlist_add_string( &ret_val, var_name, NULL, SLTYPE_NULL );
+				if( ! (options.no_1d_vars && (eff_ndims == 1) )) {
+					if( options.debug ) {
+						printf( "netcdf_fi_list_vars_inner: YES, is a displayable var: >%s< ndims=%d sizes=", 
+							grp_var_name, n_var_dims );
+						for( kk=0; kk<n_var_dims; kk++ ) 
+							printf( "%ld ", size[kk] );
+						printf( "\n" );
+						}
+					stringlist_add_string( ret_val, grp_var_name, NULL, SLTYPE_NULL );
+					}
+				}
+			else
+				if( options.debug ) printf( "netcdf_fi_list_vars_inner: NO, is size 1 so not displayable: >%s<\n", 
+					grp_var_name );
+				
 			}
+		else
+			if( options.debug ) printf( "netcdf_fi_list_vars_inner: NO, is a dim so not displayable: >%s<\n", 
+				grp_var_name );
 		}
-	
-	return( ret_val );
+}
+
+/*******************************************************************************************/
+void netcdf_fi_list_vars_v4( Stringlist **retval, int fileid )
+{
+	char 	*groupname;
+	int	i, *grp_id, err, n_groups, full_path;
+
+
+	/* Get name of this group
+	 */
+	full_path = 1;
+	ncdf_fi_name_of_group( fileid, &groupname, full_path );
+
+	netcdf_fi_list_vars_inner( retval, fileid, groupname );
+
+	/* Get number of groups in this group
+	 */
+	err = nc_inq_grps( fileid, &n_groups, NULL);
+	if( err != NC_NOERR ) {
+		fprintf( stderr, "netcdf_fi_list_vars: error on nc_inq_grps, cdfid=%d: %s\n", 
+			fileid, nc_strerror(err) );
+		exit( -1 );
+		}
+
+	/* Get group IDs
+	 */
+	grp_id = (int *)malloc( sizeof(int) * n_groups );
+	err = nc_inq_grps( fileid, &n_groups, grp_id );
+	for( i=0; i<n_groups; i++ ) {
+		netcdf_fi_list_vars_v4( retval, grp_id[i] );
+		}
+
+	free( grp_id );
+
+}
+
+/*******************************************************************************************
+ * This provides the same interface for the requirements of groups introduced
+ * in netcdf library version 4
+ */
+Stringlist *netcdf_fi_list_vars( int fileid )
+{
+	Stringlist	*retval = NULL;
+
+	if( options.debug ) printf( "netcdf_fi_list_vars: entering for file %d\n", fileid );
+
+	netcdf_fi_list_vars_v4( &retval, fileid );
+
+	if( options.debug ) {
+		printf( "netcdf_fi_list_vars: exiting with list of DISPLAYABLE vars in this file:\n" );
+		stringlist_dump( retval );
+		}
+
+	return( retval );
 }
 
 /*******************************************************************************************/
 Stringlist *netcdf_scannable_dims( int fileid, char *var_name )
 {
-	int	var_id, n_dims, i, err;
+	int	var_id, n_dims, i, err, gid;
 	char	*dim_name;
 	size_t	dim_size;
 	Stringlist *dimlist = NULL;
 	int	n_atts, dim[MAX_VAR_DIMS];
 	nc_type	var_type;
+	char	var_name_ng[MAX_NC_NAME];
 
 	dim_name = (char *)malloc( MAX_NC_NAME ); /* defined in netcdf.h */
 
-	err = nc_inq_varid( fileid, var_name, &var_id );
+	err = nc_inq_varid_grp( fileid, var_name, &var_id, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_scannable_dims: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
-	err = nc_inq_var( fileid, var_id, var_name, &var_type, &n_dims, dim, &n_atts );
+
+	varname_no_groups( var_name, var_name_ng );
+
+	err = nc_inq_var( gid, var_id, var_name_ng, &var_type, &n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "netcdf_scannable_dims: Error on nc_inq_var call for var %s\n", var_name );
 		exit(-1);
 		}
 
 	for( i=0; i<n_dims; i++ ) {
-		err = nc_inq_dim( fileid, *(dim+i), dim_name, &dim_size );
+		err = nc_inq_dim( gid, *(dim+i), dim_name, &dim_size );
 		if( err < 0 ) {
 			fprintf( stderr, "ncview: netcdf_scannable_dims: ");
 			fprintf( stderr, "error on nc_inq_dim call\n" );
@@ -212,20 +341,27 @@ Stringlist *netcdf_scannable_dims( int fileid, char *var_name )
 	return( dimlist );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On input, var_name might have prepended group names of the form "group0/group1/varname"
+ */
 int netcdf_fi_n_dims( int fileid, char *var_name )
 {
-	int	n_dims, err, varid;
+	int	n_dims, err, varid, groupid;
 	int	n_atts, dim[MAX_VAR_DIMS];
+	char	var_name_nogroups[MAX_NC_NAME];
 	nc_type	var_type;
 
-	err = nc_inq_varid( fileid, var_name, &varid );
+	err = nc_inq_varid_grp( fileid, var_name, &varid, &groupid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_fi_n_dims: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
-	err = nc_inq_var( fileid, varid, var_name, &var_type, &n_dims, dim, &n_atts );
+
+	/* Strip off leading group names */
+	varname_no_groups( var_name, var_name_nogroups );
+
+	err = nc_inq_var( groupid, varid, var_name_nogroups, &var_type, &n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "netcdf_fi_n_dims: error on nc_inq_var\n" );
 		fprintf( stderr, "netcdfid=%d, var_name=%s\n",
@@ -249,25 +385,35 @@ size_t netcdf_dim_size( fileid, dimid )
 	return( ret_val );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On input, var_name might have prepended group names of the form "group0/group1/varname"
+ */
 size_t * netcdf_fi_var_size( int fileid, char *var_name )
 {
-	int	n_dims, varid, err, i;
+	int	n_dims, varid, err, i, groupid;
 	size_t	*ret_val, dim_size;
-	int	n_atts, dim[MAX_VAR_DIMS];
+	int	n_atts, dim[MAX_VAR_DIMS], debug;
+	char	var_name_nogroups[MAX_NC_NAME];
 	nc_type var_type;
 
+	debug = 0;
+
+	if( debug==1 ) printf( "netcdf_fi_var_size: entering for fileid=%d varname=>%s<\n", fileid, var_name );
+
 	n_dims  = netcdf_fi_n_dims( fileid, var_name );
 	ret_val = (size_t *)malloc( n_dims * sizeof(size_t) );
 
-	err = nc_inq_varid( fileid, var_name, &varid );
+	err = nc_inq_varid_grp( fileid, var_name, &varid, &groupid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_fi_var_size: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
 
-	err = nc_inq_var( fileid, varid, var_name, &var_type, &n_dims, dim, &n_atts );
+	/* Strip off leading group names */
+	varname_no_groups( var_name, var_name_nogroups );
+
+	err = nc_inq_var( groupid, varid, var_name_nogroups, &var_type, &n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "netcdf_fi_var_size: error on nc_inq_var\n" );
 		fprintf( stderr, "netcdfid=%d, var_name=%s\n",
@@ -275,9 +421,11 @@ size_t * netcdf_fi_var_size( int fileid, char *var_name )
 		exit( -1 );
 		}
 
+	if( debug==1 ) printf( "netcdf_fi_var_size: here are dim sizes:\n" );
 	for( i=0; i<n_dims; i++ ) {
-		err = nc_inq_dimlen( fileid, *(dim+i), &dim_size );
+		err = nc_inq_dimlen( groupid, *(dim+i), &dim_size );
 		*(ret_val+i) = dim_size;
+		if( debug==1 ) printf( "dim=%d size=%ld\n", i, dim_size );
 		}
 
 	return( ret_val );
@@ -286,24 +434,27 @@ size_t * netcdf_fi_var_size( int fileid, char *var_name )
 /*******************************************************************************************/
 char *netcdf_dim_id_to_name( int fileid, char *var_name, int dim_id )
 {
-	int	netcdf_dim_id, netcdf_var_id;
+	int	netcdf_dim_id, netcdf_var_id, gid;
 	int	n_dims, *dim, err, n_atts;
-	char	*dim_name;
+	char	*dim_name, var_name_ng[MAX_NC_NAME];
 	nc_type	var_type;
 
 	/* see notes under "netcdf_dim_name_to_id".  "dim_id" is NOT
 	 * the netCDF dimension ID, it is the entry into the size array
 	 * for the passed variable.
 	 */
-	err = nc_inq_varid( fileid, var_name, &netcdf_var_id );
+	err = nc_inq_varid_grp( fileid, var_name, &netcdf_var_id, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_dim_id_to_name: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
-	n_dims        = fi_n_dims( fileid, var_name );
-	dim           = (int *)malloc( n_dims * sizeof( int ));
-	err           = nc_inq_var( fileid, netcdf_var_id, var_name, &var_type,
+
+	varname_no_groups( var_name, var_name_ng );
+
+	n_dims = fi_n_dims( gid, var_name_ng );
+	dim    = (int *)malloc( n_dims * sizeof( int ));
+	err    = nc_inq_var( gid, netcdf_var_id, var_name_ng, &var_type,
 				&n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "ncview: netcdf_dim_id_to_name: error on ");
@@ -313,7 +464,7 @@ char *netcdf_dim_id_to_name( int fileid, char *var_name, int dim_id )
 
 	netcdf_dim_id = *(dim+dim_id);
 	dim_name = (char *)malloc( MAX_NC_NAME ); /* defined in netcdf.h */
-	err      = nc_inq_dimname( fileid, netcdf_dim_id, dim_name );
+	err      = nc_inq_dimname( gid, netcdf_dim_id, dim_name );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "ncview: netcdf_dim_id_to_name: error on ");
 		fprintf( stderr, "nc_inq_dimname call.  Variable=%s\n", var_name );
@@ -322,11 +473,19 @@ char *netcdf_dim_id_to_name( int fileid, char *var_name, int dim_id )
 	return( dim_name );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On entry var_name could be something like "group0/group1/varname"
+ */
 int netcdf_dim_name_to_id( int fileid, char *var_name, char *dim_name )
 {
-	int	netcdf_dim_id, netcdf_var_id, n_dims, *dim, err, i, n_atts;
+	int	netcdf_dim_id, netcdf_var_id, n_dims, *dim, err, i, n_atts, gid, debug;
 	nc_type	var_type;
+	char	var_name_ng[MAX_NC_NAME];
+
+	debug = 0;
+
+	if( debug == 1 ) printf( "netcdf_dim_name_to_id: entering with fileid=%d var_name=%s dim_name=%s\n",
+		fileid, var_name, dim_name );
 
 	/* It is important to note that this routine does NOT return
 	 * the dimension ID of the passed dimension.  That concept is
@@ -338,19 +497,37 @@ int netcdf_dim_name_to_id( int fileid, char *var_name, char *dim_name )
 	 * in the requested variable.
 	 */
 
-	netcdf_dim_id = safe_ncdimid( fileid, dim_name );
-	if( netcdf_dim_id == -1 )
-		return( -1 );
-	err = nc_inq_varid( fileid, var_name, &netcdf_var_id );
+	err = nc_inq_varid_grp( fileid, var_name, &netcdf_var_id, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_dim_name_to_id: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
+	if( debug == 1 ) {
+		printf( "netcdf_dim_name_to_id: nc_inq_varid_grp reported that var >%s< of gid=%d (%s)",
+			var_name, 
+			fileid,
+			ncview_groupname(fileid) ); 
+		printf( " is varid %d of gid=%d (%s), which ACTUALLY has name >%s<\n", 
+			netcdf_var_id, 
+			gid,
+			ncview_groupname(gid), 
+			ncview_varname(gid, netcdf_var_id) );
+		}
+
+	varname_no_groups( var_name, var_name_ng );
+
+	if( debug == 1 ) printf( "netcdf_dim_name_to_id: group_id=%d var_name_no_groups=%s\n", 
+		gid, var_name_ng );
+
+	netcdf_dim_id = safe_ncdimid( gid, dim_name );
+	if( debug == 1 ) printf( "netcdf_dim_name_to_id: netcdf_dim_id=%d\n", netcdf_dim_id );
+	if( netcdf_dim_id == -1 )
+		return( -1 );
 
-	n_dims = fi_n_dims( fileid, var_name );
+	n_dims = fi_n_dims( gid, var_name_ng );
 	dim    = (int *)malloc( n_dims * sizeof( int ));
-	err    = nc_inq_var( fileid, netcdf_var_id, var_name, &var_type,
+	err    = nc_inq_var( gid, netcdf_var_id, var_name_ng, &var_type,
 				&n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "ncview: netcdf_dim_name_to_id: error on ");
@@ -366,25 +543,39 @@ int netcdf_dim_name_to_id( int fileid, char *var_name, char *dim_name )
 	return( -1 );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On entry var_name could be something like "group0/group1/varname"
+ */
 void netcdf_fi_get_data( int fileid, char *var_name, size_t *start_pos, 
 		size_t *count, float *data, NetCDFOptions *aux_data )
 {
-	int	i, err, varid;
+	int	i, err, varid, gid, debug;
+	char	var_name_ng[MAX_NC_NAME];
 	size_t	tot_size, n_dims;
 
-	tot_size = 1L;
-	n_dims = netcdf_fi_n_dims( fileid, var_name );
-	for( i=0; i<n_dims; i++ )
-		tot_size *= *(count+i);
+	debug = 0;
 
-	err = nc_inq_varid( fileid, var_name, &varid );
+	if( debug==1 ) printf( "netcdf_fi_get_data: entering for fileid=%d var_name=%s\n",
+		fileid, var_name );
+
+	err = nc_inq_varid_grp( fileid, var_name, &varid, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_fi_get_data: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
 
+	varname_no_groups( var_name, var_name_ng );
+
+	tot_size = 1L;
+	n_dims = netcdf_fi_n_dims( gid, var_name_ng );
+	if( debug==1 ) printf( "netcdf_fi_get_data: ndims=%ld\n", n_dims );
+	for( i=0; i<n_dims; i++ ) {
+		tot_size *= *(count+i);
+		if( debug==1 ) printf( "start[%d]=%ld count[%d]=%ld\n", i, start_pos[i], i, count[i] );
+		}
+
+
 	if( options.debug ) {
 		fprintf( stderr, "About to call nc_get_vara_float on variable %s\n",
 				var_name );
@@ -393,7 +584,7 @@ void netcdf_fi_get_data( int fileid, char *var_name, size_t *start_pos,
 			fprintf( stderr, "[%d]: %ld %ld\n", i, *(start_pos+i), *(count+i) );
 		}
 
-	err = nc_get_vara_float( fileid, varid, start_pos, count, data );
+	err = nc_get_vara_float( gid, varid, start_pos, count, data );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "netcdf_fi_get_data: error on nc_get_vara_float call\n" );
 		fprintf( stderr, "cdfid=%d   variable=%s\n", fileid, var_name );
@@ -478,6 +669,107 @@ void netcdf_fi_close( int fileid )
 /* netCDF utility routines.  Analogs are not required for each data file format.	*/
 /****************************************************************************************/
 
+/*******************************************************************************************
+ * Given a varname string of format: groupname0/groupname1/groupnameN/varname
+ * this returns ONLY the trailing groupname
+ */
+void varname_no_groups( char *varname, char *varname_sans_groups )
+{
+	int	i, i0, i1, idx_slash[MAX_NC_NAME], nslash;
+	char	ts[MAX_NC_NAME];
+
+	/* Get indices of the slashes */
+	nslash = 0;
+	for( i=0; i<strlen(varname); i++ ) {
+		if( varname[i] == '/' ) {
+			idx_slash[nslash] = i;
+			nslash++;
+			}
+		}
+
+	if( nslash == 0 ) {
+		strcpy( varname_sans_groups, varname );
+		return;
+		}
+
+	strcpy( varname_sans_groups, varname+idx_slash[nslash-1]+1 );
+}
+
+/*******************************************************************************************
+ * A version of 'nc_inq_varid' that has been enhanced to return a groupid/varid pair
+ * given a var name of form "groupname/varname" (NOTE: *NO* leading slash!!)
+ */
+int nc_inq_varid_grp( int ncid, char *varname, int *varid, int *groupid )
+{
+	int	ns, ig, gid, ierr, group_depth, cur_gid, debug, retval;
+	char	groupname[MAX_NC_NAME], varname_sans_groups[MAX_NC_NAME], cur_gid_groupname[MAX_NC_NAME];
+
+	debug = 0;
+
+	if( debug ) printf( "nc_inq_varid_grp: entering with ncid=%d (%s) varname=>%s<\n", 
+		ncid, ncview_groupname(ncid), varname );
+
+	if( varname[0] == '/' ) {
+		fprintf( stderr, "Internal error, called nc_inq_varid_grp with a varname that starts with a slash: >%s<\n",
+			varname );
+		exit(-1);
+		}
+
+	ns = count_nslashes( varname );
+	if( debug ) printf( "nc_inq_varid_grp: number of slashes in varname: %d\n", ns );
+
+	if( ns > 0 ) {
+		cur_gid = ncid;
+		group_depth = ns;
+
+		/* Traverse to the LAST group in the chain of groups, that's where
+		 * we should find this var.
+		 */
+		if( debug ) printf( "nc_inq_varid_grp: traversing to the LAST group of var >%s<\n", varname );
+		for( ig=0; ig<group_depth; ig++ ) {
+
+			ierr = nc_inq_grpname( cur_gid, cur_gid_groupname );
+			if( debug ) printf( "nc_inq_varid_grp: traversing group, cur depth=%d cur root name=>%s<\n", 
+				ig, cur_gid_groupname );
+
+			ierr = unpack_groupname( varname, ig, groupname );	/* if ig==0, returns first groupname, etc */
+
+			if( debug ) printf( "nc_inq_varid_grp: looking for subgroup >%s< in root group >%s<\n",
+				groupname, cur_gid_groupname );
+
+			ierr = nc_inq_ncid( cur_gid, groupname, &gid );
+			if( ierr != NC_NOERR ) {
+				fprintf( stderr, "nc_inq_varid_grp: Error, did not find group named >%s< in base group >%s<\n",
+					groupname, cur_gid_groupname );
+				fprintf( stderr, "nc_inq_varid_grp was called with id=%d (%s) varname=>%s<\n", ncid, cur_gid_groupname, varname );
+				exit(-1);
+				return(-1);
+				}
+
+			if( debug ) printf( "nc_inq_varid_grp: group >%s< has groupid %d\n", groupname, gid );
+
+			cur_gid = gid;
+			}
+
+		*groupid = cur_gid;
+		if( debug ) printf( "nc_inq_varid_grp: should now be on the LAST group, here is groupname: >%s<\n", ncview_groupname( cur_gid ));
+
+		varname_no_groups( varname, varname_sans_groups );
+		if( debug ) printf( "nc_inq_varid_grp: calling regular nc_inq_varid with group %d (%s) and varname_sans_gruops >%s<\n",
+			cur_gid, ncview_groupname(cur_gid), varname_sans_groups );
+		retval = nc_inq_varid( cur_gid, varname_sans_groups, varid );
+
+		if( debug ) printf( "nc_inq_varid_grp: final returned gid=%d (%s) varid=%d (which is a var named >%s<)\n",
+			*groupid, ncview_groupname(*groupid), *varid, ncview_varname( *groupid, *varid ) );
+		return( retval );
+		}
+	else
+		{
+		*groupid = ncid;
+		return( nc_inq_varid( ncid, varname, varid ));
+		}
+}
+
 /*******************************************************************************************/
 /* How many dimensions does this variable have? 
 */
@@ -642,25 +934,30 @@ char * netcdf_title( int fileid )
  * As a special case, if the attribute exited, but the length of the
  * attribute was zero, then it returns a pointer to a char string 
  * that is a single NULL.
+ *
+ * On input, var_name might be of the form "group0/group1/varname"
+ *
  */
 char *netcdf_get_char_att( int fileid, char *var_name, char *att_name )
 {
-	int	varid, err;
+	int	varid, err, gid;
 	size_t	name_length;
 	nc_type	type;
-	char	*ret_val;
+	char	*ret_val, var_name_ng[MAX_NC_NAME];
 
-	err = nc_inq_varid( fileid, var_name, &varid );
+	err = nc_inq_varid_grp( fileid, var_name, &varid, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_get_char_att: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
 
-	if( netcdf_att_id( fileid, varid, att_name ) < 0 )
+	varname_no_groups( var_name, var_name_ng );
+
+	if( netcdf_att_id( gid, varid, att_name ) < 0 )
 		return( NULL );
 
-	err = nc_inq_att( fileid, varid, att_name, &type, &name_length );
+	err = nc_inq_att( gid, varid, att_name, &type, &name_length );
 	if( (err != NC_NOERR) || (type != NC_CHAR))
 		return( NULL );
 
@@ -671,7 +968,7 @@ char *netcdf_get_char_att( int fileid, char *var_name, char *att_name )
 		return( ret_val );
 		}
 
-	err = nc_get_att_text( fileid, varid, att_name, ret_val );
+	err = nc_get_att_text( gid, varid, att_name, ret_val );
 	if( err != NC_NOERR )
 		return( NULL );
 
@@ -946,50 +1243,54 @@ nc_type netcdf_dim_value( int fileid, char *dim_name, size_t place,
 	return( ret_type );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On entry, var_name can be something like "group0/group1/varname"
+ */
 void netcdf_fill_aux_data( int id, char *var_name, FDBlist *fdb )
 {
-	int	err, varid, n_dims, dim[MAX_NC_DIMS], n_atts, unlimdimvar_id, recdim_id;
-	char	dummy_var_name[ MAX_NC_NAME ], unlimdim_name[MAX_NC_NAME];
+	int	err, varid, n_dims, dim[MAX_NC_DIMS], n_atts, unlimdimvar_id, recdim_id, gid;
+	char	dummy_var_name[ MAX_NC_NAME ], var_name_ng[MAX_NC_NAME], unlimdim_name[MAX_NC_NAME];
 	nc_type	type;
 	NetCDFOptions *netcdf;
 
 	netcdf = (NetCDFOptions *)(fdb->aux_data);
 
-	err = nc_inq_varid( id, var_name, &varid );
+	err = nc_inq_varid_grp( id, var_name, &varid, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_fill_aux_data: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
 
+	varname_no_groups( var_name, var_name_ng );
+
 	/* Record the recdim units in this file
 	 */
-	recdim_id = netcdf_fi_recdim_id( id );
+	recdim_id = netcdf_fi_recdim_id( gid );
 	if( recdim_id == -1 ) {
 		fdb->recdim_units = NULL;
 		}
 	else
 		{
 		/* Get NAME of the record dimension */
-		err = nc_inq_dimname( id, recdim_id, unlimdim_name );
+		err = nc_inq_dimname( gid, recdim_id, unlimdim_name );
 		if( err != 0 ) {
 			fprintf( stderr, "Error in netcdf_fill_aux_data: could not get recdim name\n%s\n",
 				nc_strerror( err ));
 			exit(-1);
 			}
 		/* See if there is a variable with the same name */
-		err = nc_inq_varid( id, unlimdim_name, &unlimdimvar_id );
+		err = nc_inq_varid( gid, unlimdim_name, &unlimdimvar_id );
 		if( err != 0 ) 
 			fdb->recdim_units = NULL;
 		else
 			{
 			/* Get the units for the dimvar. Note: can be NULL */
-			fdb->recdim_units = netcdf_var_units( id, unlimdim_name );
+			fdb->recdim_units = netcdf_var_units( gid, unlimdim_name );
 			}
 		}
 
-	err = nc_inq_var( id, varid, dummy_var_name, &type, &n_dims, dim, &n_atts );
+	err = nc_inq_var( gid, varid, dummy_var_name, &type, &n_dims, dim, &n_atts );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "netcdf_fill_aux_data: failed on nc_inq_var call!\n" );
 		exit(-1);
@@ -999,15 +1300,15 @@ void netcdf_fill_aux_data( int id, char *var_name, FDBlist *fdb )
 		return;
 
 	netcdf->valid_range_set = 
-	    netcdf_get_att_util( id, varid, var_name, "valid_range",  2, netcdf->valid_range );
+	    netcdf_get_att_util( gid, varid, var_name_ng, "valid_range",  2, netcdf->valid_range );
 	netcdf->valid_min_set = 
-	    netcdf_get_att_util( id, varid, var_name, "valid_min",    1, &(netcdf->valid_min) );
+	    netcdf_get_att_util( gid, varid, var_name_ng, "valid_min",    1, &(netcdf->valid_min) );
 	netcdf->valid_max_set = 
-	    netcdf_get_att_util( id, varid, var_name, "valid_max",    1, &(netcdf->valid_max) );
+	    netcdf_get_att_util( gid, varid, var_name_ng, "valid_max",    1, &(netcdf->valid_max) );
 	netcdf->add_offset_set = 
-	    netcdf_get_att_util( id, varid, var_name, "add_offset",   1, &(netcdf->add_offset) );
+	    netcdf_get_att_util( gid, varid, var_name_ng, "add_offset",   1, &(netcdf->add_offset) );
 	netcdf->scale_factor_set = 
-	    netcdf_get_att_util( id, varid, var_name, "scale_factor", 1, &(netcdf->scale_factor) );
+	    netcdf_get_att_util( gid, varid, var_name_ng, "scale_factor", 1, &(netcdf->scale_factor) );
 
 	/* Special case: if we have add_offset and scale_factor attributes,
 	 * then assume they apply to the valid range also.  Q: is this
@@ -1246,31 +1547,36 @@ int netcdf_max_option_set( NCVar *var, float *ret_max )
 	return( max_set );
 }
 
-/*******************************************************************************************/
+/*******************************************************************************************
+ * On entry var_name might be something like "group0/group1/varname"
+ */
 void netcdf_fill_value( int file_id, char *var_name, float *v, NetCDFOptions *aux_data )
 {
-	int	err, varid, foundit;
+	int	err, varid, foundit, gid;
+	char	var_name_ng[MAX_NC_NAME];
 
 	if( options.debug ) 
 		fprintf( stderr, "Checking %s for a missing value...\n",
 				var_name );
 
 	foundit = FALSE;
-	err = nc_inq_varid( file_id, var_name, &varid );
+	err = nc_inq_varid_grp( file_id, var_name, &varid, &gid );
 	if( err != NC_NOERR ) {
 		fprintf( stderr, "Error in netcdf_fill_value: could not find var named \"%s\" in file!\n",
 			var_name );
 		exit(-1);
 		}
 
-	if( netcdf_get_att_util( file_id, varid, var_name, "missing_value", 1, v ) ) {
+	varname_no_groups( var_name, var_name_ng );
+
+	if( netcdf_get_att_util( gid, varid, var_name_ng, "missing_value", 1, v ) ) {
 		if( options.debug )
 			fprintf( stderr, "found a \"missing_value\" attribute=%g\n",
 				*v );
 		foundit = TRUE;
 		}
 
-	if( netcdf_get_att_util( file_id, varid, var_name, "_FillValue", 1, v ) ) {
+	if( netcdf_get_att_util( gid, varid, var_name_ng, "_FillValue", 1, v ) ) {
 		if( options.debug )
 			fprintf( stderr, "found a \"_FillValue\" attribute=%g\n",
 				*v );
@@ -1278,7 +1584,7 @@ void netcdf_fill_value( int file_id, char *var_name, float *v, NetCDFOptions *au
 		}
 
 	/* Is there a global missing value? */
-	if( netcdf_get_att_util( file_id, NC_GLOBAL, var_name, "missing_value", 1, v ) ) {
+	if( netcdf_get_att_util( gid, NC_GLOBAL, var_name_ng, "missing_value", 1, v ) ) {
 		if( options.debug )
 			fprintf( stderr, "found a \"missing_value\" attribute=%g\n",
 				*v );
@@ -1345,23 +1651,53 @@ int safe_ncvarid( int fileid, char *varname )
  */
 int safe_ncdimid( int fileid, char *dim_name1 )
 {
-	int	n_vars, err, i, n_dims;
+	int	n_vars, err, i, n_dims, *dimids, include_parents;
 	char	dim_name2[MAX_NC_NAME];
-	int	n_gatts, rec_dim;
+	int	n_gatts, rec_dim, debug;
 	size_t	dim_size;
 
-	err = nc_inq( fileid, &n_dims, &n_vars, &n_gatts, &rec_dim );
+	debug = 0;
+
+	if( debug == 1 ) printf( "safe_ncdimid: entering with fileid=%d (group %s) dim_name=%s\n", 
+		fileid, ncview_groupname(fileid), dim_name1 );
+
+	/* Find how many dims there are available, make space
+	 * for their dimids, read them in 
+	 */
+	include_parents = 1;
+	err = nc_inq_dimids( fileid, &n_dims, NULL, include_parents );
 	if( err != NC_NOERR ) {
-		fprintf( stderr, "ncview: safe_ncdimid: error in nc_inq call\n" );
-		exit( -1 );
+		fprintf( stderr, "safe_ncdimid: error on call to nc_inq_dimids (1): %s\n", 
+			nc_strerror(err) );
+		exit(-1);
+		}
+	if( debug == 1 ) printf( "safe_ncdimid: n_dims=%d\n", n_dims );
+
+	dimids = (int *)malloc( sizeof(int) * n_dims );
+	err = nc_inq_dimids( fileid, &n_dims, dimids, include_parents );
+	if( err != NC_NOERR ) {
+		fprintf( stderr, "safe_ncdimid: error on call to nc_inq_dimids (2): %s\n", 
+			nc_strerror(err) );
+		exit(-1);
 		}
 
 	for( i=0; i<n_dims; i++ ) {
-		err = nc_inq_dim( fileid, i, dim_name2, &dim_size );
-		if( strcmp( dim_name1, dim_name2 ) == 0 )
-			return( i );
+		err = nc_inq_dim( fileid, dimids[i], dim_name2, &dim_size );
+		if( err != 0 ) {
+			fprintf( stderr, "safe_ncdimid: Error, call to nc_inq_dim returned: %s\n", 
+				nc_strerror( err ));
+			fprintf( stderr, "Called with ncid=%d dimid=%d\n", fileid, i );
+			exit(-1);
+			}
+		if( debug == 1 ) printf( "safe_ncdimid: dim #%d (dimid %d) is named >%s<\n", 
+			i, dimids[i], dim_name2 );
+		if( strcmp( dim_name1, dim_name2 ) == 0 ) {
+			if( debug==1 ) printf( "safe_ncdimid found match in dim %d: returning that value\n", dimids[i] );
+			return( dimids[i] );
+			}
 		}
 
+	if( debug==1 ) printf( "safe_ncdimid: no matches, returning -1\n" ); 
 	return( -1 );
 }	
 
@@ -1631,3 +1967,29 @@ int netcdf_dimvar_bounds_id( int fileid, char *dim_name, int *nvertices )
 	return( bounds_dimvar_id );
 }
 
+/*****************************************************************************************************
+ * Returns a pointer to a static buffer with the group name; useful for debugging & info printouts
+ */
+char *ncview_groupname( int gid ) 
+{
+	static char 	buffer[MAX_NC_NAME];
+	int	ierr;
+	size_t	tlen;
+
+	ierr = nc_inq_grpname_full( gid, &tlen, buffer );
+	return( &(buffer[0]) );
+}
+
+/*****************************************************************************************************
+ * Returns a pointer to a static buffer with the var name; useful for debugging & info printouts
+ */
+char *ncview_varname( int gid, int varid ) 
+{
+	static char 	buffer[MAX_NC_NAME];
+	int	ierr;
+	size_t	tlen;
+
+	ierr = nc_inq_varname( gid, varid, buffer );
+	return( &(buffer[0]) );
+}
+
diff --git a/src/interface/x_interface.c b/src/interface/'
similarity index 95%
copy from src/interface/x_interface.c
copy to src/interface/'
index 5dcb0b2..7eeb35a 100644
--- a/src/interface/x_interface.c
+++ b/src/interface/'
@@ -192,6 +192,9 @@ static Widget
 			print_button_widget,
 		colorbar_form_widget,
 			colorbar_widget,
+		groupsel_form_widget,
+			grouplist_label_widget,
+			*grouplist_widget,
 		varsel_form_widget,
 			*var_selection_widget,	/* the boxes with N vars per box */
 			varlist_label_widget,
@@ -440,6 +443,7 @@ void 	do_set_min_from_curdata(Widget w, XButtonEvent *e, String *p, Cardinal *n
 void 	do_set_max_from_curdata(Widget w, XButtonEvent *e, String *p, Cardinal *n );
 void 	expose_ccontour();
 void 	expose_colorbar();
+void 	x_init_widgets_groupsel( Widget parent );
 
 void 	testf(Widget w, XButtonEvent *e, String *p, Cardinal *n );
 
@@ -1501,6 +1505,143 @@ void x_init_widgets_varsel( Widget parent )
 }
 
 /*************************************************************************************************/
+void x_init_widgets_groupsel( Widget parent )
+{
+	int	n_groups, n_groupsel_boxes, which_box, i, state;
+	NCVar	*var;
+	char	widget_name[128];
+	Widget	w;
+	Pixel	col2set;
+	Stringlist *group_list, *cursor;
+
+	/* Get list of known groups */
+	group_list = get_group_list( variables );
+
+	/* Arrange the groups in boxes, n_vars_per_row variables to a box */
+	n_groups               = n_strings_in_list( group_list );
+	n_groupsel_boxes       = n_groups / app_data.n_vars_per_row + 5;
+	group_selection_widget = (Widget *)malloc( n_groupsel_boxes*sizeof( Widget ));
+
+	/* Make an array of widgets for the variables; indicate the end of the 
+	 * array by a NULL value.
+	 */
+	grouplist_widget = (Widget *)malloc( (n_groups+1)*sizeof(Widget));
+	if( grouplist_widget == NULL ) {
+		fprintf( stderr, "ncview: x_init_widgets: malloc ");
+		fprintf( stderr, "failed on grouplist_widget initializeation\n" );
+		exit( -1 );
+		}
+	*(grouplist_widget+n_groups) = NULL;
+
+	cursor = group_list;
+	which_box = 0;
+	while( cursor != NULL ) {
+		if( i == 0 ) {
+			/* The very first button box! */
+			snprintf( widget_name, 127, "groupselbox_%1d", which_box+1 );
+			*(group_selection_widget+which_box) = XtVaCreateManagedWidget(
+				widget_name,
+				boxWidgetClass,
+				parent,
+				XtNorientation, XtorientHorizontal,
+				XtNborderWidth, 0,
+				NULL);
+			snprintf( widget_name, 127, "grouplist_label_%1d", which_box+1 );
+			grouplist_label_widget = XtVaCreateManagedWidget(
+				widget_name,
+				labelWidgetClass,
+				*(group_selection_widget+which_box),
+				XtNwidth, app_data.button_width,
+				XtNlabel, "Group:",
+				XtNborderWidth, 0,
+				NULL );
+			which_box++;
+			}
+		else if( (i % app_data.n_vars_per_row) == 0 ) {
+			/* A new button box! */
+			snprintf( widget_name, 127, "box_%1d", which_box+1 );
+			*(var_selection_widget+which_box) = XtVaCreateManagedWidget(
+				widget_name,
+				boxWidgetClass,
+				parent,
+				XtNorientation, XtorientHorizontal,
+				XtNborderWidth, 0,
+				XtNfromVert, *(var_selection_widget + which_box - 1),
+				NULL);
+			snprintf( widget_name, 127, "varlist_label_%1d", which_box+1 );
+			varlist_label_widget = XtVaCreateManagedWidget(
+				widget_name,
+				labelWidgetClass,
+				*(var_selection_widget+which_box),
+				XtNwidth, app_data.button_width,
+				XtNborderWidth, 0,
+				XtNlabel, "",
+				NULL );
+			which_box++;
+			}
+
+		snprintf( widget_name, 127, "varsel_%s", var->name );
+		state = False;
+		if( i == 0 )  /* first variable button */
+			*(varlist_widget+i) = XtVaCreateManagedWidget(
+				widget_name,
+				toggleWidgetClass,
+				*(var_selection_widget+which_box-1),
+				XtNstate, state,
+				XtNlabel, var->name,
+				XtNsensitive, True,
+				XtNwidth, app_data.varlabel_width,
+				NULL );
+		else
+			*(varlist_widget+i) = XtVaCreateManagedWidget(
+				widget_name,
+				toggleWidgetClass,
+				*(var_selection_widget+which_box-1),
+				XtNradioGroup, *varlist_widget,
+				XtNstate, state,
+				XtNlabel, var->name,
+				XtNsensitive, True,
+				XtNwidth, app_data.varlabel_width,
+				NULL );
+		if( (n_vars > 1) && app_data.var_colors && options.color_by_ndims ) {
+			switch( var->effective_dimensionality ) {
+				case 1: 
+					col2set = app_data.foreground1d;
+					break;
+				case 2:
+					col2set = app_data.foreground2d;
+					break;
+				case 3:
+					col2set = app_data.foreground3d;
+					break;
+				case 4:
+					col2set = app_data.foreground4d;
+					break;
+				case 5:
+				default:
+					col2set = app_data.foreground5d;
+					break;
+				}
+			XtVaSetValues(  *(varlist_widget+i),
+					XtNforeground, col2set, NULL );
+			}
+		var = var->next;
+		}
+
+	/* In the degenerate case of only one variable, must make it a radio
+	 * group by itself; can't do it above, because it needs to know its
+	 * own widget number before it can be done.
+	 */
+	if( n_vars == 1 )
+		XtVaSetValues( *varlist_widget, XtNradioGroup, 
+				*varlist_widget, NULL );
+
+	i = 0;
+	while( (w = *(varlist_widget + i++)) != NULL )
+		XtAddCallback( w, XtNcallback, varlist_mod1, NULL );
+}
+
+/*************************************************************************************************/
 void x_init_widgets_dimlabels( Widget parent )
 {
 	labels_row_widget = XtVaCreateManagedWidget(
@@ -1626,12 +1767,43 @@ void x_init_widgets( Widget top )
 		XtAddEventHandler( colorbar_widget, ExposureMask, FALSE,
 			(XtEventHandler)expose_colorbar, NULL );
 
-	varsel_form_widget = XtVaCreateManagedWidget(
-		"varselectform",
-		formWidgetClass,
-		commandcanvas_widget,
-		XtNfromVert, colorbar_form_widget,
-		NULL);
+	if( options.enable_group_sel ) {
+		groupsel_form_widget = XtVaCreateManagedWidget(
+			"groupselectform",
+			formWidgetClass,
+			commandcanvas_widget,
+			XtNfromVert, colorbar_form_widget,
+			NULL);
+
+			grouplist_label_widget = XtVaCreateManagedWidget(
+				"grouplist_label_widget",
+				labelWidgetClass,
+				groupsel_form_widget,
+				XtNwidth, app_data.button_width,
+				XtNlabel, "Group:",
+				XtNborderWidth, 0,
+				NULL );
+
+		/* the widgets that allow the user to select the group to display */
+		x_init_widgets_groupsel( groupsel_form_widget );
+
+		/* Only diff between this and varsel_form_widget create call 
+		 * below is what widget this widget is vertically from
+		 */
+		varsel_form_widget = XtVaCreateManagedWidget(
+			"varselectform",
+			formWidgetClass,
+			commandcanvas_widget,
+			XtNfromVert, groupsel_form_widget,
+			NULL);
+		}
+	else
+		varsel_form_widget = XtVaCreateManagedWidget(
+			"varselectform",
+			formWidgetClass,
+			commandcanvas_widget,
+			XtNfromVert, colorbar_form_widget,
+			NULL);
 
 	/* the widgets that allow the user to select the variable to display */
 	x_init_widgets_varsel( varsel_form_widget );
diff --git a/src/interface/filesel.c b/src/interface/filesel.c
index 2236591..936bb00 100644
--- a/src/interface/filesel.c
+++ b/src/interface/filesel.c
@@ -227,6 +227,7 @@ fs_double_click(Widget w, XButtonEvent *e, String *p, Cardinal *n )
 {
 	XawListReturnStruct	*highlited_entry;
 	Stringlist		*files_and_dirs;
+	int			ierr;
 
 	highlited_entry = XawListShowCurrent( w );
 	if( highlited_entry->list_index == XAW_LIST_NONE )
@@ -237,7 +238,7 @@ fs_double_click(Widget w, XButtonEvent *e, String *p, Cardinal *n )
 	 * that we select this file.
 	 */
 	if( fs_is_a_directory( highlited_entry->string )) {
-		chdir( highlited_entry->string );
+		ierr = chdir( highlited_entry->string );
 		fs_get_file_dir_list( &files_and_dirs );
 		XawListChange( fs_list_widget, 
 			stringlist_to_Xawlist( files_and_dirs ),
@@ -272,7 +273,7 @@ fs_popup_callback( Widget widget, XtPointer client_data, XtPointer call_data)
 	int
 file_select( char **filename, char *init_dir )
 {
-	int		retval;
+	int		retval, ierr;
 	Stringlist	*files_and_dirs;
 	char		*tstr, orig_dir[1024];
 
@@ -284,12 +285,12 @@ file_select( char **filename, char *init_dir )
 		return(MESSAGE_CANCEL);
 		}
 
-	chdir( init_dir );
+	ierr = chdir( init_dir );
 
 	fs_get_file_dir_list( &files_and_dirs );
 	retval = fs_popup( files_and_dirs );
 
-	chdir( orig_dir );
+	ierr = chdir( orig_dir );
 
 	/* Get the final selected filename if not MESSAGE_CANCEL */
 	if( retval != MESSAGE_CANCEL ) {
@@ -379,9 +380,10 @@ fs_is_a_directory( char *name )
 	static char *
 fs_cwd()
 {
+	char	*s;
 	static char buf[2048];
 
-	getcwd( buf, 2048 );
+	s = getcwd( buf, 2048 );
 	return( buf );
 }
 
diff --git a/src/interface/x_interface.c b/src/interface/x_interface.c
index 5dcb0b2..73488f3 100644
--- a/src/interface/x_interface.c
+++ b/src/interface/x_interface.c
@@ -1252,15 +1252,22 @@ void x_sort_vars_by_ndims( NCVar *v, long **vl_1d, int *n_1d, long **vl_2d, int
 
 /*************************************************************************************************/
 void x_init_widgets_varsel_menu_inner( Widget parent, long *varlist, int nv, char *tag, Widget *var_menu_2use,
-			Widget *list_of_sel_widgets )
+			Widget *list_of_sel_widgets, int discard_grpname )
 {
 	char temp[1024], widget_name[1024], var_menu_name[1024];
 	NCVar	*cursor;
 	int	i_cursor, i;
 
-	snprintf( temp,          1020, "(%d) %s vars", nv, tag );
+	strcpy( temp, tag );
 	snprintf( var_menu_name, 1020, "var_menu_%s",  tag     );
 
+	/* For some reason not clear to me, if a widget name has a period in it, the 
+	 * X windows system cannot find that widget ???
+	 */
+	for( i=0; i<strlen(var_menu_name); i++ )
+		if( var_menu_name[i] == '.' )
+			var_menu_name[i] = '_';
+
 	varsel_menu_widget = XtVaCreateManagedWidget(
 		"varsel_menu",
 		menuButtonWidgetClass,
@@ -1282,7 +1289,10 @@ void x_init_widgets_varsel_menu_inner( Widget parent, long *varlist, int nv, cha
 			cursor = cursor->next;
 			i_cursor++;
 			}
-		snprintf( widget_name, 1020, "%s", cursor->name );
+		if( discard_grpname )
+			unpack_groupname( cursor->name, -2, widget_name );
+		else
+			snprintf( widget_name, 1020, "%s", cursor->name );
 		list_of_sel_widgets[i] = XtVaCreateManagedWidget(
 			widget_name,
 			smeBSBObjectClass,
@@ -1292,6 +1302,89 @@ void x_init_widgets_varsel_menu_inner( Widget parent, long *varlist, int nv, cha
 		}
 }
 
+/*****************************************************************************************************/
+void x_init_widgets_varsel_menu_grp( Widget menu_box, Widget *varsel_menu_widget_list ) 
+{
+	Stringlist *uniq_groups, *sl_cursor;
+	int	discard_groupname, nvars, ngrps, *my_grp_num, i, igrp, nv_in_grp;		/* Applies to each var on the global "variables" list; counting starts at zero */
+	long	*varlist;
+	NCVar	*var_cursor;
+	Widget	*var_menu_grp;
+	char	widget_name[2048];
+
+	nvars = n_vars_in_list( variables );
+
+	varlist = (long *)malloc( sizeof(long) * nvars );
+
+	/* Sort vars into lists based on their group */
+	uniq_groups = get_group_list( variables );
+	ngrps = stringlist_len( uniq_groups );
+
+	/* Get group number for each var. First group in list "uniq_groups" is
+	 * assigned ID 0, etc.
+	 */
+	my_grp_num = (int *)malloc( sizeof(int) * nvars );
+	var_cursor = variables;
+	for( i=0; i<nvars; i++ ) {
+		my_grp_num[i] = -1;
+		sl_cursor = uniq_groups;
+		for( igrp=0; igrp<ngrps; igrp++ ) {
+
+			/* Test for a var in the root group (in which case it has no slashes) matching
+			 * the group named "/"
+			 */
+			if( (count_nslashes( var_cursor->name )==0) && (strncmp( "/", sl_cursor->string, 1)==0)) {
+				my_grp_num[i] = igrp;
+				break;
+				}
+			/* Var has at least one slash in its name */
+			else if( strncmp( var_cursor->name, sl_cursor->string, strlen(sl_cursor->string) ) == 0 ) {
+				my_grp_num[i] = igrp;
+				break;
+				}
+
+			sl_cursor = sl_cursor->next;
+			}
+		if( my_grp_num[i] == -1 ) {
+			fprintf( stderr, "x_init_widgets_varsel_menu_grp: Error, did not find group for var named >%s< in the following list of unique groups:\n",
+					var_cursor->name );
+			stringlist_dump( uniq_groups );
+			exit(-1);
+			}
+
+		var_cursor = var_cursor->next;
+		}
+
+	/* Now go through the groups, and make a menu selection button for
+	 * each group that generates a pop-up menu of all vars in that group
+	 */
+	sl_cursor = uniq_groups;
+	for( igrp=0; igrp<ngrps; igrp++ ) {
+		/* The way the routine x_init_widgets_varsel_menu_inner is set up, it needs
+		 * an array of longs that hold the entry number on the global "variables"
+		 * list corresponding to each group
+		 */
+		nv_in_grp = 0;
+		for( i=0; i<nvars; i++ ) {
+			if( my_grp_num[i] == igrp ) {
+				varlist[nv_in_grp] = i;	
+				nv_in_grp++;
+				}
+			}
+		var_menu_grp = (Widget *)malloc( sizeof(Widget) );
+		discard_groupname = 1;
+		snprintf( widget_name, 2040, "%s (%d vars)", sl_cursor->string, nv_in_grp );
+		if( options.debug ) printf( "x_init_widgets_varsel_menu_grp: making menu for group selection named >%s< with %d vars\n", 
+			widget_name, nv_in_grp );
+		x_init_widgets_varsel_menu_inner( menu_box, varlist, nv_in_grp, widget_name, var_menu_grp,
+			varsel_menu_widget_list, discard_groupname );
+
+		sl_cursor = sl_cursor->next;
+		}
+
+	free( varlist );
+}
+
 /*************************************************************************************************
  * The widget selection area can be in one of two different modes.  In "menu" mode, there is one button
  * for all the 1D variables, one button for all the 2D variables, ..., up to one button for all the 
@@ -1303,9 +1396,10 @@ void x_init_widgets_varsel_menu_inner( Widget parent, long *varlist, int nv, cha
  */
 void x_init_widgets_varsel_menu( Widget parent )
 {
-	long 	*vl_1d, *vl_2d, *vl_3d, *vl_4d, *vl_other;
-	int	n_1d, n_2d, n_3d, n_4d, n_other, n_tot;
+	long 	*vl_1d, *vl_2d, *vl_3d, *vl_4d, *vl_other;	/* These have 0 if the var is NOT in the list, 1 if it is */
+	int	discard_groupname, nvars, n_1d, n_2d, n_3d, n_4d, n_other;
 	Widget	menu_box;
+	char	widget_name[2048];
 
 	menu_box = XtVaCreateManagedWidget(
 		"varsel_menu_box",
@@ -1315,36 +1409,57 @@ void x_init_widgets_varsel_menu( Widget parent )
 		XtNborderWidth, 0,
 		NULL);
 
-	n_tot = n_vars_in_list( variables );
+	nvars = n_vars_in_list( variables );
 
 	/* Allocate our "global" array that will store each menu selection widget, so we
 	 * can later find them to set their sensitivities and otherwise manipulate them.
 	 */
-	varsel_menu_widget_list = (Widget *)malloc( n_tot * sizeof(Widget) );
+	varsel_menu_widget_list = (Widget *)malloc( nvars * sizeof(Widget) );
+
+	/* If we are doing groups, we sort based on their group membership. If we
+	 * are not doing groups, we sort based on the variable's number of dims
+	 */
+	if( options.enable_group_sel ) 
+		x_init_widgets_varsel_menu_grp( menu_box, varsel_menu_widget_list );
+
+	else	
+		{
+		discard_groupname = 0;
 
-	/* Sort vars into lists based on their number of dimensions */
-	x_sort_vars_by_ndims( variables, &vl_1d, &n_1d, &vl_2d, &n_2d, &vl_3d, &n_3d, 
-			&vl_4d, &n_4d, &vl_other, &n_other );
+		/* Sort vars into lists based on their number of dimensions */
+		x_sort_vars_by_ndims( variables, &vl_1d, &n_1d, &vl_2d, &n_2d, &vl_3d, &n_3d, 
+				&vl_4d, &n_4d, &vl_other, &n_other );
 
-	if( n_1d > 0 )
-		x_init_widgets_varsel_menu_inner( menu_box, vl_1d, n_1d, "1d", &var_menu_1d,
-			varsel_menu_widget_list );
+		if( n_1d > 0 ) {
+			snprintf( widget_name, 2040, "(%d) 1d vars", n_1d );
+			x_init_widgets_varsel_menu_inner( menu_box, vl_1d, n_1d, widget_name, &var_menu_1d,
+				varsel_menu_widget_list, discard_groupname );
+			}
 
-	if( n_2d > 0 )
-		x_init_widgets_varsel_menu_inner( menu_box, vl_2d, n_2d, "2d", &var_menu_2d,
-			varsel_menu_widget_list+n_1d );
+		if( n_2d > 0 ) {
+			snprintf( widget_name, 2040, "(%d) 2d vars", n_2d );
+			x_init_widgets_varsel_menu_inner( menu_box, vl_2d, n_2d, widget_name, &var_menu_2d,
+				varsel_menu_widget_list+n_1d, discard_groupname );
+			}
 
-	if( n_3d > 0 )
-		x_init_widgets_varsel_menu_inner( menu_box, vl_3d, n_3d, "3d", &var_menu_3d, 
-			varsel_menu_widget_list+n_1d+n_2d );
+		if( n_3d > 0 ) {
+			snprintf( widget_name, 2040, "(%d) 3d vars", n_3d );
+			x_init_widgets_varsel_menu_inner( menu_box, vl_3d, n_3d, widget_name, &var_menu_3d, 
+				varsel_menu_widget_list+n_1d+n_2d, discard_groupname );
+			}
 
-	if( n_4d > 0 )
-		x_init_widgets_varsel_menu_inner( menu_box, vl_4d, n_4d, "4d", &var_menu_4d,
-			varsel_menu_widget_list+n_1d+n_2d+n_3d );
+		if( n_4d > 0 ) {
+			snprintf( widget_name, 2040, "(%d) 4d vars", n_4d );
+			x_init_widgets_varsel_menu_inner( menu_box, vl_4d, n_4d, widget_name, &var_menu_4d,
+				varsel_menu_widget_list+n_1d+n_2d+n_3d, discard_groupname );
+			}
 
-	if( n_other > 0 )
-		x_init_widgets_varsel_menu_inner( menu_box, vl_other, n_other, "5d", &var_menu_other,
-			varsel_menu_widget_list+n_1d+n_2d+n_3d+n_4d );
+		if( n_other > 0 ) {
+			snprintf( widget_name, 2040, "(%d) 5d vars", n_other );
+			x_init_widgets_varsel_menu_inner( menu_box, vl_other, n_other, widget_name, &var_menu_other,
+				varsel_menu_widget_list+n_1d+n_2d+n_3d+n_4d, discard_groupname );
+			}
+		}
 }
 
 /*************************************************************************************************/
@@ -1489,6 +1604,11 @@ void x_init_widgets_varsel_list( Widget parent )
 /*************************************************************************************************/
 void x_init_widgets_varsel( Widget parent )
 {
+	if( options.enable_group_sel && (options.varsel_style != VARSEL_MENU)) {
+		fprintf( stderr, "Internal error in x_init_widgets_varsel: if group selection is enambled, var selection must be done via menus\n" );
+		exit(-1);
+		}
+
 	if( options.varsel_style == VARSEL_LIST )
 		x_init_widgets_varsel_list( parent );
 	else if( options.varsel_style == VARSEL_MENU ) 
diff --git a/src/ncview.c b/src/ncview.c
index 1f3be77..ed974e4 100644
--- a/src/ncview.c
+++ b/src/ncview.c
@@ -110,9 +110,22 @@ main( int argc, char **argv )
 		exit( -1 );
 		}
 
+	/* If any vars are in groups, we build the interface differently. 
+	 * I pass this information through the global "options" struct.
+	 */
+	if( any_var_in_group( variables )) {
+		options.enable_group_sel = TRUE;
+		options.varsel_style = VARSEL_MENU;
+		}
+	else
+		options.enable_group_sel = FALSE;
+
 	/* This initializes the colormaps, and then the X widows system */
+	if( options.debug ) printf( "Initializing display interface...\n" );
 	initialize_display_interface(); 
+	if( options.debug ) printf( "Initializing printing subsystem...\n" );
 	print_init();
+	if( options.debug ) printf( "Initializing overlays...\n" );
 	overlay_init();
 
 	/* If there is only one variable, make it the active one */
@@ -580,9 +593,12 @@ init_cmap_from_file( char *dir_name, char *file_name )
 	void
 initialize_file_interface( Stringlist *input_files )
 {
-	int	idim, nvars, nfiles;
+	int	i, idim, nvars, nfiles;
 	NCVar	*var;
 
+	if( options.debug ) 
+		fprintf( stderr, "Initializing file interface...\n" );
+
 	nfiles = stringlist_len( input_files );
 
 	while( input_files != NULL ) {
@@ -590,7 +606,7 @@ initialize_file_interface( Stringlist *input_files )
 		input_files = input_files->next;
 		}
 	if( options.debug ) 
-		fprintf( stderr, "Calculating dim min & maxes...\n" );
+		fprintf( stderr, "...calculating dim min & maxes...\n" );
 	calc_dim_minmaxes();
 
 	/* Get the effective dimensionality of all the vars.
@@ -602,12 +618,18 @@ initialize_file_interface( Stringlist *input_files )
 	while( var != NULL ) {
 		nvars++;
 		var->effective_dimensionality = 0;
-		for( idim=0; idim<var->n_dims; idim++ ) 
+		for( idim=0; idim<var->n_dims; idim++ ) {
 			if( *(var->size + idim) > 1 )
 				var->effective_dimensionality++;
-		if( options.debug ) 
+			if( options.debug ) 
+				fprintf( stderr, "var %s has %d dims, dim %d: >%s< len %ld\n",
+					var->name, var->n_dims, idim, 
+					var->dim[idim]->name, var->dim[idim]->size );
+			}
+		if( options.debug ) {
 			fprintf( stderr, "variable %s had effective_dimensionality of %d\n",
 				var->name, var->effective_dimensionality );
+			}
 		var = var->next;
 		}
 
@@ -619,6 +641,9 @@ initialize_file_interface( Stringlist *input_files )
 
 	if( nvars > options.listsel_max )
 		options.varsel_style = VARSEL_MENU;
+
+	if( options.debug ) 
+		fprintf( stderr, "Done initializing file interface...\n" );
 }
 
 /***********************************************************************************************/
@@ -632,7 +657,9 @@ initialize_display_interface()
 	 */
 	x_check_legal_colormap_loaded();
 
+	if( options.debug ) printf( "...initializing X interface\n" );
 	in_initialize();
+	if( options.debug ) printf( "...done with initializing X interface\n" );
 }
 
 /***********************************************************************************************/
@@ -679,6 +706,21 @@ create_default_colormap()
 }
 
 /***********************************************************************************************/
+int any_var_in_group( NCVar *var ) {
+
+	NCVar	*cursor;
+
+	cursor = var;
+	while( cursor != NULL ) {
+		if( count_nslashes( cursor->name ) > 0 ) 
+			return( 1 );
+		cursor = cursor->next;
+		}
+
+	return( 0 );
+}
+
+/***********************************************************************************************/
 	void
 useage()
 {
diff --git a/src/ncview.defines.h b/src/ncview.defines.h
index d117c55..70bc97a 100644
--- a/src/ncview.defines.h
+++ b/src/ncview.defines.h
@@ -31,8 +31,8 @@
 #include <udunits2.h>
 #endif
 
-#define PROGRAM_ID		"Ncview 2.1.1 David W. Pierce  1 Aug 2011"
-#define PROGRAM_VERSION_STRING	"2.1.1"
+#define PROGRAM_ID		"Ncview 2.1.2 David W. Pierce  24 Oct 2012"
+#define PROGRAM_VERSION_STRING	"2.1.2"
 #define APP_RES_VERSION 	1.93
 
 #ifndef TRUE
@@ -399,6 +399,10 @@ typedef struct {
 					 	* are global, rather than local to
 					 	* a file.
 					 	*/
+	int	user_set_blowup;		/* Initializes to -99999, then saves user-specified
+	  					 * value of 'blowup' for this var so it can be
+						 * used again if we leave this var & then come back
+						 */
 	int	auto_set_no_range;		/* '1' if we autoset a range of -1,1 based
 						 * on not having a valid range for this var
 						 */
@@ -532,6 +536,8 @@ typedef struct {
 	int	save_frames;	/* If true, try to save frames in core for faster display */
 	float	frame_delay;	/* Normalied to be between 0.0 and 1.0 */
 
+	int	enable_group_sel;	/* TRUE if we have some vars in groups, so interface must incl. grp selection */
+
 	OverlayOptions *overlay;
 } Options;
 
diff --git a/src/ncview.protos.h b/src/ncview.protos.h
index 6fab2e0..8569552 100644
--- a/src/ncview.protos.h
+++ b/src/ncview.protos.h
@@ -141,6 +141,8 @@ void 	sl_cat		    ( Stringlist **dest, Stringlist **src );
 void 	get_min_max_onestep( NCVar *var, size_t n_other, size_t tstep, float *data, 
 					float *min, float *max, int verbose );
 void 	cache_scalar_coord_info( NCVar *vars );
+int 	count_nslashes	    ( char *s );
+Stringlist *get_group_list  ( NCVar *vars );
 
 /******************************************************************************
  * in interface.c 
@@ -279,7 +281,7 @@ int	view_draw            ( int allow_saveframes_useage, int force_range_to_frame
 void 	view_change_cur_dim  ( char *dim_name, int modifier );
 void	view_forward         ( void );
 void	view_backward        ( void );
-void	view_change_blowup   ( int delta, int redraw_flag );
+void	view_change_blowup   ( int delta, int redraw_flag, int view_var_is_valid );
 void	init_saveframes	     ( void );
 void 	redraw_dimension_info( void );
 void 	redraw_ccontour      ( void );
diff --git a/src/util.c b/src/util.c
index 5dc5488..4a6ff04 100644
--- a/src/util.c
+++ b/src/util.c
@@ -397,7 +397,7 @@ add_vars_to_list( Stringlist *var_list, int id, char *filename, int nfiles )
 	Stringlist *var;
 
 	if( options.debug )
-		fprintf( stderr, "adding vars to list for file %s\n", filename );
+		fprintf( stderr, "add_vars_to_list: entering, adding vars to list for file %s\n", filename );
 	var = var_list;
 	while( var != NULL ) {
 		if( options.debug ) 
@@ -456,6 +456,7 @@ add_var_to_list( char *var_name, int file_id, char *filename, int nfiles )
 		new_var->global_max = 0.0;
 		new_var->user_min   = 0.0;
 		new_var->user_max   = 0.0;
+		new_var->user_set_blowup   = -99999;
 		new_var->auto_set_no_range = 0;
 		new_var->have_set_range    = FALSE;
 		new_var->size       = fi_var_size( file_id, var_name );
@@ -509,9 +510,9 @@ add_var_to_list( char *var_name, int file_id, char *filename, int nfiles )
  * Go through each variable, and if it has scalar coordinate information,
  * read that in from each file that the var lives in.
  */
- 	void
- cache_scalar_coord_info( NCVar *vars )
- {
+	void
+cache_scalar_coord_info( NCVar *vars )
+{
  	NCVar		*v;
 	FDBlist		*tfile;
 	int		nfiles, ifile, nsc, isc;
@@ -519,6 +520,8 @@ add_var_to_list( char *var_name, int file_id, char *filename, int nfiles )
 	float		fval;
 	size_t		zeros[MAX_NC_DIMS], ones[MAX_NC_DIMS], n_ts, ii, i_cursor, n_ts_this_file;
 
+	if( options.debug ) printf( "cache_scalar_coord_info: entering\n" );
+
 	/* Allocate space for the timestep_2_fdb array. This points
 	 * to the file (FDBlist) associated with EACH TIMESTEP of
 	 * the variable
@@ -624,6 +627,8 @@ add_var_to_list( char *var_name, int file_id, char *filename, int nfiles )
 
 		v = v->next;
 		}
+
+	if( options.debug ) printf( "cache_scalar_coord_info: finished\n" );
  }
 
 /******************************************************************************
@@ -1303,17 +1308,22 @@ handle_dim_mapping_2d( NCVar *v, char *coord_var_name, char *coord_att, size_t *
 	void
 fill_dim_structs( NCVar *v )
 {
-	int	i, fileid;
+	int	i, fileid, debug;
 	NCDim	*d;
 	char	*dim_name, *tmp_units;
 	static  int global_id = 0;
 	FDBlist	*cursor;
 
+	debug = 0;
+
+	if( debug == 1 ) printf( "fill_dim_structs: entering for var %s, which has %d dims\n", v->name, v->n_dims );
+
 	fileid = v->first_file->id;
 	v->dim = (NCDim **)malloc( v->n_dims*sizeof( NCDim *));
 	for( i=0; i<v->n_dims; i++ ) {
+		dim_name = fi_dim_id_to_name( fileid, v->name, i );
+		if( debug == 1 ) printf( "fill_dim_structs: dim %d has name %s and length %ld\n", i, dim_name, v->size[i] );
 		if( is_scannable( v, i ) ) {
-			dim_name     	= fi_dim_id_to_name( fileid, v->name, i );
 			*(v->dim + i)	= (NCDim *)malloc( sizeof( NCDim ));
 			d            	= *(v->dim+i);
 			d->name      	= dim_name;
@@ -1333,7 +1343,8 @@ fill_dim_structs( NCVar *v )
 			/* Indicate non-scannable dimensions by a NULL */
 			*(v->dim + i) = NULL;
 			if( options.debug ) 
-				fprintf( stderr, "adding non-scannable dim to var %s\n", v->name );
+				fprintf( stderr, "adding non-scannable dim to var %s: dim name: %s size: %ld\n", 
+					v->name, fi_dim_id_to_name( fileid, v->name, i), *(v->size+i) );
 			}
 		}
 
@@ -1360,8 +1371,10 @@ fill_dim_structs( NCVar *v )
 }
 
 /******************************************************************************
- * Is this variable a "scannable" variable -- i.e., accessable by the taperecorder
- * style buttons?
+ * Is this a "scannable" dimension -- i.e., accessable by the taperecorder
+ * style buttons? Is scannable if: 
+ * 	> is unlimited
+ *	> or, is size > 1
  */
 	int
 is_scannable( NCVar *v, int i )
@@ -2283,3 +2296,130 @@ int determine_lat_lon( char *s_in, int *is_lat, int *is_lon )
 	return(1);	/* error return */
 }
 
+/*******************************************************************************************
+ * Returns the number of forward slashes in a string
+ */
+int count_nslashes( char *s ) 
+{
+	int	i, nslash;
+
+	nslash = 0;
+	for( i=0; i<strlen(s); i++ ) 
+		if( s[i] == '/' )
+			nslash++;
+
+	return( nslash );
+}
+
+/*******************************************************************************************
+ * Given a list of variables, this returns a stringlist of unique group names. If ANY var
+ * lives in the root group, then the return list includes "/". If no var lives in the root
+ * group, then the list does NOT include "/".
+ */
+Stringlist *get_group_list( NCVar *vars ) 
+{
+	Stringlist	*retval = NULL, *tg;
+	NCVar		*cursor;
+	char		group_name[ MAX_NC_NAME*20 ];	/* Assume no more than 20 levels of groups */
+	int		i, ierr, n_so_far, foundit;
+
+	cursor = vars;
+	while( cursor != NULL ) {
+
+		ierr = unpack_groupname( cursor->name, -1, group_name );	/* -1 means get full group name */
+
+		/* Only add to list if not already there */
+		n_so_far = stringlist_len( retval );
+		tg = retval;
+		foundit = 0;
+		while( tg != NULL ) {
+			if( strcmp( tg->string, group_name ) == 0 ) {
+				foundit = 1;
+				break;
+				}
+			tg = tg->next;
+			}
+		if( foundit == 0 )
+			ierr = stringlist_add_string( &retval, group_name, NULL, SLTYPE_NULL );
+
+		cursor = cursor->next;
+		}
+
+	return( retval );
+}
+
+/*******************************************************************************************
+ * Given a varname string of format: groupname0/groupname1/groupnameN/varname
+ *
+ * and an integer ig: 0...N this returns groupname correspoinding to the integer ig
+ * (NOTE: counting starts at 0, so if ig==0 then the first group name is returned)
+ *
+ * If ig == -1, then the full groupname without the varname is returned:
+ * I.e., "groupname0/groupname1/groupnameN". If the var does NOT have any
+ * forward slashes, it lives in the root group, and "/" is returned.
+ *
+ * If ig == -2, then ONLY the varname is returned. I.e., "varname"
+ *
+ * groupname must already be allocated upon entry
+ *
+ * Returns 0 on success, -1 on error
+ */
+int unpack_groupname( char *varname, int ig, char *groupname ) 
+{
+	int	i, i0, i1, idx_slash[MAX_NC_NAME], nslash;
+	char	ts[MAX_NC_NAME];
+
+	/* Get indices of the slashes */
+	nslash = 0;
+	for( i=0; i<strlen(varname); i++ ) {
+		if( varname[i] == '/' ) {
+			idx_slash[nslash] = i;
+			nslash++;
+			}
+		}
+
+	if( nslash == 0 ) {
+		if (ig == -2 ) {
+			/* Asked for varname only */
+			strcpy( groupname, varname );
+			return(0);
+			}
+		else
+			{
+			/* If no slashes in the var name, must live in root group */
+			strcpy( groupname, "/" );
+			return( 0 );
+			}
+		}
+
+	if( ig > (nslash+1) ) {
+		fprintf( stderr, "Error in unpack_groupname: varname: >%s< group to find (starting at 0)=%d invalid group to find (not this many groups in the varname)\n",
+			varname, ig );
+		exit(-1);
+		}
+
+	strcpy( ts, varname );
+
+	if( ig == -2 ) {
+		strcpy( groupname, ts+idx_slash[nslash-1]+1 );
+		return( 0 );
+		}
+
+	if( ig == -1 ) {
+		ts[ idx_slash[nslash-1] ] = '\0';
+		strcpy( groupname, ts );
+		return( 0 );
+		}
+
+	if( ig == 0 ) 
+		i0 = 0;
+	else
+		i0 = idx_slash[ig-1] + 1;
+	i1 = idx_slash[ig];
+	ts[i1] = '\0';
+
+	strcpy( groupname, ts+i0 );
+
+	return( 0 );
+}
+
diff --git a/src/view.c b/src/view.c
index 7ef0f2c..60d32f7 100644
--- a/src/view.c
+++ b/src/view.c
@@ -75,7 +75,7 @@ static void 		re_determine_scan_axes( View *new_view, NCVar *new_var, View *old_
 static void 		set_scan_place( View *new_view, NCVar *var, View *old_view );
 static void 		initial_set_scan_place( View *view, NCVar *var );
 static void 		re_set_scan_place( View *new_view, NCVar *new_var, View *old_view );
-static void		calculate_blowup( View *view, NCVar *var );
+static void		calculate_blowup( View *view, NCVar *var, int val_to_set_to );
 static void 		draw_file_info( NCVar *var );
 static void 		label_dimensions( View *view );
 static void 		show_current_dim_values( View *view );
@@ -111,8 +111,12 @@ set_scan_variable( NCVar *var )
 	float	range_x, range_y;
 	NCDim	*xdim, *ydim, *xdim_old, *xdim_new, *ydim_old, *ydim_new;
 
-	if( options.debug )
+	if( options.debug ) {
 		fprintf( stderr, "\n\n******************************************\nentering set_scan_variable with var=%s\n", var->name );
+		fprintf( stderr, "var nims:%d\n", var->n_dims );
+		for( i=0; i<var->n_dims; i++ )
+			fprintf( stderr, "dim=%ld size=%ld\n", i, *(var->size + i) );
+		}
 
 	in_set_cursor_busy();
 
@@ -132,6 +136,9 @@ set_scan_variable( NCVar *var )
 		if( options.debug )
 			fprintf( stderr, "...determining scan axes (NEW)\n" );
 		determine_scan_axes( view, var, NULL );
+		if( options.debug )
+			fprintf( stderr, "...axes ids: scan=%d y=%d x=%d\n", 
+				view->scan_axis_id, view->y_axis_id, view->x_axis_id );
 		if( var->effective_dimensionality == 1 ) {
 			start = (size_t *)malloc(view->variable->n_dims*sizeof(size_t));
 			count = (size_t *)malloc(view->variable->n_dims*sizeof(size_t));
@@ -162,7 +169,12 @@ set_scan_variable( NCVar *var )
 		/* How big should we initially make the picture? */
 		if( options.debug )
 			fprintf( stderr, "...calculating blowup (NEW)\n" );
-		calculate_blowup( view, var );
+		calculate_blowup( view, var, -99999 );	/* last val is flag meaning to do automatic calculation */
+
+		/* Save the blowup we are using in the var structure so we can
+		 * return to it later if we want
+		 */
+		var->user_set_blowup = options.blowup;
 		if( options.debug )
 			fprintf( stderr, "... ... new blowup=%d\n", options.blowup );
 		}
@@ -226,7 +238,11 @@ set_scan_variable( NCVar *var )
 			if( options.debug )
 				fprintf( stderr, "...axis change, recalculating blowup; old, new X dim=%s, %s; old, new Y dim=%s, %s\n",
 					xdim_old->name, xdim_new->name, ydim_old->name, ydim_new->name );
-			calculate_blowup( new_view, var );
+			calculate_blowup( new_view, var, var->user_set_blowup );
+			/* Save the blowup we are using in the var structure so we can
+			 * return to it later if we want
+			 */
+			var->user_set_blowup = options.blowup;
 			}
 
 		/* If the new var has a different shape than the old var,
@@ -425,7 +441,14 @@ change_view( int delta, int interpretation )
 		/* Delta is in percent of total size */
 		size              = *(view->variable->size  + view->scan_axis_id);
 		provisional_delta = (float)size * (float)delta/100.0;
-		delta             = (int)provisional_delta;
+		if( (int)provisional_delta == 0 ) {
+			if( delta < 0 )
+				delta = -1;
+			else
+				delta = 1;
+			}
+		else
+			delta = (int)provisional_delta;
 		}
 
 	place = *(view->var_place + view->scan_axis_id) + delta;
@@ -974,9 +997,16 @@ initial_determine_scan_axes( View *view, NCVar *var )
 	Stringlist	*dimlist;
 	int		n_dims;
 
+	if( options.debug ) fprintf( stderr, "initial_determine_scan_axes: entering for var %s\n", var->name );
+
 	/* Get a list of all possible scannable dimensions */
 	dimlist = fi_scannable_dims( var->first_file->id, var->name );
 
+	if( options.debug ) {
+		fprintf( stderr, "initial_determine_scan_axes: scannable dims:\n" );
+		stringlist_dump( dimlist );
+		}
+
 	/* For now, just pick the last two to be the Y and X axes.
 	 */ 
 	n_dims = stringlist_len( dimlist );
@@ -996,11 +1026,21 @@ initial_determine_scan_axes( View *view, NCVar *var )
 					var->first_file->id,
 					var->name,
 					dimlist->string );
+			if( view->y_axis_id == -1 ) {
+				fprintf( stderr, "initial_determine_scan_axes: internal error: dim >%s< was indicated by routine fi_scannable_dims to be a scannable dim for var >%s<, but routine fi_dim_name_to_id did not find that dim for the var\n",
+					dimlist->string, var->name );
+				exit(-1);
+				}
 			dimlist            = dimlist->next;
 			view->x_axis_id    = fi_dim_name_to_id( 
 					var->first_file->id,
 					var->name,
 					dimlist->string );
+			if( view->x_axis_id == -1 ) {
+				fprintf( stderr, "initial_determine_scan_axes: internal error: dim >%s< was indicated by routine fi_scannable_dims to be a scannable dim for var >%s<, but routine fi_dim_name_to_id did not find that dim for the var\n",
+					dimlist->string, var->name );
+				exit(-1);
+				}
 			break;
 
 		default:
@@ -1012,6 +1052,11 @@ initial_determine_scan_axes( View *view, NCVar *var )
 					var->first_file->id,
 					var->name,
 					dimlist->string );
+			if( view->scan_axis_id == -1 ) {
+				fprintf( stderr, "initial_determine_scan_axes: internal error: dim >%s< was indicated by routine fi_scannable_dims to be a scannable dim for var >%s<, but routine fi_dim_name_to_id did not find that dim for the var\n",
+					dimlist->string, var->name );
+				exit(-1);
+				}
 			dimlist            = dimlist->next;
 
 			/* Go to the second to the last entry */
@@ -1021,13 +1066,26 @@ initial_determine_scan_axes( View *view, NCVar *var )
 					var->first_file->id,
 					var->name,
 					dimlist->string );
+			if( view->y_axis_id == -1 ) {
+				fprintf( stderr, "initial_determine_scan_axes: internal error: dim >%s< was indicated by routine fi_scannable_dims to be a scannable dim for var >%s<, but routine fi_dim_name_to_id did not find that dim for the var\n",
+					dimlist->string, var->name );
+				exit(-1);
+				}
 			dimlist            = dimlist->next;
 			view->x_axis_id    = fi_dim_name_to_id( 
 					var->first_file->id,
 					var->name,
 					dimlist->string );
+			if( view->x_axis_id == -1 ) {
+				fprintf( stderr, "initial_determine_scan_axes: internal error: dim >%s< was indicated by routine fi_scannable_dims to be a scannable dim for var >%s<, but routine fi_dim_name_to_id did not find that dim for the var\n",
+					dimlist->string, var->name );
+				exit(-1);
+				}
 			break;
 		}
+
+	if( options.debug ) fprintf( stderr, "initial_determine_scan_axes: exiting with axis_ids: scan=%d y=%d x=%d\n",
+		view->scan_axis_id, view->y_axis_id, view->x_axis_id );
 }
 
 /********************************************************************************
@@ -1053,7 +1111,7 @@ fill_view_data( View *v )
 	*(count+v->x_axis_id) = *(v->variable->size + v->x_axis_id);
 	*(count+v->y_axis_id) = *(v->variable->size + v->y_axis_id);
 
-	if( options.show_sel ) {
+	if( options.debug || options.show_sel ) {
 		printf( "-var %s -start \\(", v->variable->name );
 		for( i=v->variable->n_dims-1; i >= 0; i-- ) {
 			printf( "%1ld", 1 + (*(v->var_place+i)) );
@@ -1079,7 +1137,7 @@ fill_view_data( View *v )
  * Alter the amount by which we are blowing up pixels
  */
 	void
-view_change_blowup( int delta, int redraw_flag )
+view_change_blowup( int delta, int redraw_flag, int view_var_is_valid )
 {
 	size_t	x_size, y_size, scaled_x_size, scaled_y_size;
 	char	blowup_label[32];
@@ -1112,6 +1170,10 @@ view_change_blowup( int delta, int redraw_flag )
 
         in_set_label( LABEL_BLOWUP, blowup_label );
 
+	if( view_var_is_valid ) {
+		view->variable->user_set_blowup = options.blowup;
+		}
+
 	free( view->pixels );
 
 	x_size       = *(view->variable->size + view->x_axis_id);
@@ -1824,22 +1886,37 @@ re_set_scan_place( View *new_view, NCVar *new_var, View *old_view )
 }
 
 /**************************************************************************************/
+/* If 'val_to_set_to' is -99999, then the blowup is calculated automatically,
+ * otherwise the blowup is set to val_to_set_to
+ */
 	static void
-calculate_blowup( View *view, NCVar *var )
+calculate_blowup( View *view, NCVar *var, int val_to_set_to )
 {
 	size_t	x_size, y_size;
 	float	fbx, fby, f_x_size, f_y_size, f_blowup;
-	int	ifbx;
+	int	ifbx, view_var_is_valid;
 
 	if( options.small )
 		return;
 
+	view_var_is_valid = 0;
+
+	if( val_to_set_to != -99999 ) {
+		while( options.blowup > val_to_set_to ) {
+			view_change_blowup( -1, FALSE, view_var_is_valid );			
+			}
+		while( options.blowup < val_to_set_to ) {
+			view_change_blowup( 1, FALSE, view_var_is_valid );			
+			}
+		return;
+		}
+
 	/* If the picture is too small, start out by blowing it up some */
 	x_size = *(var->size + view->x_axis_id);
 	y_size = *(var->size + view->y_axis_id);
 	while( (options.blowup*x_size < options.blowup_default_size) && 
 	       (options.blowup*y_size < options.blowup_default_size) ) {
-		view_change_blowup( 1, FALSE );
+		view_change_blowup( 1, FALSE, view_var_is_valid );
 		}
 
 	/* If picture is too big, reduce it some */
@@ -1851,7 +1928,7 @@ calculate_blowup( View *view, NCVar *var )
 	fbx = (fbx > fby) ? fbx : fby;
 	if( fbx > 3 ) {
 		ifbx = -(int)fbx;
-		view_change_blowup(ifbx,FALSE);
+		view_change_blowup(ifbx,FALSE, view_var_is_valid);
 		}
 }
 
diff --git a/x_interface.c b/x_interface.c
new file mode 100644
index 0000000..e69de29

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/ncview.git



More information about the Pkg-grass-devel mailing list