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 <sebas...@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

_______________________________________________
Pkg-grass-devel mailing list
Pkg-grass-devel@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel

Reply via email to