/*****************************************************************************
   Major portions of this software are copyrighted by the Medical College
   of Wisconsin, 1994-2000, and are released under the Gnu General Public
   License, Version 2.  See the file README.Copyright for details.
******************************************************************************/

#include "afni.h"
#ifndef ALLOW_PLUGINS
#  error "Plugins not properly set up -- see machdep.h"
#endif
extern char **Atlas_Names_List(ATLAS_LIST *atl);

#undef  BGCOLOR_ARG
#define BGCOLOR_ARG(str) \
  XtVaTypedArg , XmNbackground , XmRString , (str) , strlen(str)+1

#undef  RCOL
#define RCOL "#440011"  /* 26 Oct 2007 */
#undef  GCOL
#define GCOL "#003311"

/***********************************************************************
  Plugin to draw values into a dataset.
  Makes a custom interface.
************************************************************************/

/*---------- prototypes for internal routines ----------*/

char * DRAW_main( PLUGIN_interface * ) ;

void DRAW_make_widgets(void) ;

void DRAW_done_CB  ( Widget , XtPointer , XtPointer ) ;
void DRAW_undo_CB  ( Widget , XtPointer , XtPointer ) ;
void DRAW_redo_CB  ( Widget , XtPointer , XtPointer ) ;  /* 19 Nov 2003 */
void DRAW_help_CB  ( Widget , XtPointer , XtPointer ) ;
void DRAW_quit_CB  ( Widget , XtPointer , XtPointer ) ;
void DRAW_save_CB  ( Widget , XtPointer , XtPointer ) ;
void DRAW_saveas_CB( Widget , XtPointer , XtPointer ) ;  /* 24 Sep 2001 */
void DRAW_choose_CB( Widget , XtPointer , XtPointer ) ;
void DRAW_color_CB ( MCW_arrowval * , XtPointer ) ;
void DRAW_mode_CB  ( MCW_arrowval * , XtPointer ) ;
void DRAW_value_CB ( MCW_arrowval * , XtPointer ) ;
void DRAW_fillin_CB( Widget , XtPointer , XtPointer ) ; /* 19 Mar 2001 */

void DRAW_ttatlas_CB( Widget , XtPointer , XtPointer ) ; /* 22 Aug 2001 */

void DRAW_label_CB( Widget , XtPointer , XtPointer ) ; /* 15 Oct 2003 */
void DRAW_label_EV( Widget , XtPointer , XEvent * , RwcBoolean * ) ;
void DRAW_attach_dtable( Dtable *, char *, THD_3dim_dataset * ) ;

void DRAW_receiver( int , int , void * , void * ) ;
int  DRAW_into_dataset( int , int * , int * , int * , void * ) ;
void DRAW_finalize_dset_CB( Widget , XtPointer , MCW_choose_cbs * ) ;
void DRAW_2dfiller( int nx , int ny , int ix , int jy , byte * ar ) ;

void DRAW_saveas_finalize_CB( Widget , XtPointer , MCW_choose_cbs * ) ;

static int Check_value(void);
static void Sensitize_copy_bbox(int);

static void DRAW_2D_expand( int, int *, int *, int *, int, int *, int ** ) ;  /* 07 Oct 2002 */
static void DRAW_3D_expand( int, int *, int *, int *, int, int *, int ** ) ;  /* 07 Oct 2002 */

static void DRAW_2D_circle( int, int *, int *, int *, int, int *, int ** ) ;  /* 16 Oct 2002 */
static void DRAW_3D_sphere( int, int *, int *, int *, int, int *, int ** ) ;  /* 16 Oct 2002 */

#define USE_COLLAPSAR
#ifdef  USE_COLLAPSAR
static void DRAW_collapsar( int * , int * ) ;                                 /* 21 Oct 2002 */
#endif

static PLUGIN_interface * plint = NULL ;

static int infill_mode = 0 ;

void DRAW_set_value_label(void) ;
char * DRAW_value_string( float val ) ;
static void choose_new_atlas_CB(MCW_arrowval * , XtPointer );
static MCW_arrowval *Atlas_chooser(Widget rc);
static void free_plugdraw_atlas_list();
static float get_afni_thresh_percent();

static char *plugdraw_atlasname;

/***********************************************************************
   Set up the interface to the user.  Note that we bypass the
   normal interface creation, and simply have the menu selection
   directly call the main function, which will create a custom
   set of interface widgets.
************************************************************************/

DEFINE_PLUGIN_PROTOTYPE

PLUGIN_interface * PLUGIN_init( int ncall )
{

   if( ncall > 0 ) return NULL ;  /* only one interface */

   plint = PLUTO_new_interface( "Draw Dataset" , NULL , NULL ,
                                PLUGIN_CALL_IMMEDIATELY ,
                                (cptr_func *)DRAW_main ) ;

   PLUTO_add_hint( plint , "Interactive Dataset Editor" ) ;

   PLUTO_set_sequence( plint , "A:olddset:editor" ) ;

   return plint ;
}

/***************************************************************************
  Will be called from AFNI when user selects from Plugins menu.
****************************************************************************/

/* Interface widgets */

static Widget shell=NULL , rowcol , info_lab , choose_pb;
static Widget atlas_panel ;
static Widget done_pb, undo_pb,redo_pb, help_pb, quit_pb, save_pb, saveas_pb ;
static MCW_arrowval *value_av , *color_av , *mode_av ;
static MCW_arrowval *rad_av ;                         /* 16 Oct 2002 */
static Widget label_textf , label_label ;             /* 15 Oct 2003 */

#if 0
# define ENABLE_rad_av \
   AV_SENSITIZE( rad_av , (mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE) )
#else
# define ENABLE_rad_av                                                   \
   do{ if( mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE )  \
         XtManageChild( rad_av->wrowcol ) ;                             \
       else                                                             \
         XtUnmanageChild( rad_av->wrowcol ) ;                           \
   } while(0)
#endif

static MCW_arrowval * fillin_dir_av , * fillin_gap_av ; /* 19 Mar 2001 */
static Widget fillin_doit_pb ;

static Widget         ttatlas_rowcol=NULL ;             /* 22 Aug 2001 */
static MCW_arrowval * ttatlas_region_av ,
                    * ttatlas_hemisphere_av,
                    * atlas_chooser_av ;
static Widget         ttatlas_actar ;

#define HAVE_TTATLAS (ttatlas_rowcol != NULL)

#define NHEMI       3
#define HEMI_LEFT   "Left only"
#define HEMI_RIGHT  "Right only"
#define HEMI_BOTH   "Both"
static char *HEMI_strings[NHEMI] = { HEMI_LEFT , HEMI_RIGHT , HEMI_BOTH } ;

#define NUM_TTATLAS_ACT         2
#define TTATLAS_overwrite_label "Load: OverWrite"
#define TTATLAS_infill_label    "Load: InFill"

static MCW_action_item TTATLAS_act[] = {
 { TTATLAS_overwrite_label , DRAW_ttatlas_CB, NULL,NULL, NULL, 0 } ,
 { TTATLAS_infill_label    , DRAW_ttatlas_CB, NULL,NULL, NULL, 0 }
} ;

typedef struct {
   int reg_num ;                 /* number of regions     */
   char **reg_label  ; /* region labels                   */
   short *reg_tto    ; /* index into afni.h TTO_list      */
   short *reg_ttval  ; /* what value in TTatlas+tlrc      */
} ttatlas_compendium ;

static ttatlas_compendium *ttatlas_list=NULL ;

/* 24 Sep 2001: stuff for Copy on input */

static MCW_bbox *copy_bbox ;
static MCW_arrowval *copy_mode_av , *copy_type_av , *copy_datum_av ;

void DRAW_copy_bbox_CB( Widget , XtPointer , XtPointer ) ;
THD_3dim_dataset * DRAW_copy_dset( THD_3dim_dataset *, int,int,int ) ;

/* Other data */

#define MODE_CURVE       0
#define MODE_CLOSED      1
#define MODE_POINTS      2
#define MODE_FLOOD_VAL   3
#define MODE_FLOOD_NZ    4
#define MODE_FLOOD_ZERO  5   /* 30 Jan 1999 */
#define MODE_ZERO_VAL    6   /* 31 Jan 1999 */
#define MODE_FLOOD_VZ    7   /* 30 Apr 2002 */
#define MODE_FILLED      8   /* 25 Sep 2001 */

#define MODE_2D_NN1      9   /* 07 Oct 2002 */
#define MODE_2D_NN2     10
#define MODE_2D_NN3     11
#define MODE_2D_NN4     12
#define MODE_2D_NN5     13

#define MODE_3D_NN1     14
#define MODE_3D_NN2     15
#define MODE_3D_NN3     16
#define MODE_3D_NN4     17
#define MODE_3D_NN5     18
#define MODE_3D_NN6     19
#define MODE_3D_5x5     20   /* 08 Oct 2002 */

#define MODE_2D_CIRC    21   /* 16 Oct 2002 */
#define MODE_3D_SPHR    22   /* 16 Oct 2002 */

#define FIRST_2D_MODE   MODE_2D_NN1
#define LAST_2D_MODE    MODE_2D_NN5

#define FIRST_3D_MODE   MODE_3D_NN1
#define LAST_3D_MODE    MODE_3D_5x5

#define FIRST_RAD_MODE  MODE_2D_CIRC
#define LAST_RAD_MODE   MODE_3D_SPHR

static char * mode_strings[] = {
  "Open Curve"       ,                 /* MODE_CURVE      */
  "Closed Curve"     ,                 /* MODE_CLOSED     */
  "Points"           ,                 /* MODE_POINTS     */
  "Flood->Value"     ,                 /* MODE_FLOOD_VAL  */
  "Flood->Nonzero"   ,                 /* MODE_FLOOD_NZ   */
  "Flood->Zero"      ,                 /* MODE_FLOOD_ZERO */
  "Zero->Value"      ,                 /* MODE_ZERO_VAL   */
  "Flood->Val/Zero"  ,                 /* MODE_FLOOD_VZ   */
  "Filled Curve"     ,                 /* MODE_FILLED     */

  " 2D Nbhd: 1st NN" ,                 /* 07 Oct 2002 */
  " 2D Nbhd: 2nd NN" ,
  " 2D Nbhd: 3rd NN" ,
  " 2D Nbhd: 4th NN" ,
  " 2D Nbhd: 5th NN" ,

  "*3D Nbhd: 1st NN" ,
  "*3D Nbhd: 2nd NN" ,
  "*3D Nbhd: 3rd NN" ,
  "*3D Nbhd: 4th NN" ,
  "*3D Nbhd: 5th NN" ,
  "*3D Nbhd: 6th NN" ,
  "*3D Nbhd: 5x5x5"  ,

  " 2D Circle"       ,                 /* 16 Oct 2002 */
  " 3D Sphere"
} ;

static int  mode_width[] = {    /* 08 Oct 2002: line width for button2 drawing */
  2,2 , 0,0,0,0,0,0 , 2 ,
  3,3,5,5,7 ,
  3,3,3,5,5,5,5 ,
  2,2
} ;

static int    mode_ints[] = {
  DRAWING_LINES  , DRAWING_FILL   , DRAWING_POINTS ,
  DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS , DRAWING_POINTS ,
  DRAWING_POINTS ,
  DRAWING_FILL   ,
  DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  ,
  DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  , DRAWING_LINES  ,
  DRAWING_LINES  , DRAWING_LINES  ,
  DRAWING_LINES  , DRAWING_LINES
} ;

#define NUM_modes (sizeof(mode_ints)/sizeof(int))

#define NFILLIN_DIR 3
static char *fillin_dir_strings[NFILLIN_DIR] = { "A-P" , "I-S" , "R-L" } ;
#define NFILLIN_GAP 9

static MCW_DC *dc ;                 /* display context */
static Three_D_View *im3d ;         /* AFNI controller */
static THD_3dim_dataset *dset ;     /* The dataset!    */
static MCW_idcode        dset_idc ; /* 31 Mar 1999     */

static Dtable *vl_dtable=NULL ;      /* 17 Oct 2003     */

static int   color_index = 1 ;               /* from color_av */
static int   mode_ival   = MODE_FILLED ;
static int   mode_index  = DRAWING_FILL ;    /* from mode_av  */
static int   value_int   = 1 ;               /* from value_av */
static float value_float = 1.0 ;             /* ditto         */

static int editor_open  = 0 ;
static int dset_changed = 0 ;
static int recv_open    = 0 ;
static int recv_key     = -1;

/****** 19 Nov 2003: new stuff for multiple undo/redo ******/

typedef struct {   /* structure holds one drawing operation */
  int npt , btyp ; /* number of data points, type of data */
  int *xyz ;       /* 1D index into dataset array */
  void *buf ;      /* data from dataset array */
} dobuf ;

  /* macro to create an empty buffer */

#define CREATE_DOBUF(db,np,ip)                                       \
 do{ db      = (dobuf *)calloc(1 ,sizeof(dobuf)) ;                   \
     db->xyz = (int *)  calloc(np,sizeof(int)) ;                     \
     db->buf = (void *) calloc(np,mri_datum_size(ip)) ;              \
     db->npt = np ; db->btyp = ip ;                                  \
 } while(0)

  /* macro to delete a buffer from the Macrocosmic All */

#define DESTROY_DOBUF(db)  do{ if( db != NULL ){                     \
                                if( db->xyz != NULL ) free(db->xyz); \
                                if( db->buf != NULL ) free(db->buf); \
                                free(db) ;                           \
                           }} while(0)

  /* amount of memory used by a buffer */

#define SIZEOF_DOBUF(db)                                             \
  ( db->npt * ( sizeof(int) + mri_datum_size(db->btyp) ) )

static int undo_num       = 0 ;     /* depth of undo stack */
static int redo_num       = 0 ;     /* depth of redo stack */
static dobuf **undo_stack = NULL ;  /* undo stack */
static dobuf **redo_stack = NULL ;  /* redo stack */
static int undo_how       = 0 ;     /* where to save undo info */

static void DRAW_undo_sizecheck(void) ;  /* check/limit undo stack size */

static void DRAW_undo_butlab( Widget w , int ) ;  /* label undo/redo button */

#define UNDO_button_labelize DRAW_undo_butlab(undo_pb,undo_num)
#define REDO_button_labelize DRAW_undo_butlab(redo_pb,redo_num)

  /* this macro erases the undo stack */

#define CLEAR_UNDOBUF                                                \
   do{ if( undo_num > 0 || undo_stack != NULL ){                     \
        int ii ;                                                     \
        for( ii=0 ; ii < undo_num ; ii++ )                           \
          DESTROY_DOBUF( undo_stack[ii] ) ;                          \
        if( undo_stack != NULL ) free( undo_stack ) ;                \
        undo_num = 0 ; undo_stack = NULL ;                           \
       }                                                             \
       UNDO_button_labelize ;                                        \
   } while(0)

  /* this macro erases the redo stack */

#define CLEAR_REDOBUF                                                \
   do{ if( redo_num > 0 || redo_stack != NULL ){                     \
        int ii ;                                                     \
        for( ii=0 ; ii < redo_num ; ii++ )                           \
          DESTROY_DOBUF( redo_stack[ii] ) ;                          \
        if( redo_stack != NULL )free( redo_stack ) ;                 \
        redo_num = 0 ; redo_stack = NULL ;                           \
       }                                                             \
       REDO_button_labelize ;                                        \
   } while(0)

  /* this macro erases both stacks */

#define CLEAR_UNREDOBUF                                              \
   do{ CLEAR_UNDOBUF ; CLEAR_REDOBUF ; undo_how = 0 ; } while(0)

/******/

static THD_dataxes dax_save ;    /* save this for later reference */

static int old_stroke_autoplot = 0 ;  /* 27 Oct 2003 */

static char **atlas_names; /* list of atlases to select among */



char * DRAW_main( PLUGIN_interface *plint )
{
   XmString xstr ;

   /*-- sanity checks --*/

   if( ! IM3D_OPEN(plint->im3d) )
     return " \n AFNI Controller\nnot opened?! \n " ;

   if( editor_open ){
     XtMapWidget(shell) ;
     XRaiseWindow( XtDisplay(shell) , XtWindow(shell) ) ;
     return NULL ;
   }

   im3d = plint->im3d ;  /* save for local use */

   /*-- create widgets, first time through --*/

   if( shell == NULL ){
     dc = im3d->dc ;        /* save this too */
     DRAW_make_widgets() ;
     PLUTO_set_topshell( plint , shell ) ;  /* 22 Sep 2000 */
     RWC_visibilize_widget( shell ) ;       /* 27 Sep 2000 */
   }

   /*-- set titlebar --*/

   { char ttl[PLUGIN_STRING_SIZE] ;
     sprintf(ttl , "AFNI Editor %s" , AFNI_controller_label(im3d) ) ;
     XtVaSetValues( shell , XmNtitle , ttl , NULL ) ;
   }

   /*-- set the info label --*/

   xstr = XmStringCreateLtoR( "[No dataset]" , XmFONTLIST_DEFAULT_TAG ) ;
   XtVaSetValues( info_lab ,
                    XmNlabelString , xstr ,
                    BGCOLOR_ARG(RCOL) ,
                  NULL ) ;
   XmStringFree(xstr) ;

   /*-- 22 Aug 2001: perhaps allow TT Atlas stuff --*/

   if( HAVE_TTATLAS ) {
      XtSetSensitive( ttatlas_rowcol , CAN_TALTO(im3d) ) ;
   }

   /*-- pop the widget up --*/

   XtMapWidget(shell) ;
   PLUTO_cursorize(shell) ;

   /*-- misc initialization --*/

   dset         = NULL ;   /* not editing anything   */
   dset_changed = 0 ;      /* not yet changed */
   editor_open  = 1 ;      /* editor is now open for business */
   recv_open    = 0 ;      /* receiver is not yet open */
   recv_key     = -1;      /* and has no identifier key */

   if( vl_dtable != NULL ){   /* 20 Oct 2003 */
     destroy_Dtable(vl_dtable) ; vl_dtable = NULL ;
   }

   SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ;
   SENSITIZE(choose_pb,1) ;
   Sensitize_copy_bbox(1);

   /* 19 Nov 2003: new undo/redo stuff */

   undo_num = redo_num = undo_how = 0 ;
   undo_stack = redo_stack = NULL ;
   UNDO_button_labelize ; REDO_button_labelize ;

   old_stroke_autoplot = AFNI_yesenv("AFNI_STROKE_AUTOPLOT") ;
   if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=NO") ;

   return NULL ;
}

/*------------------------------------------------------------------------
  Make the control popup for this thing
--------------------------------------------------------------------------*/

/*-- structures defining action buttons (at bottom of popup) --*/

#define NACT 7  /* number of action buttons */

static MCW_action_item DRAW_actor[NACT] = {
 {"Undo[0]",DRAW_undo_CB,NULL,
  "Undoes previous draw\naction, if possible","Undo last change",0} ,

 {"Redo[0]",DRAW_redo_CB,NULL,
  "Redoes previous undone\naction, if possible","Redo last undo",0} ,

 {"Help",DRAW_help_CB,NULL,
  "Displays more help" , "Displays more help",0} ,

 {"Quit",DRAW_quit_CB,NULL,
  "Discard edits since last Save\nand close Editor" ,
   "Discard edits and close",0} ,

 {"Save",DRAW_save_CB,NULL,
  "Save edits to disk\nand continue" , "Save to disk and continue",0} ,

 {"SaveAs",DRAW_saveas_CB,NULL,                        /* 24 Sep 2001 */
  "Save edits to disk\nin a new dataset\nand continue" ,
  "Save to disk in new dataset, continue",0} ,

 {"Done",DRAW_done_CB,NULL,
  "Save edits to disk\nand close Editor" , "Save and close",1}
} ;

ttatlas_compendium * New_ttatlas_compendium(char *atname)
{
   ttatlas_compendium *ttatlas_llist = NULL;
   int tto_count = 0;

   if (!atname) return(ttatlas_llist);
   if ((tto_count = atlas_n_points(atname)) <= 0) return(ttatlas_llist);
   ttatlas_llist = (ttatlas_compendium *) calloc(1,sizeof(ttatlas_compendium));
   ttatlas_llist->reg_num = 0;
   ttatlas_llist->reg_label = (char **)calloc(tto_count,sizeof(char *));
   ttatlas_llist->reg_tto = (short *)calloc(tto_count,sizeof(short));
   ttatlas_llist->reg_ttval = (short *)calloc(tto_count,sizeof(short));

   return(ttatlas_llist);
}

void DRAW_make_widgets(void)
{
   XmString xstr ;
   ATLAS_POINT *tto_list=NULL;
   char title_str[256];

   /*** top level shell for window manager ***/

   shell =
      XtVaAppCreateShell(
           "AFNI" , "AFNI" , topLevelShellWidgetClass , dc->display ,

           XmNtitle             , "AFNI Editor" , /* top of window */
           XmNiconName          , "Editor"      , /* label on icon */
           XmNdeleteResponse    , XmDO_NOTHING  , /* deletion handled below */
           XmNallowShellResize  , True ,          /* let code resize shell? */
           XmNmappedWhenManaged , False ,         /* must map it manually */
           XmNinitialResourcesPersistent , False ,
      NULL ) ;

   DC_yokify( shell , dc ) ; /* 14 Sep 1998 */

   if( afni48_good )             /* set icon pixmap */
      XtVaSetValues( shell ,
                        XmNiconPixmap , afni48_pixmap ,
                     NULL ) ;

   if( MCW_isitmwm(shell) )      /* remove some MWM functions */
      XtVaSetValues( shell ,
                       XmNmwmFunctions ,
                       MWM_FUNC_MOVE | MWM_FUNC_CLOSE | MWM_FUNC_MINIMIZE ,
                     NULL ) ;

   XmAddWMProtocolCallback(      /* make "Close" window menu work */
           shell ,
           XmInternAtom( dc->display , "WM_DELETE_WINDOW" , False ) ,
           DRAW_quit_CB , (XtPointer) plint ) ;

   /*** rowcolumn widget to hold all user interface stuff ***/

   rowcol = XtVaCreateWidget(
             "AFNI" , xmRowColumnWidgetClass , shell ,
                XmNpacking     , XmPACK_TIGHT ,
                XmNorientation , XmVERTICAL ,
                XmNtraversalOn , True  ,
                XmNinitialResourcesPersistent , False ,
             NULL ) ;

   /*** label at top to let user know who we are ***/

   xstr = XmStringCreateLtoR( "[No dataset]" ,
                              XmFONTLIST_DEFAULT_TAG ) ;
   info_lab = XtVaCreateManagedWidget(
                 "AFNI" , xmLabelWidgetClass , rowcol ,
                    XmNlabelString , xstr ,
                    BGCOLOR_ARG(RCOL) ,
                    XmNinitialResourcesPersistent , False ,
                 NULL ) ;
   XmStringFree(xstr) ;
   MCW_register_help( info_lab , "Shows dataset being edited" ) ;
   MCW_register_hint( info_lab , "Shows dataset being edited" ) ;

   /*** separator for visual neatness ***/

   (void) XtVaCreateManagedWidget(
             "AFNI" , xmSeparatorWidgetClass , rowcol ,
                XmNseparatorType , XmDOUBLE_LINE ,
                XmNinitialResourcesPersistent , False ,
             NULL ) ;

   /*-- 24 Sep 2001: Copy mode stuff [moved up 06 Oct 2002] --*/

   { Widget rc ;
     static char *cbox_label[1]   = { "Copy Dataset" } ;
     static char *cmode_label[2]  = { "Data"  , "Zero" } ;
     static char *ctype_label[3]  = { "Show as Olay" , "Show as Ulay" } ;
     static char *cdatum_label[4] = { "As Is" , "Byte" , "Short" , "Float" } ;

     /*** rowcol to hold Copy widgets ***/

     rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol ,
                              XmNpacking      , XmPACK_TIGHT ,
                              XmNorientation  , XmHORIZONTAL ,
                              XmNmarginHeight , 0 ,
                              XmNmarginWidth  , 0 ,
                              XmNspacing      , 0 ,
                              XmNinitialResourcesPersistent , False ,
                              XmNtraversalOn , True  ,
                           NULL ) ;

     /*** button box to turn copy mode on or off ***/

     copy_bbox = new_MCW_bbox( rc, 1,cbox_label,
                               MCW_BB_check,MCW_BB_noframe, DRAW_copy_bbox_CB,NULL ) ;

     MCW_reghint_children( copy_bbox->wrowcol ,
                           "Make copy of dataset on input" ) ;
     MCW_reghelp_children( copy_bbox->wrowcol ,
                           "Make copy of dataset on input?" ) ;

     /*** arrowvals to let user choose Copy method ***/
     copy_mode_av = new_MCW_optmenu( rc , NULL ,
                                     0 , 1 , 1 , 0 , NULL,NULL ,
                         (str_func *)MCW_av_substring_CB , cmode_label ) ;

     MCW_reghint_children( copy_mode_av->wrowcol ,
                           "How to copy values from dataset" ) ;
     MCW_reghelp_children( copy_mode_av->wrowcol ,
                           "How to copy values from dataset:\n"
                           "Data => use input dataset values\n"
                           "Zero => fill dataset with zeros" ) ;

     copy_type_av = new_MCW_optmenu( rc , NULL ,
                                     0 , 1 , 0 , 0 , NULL,NULL ,
                         (str_func *)MCW_av_substring_CB , ctype_label ) ;

     MCW_reghint_children( copy_type_av->wrowcol ,
                           "Copy is shown as overlay or underlay" ) ;
     MCW_reghelp_children( copy_type_av->wrowcol ,
                           "Copy will be shown as the overlay\n"
                           "or will be shown as the underlay" ) ;

     copy_datum_av= new_MCW_optmenu( rc , NULL ,
                                     0 , 3 , 0 , 0 , NULL,NULL ,
                         (str_func *)MCW_av_substring_CB , cdatum_label ) ;

     MCW_reghint_children( copy_datum_av->wrowcol ,
                           "Data storage type for copy" ) ;
     MCW_reghelp_children( copy_datum_av->wrowcol ,
                           "Data storage type for zero-filled copy:\n"
                           "As Is => use data type in input dataset\n"
                           "Byte  => store new dataset as bytes\n"
                           "Short => store new dataset as shorts\n"
                           "Float => store new dataset as floats") ;

     MCW_set_bbox(copy_bbox, 1); /* turn copy on by default - drg 4/3/2006 */

     AV_SENSITIZE( copy_mode_av , True ) ;
     AV_SENSITIZE( copy_type_av , True ) ;
     AV_SENSITIZE( copy_datum_av, True ) ;

     XtManageChild(rc) ;

   } /* end of Copy mode stuff */

   /*** button to let user choose dataset to edit ***/

   xstr = XmStringCreateLtoR( "  Choose dataset for copying" , XmFONTLIST_DEFAULT_TAG ) ;
   choose_pb = XtVaCreateManagedWidget(
                  "AFNI" , xmPushButtonWidgetClass , rowcol ,
                     XmNlabelString , xstr ,
                     XmNtraversalOn , True  ,
                     BGCOLOR_ARG(GCOL) ,
                     XmNinitialResourcesPersistent , False ,
                  NULL ) ;
   XmStringFree(xstr) ;
   XtAddCallback( choose_pb, XmNactivateCallback, DRAW_choose_CB, NULL ) ;
   MCW_register_help( choose_pb ,
                      "Use this to popup a\n"
                      "'chooser' that lets\n"
                      "you select which\n"
                      "dataset to edit."
                    ) ;
   MCW_register_hint( choose_pb , "Popup a dataset chooser" ) ;

   /*** separator for visual neatness ***/

   (void) XtVaCreateManagedWidget(
               "AFNI" , xmSeparatorWidgetClass , rowcol ,
                  XmNseparatorType , XmDOUBLE_LINE ,
                  XmNinitialResourcesPersistent , False ,
               NULL ) ;

   /***  arrowval to choose value that is drawn into dataset voxels  ***/

   { Widget rc ;

     rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol ,
                       XmNpacking      , XmPACK_TIGHT ,
                       XmNorientation  , XmHORIZONTAL ,
                       XmNmarginHeight , 0 ,
                       XmNmarginWidth  , 0 ,
                       XmNspacing      , 0 ,
                       XmNinitialResourcesPersistent , False ,
                       XmNtraversalOn , True  ,
                    NULL ) ;

     value_av = new_MCW_arrowval( rc , "Value " ,
                                  MCW_AV_downup , -32767,32767,value_int ,
                                  MCW_AV_editext , 0 ,
                      (gen_func *)DRAW_value_CB , NULL , NULL,NULL ) ;

     MCW_reghelp_children( value_av->wrowcol ,
                           "Use this to set the value that\n"
                           "will be drawn into the dataset\n"
                           "using mouse button 2."
                         ) ;
     MCW_reghint_children( value_av->wrowcol , "Goes into dataset voxels" ) ;

     /*-- 15 Oct 2003: Label for the value --*/

     xstr = XmStringCreateLtoR( " Label" , XmFONTLIST_DEFAULT_TAG ) ;
     label_label = XtVaCreateManagedWidget(
                     "dialog" , xmLabelWidgetClass , rc ,
                       XmNlabelString   , xstr  ,
                       XmNrecomputeSize , False ,
                       XmNmarginWidth   , 0     ,
                       XmNinitialResourcesPersistent , False ,
                     NULL ) ;
     XmStringFree(xstr) ;

     label_textf = XtVaCreateManagedWidget(
                       "dialog" , xmTextFieldWidgetClass , rc ,
                           XmNcolumns      , 19 ,
                           XmNeditable     , True ,
                           XmNmaxLength    , 128 ,
                           XmNresizeWidth  , False ,
                           XmNmarginHeight , 1 ,
                           XmNmarginWidth  , 1 ,
                           XmNcursorPositionVisible , True ,
                           XmNblinkRate , 0 ,
                           XmNautoShowCursorPosition , True ,
                           XmNtraversalOn , True  ,
                           XmNinitialResourcesPersistent , False ,
                        NULL ) ;
     XtSetSensitive( label_label , (RwcBoolean)(value_int != 0) ) ;
     XtSetSensitive( label_textf , (RwcBoolean)(value_int != 0) ) ;

     XtAddCallback( label_textf, XmNactivateCallback    ,
                                 DRAW_label_CB , NULL ) ; /* return key */

     XtAddCallback( label_textf, XmNlosingFocusCallback ,
                                 DRAW_label_CB , NULL ) ; /* tab key */

     XtInsertEventHandler( label_textf ,      /* notify when */
                           LeaveWindowMask ,  /* pointer leaves */
                           FALSE ,            /* this window */
                           DRAW_label_EV ,
                           (XtPointer) NULL ,
                           XtListTail ) ;     /* last in queue */

     XtInsertEventHandler( label_label ,      /* button press in label */
                           ButtonPressMask ,
                           FALSE ,
                           DRAW_label_EV ,
                           (XtPointer) NULL ,
                           XtListTail ) ;
     POPUP_cursorize( label_label ) ;

     XtManageChild(rc) ;
   }

   /*** option menu to choose drawing color ***/

   color_av = new_MCW_colormenu( rowcol , "Color " , dc ,
                                 1 , dc->ovc->ncol_ov - 1 , color_index ,
                     (gen_func *)DRAW_color_CB , NULL ) ;

   MCW_reghelp_children( color_av->wrowcol ,
                         "Use this to set the color that is\n"
                         "shown during mouse button 2 drawing.\n"
                         "N.B.: After drawing is completed,\n"
                         "  the dataset will be displayed\n"
                         "  with the chosen value replacing\n"
                         "  the drawing color.  This color\n"
                         "  is used ONLY while button 2 is\n"
                         "  actually pressed down."
                       ) ;
   MCW_reghint_children( color_av->wrowcol , "Used when button 2 is drawing" ) ;

   /*** arrowval to choose drawing mode ***/
   /*-- 16 Oct 2002: put in a horiz rowcol, and add rad_av button --*/

   { Widget rc ;

     rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol ,
                       XmNpacking      , XmPACK_TIGHT ,
                       XmNorientation  , XmHORIZONTAL ,
                       XmNmarginHeight , 0 ,
                       XmNmarginWidth  , 0 ,
                       XmNspacing      , 0 ,
                       XmNinitialResourcesPersistent , False ,
                       XmNtraversalOn , True  ,
                    NULL ) ;

     mode_av = new_MCW_optmenu( rc , "Mode  " ,
                              0 , NUM_modes-1 , mode_ival,0 ,
                  (gen_func *)DRAW_mode_CB , NULL ,
                  (str_func *)MCW_av_substring_CB , mode_strings ) ;

     AVOPT_columnize( mode_av , 2 ) ;

     MCW_reghelp_children( mode_av->wrowcol ,
                           "Use this to set the way in which\n"
                           "drawing pixels on the screen is\n"
                           "used to select dataset voxels:\n"
                           "Open Curve      = voxels picked along lines drawn;\n"
                           "Closed Curve    = voxels forming a closed curve\n"
                           "Points          = only voxels at X11 notify pixels;\n"
                           "Flood->Value    = flood fill from the chosen point\n"
                           "                   out to points = Value\n"
                           "Flood->Nonzero  = flood fill from chosen point out\n"
                           "                   to any nonzero point\n"
                           "Flood->Zero     = flood fill from chosen point out\n"
                           "                   to any zero point\n"
                           "Zero->Value     = flood fill with zeros until the\n"
                           "                   Value is hit\n"
                           "Flood->Val/Zero = flood fill from the chosen point\n"
                           "                   until the Value OR zero is hit\n"
                           "Filled Curve    = fill inside of closed curve with\n"
                           "                   Value\n"
                           "\n"
                           "2D Nbhd         = like Open Curve, but fills in around\n"
                           "                   the in-plane neighborhood of each\n"
                           "                   drawn point 'x' with the patterns:\n"
                           "                        5 4 3 4 5\n"
                           "                        4 2 1 2 4\n"
                           "                        3 1 x 1 3\n"
                           "                        4 2 1 2 4\n"
                           "                        5 4 3 4 5\n"
                           "                   where the number indicates the\n"
                           "                   Nearest Neighbor order of the\n"
                           "                   points nearby 'x'.\n"
                           "3D Nbhd         = Similar, but in 3D (out-of-plane)\n"
                           "\n"
                           "2D Circle       = Draw a circle of given Radius\n"
                           "3D Sphere       = Draw a sphere of given Radius\n"
                         ) ;
     MCW_reghint_children( mode_av->wrowcol , "How voxels are chosen") ;

     /** 16 Oct 2002: radius chooser **/

     rad_av = new_MCW_arrowval( rc             ,    /* parent */
                                "R"            ,    /* label */
                                MCW_AV_downup  ,    /* arrow directions */
                                1              ,    /* min value (0.01 mm from decim) */
                                9999            ,   /* max value (99.99 mm) */
                                400             ,   /* init value */
                                MCW_AV_editext ,    /* input/output text display */
                                2              ,    /* decimal shift */
                                NULL           ,    /* routine to call when button */
                                NULL           ,    /* is pressed, and its data */
                                NULL,NULL           /* no special display */
                              ) ;
     XtVaSetValues( rad_av->wtext   , XmNcolumns , 5 , NULL ) ;
     MCW_reghint_children( rad_av->wrowcol , "Radius of Circles and Spheres" ) ;
     MCW_reghelp_children( rad_av->wrowcol ,
                            " \n"
                            "Sets the radius (in mm) of the 2D Circle\n"
                            "or 3D Sphere drawing modes.  Voxels whose\n"
                            "center-to-center distance is <= this value\n"
                            "will be filled in.\n"
                          ) ;
     ENABLE_rad_av ;   /* turn it on or off */

     XtManageChild(rc) ;
   }

   /*** 19 Mar 2001: stuff for linear fillin ***/

   { Widget rc ;

     /*** separator for visual neatness ***/

     (void) XtVaCreateManagedWidget(
                "AFNI" , xmSeparatorWidgetClass , rowcol ,
                   XmNseparatorType , XmDOUBLE_LINE ,
                   XmNinitialResourcesPersistent , False ,
                NULL ) ;

     rc = XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol ,
                       XmNpacking      , XmPACK_TIGHT ,
                       XmNorientation  , XmHORIZONTAL ,
                       XmNmarginHeight , 0 ,
                       XmNmarginWidth  , 0 ,
                       XmNspacing      , 0 ,
                       XmNinitialResourcesPersistent , False ,
                       XmNtraversalOn , True  ,
                    NULL ) ;

     fillin_dir_av = new_MCW_optmenu( rc , "Linear Fillin " ,
                                      0 , NFILLIN_DIR-1 , 0 , 0 ,
                                      NULL , NULL ,
                          (str_func *)MCW_av_substring_CB , fillin_dir_strings ) ;

#if 0    /* Need big gaps. ZSS Aug. 2011 */
     fillin_gap_av = new_MCW_optmenu( rc , " Gap" ,
                                      1 , NFILLIN_GAP , 4 , 0 ,
                                      NULL,NULL,NULL,NULL ) ;
#else
     fillin_gap_av = new_MCW_arrowval( rc             ,    /* parent */
                       "Gap"            ,    /* label */
                       MCW_AV_downup  ,    /* arrow directions */
                       1              ,    /* min value (0.1 mm from decim) */
                       999            ,    /* max value (99.9 mm) */
                       4             ,    /* init value */
                       MCW_AV_editext ,    /* input/output text display */
                       0              ,    /* decimal shift */
                       NULL           ,    /* routine to call when button */
                       NULL           ,    /* is pressed, and its data */
                       NULL,NULL           /* no special display */
                              ) ;
     XtVaSetValues( fillin_gap_av->wtext   , XmNcolumns , 5 , NULL ) ;
     MCW_reghint_children( fillin_gap_av->wrowcol , "Fill Gap" ) ;
     MCW_reghelp_children( fillin_gap_av->wrowcol ,
                            " \n"
                            "Sets the maximum voxel gap that gets filled in.\n"
                          ) ;
     XtManageChild( fillin_gap_av->wrowcol ) ;   
#endif

     xstr = XmStringCreateLtoR( "*Do the Fill*" , XmFONTLIST_DEFAULT_TAG ) ;
     fillin_doit_pb = XtVaCreateManagedWidget( "AFNI" , xmPushButtonWidgetClass , rc ,
                                                XmNlabelString , xstr ,
                                                XmNtraversalOn , True  ,
                                                XmNinitialResourcesPersistent , False ,
                                               NULL ) ;
     XtAddCallback( fillin_doit_pb , XmNactivateCallback, DRAW_fillin_CB, NULL ) ;
     XmStringFree(xstr) ;
     XtManageChild(rc) ;

   } /* end of fillin */

   /*** 22 Aug 2001: stuff for TT Atlas Regions ***/
   if( Atlas_With_Trimming(Current_Atlas_Default_Name(),1, NULL) > 0 ){
      Widget rc ;
      int ii , nr ;
      XmString xstr ;

      /*** separator for visual neatness ***/

      (void) XtVaCreateManagedWidget(
                 "AFNI" , xmSeparatorWidgetClass , rowcol ,
                    XmNseparatorType , XmDOUBLE_LINE ,
                    XmNinitialResourcesPersistent , False ,
                 NULL ) ;

       /*** rowcol to hold all widgets ***/

       ttatlas_rowcol = rc =
           XtVaCreateWidget( "AFNI" , xmRowColumnWidgetClass , rowcol ,
                      XmNpacking      , XmPACK_TIGHT ,
                      XmNorientation  , XmVERTICAL ,
                      XmNmarginHeight , 0 ,
                      XmNmarginWidth  , 0 ,
                      XmNspacing      , 0 ,
                      XmNinitialResourcesPersistent , False ,
                      XmNtraversalOn , True  ,
                   NULL ) ;

       /*** label at top ***/
       sprintf(title_str, "Select Atlas and Region (Current Atlas: %s)",
          Current_Atlas_Default_Name());

       xstr = XmStringCreateLtoR( title_str ,
                                  XmFONTLIST_DEFAULT_TAG ) ;
       atlas_panel = XtVaCreateManagedWidget(
                    "dialog" , xmLabelWidgetClass , rc ,
                       XmNlabelString   , xstr  ,
                       XmNrecomputeSize , False ,
                       XmNmarginWidth   , 0     ,
                       XmNinitialResourcesPersistent , False ,
                    NULL ) ;
       XmStringFree(xstr) ;

      atlas_chooser_av = Atlas_chooser(rc);

      /*** make list of atlas regions to include ***/
      plugdraw_atlasname = Current_Atlas_Default_Name(); /* use default atlas name first */

      if (!(ttatlas_list = New_ttatlas_compendium(plugdraw_atlasname))) {
         ERROR_message("Failed compending. This should not happen");
      }
      
      tto_list = atlas_points(plugdraw_atlasname);
      nr = 0 ;
      for( ii=0 ; ii < atlas_n_points(plugdraw_atlasname) ; ii++ ){
         ttatlas_list->reg_label [nr] = strdup(tto_list[ii].name) ;
         ttatlas_list->reg_tto   [nr] = ii ;
         ttatlas_list->reg_ttval [nr] = tto_list[ii].tdval ;

         nr++ ;  /* count how many regions actually have some non-zero value */
      }
      ttatlas_list->reg_num = nr ;

      /*** Region chooser ***/

      ttatlas_region_av = new_MCW_optmenu( rc , " " ,
                                           0 , nr-1 , 0 , 0 ,
                                           NULL,NULL ,
                               (str_func *)MCW_av_substring_CB ,
                                           ttatlas_list->reg_label ) ;
      AVOPT_columnize( ttatlas_region_av , 3 ) ;

      /*** Hemisphere chooser */

      ttatlas_hemisphere_av = new_MCW_optmenu( rc , " Hemisphere(s)" ,
                                               0 , NHEMI-1 , NHEMI-1 , 0 ,
                                               NULL,NULL ,
                                   (str_func *)MCW_av_substring_CB,
                                               HEMI_strings );

      /*** row of pushbuttons ***/

      ttatlas_actar = MCW_action_area( rc , TTATLAS_act , NUM_TTATLAS_ACT ) ;


      XtManageChild( rc ) ;

   } /* end of TT Atlas */

   /*** separator for visual neatness ***/

   (void) XtVaCreateManagedWidget(
             "AFNI" , xmSeparatorWidgetClass , rowcol ,
                XmNseparatorType , XmDOUBLE_LINE ,
                XmNinitialResourcesPersistent , False ,
             NULL ) ;

   /*** a set of action buttons below the line ***/

   (void) MCW_action_area( rowcol , DRAW_actor , NACT ) ;

   undo_pb   = (Widget) DRAW_actor[0].data ;
   redo_pb   = (Widget) DRAW_actor[1].data ;  /* 19 Nov 2003 */
   help_pb   = (Widget) DRAW_actor[2].data ;
   quit_pb   = (Widget) DRAW_actor[3].data ;
   save_pb   = (Widget) DRAW_actor[4].data ;
   saveas_pb = (Widget) DRAW_actor[5].data ;  /* 24 Sep 2001 */
   done_pb   = (Widget) DRAW_actor[6].data ;

   /*** that's all ***/

   XtManageChild(rowcol) ;
   XtRealizeWidget(shell) ; NI_sleep(1) ; /* will not be mapped */
   return ;
}

/*-------------------------------------------------------------------
  Callback for copy_bbox -- 24 Sep 2001 - RWCox
---------------------------------------------------------------------*/

void DRAW_copy_bbox_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   int sens = (MCW_val_bbox(copy_bbox) != 0);
   AV_SENSITIZE( copy_mode_av , sens ) ;
   AV_SENSITIZE( copy_type_av , sens ) ;
   AV_SENSITIZE( copy_datum_av, sens ) ;
   if(sens)
     MCW_set_widget_label( choose_pb , "  Choose dataset for copying" );
   else
     MCW_set_widget_label( choose_pb , "  Choose dataset to change directly" );

   return ;
}

/*
  turn on or off copy dataset check box and related buttons
*/
static void
Sensitize_copy_bbox(int  sens)
{
   XtPointer clienttemp=NULL;

   SENSITIZE(copy_bbox->wbut[0], sens);
   SENSITIZE(copy_bbox->wbut[1], sens);
   /* dummy pointers passed */
   if(sens)
      DRAW_copy_bbox_CB(copy_bbox->wbut[0], clienttemp, clienttemp);
  else
   {
      AV_SENSITIZE( copy_mode_av , 0 ) ;
      AV_SENSITIZE( copy_type_av , 0 ) ;
      AV_SENSITIZE( copy_datum_av, 0 ) ;
   }
}


/*-------------------------------------------------------------------
  Callback for done button
---------------------------------------------------------------------*/

void DRAW_done_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   if( dset != NULL ){
     if( recv_open )  /* 31 Mar 1999: changed shutdown to EVERYTHING */
       AFNI_receive_control( im3d, recv_key,EVERYTHING_SHUTDOWN, NULL ) ;
     if( dset_changed ){
       MCW_invert_widget( done_pb ) ;
       DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE",  dset ) ;
       DSET_overwrite(dset) ;
       MCW_invert_widget( done_pb ) ;
     }
     DSET_unlock(dset) ; DSET_anyize(dset) ;
     dset = NULL ; dset_changed = 0 ;
   }

   CLEAR_UNREDOBUF ;  /* 19 Nov 2003 */

   XtUnmapWidget( shell ); editor_open = 0; recv_open = 0; recv_key = -1;
   if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=YES") ;
   return ;
}

/*-------------------------------------------------------------------
  Callback for undo button [heavily modified 19 Nov 2003]
---------------------------------------------------------------------*/

void DRAW_undo_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   dobuf *sb ;  /* saved drawing buffer that will be redrawn */

   if( undo_num <= 0 || undo_stack == NULL ){ XBell(dc->display,100); return; }

   undo_how = 1 ;  /* the next drawing save will be onto redo stack */

   sb = undo_stack[undo_num-1] ;  /* saved buffer */

   DRAW_into_dataset( sb->npt , sb->xyz,NULL,NULL , sb->buf ) ;

   DESTROY_DOBUF(sb) ;  /* purge and pop top of undo stack */
   undo_num-- ;
   UNDO_button_labelize ;

   AFNI_process_drawnotice( im3d ) ;  /* 30 Mar 1999 */

   undo_how = 0 ; /* further draws go onto undo stack */
   return ;
}

/*-------------------------------------------------------------------
  Callback for redo button [from DRAW_undo_CB(), 19 Nov 2003]
---------------------------------------------------------------------*/

void DRAW_redo_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   dobuf *sb ;

   if( redo_num <= 0 || redo_stack == NULL ){ XBell(dc->display,100); return; }

   undo_how = 2 ;  /* drawing save will be onto undo stack */

   sb = redo_stack[redo_num-1] ;  /* saved buffer */

   DRAW_into_dataset( sb->npt , sb->xyz,NULL,NULL , sb->buf ) ;

   DESTROY_DOBUF(sb) ;  /* purge and pop top of redo stack */
   redo_num-- ;
   REDO_button_labelize ;

   AFNI_process_drawnotice( im3d ) ;  /* 30 Mar 1999 */

   undo_how = 0 ; /* further draws go onto undo stack */
   return ;
}

/*-------------------------------------------------------------------
  Callback for quit button
---------------------------------------------------------------------*/

void DRAW_quit_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   if( dset != NULL ){
     if( recv_open ) AFNI_receive_control( im3d,recv_key,DRAWING_SHUTDOWN,NULL );
     DSET_unlock(dset) ;
     DSET_unload(dset) ; DSET_anyize(dset) ;
     if( dset_changed ){
       if( recv_open ){
         AFNI_process_drawnotice( im3d ) ;  /* 30 Mar 1999 */
         AFNI_receive_control( im3d, recv_key,EVERYTHING_SHUTDOWN, NULL ) ; /* 25 Sep 2001 */
       }
       MCW_invert_widget(quit_pb) ;
       THD_load_statistics( dset ) ;
       PLUTO_dset_redisplay( dset ) ;
       MCW_invert_widget(quit_pb) ;
     }
     dset = NULL ; dset_changed = 0 ;
   }

   CLEAR_UNREDOBUF ;  /* 19 Nov 2003 */

   XtUnmapWidget( shell ); editor_open = 0; recv_open = 0; recv_key = -1;
   if( old_stroke_autoplot ) putenv("AFNI_STROKE_AUTOPLOT=YES") ;
   return ;
}

/*-------------------------------------------------------------------
  Callback for Save button
---------------------------------------------------------------------*/

void DRAW_save_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   if( dset == NULL ){ XBell(dc->display,100) ; return ; }

   MCW_invert_widget(save_pb) ;

   DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE",  dset ) ;
   DSET_overwrite(dset); dset_changed = 0; SENSITIZE(choose_pb,1);
   Sensitize_copy_bbox(1);   /* turn copy dataset widgets back on  - drg 4/4/2006 */
   MCW_invert_widget(save_pb) ;
   SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ;
   return ;
}

/*-------------------------------------------------------------------
  24 Sep 2001: Callback for Save As button
---------------------------------------------------------------------*/

void DRAW_saveas_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   if( dset == NULL ){ XBell(dc->display,100) ; return ; }

   MCW_choose_string( saveas_pb , "Enter new prefix" ,
                      NULL , (gen_func *)DRAW_saveas_finalize_CB , NULL ) ;
}

/*--------------------------------------------------------------------*/

void DRAW_saveas_finalize_CB( Widget w, XtPointer fd, MCW_choose_cbs * cbs )
{
   THD_3dim_dataset *cset ;
   char str[256] ;
   XmString xstr ;

   /*-- check for craziness --*/

   if( !editor_open || dset == NULL ){
     POPDOWN_strlist_chooser; XBell(dc->display,100); return;
   }

   if( !PLUTO_prefix_ok(cbs->cval) ){ XBell(dc->display,100); return; }

   /*-- make a copy of this dataset --*/

   MCW_invert_widget(saveas_pb) ;

   cset = DRAW_copy_dset( dset , 0,0,-1 ) ;
   if( cset == NULL ){                      /* should not happen */
     (void) MCW_popup_message( saveas_pb ,
                                 " \n"
                                 "*** Cannot make copy of edited  ***\n"
                                 "*** dataset for unknown reasons ***\n " ,
                               MCW_USER_KILL | MCW_TIMER_KILL ) ;

     MCW_invert_widget(saveas_pb); XBell(dc->display,100); return;
   }
   EDIT_dset_items( cset , ADN_prefix,cbs->cval , ADN_none ) ;

   if( THD_is_file(DSET_BRIKNAME(cset)) ){  /* stupid user */
     (void) MCW_popup_message( saveas_pb ,
                                 " \n"
                                 "*** Cannot SaveAs this edited   ***\n"
                                 "*** dataset since a dataset     ***\n"
                                 "*** with that prefix is on disk ***\n " ,
                               MCW_USER_KILL | MCW_TIMER_KILL ) ;
     DSET_delete(cset) ;
     MCW_invert_widget(saveas_pb); XBell(dc->display,100); return;
   }

   /*-- tell AFNI about the new dataset --*/

   PLUTO_add_dset( plint , cset , DSET_ACTION_MAKE_CURRENT ) ;

   /*-- remove current dataset from further consideration --*/

   DSET_unlock(dset) ; DSET_unload(dset) ; DSET_anyize(dset) ;

   /*-- switch current dataset to be the copy just made --*/

   dset = cset ; dset_idc = dset->idcode ;
   DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE",  dset ) ;
   DSET_overwrite(dset); DSET_mallocize(dset); DSET_load(dset); DSET_lock(dset);

   /*-- re-write the informational label --*/

   if( DSET_BRICK_FACTOR(dset,0) == 0.0 ){
     strcpy(str,DSET_BRIKNAME(dset)) ;
   } else {
     char abuf[16] ;
     AV_fval_to_char( DSET_BRICK_FACTOR(dset,0) , abuf ) ;
     sprintf(str,"%s\nbrick factor: %s", DSET_BRIKNAME(dset) , abuf ) ;
   }
   xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
   XtVaSetValues( info_lab ,
                    XmNlabelString , xstr ,
                    BGCOLOR_ARG(GCOL) ,
                  NULL ) ;
   XmStringFree(xstr) ;

   /*-- finish up --*/

   dset_changed = 0 ; SENSITIZE(choose_pb,1) ;
   MCW_invert_widget(saveas_pb) ;
   SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ;
   Sensitize_copy_bbox(1);

   return ;
}

/*-------------------------------------------------------------------
  Callback for Help button
---------------------------------------------------------------------*/

void DRAW_help_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   (void ) new_MCW_textwin( help_pb ,

  "This plugin can be used to edit interactively the voxel contents\n"
  "of a dataset.  Since such changes are irreversible, it is best that\n"
  "you either edit a copy of a dataset, or create a dataset of all zeros.\n"
  "These tasks can be done with the 'Dataset Copy' plugin.\n"
  "\n"
  "***************** Read the WARNINGS section below. *****************\n"
  "\n"
  "Step 1) Choose a dataset to edit.\n"
  "        * Only datasets that have data BRIKs stored at the current\n"
  "            resolution as the underlay dataset can be edited.\n"
  "        * It is probably best that the dataset being edited be\n"
  "            displayed.  Otherwise it will be impossible to gauge\n"
  "            the effect of the editing operations.\n"
  "        * At this time, only datasets that have a single sub-brick\n"
  "            can be edited.\n"
  "        * Datasets may be copied when chosen by selecting the\n"
  "            'Copy' toggle (on by default). The choosers to the right\n"
  "            of this let you control how the input dataset is copied:\n"
  "            (a)  Data => data value are copied\n"
  "                 Zero => copy is full of zeros\n"
  "            (b)  Show as Olay => put copy in Overlay (the usual case)\n"
  "                 Show as Ulay => put copy in Underlay\n"
  "            (c) As Is => copy is stored as input dataset is stored\n"
  "                 Byte => copy is stored as bytes\n"
  "                Short => copy is stored as shorts\n"
  "                Float => copy is stored as floats\n"
  "                NOTE: you can only change the data storage of the\n"
  "                      copy from the input if you are using 'Zero';\n"
  "                      with 'Data', the data storage of the copy will\n"
  "                      always be made 'As Is'.\n"
  "            The copy is made when you finalize the dataset choice.\n"
  "            The copied dataset has the same prefix as the input\n"
  "            dataset, with the string 'COPY_' prepended.  You can\n"
  "            alter this name later using the 'Dataset Rename' plugin,\n"
  "            AFTER the dataset has been Save-d, or with the '3drename'\n"
  "            program after you exit AFNI, or with the 'SaveAs' button\n"
  "            in this plugin.\n"
  "        * Datasets may also be copied with the 'Dataset Copy' plugin.\n"
  "            Making an empty dataset with a given geometry can be\n"
  "            done using the 'Zero [One]' option in that plugin.\n"

  "\n"
  "Step 2) Choose a drawing value.\n"
  "        * This is the number that will be placed into the dataset\n"
  "            voxels that are chosen.\n"
  "        * Integer valued datasets can only receive integer values;\n"
  "            float datasets can take floating point values.\n"
  "        * You can attach a label string to each drawing value.\n"
  "            The value-label table will be saved with in the dataset\n"
  "            .HEAD file when you use 'Save', 'SaveAs' or 'Done'.\n"
  "        * You can also setup a standard value-label table in a file,\n"
  "            whose name is specified by setting environment variable\n"
  "            AFNI_VALUE_LABEL_DTABLE -- cf. file README.environment.\n"
  "        * Button-3-clicking in the 'Label' next to the text-entry field\n"
  "            will bring up a menu of all current value-label pairs.\n"
  "            You can choose from them to set a new drawing value.\n"
  "        * Button-1-clicking in the 'Label' will ask for a filename\n"
  "            to read that loads the value-label pairs.  The format\n"
  "            of this file is described in README.environment.\n"
  "            Reading in a file like this will erase any existing\n"
  "            value-label associations in the plugin.\n"
  "\n"
  "Step 3) Choose a drawing color.\n"
  "        * This is the color that will be shown in the image windows\n"
  "            while drawing is going on (that is, while the mouse button\n"
  "            is pressed).\n"
  "        * This color is NOT the color that will be displayed once\n"
  "            you stop drawing (release the mouse button).\n"
  "        * See 5) for more details about the drawing process.\n"
  "\n"
  "Step 4) Choose a drawing mode.\n"
  "        * 'Open Curve' means to select dataset voxels that lie under\n"
  "            the pixel lines drawn on the image as you move the mouse\n"
  "            with button 2 held down.\n"
  "        * 'Closed Curve' means to close the curve drawn from the last\n"
  "            point drawn (where button 2 is released) back to the\n"
  "            first point drawn (where button 2 was pressed).\n"
  "        * 'Points' means to take only the voxels corresponding\n"
  "            to the screen pixels about which X11 sends notice.\n"
  "        * 'Flood->Value' means to flood fill outwards from the first\n"
  "            chosen voxel, stopping when the Dataset Value is reached.\n"
  "            In conjunction with 'Closed Curve', it can be used to draw\n"
  "            an entire region in a plane.\n"
  "        * 'Flood->Nonzero' means to flood fill, but stopping when any\n"
  "            nonzero voxel value is reached.\n"
  "        * 'Flood->Zero' means to flood fill, but stopping when any\n"
  "            zero voxel value is reached.\n"
  "        * 'Zero->Value' means to flood fill the slice with zeros,\n"
  "            stopping when a voxel with the drawing value is reached.\n"
  "        * 'Flood->Val/Zero' means to flood fill the slice with the\n"
  "            Value until voxels whose values are either zero or the\n"
  "            Value are hit\n"
  "        * 'Filled Curve' means to draw a closed curve and then fill\n"
  "            its interior with the drawing value.  It is similar to\n"
  "            doing 'Closed Curve' followed by 'Flood->Value', but\n"
  "            more convenient.\n"
  "        * '2D Nbhd: Kth NN' is like 'Open Curve', but each the 2D in-slice\n"
  "            neighborhood of a point 'x' is filled in with the following\n"
  "            pattern of points, for K=1..5:\n"
  "                                              5 4 3 4 5\n"
  "                                              4 2 1 2 4\n"
  "                                              3 1 x 1 3\n"
  "                                              4 2 1 2 4\n"
  "                                              5 4 3 4 5\n"
  "            In a cubical lattice with voxel edge length=1, the 2D Kth NN\n"
  "            volume is a 'circle' out to radius:\n"
  "                  K=1  r=sqrt(1)  [e.g., (+1, 0)]\n"
  "                  K=2  r=sqrt(2)  [e.g., (+1,+1) => 3x3 square]\n"
  "                  K=3  r=sqrt(4)  [e.g., (+2, 0)]\n"
  "                  K=4  r=sqrt(5)  [e.g., (+2,+1)]\n"
  "                  K=5  r=sqrt(8)  [the whole 5x5 square about 'x']\n"
  "        * '3D Nbhd: Kth NN' is similar, but with the 3D neighborhood\n"
  "            of each point (so you are drawing out-of-slice).  In this\n"
  "            case, the 3D Kth NN volume is a 'sphere' out to radius\n"
  "                  K=1  r=sqrt(1)  [e.g., (+1, 0, 0)]\n"
  "                  K=2  r=sqrt(2)  [e.g., (+1,+1, 0)]\n"
  "                  K=3  r=sqrt(3)  [e.g., (+1,+1,+1) => 3x3x3 cube]\n"
  "                  K=4  r=sqrt(4)  [e.g., (+2, 0, 0)]\n"
  "                  K=5  r=sqrt(5)  [e.g., (+2,+1, 0)]\n"
  "                  K=6  r=sqrt(6)  [e.g., (+2,+1,+1)]\n"
  "                5x5x5  fills out the 5x5x5 cube about each drawn point.\n"
  "        * '2D Circle' and '3D Sphere' draw in-plane circles and 3D spheres\n"
  "            about each drawn point 'x'.  The radius (in mm) is set using\n"
  "            the 'R' chooser that becomes active when one of these drawing\n"
  "            modes is selected.  These drawing modes use the actual voxel\n"
  "            sizes in the dataset, unlike the 'Nbhd' modes described above.\n"
  "\n"
  "Step 5) Draw something in an image window.\n"
  "        * Drawing is done using mouse button 2 (the middle button).\n"
  "        * If you have a scroll-wheel for the middle button, it can\n"
  "            be hard to use this for drawing.  Two alternatives are:\n"
  "          ** Mouse button 1 (the left button), with the keyboard\n"
  "             Shift key held down simultaneously.\n"
  "          ** Mouse button 1 by itself, if you first click the 'pen'\n"
  "             toggle (at the image viewer's right) to be 'on'.\n"
  "             (Mouse cursor should then change to a stylized pen.)\n"
  "        * In an image window, drawing a set of pixels is done\n"
  "            by pressing and holding button 2, and dragging\n"
  "            the cursor across the desired pixels.  The drawing\n"
  "            color will be painted over these pixels while the\n"
  "            painting is going on (while button 2 is held down).\n"
  "        * After mouse button 2 is released, the drawing value for\n"
  "            the chosen voxels is copied into the dataset.  The\n"
  "            dataset is then redisplayed -- this will most likely\n"
  "            change the color of the selected voxels, since display\n"
  "            colors depend on the Define Function pbar (for Func\n"
  "            datasets) or on the grayscale map (for Anat datasets).\n"
  "        * That is, the drawing color is ONLY used while button 2\n"
  "            is pressed down.  This color should simply be chosen\n"
  "            to provide good contrast for the drawing operations.\n"
  "        * Pressing and releasing button 2 in a graph window\n"
  "            sub-graph will cause that single voxel to get the\n"
  "            drawing value, as well.  You cannot select a group\n"
  "            of voxels in a graph window -- only one voxel per click.\n"
  "        * Linear Fillin provides the same functionality as program\n"
  "            3dRowFillin.  It lets you fill in gaps (zeros) between\n"
  "            the same value in a particular anatomical direction.\n"
  "            For example, you could draw on every 4th coronal slice,\n"
  "            and then use Fill in the A-P direction with a maximum\n"
  "            gap setting of 3 to fill in the slices you didn't draw.\n"
  "            (Then you could manually fix up the intermediate slices.)\n"
  "        * Atlas regions can be loaded from the current default atlas\n"
  "            into the edited volume.  The chosen region+hemisphere(s)\n"
  "            will be loaded with the current value.\n"
  "            'OverWrite' loading means that all voxels from\n"
  "               the atlas region will be replaced with the Value.\n"
  "            'InFill' loading means that only voxels that are currently\n"
  "            zero in the TT region will be replaced with the Value.\n"
  "           N.B.: Atlas regions may not be good representations of\n"
  "                   any given subject's anatomy.  You will probably\n"
  "                   want to edit the mask after doing the loading.\n"
  "                 This feature requires the presence of the atlas\n"
  "                   dataset (default is TTatlas+tlrc) in the plugin directory.\n"
  "                   It also requires that you be editing in +tlrc coordinates,\n"
  "                   or in +orig coordinates with a mapping to +tlrc\n"
  "                   coordinates having already been established.\n"
  "                 Unlike Linear Fillin, atlas drawing can be undone.\n"
  "\n"
  "Step 6) Undo and Redo.\n"
  "        * The last drawing operation can be undone -- that is,\n"
  "            pressing 'Undo' will restore the voxel values before\n"
  "            the last button 2 press-release operation.\n"
  "        * 'Redo' will undo the previous 'Undo'.\n"
  "        * Multiple levels of Undo/Redo are available.\n"
  "        * The amount of memory set aside for Undo/Redo operations\n"
  "            is controlled by environment variable AFNI_DRAW_UNDOSIZE,\n"
  "            which is in megabytes; its value defaults to 6.\n"
  "        * The numbers (as in '[3]') on the Undo and Redo buttons\n"
  "            indicate how many levels are available at any moment.\n"
  "\n"
  "Step 7) Save dataset (maybe).\n"
  "        * While a dataset is being edited, it is locked into memory.\n"
  "        * The edited values are saved to disk only when 'Save' or\n"
  "            'Done' are pressed.\n"
  "        * The 'Quit' button can be used to discard the edits of\n"
  "            a dataset.  In that case, the dataset values are\n"
  "            re-read from disk when it is redisplayed.\n"
  "        * Closing the AFNI Editor window using the window manager\n"
  "            is equivalent to pressing 'Quit'.\n"
  "        * The 'SaveAs' button lets you save the changes to a new\n"
  "            dataset.  The new dataset will become the current dataset\n"
  "            for further editing and for AFNI display.\n"
  "\n"
  "++WARNINGS++:\n"
  "  * It is important to understand the distinction between 'pixels'\n"
  "      and 'voxels'.  Pixels are on the screen, and while you are\n"
  "      drawing, you are drawing pixels with the drawing color.  When\n"
  "      you release mouse button 2, those dataset voxels to which these\n"
  "      pixels correspond are computed.  The values stored in those\n"
  "      voxels are then altered, and the dataset display is refreshed.\n"
  "  * It is possible to draw on a montaged image window.  However,\n"
  "      only voxels from the first slice drawn into will be altered.\n"
  "  * Using button 2 in an image or graph window before choosing a\n"
  "      dataset to edit will cause the display to beep.\n"
  "  * Closing the AFNI controller window that this was started from\n"
  "      is the equivalent of pressing 'Quit'.\n"
  "  * Doing something that causes the AFNI controller window to\n"
  "      alter its 3D grid location or resolution is also the\n"
  "      equivalent of pressing 'Quit'.  This is because the 3D grid\n"
  "      for the dataset being edited will no longer correspond to\n"
  "      the 3D grid in the image and graph windows.  Such actions\n"
  "      include switching from 'View Brick' to 'Warp on Demand',\n"
  "      switching datasets or sessions, and switching views.\n"
  "  * You can only draw into the windows of the AFNI controller from\n"
  "      which the Editor was started.\n"
  "  * Only one copy of the Editor can be active at a time.  If you\n"
  "      use the plugin menu to call up the Editor when it is already\n"
  "      open, that will simply pop the window up to the top of the\n"
  "      stacking order.  If you want to restart the Editor in a\n"
  "      different AFNI controller, you must first close the Editor\n"
  "      (via 'Done' or 'Quit') and then start it from the other\n"
  "      controller's window.\n"
  "  * Peculiar and confusing things can happen using 'Warp-on-Demand'\n"
  "      with the Editor.  My advice is NOT to try this.\n"
  "  * Note that using a Session rescan button (from the 'Define Datamode'\n"
  "      control panel) will close all datasets while rescanning the\n"
  "      session.  This can result in the loss of un-Saved edits.\n"
  "  * It is possible to edit the same dataset that you are also viewing\n"
  "      with the 'Render Dataset' plugin.  In this way, you can see a\n"
  "      3D visualization of your drawing as you do it.  You need to turn\n"
  "      on 'DynaDraw' in the rendering plugin; then, if the dataset you\n"
  "      are drawing on is the same as the renderer's overlay, each drawing\n"
  "      action will cause a re-rendering.  This works well if you have\n"
  "      set the renderer's 'Color Opacity' to 'ShowThru'.  This is also\n"
  "      a lot of fun.\n"
  "  * If you are drawing anatomically-based ROIs, you can only draw every\n"
  "      5th slice (say) and then use program 3dRowFillin to fill in the\n"
  "      inter-slice gaps.  Or use the Linear Fillin interactive feature.\n"
  "  * Edit at your own risk!  You can destroy datasets this way.  That's\n"
  "      why the 'Copy dataset' feature is on by default.\n"
  "  * Be careful out there.\n"
  "\n"
  "SUGGESTIONS?\n"
  "  * Please send them to " COXEMAIL "\n"
  "  * Better than suggestions are implementations.\n"
  "  * Better than implementations are pumpernickel bagels.\n"
  "Author -- RW Cox"

    , TEXT_READONLY ) ;
   return ;
}

/*-------------------------------------------------------------------
  Callback for choose button.
  Criteria for datasets that can be edited:
    - must be in current session
    - must have actual bricks
    - only datasets with nvals=1 can be edited
    - bricks must be on same grid (dataxes) as AFNI controller
  Much of this code is adapted from PLUG_choose_dataset_CB.
---------------------------------------------------------------------*/

static int                  ndsl = 0 ;
static PLUGIN_dataset_link * dsl = NULL ;

void DRAW_choose_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   THD_session * ss  = im3d->ss_now ;           /* current session */
   int           vv  = im3d->vinfo->view_type ; /* view type */
   THD_3dim_dataset * qset ;
   int id , ltop , llen ;
   char qnam[THD_MAX_NAME] , label[THD_MAX_NAME] ;
   static char ** strlist = NULL ;

   /* can't do this if a dataset is already active and changed */

   if( dset != NULL && dset_changed ){
      (void) MCW_popup_message( choose_pb ,
                                   "Can't change datasets until\n"
                                   "you save the changes you've\n"
                                   "already made.  Or you could\n"
                                   "'Quit' and re-start the Editor" ,
                                MCW_USER_KILL | MCW_TIMER_KILL ) ;
      XBell(dc->display,100) ; return ;
   }

   /* initialize */

   ndsl = 0 ;

   /* scan datasets */

   for( id=0 ; id < ss->num_dsset ; id++ ){
      qset = GET_SESSION_DSET(ss,id,vv);
/*      qset = ss->dsset_xform_table[id][vv] ;*/

      if( ! ISVALID_DSET (qset)                        ) continue ;  /* skip */
      if( ! DSET_INMEMORY(qset)                        ) continue ;
      if( DSET_NVALS(qset) > 1                         ) continue ;
      if( ! EQUIV_DATAXES(qset->daxes,im3d->wod_daxes) ) continue ;

      ndsl++ ;
      dsl = (PLUGIN_dataset_link *)
              XtRealloc( (char *) dsl , sizeof(PLUGIN_dataset_link)*ndsl ) ;

      make_PLUGIN_dataset_link( qset , dsl + (ndsl-1) ) ;
   }

   /* found nothing?  exit */

   if( ndsl < 1 ){
      (void) MCW_popup_message( choose_pb ,
                                   " \n"
                                   "Didn't find any datasets to edit!\n"
                                   "Check if:\n"
                                   " - you are in 'Warp-on-Demand' mode\n"
                                   " - you are in the correct session\n"
                                   "Also:\n"
                                   " * Only datasets with 1 sub-brick can\n"
                                   "    be edited.\n"
                                   " * The dataset must match the resolution\n"
                                   "    of the current anatomical view.\n"
                               , MCW_USER_KILL | MCW_TIMER_KILL ) ;
      XBell(dc->display,100) ; return ;
   }

   /*--- 23 Nov 1996: loop over dataset links and patch their titles
                      to include an indicator of the dataset type    ---*/

   ltop = 4 ;
   for( id=0 ; id < ndsl ; id++ ){
      llen = strlen(dsl[id].title) ;
      ltop = MAX(ltop,llen) ;
   }

   for( id=0 ; id < ndsl ; id++ ){
      qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;
      if( ! ISVALID_DSET(qset) ) continue ;
      if( ISANAT(qset) ){
         if( ISANATBUCKET(qset) )         /* 30 Nov 1997 */
            sprintf(qnam,"%-*s [%s:%d]" ,
                    ltop,dsl[id].title ,
                    ANAT_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;

         else if( DSET_NUM_TIMES(qset) == 1 )
            sprintf(qnam,"%-*s [%s]" ,
                    ltop,dsl[id].title ,
                    ANAT_prefixstr[qset->func_type] ) ;

         else
            sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
                    ltop,dsl[id].title ,
                    ANAT_prefixstr[qset->func_type] , DSET_NUM_TIMES(qset) ) ;

      } else {
         if( ISFUNCBUCKET(qset) )         /* 30 Nov 1997 */
            sprintf(qnam,"%-*s [%s:%d]" ,
                    ltop,dsl[id].title ,
                    FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;

         else if( DSET_NUM_TIMES(qset) == 1 )
            sprintf(qnam,"%-*s [%s]" ,
                    ltop,dsl[id].title ,
                    FUNC_prefixstr[qset->func_type] ) ;

         else
            sprintf(qnam,"%-*s [%s:3D+t:%d]" ,
                    ltop,dsl[id].title ,
                    FUNC_prefixstr[qset->func_type] , DSET_NVALS(qset) ) ;
      }

      if( DSET_COMPRESSED(qset) ) strcat(qnam,"z") ;

      strcpy( dsl[id].title , qnam ) ;
   }

   /*--- make a popup chooser for the user to browse ---*/

   POPDOWN_strlist_chooser ;

   strlist = (char **) XtRealloc( (char *)strlist , sizeof(char *)*ndsl ) ;
   for( id=0 ; id < ndsl ; id++ ) strlist[id] = dsl[id].title ;

   sprintf( label , "AFNI Dataset from\nthe %s" , VIEW_typestr[vv] ) ;

   MCW_choose_strlist( w , label , ndsl , -1 , strlist ,
                       (gen_func *)DRAW_finalize_dset_CB , NULL     ) ;

   return ;
}

/*-----------------------------------------------------------------------------*/

void DRAW_finalize_dset_CB( Widget w, XtPointer fd, MCW_choose_cbs *cbs )
{
   int id=cbs->ival , copied=0 ;
   THD_3dim_dataset * qset ;
   XmString xstr ;
   char str[256] , *dtit ;
   THD_slist_find slf ;   /* 29 Jul 2003 */
   MCW_choose_cbs cbss ;

   /*-- check for errors --*/

   if( !editor_open ){ POPDOWN_strlist_chooser; XBell(dc->display,100); return; }

   if( dset != NULL && dset_changed ){ XBell(dc->display,100) ; return ; }

   if( id < 0 || id >= ndsl ){ XBell(dc->display,100) ; return ; }

   qset = PLUTO_find_dset( &(dsl[id].idcode) ) ;  /* the new dataset? */

   if( qset == NULL ){ XBell(dc->display,100) ; return ; }

   if( ! EQUIV_DATAXES( im3d->wod_daxes , qset->daxes ) ){
      XBell(dc->display,100) ; return ;
   }

   /*-- 24 Sep 2001: make a copy of the dataset, if desired --*/

   if( MCW_val_bbox(copy_bbox) != 0 ){
      THD_3dim_dataset *cset ;
      int zfill , ftype , dtype ;

      zfill = (copy_mode_av->ival == 1) ;     /* zero fill? */

      switch( copy_type_av->ival ){
         default: ftype = -1 ; break ;        /* As Is  - should never get here */
         case 0:  ftype =  1 ; break ;        /* Func  */
         case 1:  ftype =  2 ; break ;        /* Anat  */
      }

      switch( copy_datum_av->ival ){
         default: dtype = -1        ; break ; /* As Is */
         case 1:  dtype = MRI_byte  ; break ; /* Byte  */
         case 2:  dtype = MRI_short ; break ; /* Short */
         case 3:  dtype = MRI_float ; break ; /* Float */
      }

      cset = DRAW_copy_dset( qset , zfill,ftype,dtype ) ; /* make copy! */

      if( cset == NULL ){                                 /* this is bad */
         (void) MCW_popup_message( choose_pb ,
                                     " \n"
                                     "*** Cannot make copy of input   ***\n"
                                     "*** dataset for unknown reasons ***\n " ,
                                   MCW_USER_KILL ) ;
         XBell(dc->display,100) ; return ;
      }

      DSET_unload(qset) ;
      PLUTO_add_dset( plint , cset , DSET_ACTION_MAKE_CURRENT ) ;
      qset = cset ; copied = 1 ;
   }

   /*-- accept this dataset --*/

   dset = qset ; dset_changed = 0 ;
   dax_save = *(dset->daxes) ;
   dset_idc = dset->idcode ;   /* 31 Mar 1999 */

   SENSITIZE(save_pb,0) ; SENSITIZE(saveas_pb,0) ;
   Sensitize_copy_bbox(0);

   /*-- write the informational label --*/

   if( copied ) dtit = DSET_BRIKNAME(dset) ;  /* 24 Sep 2001 */
   else         dtit = dsl[id].title ;

   if( DSET_BRICK_FACTOR(dset,0) == 0.0 ){
      strcpy(str,dtit) ;
   } else {
      char abuf[16] ;
      AV_fval_to_char( DSET_BRICK_FACTOR(dset,0) , abuf ) ;
      sprintf(str,"%s\nbrick factor: %s", dtit , abuf ) ;
   }
   xstr = XmStringCreateLtoR( str , XmFONTLIST_DEFAULT_TAG ) ;
   XtVaSetValues( info_lab ,
                    XmNlabelString , xstr ,
                    BGCOLOR_ARG(GCOL) ,
                  NULL ) ;
   XmStringFree(xstr) ;

   /*-- setup AFNI for drawing --*/

   if( ! recv_open ){
      recv_key = id = AFNI_receive_init( im3d, RECEIVE_DRAWING_MASK   |
                                               RECEIVE_DSETCHANGE_MASK ,  /* 31 Mar 1999 */
                                         (gen_func *)DRAW_receiver,NULL ,
                                        "DRAW_receiver" ) ;

      if( id < 0 ){
         (void) MCW_popup_message( im3d->vwid->top_shell ,
                                     "Unable to establish\n"
                                     "connection to AFNI\n"
                                     "drawing routines!" ,
                                   MCW_USER_KILL | MCW_TIMER_KILL ) ;

         dset = NULL ; XBell(dc->display,100) ; return ;
      }
   }

   DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ;

   AFNI_receive_control( im3d, recv_key,mode_index , NULL ) ;
   AFNI_receive_control( im3d, recv_key,DRAWING_OVCINDEX, ITOP(color_index) ) ;
   recv_open = 1 ;

   CLEAR_UNREDOBUF ;  /* 19 Nov 2003 */

   /* 29 Jul 2003: switch to this dataset */

   slf = THD_dset_in_session( FIND_IDCODE , &(dset->idcode) , im3d->ss_now ) ;
   if( slf.dset_index >= 0 ){
     cbss.ival = slf.dset_index ;
     if( ISFUNC(dset) ){
       AFNI_finalize_dataset_CB( im3d->vwid->view->choose_func_pb ,
                                 (XtPointer) im3d ,  &cbss         ) ;
       AFNI_SEE_FUNC_ON(im3d) ; /* 30 Apr 2002 */
     } else {
       AFNI_finalize_dataset_CB( im3d->vwid->view->choose_anat_pb ,
                                 (XtPointer) im3d ,  &cbss         ) ;
     }
   }

   /* 20 Oct 2003: get VALUE_LABEL_DTABLE, if present */

   if( vl_dtable != NULL ){ destroy_Dtable(vl_dtable); vl_dtable = NULL; }

   { ATR_string *atr ;
     atr = THD_find_string_atr( dset->dblk , "VALUE_LABEL_DTABLE" ) ;
     if( atr != NULL && atr->nch > 5 )
       vl_dtable = Dtable_from_nimlstring( atr->ch ) ;
     if( vl_dtable == NULL ){
       char *str = AFNI_suck_file( getenv("AFNI_VALUE_LABEL_DTABLE") ) ;
       if( str != NULL ){ vl_dtable = Dtable_from_nimlstring(str); free(str); }
     }
     DRAW_set_value_label() ;
   }

   return ;
}

/*-------------------------------------------------------------------
  Callback for color menu
---------------------------------------------------------------------*/

void DRAW_color_CB( MCW_arrowval * av , XtPointer cd )
{
   color_index = av->ival ;

   if( dset != NULL && recv_open )
      AFNI_receive_control( im3d, recv_key,DRAWING_OVCINDEX, ITOP(color_index) ) ;

   return ;
}

/*-------------------------------------------------------------------
  Callback for mode menu
---------------------------------------------------------------------*/

void DRAW_mode_CB( MCW_arrowval * av , XtPointer cd )
{
   mode_ival  = av->ival ;
   mode_index = mode_ints[mode_ival] ;

   if( dset != NULL && recv_open ){
      AFNI_receive_control( im3d, recv_key,mode_index , NULL ) ;

      /* 08 Oct 2002: set drawing line width */

      AFNI_receive_control( im3d, recv_key, DRAWING_LINEWIDTH ,
                            ITOP(mode_width[mode_ival])      ) ;
   }

   /* 16 Oct 2002: turn rad_av (radius) on if mode needs it */

   ENABLE_rad_av ;

   return ;
}

/*-------------------------------------------------------------------
  Callback for value menu
---------------------------------------------------------------------*/

void DRAW_value_CB( MCW_arrowval * av , XtPointer cd )
{
   value_int   = av->ival ;
   value_float = av->fval ;

   if( value_float != 0.0 ){
     XtSetSensitive( label_label , True ) ;
     XtSetSensitive( label_textf , True ) ;
     if(Check_value()) {
/*        value_float = 1.0;
	av->ival = 1;
	av->fval = 1.0;
*/        XtSetSensitive( label_label , False ) ;
        XtSetSensitive( label_textf , False ) ;
      }
   } else {
     XtSetSensitive( label_label , False ) ;
     XtSetSensitive( label_textf , False ) ;
   }

   DRAW_set_value_label() ;

   return ;
}

/* check if the value in the value field will be changed
  once it gets applied to the dataset */
/* error message is popped up if data value can not be applied*/
/* also returns 0 for data okay, 1 if can not use the data value */
static int Check_value(void)
{
   int   ityp;
   float bfac;
   float value_float2, delta;

   /* sanity check */

   if( dset==NULL) {
     (void) MCW_popup_message( label_textf , \
        "Please choose dataset first\n", \
        MCW_USER_KILL | MCW_TIMER_KILL) ;
     PLUTO_beep() ;

    return(1) ;
   }
   ityp = DSET_BRICK_TYPE(dset,0) ;
   bfac = DSET_BRICK_FACTOR(dset,0) ;

   if( bfac == 0.0 ) bfac = 1.0 ;

   switch( ityp ){

      default: fprintf(stderr,"Illegal brick type=%s in AFNI Editor!\n",
                       MRI_TYPE_name[ityp] ) ;
               return(0);
      break ;

      case MRI_short:{
        short   val = (short)   (value_float/bfac) ;
	value_float2 = val * bfac;
      }
      break ;

      case MRI_byte:{
        byte   val = (byte)   (value_float/bfac) ;
	value_float2 = val * bfac;
      }
      break ;

      case MRI_float:{
        float   val = (value_float/bfac) ;
	value_float2 = val * bfac;
      }
      break ;

      case MRI_complex:{
        complex   val ;
        /*static complex cxzero = { 0.0 , 0.0 } ;*/

        val = CMPLX( (value_float/bfac) , 0.0 ) ;
        return(0);  /* assume everything is okay for complex for now */
      }
      break ;

   } /* end of switch on brick type */

   delta = fabs(value_float2 - value_float);
   if(delta>0.000001) {
     (void) MCW_popup_message( label_textf , \
          "**************************************************************\n" \
	  "This dataset type does not accept this value in this plug-in\n" \
	  "Use 3D Edit plug-in, 3dcalc or 3dmerge to copy the dataset\n"   \
	  "to a new datum type.\n"
          "**************************************************************",\
	 MCW_USER_KILL | MCW_TIMER_KILL) ;
     PLUTO_beep() ;
     AV_assign_fval( value_av ,  value_float2 ) ;
     value_int   = value_av->ival ;
     value_float = value_av->fval ;
     DRAW_set_value_label();  /* reset value and label field to previous entry */
     return(1);
   }

   return(0);
}


/*---------------------------------------------------------------------
  Callbacks and functions for value label and text field [15 Oct 2003]
-----------------------------------------------------------------------*/

static void dump_vallab(void)
{

#if 0
   char str[256]={""};
   char *cstr;
   int i;
   
   if( dset == NULL ) return ;

   for(i=1;i<=3;i++){
      AFNI_get_dset_val_label(dset, i, str);
      printf("roi %d: %s\n", i,str);
   }

   cstr = Dtable_to_nimlstring( vl_dtable , "VALUE_LABEL_DTABLE" ) ;
   if( cstr != NULL ){ printf("%s\n",cstr); free(cstr); }
#endif
   return ;
}

/*---------------------------------------------------------------------*/

char * DRAW_value_string( float val )  /* returns a fixed pointer! */
{
   static char str[32] ;
   sprintf(str,"%.5g",val) ;
   return str ;
}

/*---------------------------------------------------------------------*/

void DRAW_set_value_label(void)
{
   if( vl_dtable == NULL || value_float == 0.0 ){
     XmTextFieldSetString( label_textf , "" ) ;
   } else {
     char *str_val = DRAW_value_string( value_float ) ;
     char *str_lab = findin_Dtable_a( str_val , vl_dtable ) ;
     XmTextFieldSetString( label_textf ,
                           (str_lab != NULL) ? str_lab : "" ) ;
   }
   return ;
}

/*---------------------------------------------------------------------*/

void DRAW_label_CB( Widget wtex , XtPointer cld, XtPointer cad )
{
   char *str_val , *str_lab , *str_old ;
   int ll , ii ;

   /* get string from text field, see if it is empty or ends in blanks */

   str_lab = XmTextFieldGetString( label_textf ) ;
   if( str_lab == NULL ){
     if( vl_dtable == NULL ) return ;  /* do nothing */
   } else {
     ll = strlen(str_lab) ;
     for( ii=ll-1 ; ii >= 0 && isspace(str_lab[ii]) ; ii-- ) ; /* nada */
     if( ii < 0 ){                       /*-- all blanks */
       if( vl_dtable == NULL ) return ;    /* do nothing */
       free(str_lab) ; str_lab = NULL ;    /* otherwise, clobber entry */
     } else if( ii < ll-1 ){             /*-- ends in blanks */
       str_lab[ii+1] = '\0' ;              /* so truncate them */
     }
   }

   /* create (value,label) pair Dtable -- NULL label ==> erase old label */

   if( vl_dtable == NULL ) vl_dtable = new_Dtable(7) ;

   str_val = DRAW_value_string( value_float ) ;   /* value string */

   /* check if old label for this value is same as new label;
      if it is, then don't need to do anything                */

   str_old = findin_Dtable_a( str_val , vl_dtable ) ;
   if( str_old != NULL ){
     if( str_lab != NULL && strcmp(str_old,str_lab) == 0 ){  /* same as old */
       free(str_lab) ; return ;
     } else if( str_lab == NULL ){                     /* erase the old one */
       removefrom_Dtable_a( str_val , vl_dtable ) ;
       dump_vallab() ;
       return ;
     }
   }
   if( str_lab == NULL ) return ;  /* is NULL ==> nothing to do here */

   /* check if new label is already in the table under a different value */

   str_old = findin_Dtable_b( str_lab , vl_dtable ) ;
   if( str_old != NULL && strcmp(str_old,str_val) != 0 ){
     char msg[1024] ;
     sprintf(msg," \n"
                 " *********************************** \n"
                 " ** ERROR * ERROR * ERROR * ERROR ** \n"
                 " **\n"
                 " ** Label = %s\n"
                 " **   is already associated with\n"
                 " ** Value = %s\n"
                 " **\n"
                 " ** Value,Label pairs must be unique \n"
                 " *********************************** \n"
             , str_lab , str_old ) ;
     /* changed popup to disappear with timer to make it easier to continue */
     (void) MCW_popup_message( label_textf , msg , MCW_USER_KILL | MCW_TIMER_KILL) ;
     PLUTO_beep() ;
     DRAW_set_value_label();
     free(str_lab) ; return ;
   }

   /* add new value,label pair to Dtable (will clobber old one, if present) */

   addto_Dtable( str_val , str_lab , vl_dtable ) ;
   free(str_lab) ;
   DRAW_attach_dtable( vl_dtable, "VALUE_LABEL_DTABLE",  dset ) ;
   dset_changed = 1;

   dump_vallab() ;
   return ;
}

/*--------------------------------------------------------------------------*/

static char **vl_strlist=NULL ;
static  int  vl_nstrlist=0 ;

static void DRAW_label_finalize( Widget w, XtPointer cd, MCW_choose_cbs *cbs )
{
   int ival = cbs->ival , nn ;
   float val=0.0 ;

   if( !editor_open ){ PLUTO_beep(); POPDOWN_strlist_chooser; return; }

   nn = sscanf( vl_strlist[ival] , "%f" , &val ) ;
   if( nn == 0 || val == 0.0 ) return ;

   AV_assign_fval( value_av ,  val ) ;
   value_int   = value_av->ival ;
   value_float = value_av->fval ;
   DRAW_set_value_label() ;
   return ;
}

/*---------------------------------------------------------------------*/

static void DRAW_label_getfile( Widget w, XtPointer cd, MCW_choose_cbs *cbs )
{
   char *str ;

   if( !editor_open ){ PLUTO_beep(); POPDOWN_string_chooser; return; }

   str = AFNI_suck_file( cbs->cval ) ;
   if( str != NULL ){
     if( vl_dtable != NULL ) destroy_Dtable(vl_dtable) ;
     vl_dtable = Dtable_from_nimlstring(str) ;
     DRAW_set_value_label() ;
   } else {
     PLUTO_beep() ;
   }
   return ;
}

/*---------------------------------------------------------------------*/

void DRAW_label_EV( Widget w , XtPointer cld ,
                    XEvent *ev , RwcBoolean *continue_to_dispatch )
{

   /* handle leave event in text field */

   if( w == label_textf ){
     XmAnyCallbackStruct cbs ;
     XLeaveWindowEvent *lev = (XLeaveWindowEvent *) ev ;
     if( lev->type != LeaveNotify ) return ;
     cbs.reason = XmCR_ACTIVATE ;  /* simulate a return press */
     DRAW_label_CB( w , NULL , &cbs ) ;
   }

   /* handle Button-3 press in label */

   else if( w == label_label ){
     XButtonEvent *bev = (XButtonEvent *) ev ;
     int nn,ic,ll ; char **la, **lb ; float val ;

     if( bev->button == Button1 ){
       MCW_choose_string( w , "Enter Value-Label filename:" ,
                          NULL , (gen_func *)DRAW_label_getfile , NULL   ) ;
       return ;
     }
     if( bev->button != Button3 ) return ;
     nn = listize_Dtable( vl_dtable , &la , &lb ) ;
     if( nn <= 0 || la == NULL || lb == NULL ) return ;

     /** get ready to popup a new list chooser **/

     POPDOWN_strlist_chooser ;

     /** clear old strings **/

     for( ic=0 ; ic < vl_nstrlist ; ic++ ) free(vl_strlist[ic]) ;

     /** make a list of value-label strings **/

     vl_nstrlist = nn ;
     vl_strlist  = (char **) realloc( vl_strlist , sizeof(char *)*vl_nstrlist ) ;
     for( nn=ic=0 ; ic < vl_nstrlist ; ic++ ){
       if( la[ic] != NULL && lb[ic] != NULL ){  /* should always be true */
         ll = strlen(la[ic])+strlen(lb[ic])+8 ;
         vl_strlist[nn] = calloc(1,ll) ;
         sprintf( vl_strlist[nn] , "%s = %s" , la[ic],lb[ic] ) ;
         nn++ ;
       }
     }
     free(la); free(lb); if( nn == 0 ) return ;

     /* sort list for the user's convenience */

     if( nn > 1 ){
       int redo ; char *t ;
      BSort:
       for( redo=ic=0 ; ic < nn-1 ; ic++ ){
         if( strcmp(vl_strlist[ic],vl_strlist[ic+1]) > 0 ){
           t=vl_strlist[ic]; vl_strlist[ic]=vl_strlist[ic+1]; vl_strlist[ic+1]=t;
           redo = 1 ;
         }
       }
       if( redo ) goto BSort ;
     }

     /* find current value in list, if any */

     for( ic=0 ; ic < nn ; ic++ ){
       sscanf( vl_strlist[ic] , "%f" , &val ) ;
       if( val == value_float ) break ;
     }
     if( ic == nn ) ic = -1 ;

     /* let the user choose one */

     MCW_choose_strlist( w , "Value = Label" , nn ,
                         ic , vl_strlist ,
                         (gen_func *)DRAW_label_finalize , NULL ) ;
   }

   return ;
}

/*---------------------------------------------------------------------*/

void DRAW_attach_dtable( Dtable *dt, char *atname, THD_3dim_dataset *ds )
{
   char *str ;
   if( dt == NULL || atname == NULL || ds == NULL ) return ;
   if (ds->Label_Dtable) {
      destroy_Dtable(ds->Label_Dtable); ds->Label_Dtable=NULL;
   }
   str = Dtable_to_nimlstring( dt , atname ) ;
   if( str == NULL ) return ;
   THD_set_string_atr( ds->dblk , atname , str ) ;
   free(str) ; return ;
}

/*******************************************************************
   Receive data from AFNI after drawing, etc.
********************************************************************/

void DRAW_receiver( int why , int np , void * vp , void * cbd )
{

ENTRY("DRAW_receiver") ;

   switch( why ){

      default:
         fprintf(stderr,"DRAW_receiver: illegal why=%d\n",why) ;
      EXRETURN ;

      /*-- we like this one --*/

      case RECEIVE_POINTS:{
         int **ip = (int **)vp ;
         int *xd=ip[0] , *yd=ip[1] , *zd=ip[2] ; /* pts coords */
         int mode=ip[3][0] ;                     /* how pts are organized */
         int plane ;

         /*-- 20 Feb 2003: undo via keypress --*/

         if( mode == UNDO_MODE ){
           if( undo_num > 0 ) DRAW_undo_CB( undo_pb,NULL,NULL ) ;
           else               XBell(dc->display,100) ;
           EXRETURN ;
         }

         if( mode == INCVAL_MODE || mode == DECVAL_MODE ){ /* 13 Sep 2008 */
           float nval ;
           nval = value_float + ((mode==INCVAL_MODE) ? (1.0f) : (-1.0f)) ;
           AV_assign_fval( value_av , nval ) ;
           value_int   = value_av->ival ;
           value_float = value_av->fval ;
           DRAW_set_value_label() ;
           EXRETURN ;
         }

         /*-- Did we get points? --*/

         if( np <= 0 ) EXRETURN ;

         plane = mode - SINGLE_MODE ;
         if( plane < 1 || plane > 3 ) plane = mode - PLANAR_MODE ;
         if( plane < 1 || plane > 3 ) plane = 0 ;

         /* anything but flood mode --> just draw given points */

         if( plane == 0 ||
             ((mode_ival != MODE_FLOOD_VAL )  &&
              (mode_ival != MODE_FLOOD_NZ  )  &&
              (mode_ival != MODE_FLOOD_ZERO)  &&
              (mode_ival != MODE_ZERO_VAL  )  &&
              (mode_ival != MODE_FLOOD_VZ  )  &&
              (mode_ival != MODE_FILLED    ))   ){

            /* 07 Oct 2002: expand set of points using a mask? */

            if( plane != 0 && mode_ival >= FIRST_2D_MODE && mode_ival <= LAST_2D_MODE ){
              int nfill=0, *xyzf=NULL ;

              DRAW_2D_expand( np , xd,yd,zd , plane , &nfill , &xyzf ) ;
              if( nfill > 0 && xyzf != NULL ){
                DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ;
                free(xyzf) ;
              }

            } else if( plane != 0 && mode_ival >= FIRST_3D_MODE && mode_ival <= LAST_3D_MODE ){
              int nfill=0, *xyzf=NULL ;

              DRAW_3D_expand( np , xd,yd,zd , plane , &nfill , &xyzf ) ;
              if( nfill > 0 && xyzf != NULL ){
                DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ;
                free(xyzf) ;
              }

            /* 16 Oct 2002: expand geometrically (circle or sphere)? */

            } else if( plane != 0 && mode_ival >= FIRST_RAD_MODE && mode_ival <= LAST_RAD_MODE ){
              int nfill=0, *xyzf=NULL ;

              switch( mode_ival ){
                case MODE_2D_CIRC:
                  DRAW_2D_circle( np , xd,yd,zd , plane , &nfill , &xyzf ) ;
                break ;
                case MODE_3D_SPHR:
                  DRAW_3D_sphere( np , xd,yd,zd , plane , &nfill , &xyzf ) ;
                break ;
              }

              if( nfill > 0 && xyzf != NULL ){
                DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ;
                free(xyzf) ;
              } else {
                DRAW_into_dataset( np , xd,yd,zd , NULL ) ; /* should never happen */
              }

            } else {                                        /* the old way:     */
              DRAW_into_dataset( np , xd,yd,zd , NULL ) ;   /* just draw points */
            }

         } else {

            /* flood mode! */

            int   ityp = DSET_BRICK_TYPE(dset,0) ;
            float bfac = DSET_BRICK_FACTOR(dset,0) ;
            int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) ,
                nxy = nx*ny , ii,jj , ixyz ;
            int base=0 , di=0,dj=0 , itop=0,jtop=0,nij , xx=xd[0],yy=yd[0],zz=zd[0] , ix=0,jy=0 ;
            byte * pl ;
            int nfill , *xyzf , nf ;

            /* compute stuff for which plane we are in:
                1 -> yz , 2 -> xz , 3 -> xy            */

            switch(plane){
              case 1: base=xx    ; di=nx; dj=nxy; itop=ny; jtop=nz; ix=yy; jy=zz; break;
              case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ix=xx; jy=zz; break;
              case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ix=xx; jy=yy; break;
            }

            /* create a 2D array with 0 where dataset != blocking value
                             and with 1 where dataset == blocking value */

            nij = itop*jtop ;
            pl  = (byte *) calloc( nij , sizeof(byte) ) ;

            if( mode_ival != MODE_FILLED ){  /* old code: flood to a dataset value */

              if( bfac == 0.0 ) bfac = 1.0 ;
              switch(ityp){

                 case MRI_short:{
                    short * bp  = (short *) DSET_BRICK_ARRAY(dset,0) ;
                    short   val = (short)   (value_float/bfac) ;

                    if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ;

                    if( mode_ival == MODE_FLOOD_VAL  ||
                        mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ;
                          }
                    } else if( mode_ival == MODE_FLOOD_VZ ){  /* 30 Apr 2002 */
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ;
                          }
                    } else {
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] != 0 ) pl[ii+jj*itop] = 1 ;
                          }
                    }
                 }
                 break ;

                 case MRI_byte:{
                    byte * bp  = (byte *) DSET_BRICK_ARRAY(dset,0) ;
                    byte   val = (byte)   (value_float/bfac) ;

                    if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ;

                    if( mode_ival == MODE_FLOOD_VAL  ||
                        mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ;
                          }
                    } else if( mode_ival == MODE_FLOOD_VZ ){  /* 30 Apr 2002 */
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ;
                          }
                    } else {
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] != 0 ) pl[ii+jj*itop] = 1 ;
                          }
                    }
                 }
                 break ;

                 case MRI_float:{
                    float * bp  = (float *) DSET_BRICK_ARRAY(dset,0) ;
                    float   val = (value_float/bfac) ;

                    if( mode_ival == MODE_FLOOD_ZERO ) val = 0 ;

                    if( mode_ival == MODE_FLOOD_VAL  ||
                        mode_ival == MODE_FLOOD_ZERO || mode_ival == MODE_ZERO_VAL ){
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val ) pl[ii+jj*itop] = 1 ;
                          }
                    } else if( mode_ival == MODE_FLOOD_VZ ){  /* 30 Apr 2002 */
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] == val || bp[ixyz] == 0 ) pl[ii+jj*itop] = 1 ;
                          }
                    } else {
                       for( jj=0 ; jj < jtop ; jj++ )
                          for( ii=0 ; ii < itop ; ii++ ){
                             ixyz = base + ii*di + jj*dj ;
                             if( bp[ixyz] != 0.0 ) pl[ii+jj*itop] = 1 ;
                          }
                    }
                 }
                 break ;

                 default:
                    free(pl) ;
                    fprintf(stderr,
                           "Flood not implemented for datasets of type %s\a\n",
                           MRI_TYPE_name[ityp] ) ;
                 EXRETURN ;

              } /* end of switch on type */

              /* start point must be a 0 (can't fill from an edge) */

              if( pl[ix+jy*itop] == 1 ){
                 free(pl) ; XBell(dc->display,100) ; EXRETURN ;
              }

              /* call a routine to fill the array */

              DRAW_2dfiller( itop,jtop , ix,jy , pl ) ;

              /* all filled points are 2 --> these are the locations to draw */

              nfill = 0 ;
              for( ii=0 ; ii < nij ; ii++ ) nfill += (pl[ii] == 2) ;
              if( nfill == 0 ){ free(pl) ; XBell(dc->display,100) ; EXRETURN ; }

              xyzf = (int *) malloc( sizeof(int) * nfill ) ;

              for( nf=0,jj=0 ; jj < jtop ; jj++ ){
                 for( ii=0 ; ii < itop ; ii++ ){
                    if( pl[ii+jj*itop] == 2 )
                       xyzf[nf++] = base + ii*di + jj*dj ;
                 }
              }

              free(pl) ;

              if( mode_ival == MODE_ZERO_VAL ){ bfac = value_float; value_float = 0.0; }

              DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ;

              if( mode_ival == MODE_ZERO_VAL ) value_float = bfac ;

              free(xyzf) ;

            } /*-- end of flood code --*/

            else {  /*-- 25 Sep 2001: fill the interior of the drawn curve --*/

              int *iip=NULL , *jjp=NULL ;

              switch(plane){                           /* select which   */
                case 1: iip = yd ; jjp = zd ; break ;  /* arrays to draw */
                case 2: iip = xd ; jjp = zd ; break ;  /* curve from     */
                case 3: iip = xd ; jjp = yd ; break ;
              }

              for( ii=0 ; ii < np ; ii++ ){  /* draw curve into fill array */
                 pl[ iip[ii] + jjp[ii]*itop ] = 1 ;
              }

              /* now find an edge point that is not on the curve */

              ix = -1 ;
              for( ii=0 ; ii < itop ; ii++ ){
                 if( pl[ii]               == 0 ){ ix = ii; jy = 0     ; break; }
                 if( pl[ii+(jtop-1)*itop] == 0 ){ ix = ii; jy = jtop-1; break; }
              }
              if( ix < 0 ){
                for( jj=0 ; jj < jtop ; jj++ ){
                  if( pl[jj*itop]          == 0 ){ ix = 0     ; jy = jj; break; }
                  if( pl[(itop-1)+jj*itop] == 0 ){ ix = itop-1; jy = jj; break; }
                }
              }
              if( ix < 0 ){ /* should never happen */
                 free(pl) ; XBell(dc->display,100) ; EXRETURN ;
              }

              /* fill the array from the edge */

              DRAW_2dfiller( itop,jtop , ix,jy , pl ) ;

              /* all filled points are 2 --> these are NOT the locations to draw */

              nfill = 0 ;
              for( ii=0 ; ii < nij ; ii++ ) nfill += (pl[ii] != 2) ;
              if( nfill == 0 ){ free(pl) ; XBell(dc->display,100) ; EXRETURN ; }

              xyzf = (int *) malloc( sizeof(int) * nfill ) ;

              for( nf=0,jj=0 ; jj < jtop ; jj++ ){
                 for( ii=0 ; ii < itop ; ii++ ){
                    if( pl[ii+jj*itop] != 2 )
                       xyzf[nf++] = base + ii*di + jj*dj ;
                 }
              }

              free(pl) ;

              DRAW_into_dataset( nfill , xyzf,NULL,NULL , NULL ) ;

              free(xyzf) ;

            } /* end of interior fill code */

         } /* end of flooding or filling */

      } /* end of dealing with drawn points */
      break ;

      /*-- user closed the controller window!? (the fiend) */

      case RECEIVE_CLOSURE:{
         if( dset != NULL && dset_changed ) XBell(dc->display,100) ; /* protest */
         DRAW_quit_CB(NULL,NULL,NULL) ;                              /* and die */
      }
      break ;

      /*-- user altered the controller window!? */

      case RECEIVE_ALTERATION:{

         /* if we are already editing a dataset, then
            check if the grid has changed -- if it has, must quit */

         if( dset != NULL ){
            if( ! EQUIV_DATAXES( im3d->wod_daxes , &dax_save ) ){
               XBell(dc->display,100) ;        /* feeble protest */
               DRAW_quit_CB(NULL,NULL,NULL) ;  /* die */

               /* less feeble protest */

               ERROR_message( "Controller grid was altered!\n"
                              "          Editor was forced to quit!\n"
                              "          Any un-Saved changes were lost!" ) ;

               (void) MCW_popup_message( im3d->vwid->top_shell ,
                                           "Controller grid was altered!\n"
                                           "Editor was forced to quit.\n"
                                           "Any un-Saved changes were lost." ,
                                         MCW_USER_KILL | MCW_TIMER_KILL ) ;
            }
         }
      }
      break ;

      /*-- user changed dataset pointers on us? --*/

      case RECEIVE_DSETCHANGE:{   /* 31 Mar 1999 */
         if( dset != NULL ){
            dset = PLUTO_find_dset( &dset_idc ) ;
            DSET_mallocize(dset) ; DSET_lock(dset) ; DSET_load(dset) ;
            if( dset_changed ){
               THD_load_statistics( dset ) ;
               PLUTO_dset_redisplay( dset ) ;

               XBell(dc->display,100) ;
               (void) MCW_popup_message( im3d->vwid->top_shell ,
                                            "********* WARNING *********\n"
                                            "* Session rescan may have *\n"
                                            "* caused loss of unsaved  *\n"
                                            "* editing changes!        *\n"
                                            "***************************"   ,
                                         MCW_USER_KILL | MCW_TIMER_KILL ) ;
            }
         }
      }
      break ;

   } /* end of switch on why */

   EXRETURN ;  /* unreachable */
}


/*--------------------------------------------------------------------------
  Routine to draw into a dataset.
  If yd & zd are NULL, then xd is used as the direct 3D array index,
    otherwise xd,yd,zd are used as the 3-index.
  If var == NULL, then the value_av is used, otherwise the array var[]
    will be the source of the data.
----------------------------------------------------------------------------*/

int DRAW_into_dataset( int np , int *xd , int *yd , int *zd , void *var )
{
   int   ityp = DSET_BRICK_TYPE(dset,0) ;
   float bfac = DSET_BRICK_FACTOR(dset,0) ;
   int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) ,
       nxy = nx*ny , nxyz = nxy*nz , ii , ixyz ;
   int ndrawn=0 ;
   dobuf *sb ;  /* 19 Nov 2003: save buffer */
   int *xyz ;

   /* sanity check */

   if( dset==NULL || np <= 0 || xd==NULL ) return 0 ;

   /* make space for undo/redo (save old state in buffer) [19 Nov 2003] */

   CREATE_DOBUF(sb,np,ityp) ;
   xyz = sb->xyz ;             /* list of indexes to be altered */

   /* compute (or copy) data index into save buffer */

   if( yd == NULL ){                       /* direct supply of 1-index */
     memcpy(xyz,xd,sizeof(int)*np) ;
   } else {                                /* collapse 3-index into 1 */
     for( ii=0 ; ii < np ; ii++ )
       xyz[ii] = xd[ii] + yd[ii] * nx + zd[ii] * nxy ;
   }

   /* copy data into save buffer, based on type */

   if( bfac == 0.0 ) bfac = 1.0 ;

   switch( ityp ){

      default: fprintf(stderr,"Illegal brick type=%s in AFNI Editor!\n",
                       MRI_TYPE_name[ityp] ) ;
      break ;

#define DOIT (infill_mode==0 || bp[ixyz]==0)

      case MRI_short:{
        short * bp  = (short *) DSET_BRICK_ARRAY(dset,0) ;
        short * up  = (short *) sb->buf ;
        short * vvv = (short *) var ;
        short   val = (short)   (value_float/bfac) ;

        for( ii=0 ; ii < np ; ii++ ){  /* save into buffer */
          ixyz = xyz[ii] ;
          up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0 ;
        }
        for( ii=0 ; ii < np ; ii++ ){  /* put into dataset */
          ixyz = xyz[ii] ;
          if( ixyz >= 0 && ixyz < nxyz && DOIT ){
            bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ;
          }
        }
      }
      break ;

      case MRI_byte:{
        byte * bp  = (byte *) DSET_BRICK_ARRAY(dset,0) ;
        byte * up  = (byte *) sb->buf ;
        byte * vvv = (byte *) var ;
        byte   val = (byte)   (value_float/bfac) ;

        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0 ;
        }
        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          if( ixyz >= 0 && ixyz < nxyz && DOIT ){
            bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ;
          }
        }
      }
      break ;

      case MRI_float:{
        float * bp  = (float *) DSET_BRICK_ARRAY(dset,0) ;
        float * up  = (float *) sb->buf ;
        float * vvv = (float *) var ;
        float   val = (value_float/bfac) ;

        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : 0.0 ;
        }
        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          if( ixyz >= 0 && ixyz < nxyz && DOIT ){
            bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ;
          }
        }
      }
      break ;

      case MRI_complex:{
        complex * bp  = (complex *) DSET_BRICK_ARRAY(dset,0) ;
        complex * up  = (complex *) sb->buf ;
        complex * vvv = (complex *) var ;
        complex   val ;
        static complex cxzero = { 0.0 , 0.0 } ;

        val = CMPLX( (value_float/bfac) , 0.0 ) ;

        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          up[ii] = (ixyz >= 0 && ixyz < nxyz) ? bp[ixyz] : cxzero ;
        }
        for( ii=0 ; ii < np ; ii++ ){
          ixyz = xyz[ii] ;
          if( ixyz >= 0 && ixyz < nxyz && (infill_mode==0 || bp[ixyz].r==0) ){
            bp[ixyz] = (vvv==NULL) ? val : vvv[ii] ; ndrawn++ ;
          }
        }
      }
      break ;

   } /* end of switch on brick type */

   /* recompute statistics */

   THD_load_statistics( dset ) ;

   /* now redisplay dataset, in case anyone is looking at it */

   PLUTO_dset_redisplay( dset ) ;
   dset_changed = 1 ;
   SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ;
   SENSITIZE(choose_pb,0) ;
   Sensitize_copy_bbox(0);

   /* save buffer pushed onto appropriate stack */

   if( undo_how == 1 ){   /* save on redo stack */
     redo_stack = realloc( (void *)redo_stack, sizeof(dobuf *)*(redo_num+1) );
     redo_stack[redo_num++] = sb ;
     REDO_button_labelize ;
   } else {               /* save on undo stack */
     undo_stack = realloc( (void *)undo_stack, sizeof(dobuf *)*(undo_num+1) );
     undo_stack[undo_num++] = sb ;
     UNDO_button_labelize ;
     DRAW_undo_sizecheck() ;
     if( undo_how == 0 ){  /* normal draw ==> can't redo */
       CLEAR_REDOBUF ;
     }
   }

   return ndrawn ;
}

/*---------------------------------------------------------------------------*/
/*!  Limit size of data allowed in undo buffers. [19 Nov 2003]
-----------------------------------------------------------------------------*/

static void DRAW_undo_sizecheck(void)
{
  int ii,jj , ss , lim=6 ;
  char *eee ;

  if( undo_num <= 1 ) return ;  /* will always keep 1 level of undo */

  /* get the limit of allowed mem usage for the undo buffers */

  eee = getenv("AFNI_DRAW_UNDOSIZE") ;
  if( eee != NULL ){
    ii = 0 ; sscanf(eee,"%d",&ii) ;
    if( ii > 0 ) lim = ii ; if( lim > 1024 ) lim = 1024 ;
  }
  lim *= (1024*1024) ;  /* megabytes */

  /* scan from top of stack,
     stopping when total size goes over the limit */

  for( ss=0,ii=undo_num-1 ; ii >= 0 && ss < lim ; ii-- )
    ss += SIZEOF_DOBUF( undo_stack[ii] ) ;

  if( ii <= 0 ) return ;  /* didn't go over limit before bottom */

  /* if here, stack elements from 0..ii-1 should be removed
              and the elements above them moved down to fill in */

  for( jj=0 ; jj < ii ; jj++ )         /* removal */
    DESTROY_DOBUF( undo_stack[jj] ) ;

  for( jj=ii ; jj < undo_num ; jj++ )  /* move-al */
    undo_stack[jj-ii] = undo_stack[jj] ;

  undo_num = undo_num - ii ;
  return ;
}

/*---------------------------------------------------------------------------*/
/*! Set label of Undo or Redo button to reflect number of levels
    available, and set sensitivity while we are at it.  [19 Nov 2003]
-----------------------------------------------------------------------------*/

static void DRAW_undo_butlab( Widget w , int n )
{
   XmString xstr ;
   char label[32] ;
   int nfmt ;
   static char *fmt[3] = { "%s[%d]" , "%s:%d" , "%s%03d" } ;

   if( w == (Widget)NULL ) return ;  /* oom-possible? */

        if( n <  10  ) nfmt = 0 ;     /* choose format based */
   else if( n < 100  ) nfmt = 1 ;     /* on number of digits */
   else                nfmt = 2 ;

   sprintf( label, fmt[nfmt], (w==undo_pb) ? "Undo" : "Redo" , n%1000 ) ;

   xstr = XmStringCreateLtoR( label , XmFONTLIST_DEFAULT_TAG ) ;
   XtVaSetValues( w , XmNlabelString , xstr , NULL ) ;
   XmStringFree(xstr) ;

   SENSITIZE( w , (n>0) ) ;
   return ;
}

/*---------------------------------------------------------------------------
   Flood filling a byte array:
     nx = 1st dimension
     ny = 2nd dimension
     ix = start point
     jy = end point
     ar = array, with 0's everywhere except 1's as barriers to flooding

   All filled points (starting with ix,jy) will get the value 2.
-----------------------------------------------------------------------------*/

void DRAW_2dfiller( int nx , int ny , int ix , int jy , byte * ar )
{
   int ii,jj , ip,jp , num ;

#define AR(i,j) ar[(i)+(j)*nx]

   /* fill out in cross from 1st point */

   ip = ix ; jp = jy ; AR(ip,jp) = 2 ;

   for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ) AR(ii,jp) = 2;
   for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ) AR(ii,jp) = 2;
   for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ) AR(ip,jj) = 2;
   for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ) AR(ip,jj) = 2;

   /* brute force repetition of the cross technique */

   do {
      num = 0 ;
      for( jp=0 ; jp < ny ; jp++ ){
         for( ip=0 ; ip < nx ; ip++ ){
            if( AR(ip,jp) == 2 ){
               for( ii=ip+1; ii < nx && AR(ii,jp) == 0; ii++ ){ AR(ii,jp) = 2; num++; }
               for( ii=ip-1; ii >= 0 && AR(ii,jp) == 0; ii-- ){ AR(ii,jp) = 2; num++; }
               for( jj=jp+1; jj < ny && AR(ip,jj) == 0; jj++ ){ AR(ip,jj) = 2; num++; }
               for( jj=jp-1; jj >= 0 && AR(ip,jj) == 0; jj-- ){ AR(ip,jj) = 2; num++; }
            }
         }
      }
   } while( num > 0 ) ;

   return ;
}

/*----------------------------------------------------------------------------------*/

void DRAW_fillin_CB( Widget w , XtPointer cd , XtPointer cb )
{
   int dcode=-1 , maxgap , nftot ;
   char dir ;
   MRI_IMAGE *bim , *tbim ; /* 21 Nov 2003: to allow undo of fillin */

   /* check for errors */

   if( !editor_open || dset == NULL ){ XBell(dc->display,100); return; }

   dir = fillin_dir_strings[ fillin_dir_av->ival ][0] ;

   if( dir == ORIENT_tinystr[dset->daxes->xxorient][0] ||
       dir == ORIENT_tinystr[dset->daxes->xxorient][1]   ) dcode = 1 ;

   if( dir == ORIENT_tinystr[dset->daxes->yyorient][0] ||
       dir == ORIENT_tinystr[dset->daxes->yyorient][1]   ) dcode = 2 ;

   if( dir == ORIENT_tinystr[dset->daxes->zzorient][0] ||
       dir == ORIENT_tinystr[dset->daxes->zzorient][1]   ) dcode = 3 ;

   if( dcode < 0 ){ XBell(dc->display,100) ; return ; } /* should not happen! */

   #if 0    /* Need big gaps. ZSS Aug. 2011 */
   maxgap = fillin_gap_av->ival ;
   #else
   maxgap = (int)fillin_gap_av->fval;
   #endif
   if( maxgap < 1 ){ XBell(dc->display,100) ; return ; } /* should not happen! */

   bim  = DSET_BRICK(dset,0) ;  /* 21 Nov 2003: for undo */
   tbim = mri_copy( bim ) ;     /* copy brick before the change */

   nftot = THD_dataset_rowfillin( dset , 0 , dcode , maxgap ) ;
   if( nftot > 0 ){
     fprintf(stderr,"++ Fillin filled %d voxels\n",nftot) ;
     PLUTO_dset_redisplay( dset ) ;
     dset_changed = 1 ;
     SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ;
     if( recv_open ) AFNI_process_drawnotice( im3d ) ;

     {     /* 21 Nov 2003: compute the undo stuff */
       int ityp=bim->kind, ii,jj, nvox=bim->nvox, ndel=0 ;
       dobuf *sb=NULL ;
       switch( ityp ){
         case MRI_short:{
           short *bar = MRI_SHORT_PTR(bim), *tbar = MRI_SHORT_PTR(tbim), *up ;
           for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ;
           if( ndel > 0 ){
             CREATE_DOBUF(sb,ndel,MRI_short) ; up = (short *)sb->buf ;
             for( ii=jj=0 ; ii < nvox ; ii++ )
               if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; }
           }
         }
         break ;
         case MRI_float:{
           float *bar = MRI_FLOAT_PTR(bim), *tbar = MRI_FLOAT_PTR(tbim), *up ;
           for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ;
           if( ndel > 0 ){
             CREATE_DOBUF(sb,ndel,MRI_float) ; up = (float *)sb->buf ;
             for( ii=jj=0 ; ii < nvox ; ii++ )
               if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; }
           }
         }
         break ;
         case MRI_byte:{
           byte *bar = MRI_BYTE_PTR(bim), *tbar = MRI_BYTE_PTR(tbim), *up ;
           for( ii=0 ; ii < nvox ; ii++ ) if( bar[ii] != tbar[ii] ) ndel++ ;
           if( ndel > 0 ){
             CREATE_DOBUF(sb,ndel,MRI_byte) ; up = (byte *)sb->buf ;
             for( ii=jj=0 ; ii < nvox ; ii++ )
               if( bar[ii] != tbar[ii] ){ sb->xyz[jj]=ii; up[jj++]=tbar[ii]; }
           }
         }
         break ;
       } /* end of switch on brick type */

       if( sb != NULL ){  /* if we created an undo buffer, push onto stack */
         undo_stack = realloc( (void *)undo_stack, sizeof(dobuf *)*(undo_num+1) );
         undo_stack[undo_num++] = sb ;
         UNDO_button_labelize ;
         DRAW_undo_sizecheck() ;
         CLEAR_REDOBUF ;         /* can't redo after a drawing */
       }
     } /* 21 Nov 2003: end of allowing for undo stuff */

   } else if( nftot < 0 ) {
      fprintf(stderr,"** Fillin failed for some reason!\n") ;
      XBell(dc->display,100) ;
   } else {
      fprintf(stderr,"++ No Fillin voxels found\n") ;
   }

   mri_free(tbim) ; /* 21 Nov 2003: toss old copy */
   return ;
}

/*--------------------------------------------------------------------------
   22 Aug 2001: TT Atlas Regions action callback
----------------------------------------------------------------------------*/
/******* Note any changes here should be reflected in both 
     plug_drawdset.c:DRAW_ttatlas_CB()
     3dfractionize.c:main()
  or updated to use the same function instead in the future....
  This function doesn't really need to do this the 3dfractionize way;
  it can instead use the 3dresample or future warp-on-demand way.
  Resulting ROIs are different between this method and "Show Atlas Colors"
*/
void DRAW_ttatlas_CB( Widget w, XtPointer client_data, XtPointer call_data )
{
   THD_3dim_dataset *dseTT ;
   float *voxout ;
   byte *bb=NULL;
   short *ss=NULL, sval ;
   float *ff=NULL;
   MRI_IMAGE *b0im;
   int nvoxTT, nvoxout , iv,jv,kv , ijk ;
   int hbot,htop , nzTT,nyTT,nxTT,nxyTT ,
       nxout,nyout,nzout,nxyout , i,j,k,ip,jp,kp , nftot ;
   float dxTT,dyTT,dzTT , xorgTT,yorgTT,zorgTT ;
   float dxout,dyout,dzout , xorgout,yorgout,zorgout ;
   float z1,z2 , y1,y2 , x1,x2 , xx1,xx2,yy1,yy2,zz1,zz2 ;
   float f1,f2,f , g1,g2,g , h1,h2,h , sx,sy,sz , tx,ty,tz , sxyz ;
   float vthresh;
   int at_sbi, fim_type, at_nsb; 
   THD_fvec3 vv ;

   /* sanity checks */

   if( !editor_open || dset == NULL ){ XBell(dc->display,100) ; return ; }

   if( !CAN_TALTO(im3d) ){ XBell(dc->display,100); return; }

   /* get default atlas dataset, maybe TTatlas+tlrc */
   
   if (!(dseTT = TT_retrieve_atlas_dset(plugdraw_atlasname, 1))) {
      return;   
   }
   DSET_load(dseTT) ;

   /* setup other info */

   sval = ttatlas_list->reg_ttval [ ttatlas_region_av->ival ] ;

   nvoxTT= DSET_NVOX(dseTT) ;
   nxTT  =dseTT->daxes->nxx  ; nyTT  =dseTT->daxes->nyy  ; nzTT  =dseTT->daxes->nzz  ;
   dxTT  =dseTT->daxes->xxdel; dyTT  =dseTT->daxes->yydel; dzTT  =dseTT->daxes->zzdel;
   xorgTT=dseTT->daxes->xxorg; yorgTT=dseTT->daxes->yyorg; zorgTT=dseTT->daxes->zzorg;

   nvoxout= DSET_NVOX(dset) ;
   voxout = (float *) calloc(sizeof(float),nvoxout) ;
   nxout  =dset->daxes->nxx  ; nyout  =dset->daxes->nyy  ; nzout  =dset->daxes->nzz  ;
   dxout  =dset->daxes->xxdel; dyout  =dset->daxes->yydel; dzout  =dset->daxes->zzdel;
   xorgout=dset->daxes->xxorg; yorgout=dset->daxes->yyorg; zorgout=dset->daxes->zzorg;
   nxyout = nxout*nyout ;
   nxyTT  = nxTT *nyTT  ;

   switch( ttatlas_hemisphere_av->ival ){
      case TTRR_HEMI_LEFT:  hbot=1+nxTT/2 ; htop=nxTT     ; break ;
      case TTRR_HEMI_RIGHT: hbot= 0       ; htop=1+nxTT/2 ; break ;

      default:
      case TTRR_HEMI_BOTH:  hbot= 0       ; htop=nxTT     ; break ;
   }


   /* loop over voxels in the default atlas dataset,
      transform to current dataset coordinates,
      count overlap (a la 3dfractionize)            */

   at_nsb = DSET_NVALS(dseTT);
   for( at_sbi=0; at_sbi < at_nsb; at_sbi++) {
      b0im = DSET_BRICK(dseTT, at_sbi);
      if( b0im == NULL )
         return ;
      fim_type = b0im->kind ;
      switch( fim_type ){
         default:
            return ;
         case MRI_byte:
            bb = MRI_BYTE_PTR(b0im);
         break ;
         case MRI_short:
            ss = MRI_SHORT_PTR(b0im);
         break ;
         case MRI_float:
            ff = MRI_FLOAT_PTR(b0im);
         break ;
      }

      for( kv=0 ; kv < nzTT ; kv++ ){
       z1 = zorgTT + dzTT * (kv-0.5) ; z2 = zorgTT + dzTT * (kv+0.49999) ;

       for( jv=0 ; jv < nyTT ; jv++ ){
        y1 = yorgTT + dyTT * (jv-0.5) ; y2 = yorgTT + dyTT * (jv+0.49999) ;

        for( iv=hbot ; iv < htop ; iv++ ){
         ijk = iv + jv*nxTT + kv*nxyTT ;   /* 1D index of voxel (iv,jv,kv) */

         switch( fim_type ){
            default:
               continue ;  /* shouldn't ever get to this */
            case MRI_byte:
               if( bb[ijk] != (byte) sval ) continue ;  /* not the right value, so skip it */
            break ;
            case MRI_short:
               if( ss[ijk] != sval ) continue ;  /* not the right value, so skip it */
            break ;
            case MRI_float:
               if( ff[ijk] != (float) sval ) continue ;  /* not the right value, so skip it */
            break ;
         }


         /* got here, so voxel found in atlas that matches region number */

         x1 = xorgTT + dxTT * (iv-0.5) ; x2 = xorgTT + dxTT * (iv+0.49999) ;

         /* input voxel (iv,jv,kv) spans coordinates [x1,x2] X [y1,y2] X [z1,z2] */

         /* transform these corner coordinates to output dataset grid coordinates */

         if( dset->view_type == VIEW_TALAIRACH_TYPE ){
            xx1 = x1 ; yy1 = y1 ; zz1 = z1 ;
            xx2 = x2 ; yy2 = y2 ; zz2 = z2 ;
         } else {
            LOAD_FVEC3(vv , x1,y1,z1) ;
            vv = AFNI_transform_vector( im3d->anat_dset[VIEW_TALAIRACH_TYPE] ,
                                        vv , im3d->anat_now ) ;
            vv = THD_dicomm_to_3dmm( dset , vv );
            UNLOAD_FVEC3(vv , xx1,yy1,zz1) ;

            LOAD_FVEC3(vv , x2,y2,z2) ;
            vv = AFNI_transform_vector( im3d->anat_dset[VIEW_TALAIRACH_TYPE] ,
                                        vv , im3d->anat_now ) ;
            vv = THD_dicomm_to_3dmm( dset , vv ) ;
            UNLOAD_FVEC3(vv , xx2,yy2,zz2) ;
         }

         /* [xx1,xx2] X [yy1,yy2] X [zz1,zz2] is now in coordinates of output dataset */

         /* compute indices into output dataset voxel (keeping fractions) */

         f1 = (xx1-xorgout)/dxout + 0.49999 ; f2 = (xx2-xorgout)/dxout + 0.49999 ;
         if( f1 > f2 ){ tx = f1 ; f1 = f2 ; f2 = tx ; }
         if( f1 >= nxout || f2 <= 0.0 ) continue ;
         if( f1 < 0.0 ) f1 = 0.0 ;  if( f2 >= nxout ) f2 = nxout - 0.001 ;

         g1 = (yy1-yorgout)/dyout + 0.49999 ; g2 = (yy2-yorgout)/dyout + 0.49999 ;
         if( g1 > g2 ){ ty = g1 ; g1 = g2 ; g2 = ty ; }
         if( g1 >= nyout || g2 <= 0.0 ) continue ;
         if( g1 < 0.0 ) g1 = 0.0 ;  if( g2 >= nyout ) g2 = nyout - 0.001 ;

         h1 = (zz1-zorgout)/dzout + 0.49999 ; h2 = (zz2-zorgout)/dzout + 0.49999 ;
         if( h1 > h2 ){ tz = h1 ; h1 = h2 ; h2 = tz ; }
         if( h1 >= nzout || h2 <= 0.0 ) continue ;
         if( h1 < 0.0 ) h1 = 0.0 ;  if( h2 >= nzout ) h2 = nzout - 0.001 ;

         /* input voxel covers voxels [f1,f2] X [g1,g2] X [h1,h2] in the output */

         /* For example, [6.3,7.2] X [9.3,9.6] X [11.7,13.4], which must be     */
         /* distributed into these voxels:                                      */
         /*  (6,9,11), (7,9,11), (6,9,12), (7,9,12), (6,9,13), and (7,9,13)     */

         for( f=f1 ; f < f2 ; f = ip ){
            i = (int) f ; ip = i+1 ; tx = MIN(ip,f2) ; sx = tx - f ;
            for( g=g1 ; g < g2 ; g = jp ){
               j = (int) g ; jp = j+1 ; ty = MIN(jp,g2) ; sy = ty - g ;
               for( h=h1 ; h < h2 ; h = kp ){
                  k = (int) h ; kp = k+1 ; tz = MIN(kp,h2) ; sz = tz - h ;
                  sxyz = sx * sy * sz ;
                  voxout[ i + j*nxout + k * nxyout ] += (float)(100.0*sxyz) ;
               }
            }
         }

   }}}} /* end of loop over voxels and sub-bricks */

   /** at this point, voxout[ijk] stores how much overlap each output
       voxel has with an Atlas voxel which had the target value;
       now, count voxels with enough overlap, and store their indexes **/

   vthresh = get_afni_thresh_percent();

   for( nftot=ijk=0 ; ijk < nvoxout ; ijk++ ){
      if(voxout[ijk] >= vthresh) nftot++ ;
   }
   /* now load results into dataset */

   if( nftot > 0 ){
     int *xd = (int *) malloc(sizeof(int)*nftot) , ff ;

     for( ff=ijk=0 ; ijk < nvoxout ; ijk++ )
       if( voxout[ijk] >= vthresh ) xd[ff++] = ijk ;

     infill_mode = (strcmp(XtName(w),TTATLAS_infill_label) == 0) ;
     ff = DRAW_into_dataset( nftot , xd,NULL,NULL , NULL ) ;
     infill_mode = 0 ;

     free(xd) ;

     fprintf(stderr,"++ %d TT Atlas voxels drawn into dataset\n",ff) ;
     PLUTO_dset_redisplay( dset ) ;
     dset_changed = 1 ;
     SENSITIZE(save_pb,1) ; SENSITIZE(saveas_pb,1) ;
     if( recv_open ) AFNI_process_drawnotice( im3d ) ;
   } else {
      fprintf(stderr,"++ No atlas voxels found at all for some reason!?\a\n") ;
      fprintf(stderr,"   Consider setting the value for AFNI_DRAW_THRESH to less than %f\n", vthresh);
   }

   free(voxout) ; /* toss trash */
   return ;
}

/*-----------------------------------------------------------------------
  Copy a dataset; new prefix is "COPY_" + old prefix.
    zfill != 0  ==> zero fill
    ftyp  <= 0  ==> same func type
    ftyp  == 1  ==> fim
    ftyp  == 2  ==> anat (omri)
    dtype <  0  ==> same datum
    dtype >= 0  ==> new datum (only valid for zfill)
                          Adapted from plug_copy.c -- 24 Sep 2001 - RWCox
-------------------------------------------------------------------------*/

THD_3dim_dataset * DRAW_copy_dset( THD_3dim_dataset *dset ,
                                   int zfill , int ftyp , int dtype )
{
   THD_3dim_dataset *new_dset ;
   char new_prefix[THD_MAX_PREFIX] ;
   int ival ;

   if( !ISVALID_DSET(dset) ) return NULL ;

   if( strstr(DSET_PREFIX(dset),"COPY") != NULL ) strcpy(new_prefix,"C") ;
   else                                           strcpy(new_prefix,"COPY_") ;
   ival = strlen(new_prefix) ;
   MCW_strncpy(new_prefix+ival,DSET_PREFIX(dset),THD_MAX_PREFIX-ival) ;

   /*-- make a new dataset, somehow --*/

   if( zfill == 0 ){
     new_dset = PLUTO_copy_dset( dset , new_prefix ) ;  /* full copy */
     dtype = -1 ;
   } else {
     new_dset = EDIT_empty_copy( dset ) ;               /* zero fill */
     EDIT_dset_items( new_dset, ADN_prefix,new_prefix, ADN_none ) ;
   }

   if( new_dset == NULL ) return NULL ; /* bad, real bad */

   tross_Copy_History( dset , new_dset ) ;  /* make some History, dude! */
   { char str[256] ;
     strcpy(str,"Drawing plugin COPY:") ;
     if( zfill ) strcat(str," Fill->Zero") ;
     else        strcat(str," Fill->Data") ;
          if( ftyp == 1 ) strcat(str," Type->Func") ;
     else if( ftyp == 2 ) strcat(str," Type->Anat") ;
     if( dtype >= 0 ){
       strcat(str," Datum->") ; strcat(str,MRI_TYPE_name[dtype]) ;
     }
     tross_Append_History( new_dset , str ) ;
   }

   /*--- modify new dataset, if desired ---*/

   if( ftyp == 1 )
      EDIT_dset_items( new_dset ,
                         ADN_type      , HEAD_FUNC_TYPE ,
                         ADN_func_type , FUNC_FIM_TYPE  ,
                       ADN_none ) ;
   else if( ftyp == 2 )
      EDIT_dset_items( new_dset ,
                         ADN_type      , HEAD_ANAT_TYPE ,
                         ADN_func_type , ANAT_OMRI_TYPE ,
                       ADN_none ) ;

   if( zfill == 0 ) return new_dset ;  /* done if not zero-filling */

   /*--- change type of data stored? ---*/

   if( dtype >= 0 ) EDIT_dset_items( new_dset ,
                                       ADN_datum_all, dtype,
                                     ADN_none ) ;
   /* zero fill */

   {  int ityp , nbytes , nvals , ival ;
      void * new_brick , * bp ;

      nvals = DSET_NVALS(new_dset) ;

      for( ival=0 ; ival < nvals ; ival++)             /* get memory for bricks */
      {                                                /* and zero fill */
         ityp      = DSET_BRICK_TYPE(new_dset,ival) ;
         nbytes    = DSET_BRICK_BYTES(new_dset,ival) ; /* how much data */
         new_brick = malloc( nbytes ) ;
         EDIT_substitute_brick( new_dset , ival , ityp , new_brick ) ;

         bp = DSET_BRICK_ARRAY(new_dset,ival) ;        /* brick pointer */
         EDIT_BRICK_FACTOR(new_dset,ival,0.0) ;        /* brick factor  */
         memset( bp , 0 , nbytes ) ;
      }
   }

   /* 20 Oct 2003: copy VALUE_LABEL_DTABLE attribute, if present */

   { ATR_string *atr ;
     atr = THD_find_string_atr( dset->dblk , "VALUE_LABEL_DTABLE" ) ;
     if( atr != NULL )
       THD_set_char_atr( new_dset->dblk , "VALUE_LABEL_DTABLE" ,
                         atr->nch , atr->ch                     ) ;
   }

   new_dset->int_cmap = 1;
   THD_set_int_atr(new_dset->dblk, "INT_CMAP", 1, &new_dset->int_cmap);

   /*-- done successfully!!! --*/

   return new_dset ;
}

/*-----------------------------------------------------------------------------*/
/*! Expand set of points in 2D plane.  RWCox - 07 Oct 2002.
-------------------------------------------------------------------------------*/

static void DRAW_2D_expand( int np, int *xd, int *yd, int *zd, int plane ,
                            int *nfill , int **xyzf )
{
   int base , di,dj , itop,jtop , xx,yy,zz , ix,jy , *ip,*jp ;
   int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ;
   int kadd , ii,jj,kk , ixn,jyn , mm,qq ;
   int *xyzn ;

   static int nadd[5] = { 4 , 8 , 12 , 20 , 24 } ;
   static int nn[24][2] = { {-1, 0} , { 1, 0} , { 0, 1} , { 0,-1} ,
                            {-1,-1} , {-1, 1} , { 1,-1} , { 1, 1} ,
                            {-2, 0} , { 2, 0} , { 0, 2} , { 0,-2} ,
                            {-2, 1} , {-2,-1} , {-1, 2} , {-1,-2} ,
                            { 2, 1} , { 2,-1} , { 1, 2} , { 1,-2} ,
                            {-2,-2} , {-2, 2} , { 2,-2} , { 2, 2}  } ;

   /* check inputs */

   if( np <= 0 || xd == NULL || yd == NULL || zd == NULL )     return ;
   if( mode_ival < FIRST_2D_MODE && mode_ival > LAST_2D_MODE ) return ;
   if( nfill == NULL || xyzf == NULL )                         return ;

   /* compute stuff for which plane we are in:
       1 -> yz , 2 -> xz , 3 -> xy            */

   xx = xd[0] ; yy = yd[0] ; zz = zd[0] ;
   switch(plane){
     case 1: base=xx    ; di=nx; dj=nxy; itop=ny; jtop=nz; ip=yd; jp=zd; break;
     case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ip=xd; jp=zd; break;
     case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ip=xd; jp=yd; break;
     default: return ;  /* bad input */
   }

   kadd = nadd[mode_ival-FIRST_2D_MODE] ;  /* how many pts around each input pt */

   xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ;   /* output array */

   /** add points around each input point, culling duplicates **/

   for( ii=jj=0 ; ii < np ; ii++ ){
     ix = ip[ii] ; jy = jp[ii] ;                                /* drawn point 2D index */
     if( ix >= 0 && ix < itop && jy >= 0 && jy < jtop ){
       xyzn[jj++] = base + ix*di + jy*dj ;                      /* load 3D index */
       for( kk=0 ; kk < kadd ; kk++ ){
         ixn = ix+nn[kk][0] ; jyn = jy+nn[kk][1] ;              /* nbhd pt 2D index */
         if( ixn >= 0 && ixn < itop && jyn >= 0 && jyn < jtop ){
           mm = base + ixn*di + jyn*dj ;                        /* 3D index */
           if( ii > 0 )
             for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ;   /* nada */
           else
             qq = jj ;
           if( qq == jj ) xyzn[jj++] = mm ;                     /* save 3D index */
         }
       }
     }
   }

   *nfill = jj ; *xyzf  = xyzn ; return ;
}

/*-----------------------------------------------------------------------------*/
/*! Expand set of points in 3D space.  RWCox - 07 Oct 2002.
-------------------------------------------------------------------------------*/

static void DRAW_3D_expand( int np, int *xd, int *yd, int *zd, int plane ,
                            int *nfill , int **xyzf )
{
   int ix,jy,kz ;
   int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ;
   int kadd , ii,jj,kk , ixn,jyn,kzn , mm,qq ;
   int *xyzn ;

   static int nadd[7] = { 6 , 18 , 26 , 32 , 56 , 80 , 124 } ;

   static int nn[124][3] ={ {-1, 0, 0} , { 1, 0, 0} ,  /* r**2 = 1 */
                            { 0,-1, 0} , { 0, 1, 0} ,
                            { 0, 0,-1} , { 0, 0, 1} ,

                            {-1,-1, 0} , {-1, 1, 0} ,  /* r**2 = 2 */
                            { 1,-1, 0} , { 1, 1, 0} ,
                            { 0,-1,-1} , { 0,-1, 1} ,
                            { 0, 1,-1} , { 0, 1, 1} ,
                            {-1, 0,-1} , {-1, 0, 1} ,
                            { 1, 0,-1} , { 1, 0, 1} ,

                            {-1,-1,-1} , {-1,-1, 1} ,  /* r**2 = 3 */
                            {-1, 1,-1} , {-1, 1, 1} ,
                            { 1,-1,-1} , { 1,-1, 1} ,
                            { 1, 1,-1} , { 1, 1, 1} ,

                            {-2, 0, 0} , { 2, 0, 0} ,  /* r**2 = 4 */
                            { 0,-2, 0} , { 0, 2, 0} ,
                            { 0, 0,-2} , { 0, 0, 2} ,

                            {-2,-1, 0} , {-2, 1, 0} ,  /* r**2 = 5 */
                            { 2,-1, 0} , { 2, 1, 0} ,
                            { 0,-2,-1} , { 0,-2, 1} ,
                            { 0, 2,-1} , { 0, 2, 1} ,
                            {-2, 0,-1} , {-2, 0, 1} ,
                            { 2, 0,-1} , { 2, 0, 1} ,
                            {-1,-2, 0} , {-1, 2, 0} ,
                            { 1,-2, 0} , { 1, 2, 0} ,
                            { 0,-1,-2} , { 0,-1, 2} ,
                            { 0, 1,-2} , { 0, 1, 2} ,
                            {-1, 0,-2} , {-1, 0, 2} ,
                            { 1, 0,-2} , { 1, 0, 2} ,

                            {-2,-1,-1} , {-2,-1, 1} ,  /* r**2 = 6 */
                            {-2, 1,-1} , {-2, 1, 1} ,
                            { 2,-1,-1} , { 2,-1, 1} ,
                            { 2, 1,-1} , { 2, 1, 1} ,
                            {-1,-2,-1} , {-1,-2, 1} ,
                            {-1, 2,-1} , {-1, 2, 1} ,
                            { 1,-2,-1} , { 1,-2, 1} ,
                            { 1, 2,-1} , { 1, 2, 1} ,
                            {-1,-1,-2} , {-1,-1, 2} ,
                            {-1, 1,-2} , {-1, 1, 2} ,
                            { 1,-1,-2} , { 1,-1, 2} ,
                            { 1, 1,-2} , { 1, 1, 2} ,

                            {-2,-2, 0} , {-2, 2, 0} ,  /* r**2 = 8 */
                            { 2,-2, 0} , { 2, 2, 0} ,
                            { 0,-2,-2} , { 0,-2, 2} ,
                            { 0, 2,-2} , { 0, 2, 2} ,
                            {-2, 0,-2} , {-2, 0, 2} ,
                            { 2, 0,-2} , { 2, 0, 2} ,

                            {-2,-2, 1} , {-2, 2, 1} ,  /* r**2 = 9 */
                            { 2,-2, 1} , { 2, 2, 1} ,
                            { 1,-2,-2} , { 1,-2, 2} ,
                            { 1, 2,-2} , { 1, 2, 2} ,
                            {-2, 1,-2} , {-2, 1, 2} ,
                            { 2, 1,-2} , { 2, 1, 2} ,
                            {-2,-2,-1} , {-2, 2,-1} ,
                            { 2,-2,-1} , { 2, 2,-1} ,
                            {-1,-2,-2} , {-1,-2, 2} ,
                            {-1, 2,-2} , {-1, 2, 2} ,
                            {-2,-1,-2} , {-2,-1, 2} ,
                            { 2,-1,-2} , { 2,-1, 2} ,

                            {-2,-2,-2} , {-2,-2, 2} ,  /* r**2 = 12          */
                            {-2, 2,-2} , {-2, 2, 2} ,  /* [corners of 5x5x5] */
                            { 2,-2,-2} , { 2,-2, 2} ,
                            { 2, 2,-2} , { 2, 2, 2}
                          } ;

   /* check inputs */

   if( np <= 0 || xd == NULL || yd == NULL || zd == NULL )     return ;
   if( mode_ival < FIRST_3D_MODE && mode_ival > LAST_3D_MODE ) return ;
   if( nfill == NULL || xyzf == NULL )                         return ;

   kadd = nadd[mode_ival-FIRST_3D_MODE] ;  /* how many pts around each input pt */

   xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ;   /* output array */

   /** add points around each input point, culling duplicates **/

   for( ii=jj=0 ; ii < np ; ii++ ){
     ix = xd[ii] ; jy = yd[ii] ; kz = zd[ii] ;
     if( ix >= 0 && ix < nx && jy >= 0 && jy < ny && kz >= 0 && kz <= nz ){
       xyzn[jj++] = ix + jy*nx + kz*nxy ;                       /* load 3D index */
       for( kk=0 ; kk < kadd ; kk++ ){
         ixn = ix+nn[kk][0] ; jyn = jy+nn[kk][1] ; kzn = kz+nn[kk][2] ;
         if( ixn >= 0 && ixn < nx && jyn >= 0 && jyn < ny && kzn >= 0 && kzn < nz ){
           mm = ixn + jyn*nx + kzn*nxy ;                        /* 3D index */
           if( ii > 0 )
             for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ;   /* nada */
           else
             qq = jj ;
           if( qq == jj ) xyzn[jj++] = mm ;                     /* save 3D index */
         }
       }
     }
   }

   *nfill = jj ; *xyzf  = xyzn ; return ;
}

/*-----------------------------------------------------------------------------*/
/*! Expand set of points in 2D plane, in a circle.  RWCox - 16 Oct 2002.
-------------------------------------------------------------------------------*/

static void DRAW_2D_circle( int np, int *xd, int *yd, int *zd, int plane ,
                            int *nfill , int **xyzf )
{
   int base , di,dj , itop,jtop , xx,yy,zz , ix,jy , *ip,*jp ;
   int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ;
   int kadd , ii,jj,kk , ixn,jyn , mm ;
   int *xyzn ;

   float dx = fabs(DSET_DX(dset)) ;
   float dy = fabs(DSET_DY(dset)) ;
   float dz = fabs(DSET_DZ(dset)) ;
   float rad= rad_av->fval ;
   float fdi,fdj , xq,yq,radq ;
   int idx , jdy , *nn ;

   /* check inputs */

   if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ;
   if( nfill == NULL || xyzf == NULL )                     return ;

   /* compute stuff for which plane we are in:
       1 -> yz , 2 -> xz , 3 -> xy            */

   xx = xd[0] ; yy = yd[0] ; zz = zd[0] ;
   switch(plane){
     case 1: base=xx    ; di=nx; dj=nxy; itop=ny; jtop=nz; ip=yd; jp=zd; fdi=dy; fdj=dz; break;
     case 2: base=yy*nx ; di=1 ; dj=nxy; itop=nx; jtop=nz; ip=xd; jp=zd; fdi=dx; fdj=dz; break;
     case 3: base=zz*nxy; di=1 ; dj=nx ; itop=nx; jtop=ny; ip=xd; jp=yd; fdi=dx; fdj=dy; break;
     default: return ;  /* bad input */
   }

   idx = rad / fdi ; jdy = rad / fdj ;
   if( idx < 1 && jdy < 1 ) return ;       /* circle smaller than in-plane voxel */

   /* make incremental mask */

   radq = 1.001*rad*rad ;
   nn   = (int *) malloc( sizeof(int)*(2*idx+1)*(2*jdy+1)*2 ) ;
   kadd = 0 ;
   for( jj=-jdy ; jj <= jdy ; jj++ ){
     yq = (jj*fdj)*(jj*fdj) ;
     for( ii=-idx ; ii <= idx ; ii++ ){
       xq = (ii*fdi)*(ii*fdi) + yq ;
       if( xq <= radq && xq > 0.0 ){
         nn[2*kadd]   = ii ;
         nn[2*kadd+1] = jj ;
         kadd++ ;
       }
     }
   }

   xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ;   /* output array */

   /** add points around each input point, culling duplicates **/

   for( ii=jj=0 ; ii < np ; ii++ ){
     ix = ip[ii] ; jy = jp[ii] ;                                /* drawn point 2D index */
     if( ix >= 0 && ix < itop && jy >= 0 && jy < jtop ){
       xyzn[jj++] = base + ix*di + jy*dj ;                      /* load 3D index */
       for( kk=0 ; kk < kadd ; kk++ ){
         ixn = ix+nn[2*kk] ; jyn = jy+nn[2*kk+1] ;              /* nbhd pt 2D index */
         if( ixn >= 0 && ixn < itop && jyn >= 0 && jyn < jtop ){
           mm = base + ixn*di + jyn*dj ;                        /* 3D index */
#ifndef USE_COLLAPSAR
           if( ii > 0 )
             for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ;   /* nada */
           else
             qq = jj ;
           if( qq == jj ) xyzn[jj++] = mm ;                     /* save 3D index */
#else
           xyzn[jj++] = mm ;                                    /* save 3D index */
#endif
         }
       }

#ifdef USE_COLLAPSAR
       if( ii > 9 && (ii==np-1 || ii%20==0) ) DRAW_collapsar( &jj , xyzn ) ;
#endif
     }
   }

   *nfill = jj ; *xyzf = xyzn ; free(nn) ; return ;
}

/*-----------------------------------------------------------------------------*/
/*! Expand set of points in 3D, in a sphere.  RWCox - 16 Oct 2002.
-------------------------------------------------------------------------------*/

static void DRAW_3D_sphere( int np, int *xd, int *yd, int *zd, int plane ,
                            int *nfill , int **xyzf )
{
   int ix,jy,kz ;
   int nx=DSET_NX(dset) , ny=DSET_NY(dset) , nz=DSET_NZ(dset) , nxy = nx*ny ;
   int kadd , ii,jj,kk , ixn,jyn,kzn , mm ;
   int *xyzn ;

   float dx = fabs(DSET_DX(dset)) ;
   float dy = fabs(DSET_DY(dset)) ;
   float dz = fabs(DSET_DZ(dset)) ;
   float rad= rad_av->fval ;
   float xq,yq,zq,radq ;
   int idx , jdy , kdz , *nn ;
   int www ;

   /* check inputs */

   if( np <= 0 || xd == NULL || yd == NULL || zd == NULL ) return ;
   if( nfill == NULL || xyzf == NULL )                     return ;

   idx = rad/dx ; jdy = rad/dy ; kdz = rad/dz ;
   if( idx < 1 && jdy < 1 && kdz < 1 ) return ;   /* sphere smaller than voxel */

#if 0
fprintf(stderr,"DRAW_3D_sphere: rad=%g  dx=%g idx=%d  dy=%g jdy=%d  dz=%g kdz=%d\n",
        rad,dx,idx,dy,jdy,dz,kdz ) ;
#endif

   /* make incremental mask */

   radq = 1.001*rad*rad ;
   nn   = (int *) malloc( sizeof(int)*(2*idx+1)*(2*jdy+1)*(2*kdz+1)*3 ) ;
   kadd = 0 ;
   for( kk=-kdz ; kk <= kdz ; kk++ ){
     zq = (kk*dz)*(kk*dz) ;
     for( jj=-jdy ; jj <= jdy ; jj++ ){
       yq = zq + (jj*dy)*(jj*dy) ;
       for( ii=-idx ; ii <= idx ; ii++ ){
         xq = yq + (ii*dx)*(ii*dx) ;
         if( xq <= radq && xq > 0.0 ){
           nn[3*kadd]   = ii ;
           nn[3*kadd+1] = jj ;
           nn[3*kadd+2] = kk ;
           kadd++ ;
         }
       }
     }
   }

   xyzn = (int *) malloc( sizeof(int)*np*(kadd+1) ) ;   /* output array */

   if( xyzn == NULL ){
     fprintf(stderr,"\n** DRAW_3D_sphere ERROR: can't allocate memory!\n\a");
     free(nn); return;
   }

   www = (np*(kadd+1) > 1234567) && (np > 1) ;          /* show waiting? */
   if( www ) SHOW_AFNI_PAUSE ;

   /** add points around each input point **/

   for( ii=jj=0 ; ii < np ; ii++ ){
     ix = xd[ii] ; jy = yd[ii] ; kz = zd[ii] ;
     if( ix >= 0 && ix < nx && jy >= 0 && jy < ny && kz >= 0 && kz <= nz ){
       xyzn[jj++] = ix + jy*nx + kz*nxy ;                       /* load 3D index */
       for( kk=0 ; kk < kadd ; kk++ ){
         ixn = ix+nn[3*kk] ; jyn = jy+nn[3*kk+1] ; kzn = kz+nn[3*kk+2] ;
         if( ixn >= 0 && ixn < nx && jyn >= 0 && jyn < ny && kzn >= 0 && kzn < nz ){
           mm = ixn + jyn*nx + kzn*nxy ;                        /* 3D index */
#ifndef USE_COLLAPSAR
           if( ii > 0 )
             for( qq=0 ; qq < jj && xyzn[qq] != mm ; qq++ ) ;   /* nada */
           else
             qq = jj ;
           if( qq == jj ) xyzn[jj++] = mm ;                     /* save 3D index */
#else
           xyzn[jj++] = mm ;                                    /* save 3D index */
#endif
         }
       }

#ifdef USE_COLLAPSAR
       if( ii > 5 && (ii==np-1 || ii%16==0) ) DRAW_collapsar( &jj , xyzn ) ;
#endif
     }
   }

   if( www ) SHOW_AFNI_READY ;

   *nfill = jj ; *xyzf = xyzn ; free(nn) ; return ;
}

/*--------------------------------------------------------------------------------*/

#ifdef USE_COLLAPSAR

/*! Collapses the input list of points to non-duplicates. */

static void DRAW_collapsar( int *npt , int *xyzn )
{
   int ii , jj , np ;

   if( npt == NULL || xyzn == NULL ) return ;
   np = *npt ; if( np <= 1 ) return ;

   qsort_int( np , xyzn ) ;                /* sort */

   for( ii=1 ; ii < np ; ii++ )            /* find 1st duplicates */
     if( xyzn[ii] == xyzn[ii-1] ) break ;
   if( ii == np ) return ;                 /* no duplicate => done */

   /* if [ii] is different from [jj],
      then add 1 to jj, and copy [ii] into the new [jj] location;
      otherwise, keep jj fixed (thus skipping [ii])               */

   for( jj=ii-1 ; ii < np ; ii++ ){
     if( xyzn[ii] != xyzn[jj] ) xyzn[++jj] = xyzn[ii] ;
   }

   *npt = jj+1 ; return ;
}
#endif  /* USE_COLLAPSAR */

static MCW_arrowval *
Atlas_chooser(Widget rc)
{
   int k, nr , atlasind=0 ;
   ATLAS_LIST *atl=NULL;
   MCW_arrowval *atlas_av;
   
   if( Atlas_With_Trimming(Current_Atlas_Default_Name(),1, NULL) <= 0 )
      return(NULL);

    /*** make list of atlases to include ***/
 
   atl = env_atlas_list();
   nr = atl->natlases;
   if (!(atlas_names = Atlas_Names_List(atl))) {
      WARNING_message("No atlases in default list.");
      return(NULL);
   }
 
   /* get the actual atlas index to put in as default */

   for (k=0; k<nr; ++k) {
     if(strcmp(Current_Atlas_Default_Name(), atlas_names[k])==0)
        atlasind = k; 
   }


   /*** Atlas chooser ***/
   atlas_av = new_MCW_optmenu( rc , "Choose Atlas" ,
                                        0 , nr-1 , atlasind , 0 ,
                            (gen_func *)choose_new_atlas_CB, NULL ,
                            (str_func *)MCW_av_substring_CB ,
                                        atlas_names ) ;

    MCW_reghelp_children( atlas_av->wrowcol ,
                       "Use this to popup a\n"
                       "'chooser' that lets\n"
                       "you select which\n"
                       "atlas to edit. The list\n"
                       "comes from AFNI_ATLAS_LIST\n"
                       "or default list\n"
                     ) ;
    MCW_reghint_children( atlas_av->wrowcol , "Popup an atlas chooser" ) ;

   XtManageChild( rc ) ;

   return(atlas_av);
}

/* callback function for choosing a new atlas */
static void 
choose_new_atlas_CB(MCW_arrowval *av , XtPointer cd )
{
   int ii, nr;
   ATLAS_POINT *tto_list=NULL;
   char **textdata;
   char *tempatlasname;
   char title_str[256];
   XmString xstr ;
   
   textdata = av->text_data;
   

   /* check if the atlas is the same one we already have */
   if(strcmp(plugdraw_atlasname, textdata[av->ival])==0)
      return;


   tempatlasname = textdata[av->ival]; /* point to current atlas name */
   if(atlas_n_points(tempatlasname)<=0){
      ERROR_message(
          "atlas %s not available.\n"
          "Check that it exists or remove from AFNI_ATLAS_LIST", tempatlasname);
      return;
   }
   plugdraw_atlasname = textdata[av->ival]; /* point to current atlas name */
   free_plugdraw_atlas_list();
  
   /*** make list of atlas regions to include ***/
   if (!(ttatlas_list = New_ttatlas_compendium(plugdraw_atlasname))) {
      ERROR_message("Failed compending on atlas change.");
   }
   tto_list = atlas_points(plugdraw_atlasname);
   nr = 0 ;
   for( ii=0 ; ii < atlas_n_points(plugdraw_atlasname) ; ii++ ){
      ttatlas_list->reg_label [nr] = strdup(tto_list[ii].name) ;
      ttatlas_list->reg_tto   [nr] = ii ;
      ttatlas_list->reg_ttval [nr] = tto_list[ii].tdval ;

      nr++ ;  /* count how many regions actually have some non-zero value */
   }
   ttatlas_list->reg_num = nr ;

   /*** Update region chooser ***/
   refit_MCW_optmenu( ttatlas_region_av ,
                      0 , nr-1 , 0 , 0 ,
                      (str_func *)MCW_av_substring_CB ,
                      ttatlas_list->reg_label ) ;

   AVOPT_columnize( ttatlas_region_av , 3 ) ;
   sprintf(title_str, "Select Atlas and Region (Current Atlas: %s)",
      plugdraw_atlasname);

   xstr = XmStringCreateLtoR( title_str , XmFONTLIST_DEFAULT_TAG ) ;
   XtVaSetValues( atlas_panel ,
                    XmNlabelString , xstr ,
                  NULL ) ;
   XmStringFree(xstr) ;


   return;
}

/* free the region names from menu list */
static void
free_plugdraw_atlas_list()
{
   int i;

   for(i=0;i<ttatlas_list->reg_num;i++){
      free(ttatlas_list->reg_label[i]);
   }
   free(ttatlas_list->reg_label);
   free(ttatlas_list->reg_tto);
   free(ttatlas_list->reg_ttval);
   free(ttatlas_list);

   return;
}

/* return the threshold in percent to cutoff atlas voxels */
static float get_afni_thresh_percent()
{
   static int thresh_set = -1;
   float thresh = 49.0;
   float ppp;

   if(thresh_set > 0)
     return(thresh);

   ppp = AFNI_numenv("AFNI_DRAW_THRESH");

   if( ppp > 0.0f && ppp <= 100.0f ) {
      thresh = ppp; 
   }
   return(thresh);
}
