/* InspectTuple	by Paul Kunz	October 1991
 * The supervisor for the tuple inspection panel
 * 
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University.  All Rights Reserved.
 */ 

#import "Draw.subproj/draw.h"
#import "HGraphicView.h"
#import "InspectTuple.h"

const char InspectTuple_h_rcsid[] = INSPECTTUPLE_H_ID;
const char InspectTuple_m_rcsid[] = "$Id: InspectTuple.m,v 2.35.2.3 1994/02/08 20:29:50 rensing Exp $";

#import "HDrawApp.h"
#import "HTuple.h"
#import "NewInspector.h"
#import "Plot.h"

#define FN_INDEX 0
#define BY_INDEX 1
#define COL_INDEX 2
#define ROW_INDEX 3
#define MB_INDEX 4
#define LOW_RANGE 0
#define HIGH_RANGE 1
#define LOW_X 0
#define LOW_Y 1
#define HIGH_X 2
#define HIGH_Y 3
#define MAX_VALUE 0
#define MIN_VALUE 1
#define SUM_VALUE 2
#define AX_X 0
#define AX_Y 1
#define AX_Z 2
#define AX_W 2
#define AX_XE 3
#define AX_YE 4


@interface InspectTuple(PrivateMethods)
- hTupleForTuple:(ntuple) nt;
  /*
   * Returns the HTuple object managing the ntuple nt, or
   * nil if none found.
   */
   
- (BOOL) isNewTupleFile:(const char *)filename;
  /* return YES if tuple file with filename is not in list of
   * currently opened files, otherwise returns NO.
   */
   
- (BOOL) isReplaceOK:(List *)list with:(HTuple *)tuple;
 /*
  * Checks to see if tuple can be used as replacement for list of Plots
  * in list.   Checks all binding axes of displays and plot and 
  * cut binding if any.
  */
  
- openTupleFile;
 /*
  * Opens the tuple file selected by the OpenPanel.  This method
  * does not run the OpenPanel, rather it is called after it has been run.
  */
  
- openTupleFromFile:(const char *)filename by:(BOOL)refFlag;
  /*
   * Opens ntuple file filename and returns a List of
   * HTuple objects created or nil if file could not be opened.
   * The refFlag is passed to each HTuple object.
   */
   
- (int) openAltTupleFile:(const char *)filename;
  /*
   * Runs the OpenPanel to select a file to replace filename and
   * opens the selected n-tuple file.  The altfilename of the
   * opened Tuples is set to filename.   This method is used
   * when opening a document with tuples by reference, and the
   * ntuple file has changed it's path or doesn't exists
   */
   
- closeTupleFile:(const char *)filename;
 /*
  * Close an n-tuple file after informing each DrawDocument's View
  * that file is about to close.
  */
  
- (int) runModelForTypes;
 /*
  * Runs the openPanel to select tuple binary file with suffix .hippo.
  * Returns HD_OK on success, HD_CANCEL if user canceled.
  */
  
- showPanel;
 /*
  * Orders the NewInspector panel to show the Tuple List and brings
  * Panel front
  */
  
- replaceWithCuts:hTuple;
  /*
   * Replaces the HTuple of selected Plots, their cuts, and
   * all plots dependent on those cuts.
   */
   
- setButtonsAndTitles;
 /*
  * Enables appropriate axis buttons and titles
  */

- setDisable:(int) axisIndex;
 /*
  * Disables axis button and clears Form for specified Axis index.
  */
  
- setEnable:(int) axisIndex;
 /*
  * Enables axis button and sets label for specified Axis index.
  */
  
- showBinding;
 /*
  * Updates inspector view to show bindings of selected plot
  */
 
  
- updateFileForm;
  /* 
   * Updates the information Form and copyButton.
   */
   
@end

@implementation InspectTuple
- initInspFor:aDraw
{
    PopUpList	*popUpList;
    NXBundle	*bundle;
    char	buffer[MAXPATHLEN+1];
    
    [super initInspFor:aDraw];
    
    bundle = [NXBundle bundleForClass:[self class]];
    if ( [bundle getPath:buffer forResource:"InspectTuple" ofType:"nib"] ) {
    	[NXApp loadNibFile:buffer owner:self
                 withNames:NO  fromZone:[self zone]];
    }
    [theInspector addView:[contentBox contentView]
                  withName:"Data Selection"
		  withSupervisor:self];
    [colBrowser setDoubleAction:@selector(colBrowserDoubleClicked:)];
    openPanel = [OpenPanel new];
  /* the following avoids getting "/private/Net/..." as default */
    [openPanel setDirectory:NXHomeDirectory()];

    tupleList = [[ List allocFromZone:[self zone]] init]; 
    
    popUpList = [popUpButton target];
    [popUpList setTarget:self];
    [popUpList setAction:@selector(formChange:)];
    graphtype = HISTOGRAM;
    tpAxisIndex[0] = 0;
    tpAxisIndex[1] = 0;
    tpAxisIndex[2] = -1;
    tpAxisIndex[3] = -1;
    tpAxisIndex[4] = -1;
    
    return self;
}

/* Action methods */
- axisMatrixClick:sender
{
    Matrix	*colBrowserCells;
    int		currentButton;
    
    currentButton = [tpAxisButtons selectedRow];

    colBrowserCells = [colBrowser matrixInColumn:0];
    [colBrowserCells selectCellAt:tpAxisIndex[currentButton] :0 ];
    [colBrowserCells scrollCellToVisible:tpAxisIndex[currentButton]  :0];
    return self;
}
- colBrowserSingleClicked:sender
{
    Matrix	*colBrowserCells;
    int		tpColSelected, currentButton;
    static int previousTupleN = -1, previousButton = -1;

    colBrowserCells = [colBrowser matrixInColumn:0];
    tpColSelected = [colBrowserCells selectedRow ];
    currentButton = [tpAxisButtons selectedRow];
    if ( popUpRow == 0 ) {
	[infoForm setStringValue:
		    [[colBrowserCells cellAt:tpColSelected :0] stringValue]
		    at:currentButton];
    }
    switch( currentButton ) {
    case AX_X:
    	tpAxisIndex[0] = tpColSelected;
	break;
    case AX_Y:
    	tpAxisIndex[1] = tpColSelected;
	break;
    case AX_W:		/* or AX_Z   */
	if (graphtype == THREEDSCATTER) {
    	    /* case AX_Z: */
    	    tpAxisIndex[2] = tpColSelected;
	    break;
	} else {
    	    /* case AX_W: */
	    if (tpColSelected == previousTupleN &&
	        currentButton == previousButton ) {
		    tpColSelected = -1;
		    if ( popUpRow == 0 ) {
		    	[infoForm setStringValue:"" at:AX_W];
		    }
	    }
	    tpAxisIndex[2] = tpColSelected;
	    previousTupleN = tpColSelected;
	    previousButton = currentButton;
	    break;
	}
    case AX_XE:
	if (graphtype == XYPLOT || graphtype == STRIPCHART) {
		if (tpColSelected == previousTupleN &&
		    currentButton == previousButton) {
			tpColSelected = -1;
			if ( popUpRow == 0 ) {
			    [infoForm setStringValue:"" at:AX_XE];
			}
		}
    		tpAxisIndex[3] = tpColSelected;
	}
	previousTupleN = tpColSelected;
	previousButton = currentButton;
	break;
    case AX_YE:
	if ( tpColSelected == previousTupleN &&
	     currentButton == previousButton    ) {
		tpColSelected = -1;
		if ( popUpRow == 0 ) {
		    [infoForm setStringValue:"" at:AX_YE];
		}
	}
    	tpAxisIndex[4] = tpColSelected;
	previousTupleN = tpColSelected;
	previousButton = currentButton;
	break;
    }
    return self;
}
- colBrowserDoubleClicked:sender
{
    Matrix	*colBrowserCells;
    int		tpColSelected, currentButton;
    static int previousTupleN = -1, previousButton = -1;

    colBrowserCells = [colBrowser matrixInColumn:0];
    tpColSelected = [colBrowserCells selectedRow ];
    currentButton = [tpAxisButtons selectedRow];
    if ( popUpRow == 0 ) {
	[infoForm setStringValue:
		    [[colBrowserCells cellAt:tpColSelected :0] stringValue]
		    at:currentButton];
    }
    switch( currentButton ) {
    case AX_X:
	[graphicView graphicsPerform:@selector(bindAxisX:)
		with :(id)&tpColSelected andDraw:YES];
    	tpAxisIndex[0] = tpColSelected;
	break;
    case AX_Y:
	[graphicView graphicsPerform:@selector(bindAxisY:)
		with :(id)&tpColSelected andDraw:YES];
    	tpAxisIndex[1] = tpColSelected;
	break;
    case AX_W:		/* or AX_Z   */
	if (graphtype == THREEDSCATTER) {
    	    /* case AX_Z: */
	    [graphicView graphicsPerform:@selector(bindAxisZ:)
		with :(id)&tpColSelected andDraw:YES];
    	    tpAxisIndex[2] = tpColSelected;
	    break;
	} else {
    	    /* case AX_W: */
	    if (tpColSelected == previousTupleN &&
	        currentButton == previousButton ) {
		    tpColSelected = -1;
		    if ( popUpRow == 0 ) {
		    	[infoForm setStringValue:"" at:AX_W];
		    }
	    }
	    [graphicView graphicsPerform:@selector(bindAxisW:)
		with :(id)&tpColSelected andDraw:YES];
	    tpAxisIndex[2] = tpColSelected;
	    previousTupleN = tpColSelected;
	    previousButton = currentButton;
	    break;
	}
    case AX_XE:
	if (graphtype == XYPLOT || graphtype == STRIPCHART) {
		if (tpColSelected == previousTupleN &&
		    currentButton == previousButton) {
			tpColSelected = -1;
			if ( popUpRow == 0 ) {
			    [infoForm setStringValue:"" at:AX_XE];
			}
		}
		[graphicView graphicsPerform:@selector(bindAxisXE:)
			with :(id)&tpColSelected andDraw:YES];
    		tpAxisIndex[3] = tpColSelected;
	}
	previousTupleN = tpColSelected;
	previousButton = currentButton;
	break;
    case AX_YE:
	if ( tpColSelected == previousTupleN &&
	     currentButton == previousButton    ) {
		tpColSelected = -1;
		if ( popUpRow == 0 ) {
		    [infoForm setStringValue:"" at:AX_YE];
		}
	}
	[graphicView graphicsPerform:@selector(bindAxisYE:)
		with :(id)&tpColSelected andDraw:YES];
    	tpAxisIndex[4] = tpColSelected;
	previousTupleN = tpColSelected;
	previousButton = currentButton;
	break;
    }
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}

- graphOption:sender
{
    int		color;
    drawtype_t	dt;
	
    if ( [selectedPlot hasFunction] ) {
        NXRunAlertPanel( "Alert", "Can not change plot with function attached",
		          NULL, NULL, NULL );
	return self;
    }	
    graphtype = [[graphTypMatrix selectedCell] tag]; 
    color = 0;
    if ((abs(graphtype) == COLORPLOT) && graphtype > 0) 
	color = 1;

    graphtype = abs(graphtype);

    if (graphtype == XYPLOT || graphtype == STRIPCHART) {
	[selectedPlot getDrawType: &dt];
	if (!(dt & (LINE+POINT+ERRBAR))) {
	    dt += POINT;
	    [graphicView graphicsPerform:@selector(setDrawType:)
		with :(id)&dt andDraw:NO];
	}
    }
	
    if (graphtype == THREEDSCATTER)
    {
	if (tpAxisIndex[2] == -1)
	    tpAxisIndex[2] = 0;	    		
    }
	
    [graphicView graphicsPerform:@selector(setDispType:)
	with :(id)&graphtype andDraw:NO];
    [graphicView graphicsPerform:@selector(setColorType:)
	with :(id)&color andDraw:YES];
    [[[graphicView window] flushWindow] makeKeyWindow];

    lastPlot = nil;

    if(!selectedPlot && !firstPlot)
	[self setButtonsAndTitles];

    return self;
}

- addPlot:sender
{
    Matrix	*colCells;
    Cell	*aCell;
    Plot	*plot = nil;
    graphtype_t	plotType;
    int		i, count;
    int		color, curAxis;
    
    if ( !selectedTuple ) {
        return self;
    }
    colCells = [colBrowser matrixInColumn:0];
    count = [colCells cellCount];
    if ( !count ) return self;
    
    [graphicView deselectAll:self];
    
    plotType = [[graphTypMatrix selectedCell] tag]; 
    color = 0;
    if ( ( abs(plotType) == COLORPLOT) && plotType > 0 ) {
	color = 1;
    }

    plotType = abs(plotType);
    curAxis = [tpAxisButtons selectedRow];
    for ( i = 0; i < count; i++ ) {
        aCell = [colCells cellAt:i :0];
	if ( [aCell isHighlighted] ) {
	    tpAxisIndex[curAxis] = i;
	    plot = [self addPlotOfType:plotType];
	    if ( color ) {
		[plot setColorType:&color];
	    }
	    [graphicView addPlot:plot andSelect:NO];
	}
    }
    [plot select];
    [graphicView getSelection];
    [graphicView recacheSelection];
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}

- openTuple:sender
{
    [openPanel setAccessoryView:nil];
    if ( [self runModelForTypes] != HD_CANCEL ) {
	[self openTupleFile];
    }
    return self;
}

- openTupleAsText:sender
{
    [openPanel setAccessoryView:nil];
    if ( ![openPanel runModal] ) {
    	return self;
    }
    [self openTupleFile];
    return self;
}

- tupleBrowserClick:sender
{
    List		*slist;
    id 			browserMatrix;
    id 			doc;
    id			hTuple;
    int			irc, index;
    BOOL		ok;
    
    browserMatrix = [tupleBrowser matrixInColumn:0];
    index = [browserMatrix selectedRow];
    if ( index < 0 ) {
    	[self setTuple: NULL];
        return self;
    }
    hTuple = [tupleList objectAt:index];
    selectedTuple = [hTuple ntuple];
    [self updateColBrowser];
    [self updateFileForm];
    
    doc = [drawInstance currentDocument];
    if ( !doc ) return self;
    graphicView = [doc view];
    if ( [graphicView hasCutsSelected] ) {
	irc = NXRunAlertPanel( "Alert", "Selected Plot(s) contain cuts,"
	                 " replacing ntuple will effect multiple plots.",
				"OK", "Cancel", NULL);
	if ( irc == NX_ALERTDEFAULT ) {
	    [self replaceWithCuts:hTuple];
	}			
    } else {
        slist = [graphicView selectedGraphics];
	ok = [self isReplaceOK:slist with:hTuple];
	if ( !ok ) return self;
	
	[graphicView graphicsPerform:@selector(replaceTupleWith:)
		with: hTuple andDraw:YES ];
	[[graphicView window] flushWindow];
    }
    return self;
}
- closeTuple:sender
{
    id		windowList;
    id		window, document, view;
    id		usedTupleList;
    id		hTuple;
    id 		browserMatrix;
    int		wcount;
    int		i, irc, index;
    
    browserMatrix = [tupleBrowser matrixInColumn:0];
    index = [browserMatrix selectedRow];
    if ( index < 0 ) return self;
    hTuple = [tupleList objectAt:index];
    windowList = [NXApp windowList];
    wcount = [windowList count];
    while ( wcount-- ) {
        window = [windowList objectAt:wcount];
	document = [window delegate];
	if ( [document isKindOf:[DrawDocument class]] ) {
	    view = [document view];
	    usedTupleList = [view tupleList];
	    i = [usedTupleList indexOf:hTuple];
	    if ( i != NX_NOT_IN_LIST ) {
        	irc = NXRunAlertPanel( "Close", "Tuple in use",
	                               "OK", NULL, NULL);
		return self;
	    }
	}
    }        
    [self closeTupleFile:[hTuple filename]];
    // since this tuple is not in use, there must be no plot selected.
    // make sure selectedPlot == nil in case it has not been updated.
    selectedPlot = nil;
    firstPlot = nil;
    [self tupleBrowserClick: self];
    
    /*
     * force an update.
     * make sure to get the current view. it may have been freed.
     */
    [self load:[[drawInstance currentDocument] view]];
    return self;
}
- changeImportMode:sender
{
    id	hTuple;
    
    hTuple = [self currentHTuple];
    if ( [hTuple isRef ] ) {
        [hTuple setIsRef:NO];
    } else {
        [hTuple setIsRef:YES];
    }
    [self updateFileForm];        
    return self;
}
- formChange:sender
{
    popUpRow = [sender selectedRow];
    switch (popUpRow ) {
        case 0 :
	    [self setButtonsAndTitles];
	    break;
	case 1 :
	    [self updateFileForm];
	    break;
	default :
	    ; /* should never happen */
    }
    return self;
}
	    
/* Methods invoked by other objects */
- hTupleForFile:(const char *)filename index:(int)iValue
{
    const char	*altfile;
    id		hTuple;
    int		i;
    
    i = [tupleList count];
    while ( i-- ) {
        hTuple = [tupleList objectAt:i];
	if ( [hTuple index] == iValue ) {
	    if ( strcmp( [hTuple filename], filename ) == 0 ) {
		return hTuple;
	    }
	    altfile = [hTuple altfilename];
	    if ( altfile && (strcmp(altfile, filename) == 0 ) ) {
		return hTuple;
	    }
	}
    }
    
    return nil;
}

- openTuple:pasteBoard userData:(const char *)args error:(char **)errorMsg
{
    id		hTuple;
    FILE 	*infile;
    ntuple	*ntlist;
    const char *const *ptypes;
    const char *data;
    char 	filename[25];
    char	*outbuffer;
    int		i, irc, length;
    BOOL	binFlag;

    ptypes = [pasteBoard types];
    for ( i = 0; ptypes[i]; i++ ) {
        if ( !strcmp(ptypes[i], NXAsciiPboardType) ) {
	    [pasteBoard readType:ptypes[i] data:&data length:&length];
	    NX_ZONEMALLOC([self zone], outbuffer, char, (length+2) );
	    strncpy( outbuffer, data, length);
	    outbuffer[length] = '\n';
	    outbuffer[length+1] = '\0';
	    strcpy(filename,"/tmp/file000000Selection" );
	    NXGetTempFilename(filename,9);
	    infile=fopen(filename,"w");
	    fprintf(infile,outbuffer);
	    fclose(infile);
	    NXZoneFree([self zone], outbuffer);
	    if ( (infile = fopen(filename, "r") ) == NULL ) {
		*errorMsg = "Couldn't open /tmp file";
		return nil;
	    }
	    binFlag = NO;
	    NX_ZONEMALLOC( [self zone], ntlist, ntuple, 2 );
	    if ( !(ntlist[0] = h_fileParse( infile, NULL, 0) ) ) {
		NXZoneFree([self zone], ntlist);
		*errorMsg = "Couldn't interpret data as n-tuple";
		return nil;
	    }
	    ntlist[1] = NULL;
	    irc = fclose( infile );
	    unlink(filename);
	    hTuple = [[HTuple allocFromZone:[self zone]]
	                      initTuple:ntlist[0] file:"From Pasteboard"
			      by:NO mode:NO index:0];
	    [hTuple setFakeFilename:YES];
	    [tupleList addObject:hTuple];
	    [tupleBrowser loadColumnZero];
	    [self setTuple:ntlist[0] ];
	    [self showPanel];
	}
    }
    return self;
}
- addTuple:(ntuple) nt
{
    id			hTuple;
    
    hTuple = [[HTuple allocFromZone:[self zone]]
     		      initTuple:nt file:"none"
                      by:NO mode:YES index:0];
    [hTuple setFakeFilename:YES];
    [tupleList addObject:hTuple];
    [tupleBrowser loadColumnZero];
    [ self setTuple:nt];
    return hTuple;
}
- addPlotOfType:(graphtype_t) plotType
{
    HTuple	*hTuple;
    Plot	*plot;
    drawtype_t  drawtype;
    int		saveAxisIndex[5];
    int		i, max;
    
    if ( !selectedTuple ) {
        ntuple nt = [self currentTuple];
	if ( !nt ) return nil;
	[self setTuple:nt];
    }
    
    if ( (plotType !=HISTOGRAM) && (h_getNtDim(selectedTuple) < 2) ) {
        return nil;
    }
    if ( !graphicView ) {
        return nil;
    }

    [self load:graphicView];	/* force an update */
    hTuple = [self currentHTuple];

    plot = [[Plot allocFromZone:[graphicView zone]] 
       initPlotWithTuple:hTuple Type:plotType];
    
    max = h_getNtDim(selectedTuple) - 1;
    for ( i = 0; i < 5; i++ ) {
	saveAxisIndex[i] = MIN( tpAxisIndex[i], max );
    }
 /* do bindings as neccessary */
    [plot bindAxisX:&saveAxisIndex[0]];
    if (plotType != HISTOGRAM) {
        saveAxisIndex[1] = MAX(0, saveAxisIndex[1]);
	[plot bindAxisY:&saveAxisIndex[1]];
    }
    if (plotType == XYPLOT) {
        if ( saveAxisIndex[3] >= 0 || saveAxisIndex[4] >= 0 ) {
	    drawtype = POINT + ERRBAR;
	} else {
	    drawtype = POINT;
	}
	[plot setDrawType:&drawtype];
	[plot bindAxisXE:&saveAxisIndex[3]];
	[plot bindAxisYE:&saveAxisIndex[4]];

    } else {
    	if (plotType == THREEDSCATTER) {
            saveAxisIndex[2] = MAX(0, saveAxisIndex[2]);
    	    [plot bindAxisZ:&saveAxisIndex[2]];
	} else {
            [plot bindAxisW:&saveAxisIndex[2]];
	}
    }
 /* bindings done */
     
    [self setInspectorFor:plot];
    return plot;
}

- (ntuple) currentTuple
{
    id			browserMatrix;
    id			hTuple;
    ntuple		nt;
    int			index;
    
    browserMatrix = [tupleBrowser matrixInColumn:0];
    index = [browserMatrix selectedRow];
    if ( index >= 0 ) {
        hTuple = [tupleList objectAt:index];
        nt = [hTuple ntuple];
    } else {
        nt = NULL;
    }
    return nt;
}

- currentHTuple
{
    id		browserMatrix;
    id		hTuple;
    int		index;
    
    browserMatrix = [tupleBrowser matrixInColumn:0];
    index = [browserMatrix selectedRow];
    if ( index >= 0 ) {
        hTuple = [tupleList objectAt:index];
    } else {
        hTuple = nil;
    }
    return hTuple;
}

- bindDisplays
{
    id		windowList;
    id		window, document, view;
    int		wcount;
    
    windowList = [NXApp windowList];
    wcount = [windowList count];
    while ( wcount-- ) {
        window = [windowList objectAt:wcount];
	document = [window delegate];
	if ( [document isKindOf:[DrawDocument class]] ) {
	    view = [document view];
	    [view bindDisplays];
            [view reDrawPlot];
	}
    }        
    return self;
}

- (int) addTuplesIfAbsent:newList
{
    id		newTuple, hTuple;
    int		newCount, count;
    int		i, j, irc;
    BOOL	isNew;
    
    graphicView = nil;	/* to keep open methods from messaging the view */
    newCount = [newList count];
    for ( i = 0; i < newCount; i++ ) {
        newTuple = [newList objectAt:i];
	isNew = YES;
	count = [tupleList count];
	for ( j = 0; j < count; j++ ) {
	    hTuple = [tupleList objectAt:j];
	    if ( [hTuple isSameTupleAs:newTuple] ) {
	        isNew = NO;
		[newList replaceObjectAt:i with:hTuple];
		[newTuple free];
		break;
	    }
	}
	if ( isNew ) {
	    if ( [newTuple isRef ] ) {
	        irc = [self openTupleFile:[newTuple filename] by:YES];
		if ( irc == HD_NOTEXIST ) {
		    irc = [ self openAltTupleFile:[newTuple filename]];
		    if (irc != HD_OK) return irc;
		}
		j = [tupleList count];
		while ( j-- ) {
		    hTuple = [tupleList objectAt:j];
		    if ( [hTuple isSameTupleAs:newTuple] ) {
		        [hTuple takeFunctionList:[newTuple functionList]];
			[newList replaceObjectAt:i with:hTuple];
			[newTuple free];
			break;
		    }
		}
	    } else {
		[tupleList addObject:newTuple];
		[tupleBrowser loadColumnZero];
		[ self setTuple:[newTuple ntuple]];
	    }
	}
    }
    [self showPanel];
    return HD_OK;
}
- (int) openTupleFile:(const char *)filename by:(BOOL) refFlag
{
    id			newTupleList;
    struct stat		statbuf;
    int			irc, rvalue;
    
    
    if ( stat(filename, &statbuf) ) {
        return HD_NOTEXIST;
    }
    rvalue = HD_OK;
    if( ![self isNewTupleFile:filename] ) {
        irc = NXRunAlertPanel( "Open", "Tuple file already open",
	                       "Replace", "Cancel", NULL);
	if ( irc != NX_ALERTDEFAULT ) {
	    return HD_CANCEL;
	}
	[self closeTupleFile:filename];
	rvalue = HD_REPLACE;
    }		       
    if ( !(newTupleList = [self openTupleFromFile:filename by:refFlag]) ) {
	irc = NXRunAlertPanel("Open", "Couldn't open file",
			"OK", NULL, NULL);
	return HD_CANCEL;
    }
    [newTupleList free];
    return rvalue;
}
- saveAsExportFile:(const char *)filename
{
    HTuple	*htuple;
    ntuple	nt;
    display	disp;
    display	*dispList;
    const char	*ntFilename;
    int		i, irc;
    BOOL	refFlag;
    
    dispList = [graphicView displayList];
    if ( dispList == NULL ) {
        return self;
    }
    
 /* update the ntuple filename for referenced tuples before saving */
    for ( i = 0; (disp = dispList[i]) != NULL; i++) {
	h_setNtByRef( disp, NO, filename );
    }
    if ( h_write( filename, dispList, NULL ) ) {
	irc = NXRunAlertPanel("Alert", "Couldn't save export file",
				"OK", NULL, NULL);
    }
    for ( i = 0; (disp = dispList[i]) != NULL; i++) {
        nt = h_getNtuple( disp );
	htuple = [self hTupleForTuple:nt];
	refFlag = [htuple isRef];
	ntFilename = [htuple filename];
	h_setNtByRef( disp, refFlag, ntFilename );
    }
    return self;
}

- tupleList
{
    return tupleList;
}

- tupleListForFile:(const char *) filename
{
    id		hTuple;
    int		i, count;
    
    if ( tmpTupleList ) {
        [tmpTupleList empty];
    } else {
        tmpTupleList = [[List allocFromZone:[self zone]] initCount: 0];
    }
    count = [tupleList count];
    for ( i = 0; i < count; i++ ) {
        hTuple = [tupleList objectAt:i];
	if ( strcmp( [hTuple filename], filename ) == 0 ) {
	        [tmpTupleList addObject:hTuple];
	}
    }
    return tmpTupleList;
}
- replaceNtuple:(ntuple)oldnt with:(ntuple)newnt andFree:(BOOL)flag
{
    id		oldTuple, newTuple;
    id		document;
    List	*windowList;
    id		view;
    Window	*window;
    int		i, j;
    
    i = [tupleList count];
    while (i--) {
        oldTuple = [tupleList objectAt:i];
	if ( oldnt == [oldTuple ntuple] ) {
	    newTuple = [self addTuple:newnt];
	    [newTuple takeFunctionList:[oldTuple functionList]];
	    windowList = [NXApp windowList];
	    j = [windowList count];
	    while (j-- ) {
	        window = [windowList objectAt:j];
		document = [window delegate];
	        if ( [document isKindOf:[DrawDocument class]] ) {
		    view = [document view];
		    [view replace:oldTuple with:newTuple];
		}
	    }
	    if ( flag ) {
		[tupleList removeObject:oldTuple];
		[oldTuple free];
	    }
	}
	break;
    }
    [tupleBrowser reloadColumn:0];
    [self updateFileForm];
		    
    return self;
}
- setInspectorFor:(Plot *)plot
{
    display     disp;
    graphtype_t type;
	
    disp = [plot histDisplay];
    type = h_getDispType(disp);
    if ([plot isCutPlot])
	[plot setInspector : [hDraw inspectCut]];
    else
	[plot setInspector:[hDraw inspectAxes]];
	
    return self;
}
- updateView
{
    if ( lastPlot == firstPlot &&
         selectedTuple == [firstPlot ntuple] ) {
        return self;
    }
    if ( firstPlot ) {
        [self setTuple:[firstPlot ntuple] ];
	[self showBinding];
	lastPlot = firstPlot;
    } else {
        lastPlot = nil;
    }
    return self;
}
- updateEmptySelection
{
    unsigned int	i;
    
    lastPlot = nil;
    if ( popUpRow != 0 ) return self;
    
    for ( i = 0; i < 5; i++ ) {
	[[tpAxisButtons cellAt:i :0] setEnabled:YES];
    }
    if (comingForward) {
        [self setButtonsAndTitles];
    }
    
    if (!selectedTuple) [self updateColBrowser];
    
    return self;
}
- setTuple:(ntuple) atuple
{
    id                  browserMatrix;
    id			hTuple;
    display		disp;
    unsigned int	i, count;
    
    if (selectedTuple == atuple)
	return self;
    selectedTuple = atuple;
    [self updateColBrowser];
    if (selectedTuple == NULL) return self;

    browserMatrix = [tupleBrowser matrixInColumn:0];
    count = [tupleList count];
    for ( i = 0; i < count; i++ ) {
        hTuple = [tupleList objectAt:i];
        if ( selectedTuple == [hTuple ntuple] ) {
	    [ browserMatrix selectCellAt:i :0];
	    [ self updateFileForm];
	    break;
	}
    }

    if ( firstPlot && graphicView ) {
        disp = [firstPlot histDisplay];
	graphtype = h_getDispType( disp );
    	if ( graphtype == XYPLOT ) {
	    [tpAxisButtons selectCellAt:AX_Y :0];
	}
    }

    return self;
}
- updateColBrowser
{
    [colBrowser loadColumnZero];
    [self showBinding];
    return self;
}
- startUnarchivingFrom:(const char *)directory
{
    return self;
}
/* Delegate Methods for NXBrowsers */
- (BOOL)browser:sender columnIsValid:(int)column;
{
    return YES;
}
- (BOOL)browser:sender selectCell:(const char *)title inColumn:(int)column
{
    return YES;
}
- (int) browser: sender fillMatrix: matrix inColumn: (int) column
{
    int		nrows = 0;
    if ( sender == tupleBrowser ) { 
	nrows = [self fillTupleMatrix: matrix]; 
    }
    else if ( sender == colBrowser ) {
	nrows = [self fillColumnMatrix: matrix];
    }
    return nrows;
}
- (int) fillTupleMatrix:matrix
{
    NXBrowserCell	*aCell;
    HTuple		*hTuple;
    const char		*title;
    int                 i, nrows = 0;

    nrows = [tupleList count];
    for (i = 0; i < nrows; i++) {
	[matrix insertRowAt:i];
	aCell = [matrix cellAt:i :0];
	hTuple = [tupleList objectAt:i];
	title = [hTuple title];
	if ( title && strcmp(title, "") ) {
	    [aCell setStringValue:title];
	} else {
	    [aCell setStringValue:"<none>"];
	}
	[aCell setLeaf:YES];
	[aCell setLoaded:YES];
    }
    [matrix setEmptySelectionEnabled:NO];
    return nrows;
}
- (int) fillColumnMatrix:matrix
{
    NXBrowserCell	*aCell;
    int                 i, nrows = 0;
    if ( selectedTuple ) {
	nrows = h_getNtDim(selectedTuple);
    } else { 
	nrows = 0;
    }
    for (i = 0; i < nrows; i++) {
	[matrix insertRowAt:i];
	aCell = [matrix cellAt:i :0];
	[aCell setStringValue:h_getNtLabel(selectedTuple, i)];
	[aCell setLeaf:YES];
	[aCell setLoaded:YES];
    }
    return nrows;
}

- setMsgMatrix:anObject
{
    float	gray;
    int		i, rows, cols;
    
    msgMatrix = anObject;
    gray = [msgMatrix backgroundGray ];
    [msgMatrix getNumRows:&rows numCols:&cols];
    for ( i = 0; i < rows; i++ ) {
       [[msgMatrix cellAt:i :0] setTextGray:gray];
    }
    return self;
}
/* Obsolete methods, maintained for backward compatibility */
- newTuple:(ntuple) nt 
{

   [self addTuple:nt];
   return self;
}
@end

@implementation InspectTuple(PrivateMethods)
- hTupleForTuple:(ntuple) nt
{
    HTuple	*hTuple;
    int		i;
    
    i = [tupleList count];
    while ( i-- ) {
        hTuple = [tupleList objectAt:i];
	if ( nt == [hTuple ntuple] ) {
	    return hTuple;
	}
    }
    return nil;
}

- (BOOL) isNewTupleFile:(const char *)filename
{
    HTuple	*hTuple;
    int		i;
    
    i = [tupleList count];
    while ( i-- ) {
        hTuple = [tupleList objectAt:i];
	if ( strcmp( [hTuple filename], filename ) == 0 ) {
	    return NO;
	}
    }
    return YES;
}

- (BOOL) isReplaceOK:(List *)list with:(HTuple *)tuple
{
    Plot	*plot;
    ntuple	nt;
    display	disp;
    func_id	cut = NULL;
    int		i, j, ndim;
    BOOL	ok = YES;
    
    nt = [tuple ntuple];
    ndim = h_getNtDim(nt);
    
    i = [list count];
    while( i-- ) {
        plot = [list objectAt:i];
	disp = [plot histDisplay];
	for ( j = 0; j < 5; j++ ) {
	    if ( h_getBinding( disp, j) >= ndim ) {
	        ok = NO;
		break;
	    }
	}
	if ( !ok ) break;
	
        if ( [plot hasCut] ) {
	    cut = NULL;
	    while ( (cut = h_nextCut( disp, cut ) ) != NULL ) {
	        if ( (int)cut->paramBlk[0] >= ndim ) {
		    ok = NO;
		    break;
		}
	    }
	}
	if ( !ok ) break;
    }
    if ( !ok ) {
	NXRunAlertPanel( "Alert", "Replacement n-tuple does not have"
	                 " enough columns to be used on effected plots."
			 "  No replacement made",
	  		 "OK", NULL, NULL);
	selectedTuple = NULL;
	[self updateView];
    }
    return ok;
}

- openTupleFile
{ 
    const char	*filename;
    int         irc;
    BOOL	refFlag;

    filename = [openPanel filename];
    refFlag = YES;
    irc = [self openTupleFile:filename by:refFlag];
    if ( irc == HD_REPLACE ) {
        [self bindDisplays];
    }
    [self showPanel];
    return self;
}

- openTupleFromFile:(const char *)filename by:(BOOL)refFlag
{
    List               *newTupleList;
    HTuple             *hTuple;
    FILE               *infile;
    ntuple             *ntlist = NULL;
    display            *dlist = NULL;
    int                 i;
    BOOL                binFlag;

    if (h_isHippoFile(filename)) {
	binFlag = YES;
	if (h_read(filename, &dlist, &ntlist) != 0) {
	    return nil;
	}
    } else {
	if ((infile = fopen(filename, "r")) == NULL) {
	    NXRunAlertPanel("Reason Alert", "Couldn't open file",
			    "OK", NULL, NULL);
	    return nil;
	}
	binFlag = NO;
	NX_ZONEMALLOC([self zone], ntlist, ntuple, 2);
	if (!(ntlist[0] = h_fileParse(infile, NULL, 0))) {
	    NXZoneFree([self zone], ntlist);
	    return nil;
	}
	ntlist[1] = NULL;
	fclose(infile);
    }

    newTupleList = [[List allocFromZone:[self zone]] initCount:0];
    for (i = 0; ntlist[i] != NULL; i++) {
	hTuple = [[HTuple allocFromZone:[self zone]]
		  initTuple:ntlist[i] file:filename
		  by:refFlag mode:binFlag index:i];
	[tupleList addObject:hTuple];
	[newTupleList addObject:hTuple];
    }
 /* update graphicView because old one could have been freed */
    graphicView = [[drawInstance currentDocument] view];
    [graphicView deselectAll:self];
    firstPlot = nil;
    lastPlot = nil;
    [tupleBrowser loadColumnZero];
    [self setTuple:ntlist[0]];
    return newTupleList;
}

- (int) openAltTupleFile:(const char *)filename
{
    List		*newTupleList;
    HTuple		*hTuple;
    const char 		*newfilename;
    char		*suffix;
    float		gray;
    int			i, count;
    int			rows, cols;
    int                 irc;
    BOOL		refFlag;

    [msgMatrix getNumRows:&rows numCols:&cols];
    for (i = 0; i < rows; i++) {
	[[msgMatrix cellAt:i :0] setTextGray:0.];
    }
    [[msgMatrix cellAt:1 :0] setStringValue:filename];
    
    [openPanel setAccessoryView:accessoryView];
    suffix = rindex(filename, '.');
    if (suffix && !strcmp(suffix, ".hippo") ) {
	irc = [self runModelForTypes];
    } else {
	if ( ![openPanel runModal] ) {
	    irc = HD_CANCEL;
	} else {
	    irc = HD_OK;
        }
    }
    gray = [msgMatrix backgroundGray];
    for (i = 0; i < rows; i++) {
	[[msgMatrix cellAt:i :0] setTextGray:gray];
    }
    if ( irc == HD_CANCEL ) {
        return irc;
    }
    
    newfilename = [openPanel filename];
    if ( ![self isNewTupleFile:newfilename] ) {
        newTupleList = [self tupleListForFile:newfilename];
	count = [newTupleList count];
	for ( i = 0; i < count; i++ ) {
	    hTuple = [newTupleList objectAt:i];
	    [hTuple setAltFilename:filename];
	}
	return HD_OK;
    }
    refFlag = YES;
    if ( !(newTupleList = [self openTupleFromFile:newfilename by:refFlag]) ) {
	irc = NXRunAlertPanel("Open", "Couldn't open file",
			"OK", NULL, NULL);
	return HD_CANCEL;
    }
    count = [newTupleList count];
    for ( i = 0; i < count; i++ ) {
        hTuple = [newTupleList objectAt:i];
	[hTuple setAltFilename:filename];
    }
    return HD_OK;
}

- closeTupleFile:(const char *)filename
{
    List	 *windowList;
    Window	 *window;
    DrawDocument *document;
    HGraphicView *view;
    HTuple	 *hTuple;
    int		 count;
    
    windowList = [NXApp windowList];
    count = [windowList count];
    while ( count-- ) {
        window = [windowList objectAt:count];
	document = [window delegate];
	if ( [document isKindOf:[DrawDocument class]] ) {
	    view = [document view];
	    [view closeTupleFile:filename];
	}
    }
    tmpTupleList = [self tupleListForFile:filename];
    count = [tmpTupleList count];
    while ( count-- ) {
        hTuple = [tmpTupleList objectAt:count];
	[tupleList removeObject:hTuple];
    }
    [tupleBrowser reloadColumn:0];
    [self updateFileForm];
    [tmpTupleList freeObjects];
    return self;
}

- (int) runModelForTypes
{
    static const char	*const filetype[2] = {"hippo", NULL};
    
    if ( ![openPanel runModalForTypes:filetype] ) {
    	return HD_CANCEL;
    }
    return HD_OK;
}

- replaceWithCuts:hTuple
{
    List	*plotList, *slist;
    List  	*list, *cutList;
    List	*depList;
    Plot        *cut, *plot;
    int	        i, j;
    BOOL	ok;
    
  /* make plotList list of Plots amongst selected graphics */
    slist = [graphicView selectedGraphics];
    plotList = [[List allocFromZone:[self zone]] initCount:0];
    [slist makeObjectsPerform:@selector(addPlotToList:) with:plotList];
    [graphicView deselectAll:self];
    slist = [graphicView selectedGraphics];
    
  /* add to plotList and Plots dependent on cuts amongst selected graphics */
    depList = [[plotList copyFromZone:[self zone]] initCount:0];
    i = [depList count];
    while (i--) {
        cut = [depList objectAt:i];
	if ( [cut isCutPlot] ) {
	    list = [cut dependList];
	    j = [list count];
	    while (j--) {
	        plot = [list objectAt:i];
		[plotList addObjectIfAbsent:plot];
	    }
	}
    }
    [depList free];
	
  /* find the complete list of cuts  and updated slist from plotList */
    cutList = [[List allocFromZone:[self zone]] initCount:0];
    i = [plotList count];
    while (i--) {
        plot = [plotList objectAt:i];
	[slist addObjectIfAbsent:plot];
	list = [plot cutList];
	j = [list count];
	while (j--) {
	    cut = [list objectAt:j];
	    [cutList addObjectIfAbsent:cut];
	}
    }
    [plotList free];
    
  /* update slist with cutList and each cut's depend list */
    i = [cutList count];
    while (i--) {
        cut = [cutList objectAt:i];
	[slist addObjectIfAbsent:cut];
	list = [cut dependList];
	j = [list count];
	while ( j-- ) {
	    plot = [list objectAt:j];
	    [slist addObjectIfAbsent:plot];
	}
    }
    [cutList free];
    
    ok = [self isReplaceOK:slist with:hTuple];
    if ( !ok ) {
	return self;
    }
    [graphicView recacheSelection];
    [graphicView graphicsPerform:@selector(replaceTupleWith:)
	    with: hTuple andDraw:YES ];
    [[graphicView window] flushWindow];
    
    return self;
}

- showPanel
{
    [theInspector showPanelWithName:"Data Selection"];
    [theInspector orderFrontPanel:self];
    [[graphicView window] orderFront:self];
    return self;
}

- setDisable:(int) axisIndex
{
    if ( popUpRow != 0 ) return self;
    
    [[tpAxisButtons cellAt:axisIndex :0] setTitle:""];
    [[tpAxisButtons cellAt:axisIndex :0] setEnabled:NO];
    [infoForm setStringValue:"" at:axisIndex];
    [infoForm drawCellAt:axisIndex :0];
    return self;
}

- setEnable:(int) axisIndex
{
    char *titles[5] = {"x-axis", "y-axis", "weight", "x-error", "y-error"};

    if ( popUpRow != 0 ) return self;
    
    [[tpAxisButtons cellAt:axisIndex :0] setTitle:titles[axisIndex]];
    if ((graphtype == THREEDSCATTER) && (axisIndex == AX_Z))
    	[[tpAxisButtons cellAt:axisIndex :0] setTitle:"z-axis"];
    [[tpAxisButtons cellAt:axisIndex :0] setEnabled:YES];

    /* add tuple column name if anything selected, else blank */
    if (tpAxisIndex[axisIndex] >= 0) 
    	[infoForm setStringValue:h_getNtLabel(selectedTuple, 
	    tpAxisIndex[axisIndex]) at:axisIndex];
    else
	[infoForm setStringValue:"" at:axisIndex];

    [infoForm drawCellAt:axisIndex :0];
    return self;
}

- setButtonsAndTitles
{
    if (!selectedTuple) {
	[self setDisable:AX_X];	/* disable everything if no tuple */
	[self setDisable:AX_Y];	
	[self setDisable:AX_W];
	[self setDisable:AX_XE];
	[self setDisable:AX_YE];
	return self;
    }

     switch (graphtype) {
     case HISTOGRAM:
    	[self setEnable:AX_X];	
	[self setDisable:AX_Y];	
    	[self setEnable:AX_W];	
	[self setDisable:AX_XE];
	[self setDisable:AX_YE];
	break;
	  
     case XYPLOT:
     case STRIPCHART:
    	[self setEnable:AX_X];	
    	[self setEnable:AX_Y];	
	[self setDisable:AX_W];
    	[self setEnable:AX_XE];	
    	[self setEnable:AX_YE];	
	break;
	  
     case SCATTERPLOT:
    	[self setEnable:AX_X];	
    	[self setEnable:AX_Y];	
	[self setDisable:AX_W];
	[self setDisable:AX_XE];
	[self setDisable:AX_YE];
	break;
	  
     case COLORPLOT:
     case LEGOPLOT:
    	[self setEnable:AX_X];	
    	[self setEnable:AX_Y];	
	[self setEnable:AX_W];
	[self setDisable:AX_XE];
	[self setDisable:AX_YE];
	break;
	  
     case THREEDSCATTER:
    	[self setEnable:AX_X];	
    	[self setEnable:AX_Y];	
	[self setEnable:AX_Z];
	[self setDisable:AX_XE];
	[self setDisable:AX_YE];
	break;
     }

     return self;
}
- showBinding
  /*
   * called when we click on a different plot -
   * load up the options panel to correspond to the selected plot
   */
{
    int         pos;
    int         neg;
    int 	row;
    display 	adisplay;
    drawtype_t  drawtype;

    if ( !firstPlot ) return self;
    adisplay = [firstPlot histDisplay];
    if ( !adisplay ) return self;
    
    graphtype = h_getDispType(adisplay);
    
    pos = graphtype;
    neg = -pos;
    if ( graphtype != COLORPLOT ) {
        [graphTypMatrix selectCellWithTag:pos];
    } else {
	[selectedPlot getDrawType:(drawtype_t *) &drawtype];
        if ( drawtype & COLOR ) {
	    [graphTypMatrix selectCellWithTag:pos];
	} else {
	    [graphTypMatrix selectCellWithTag:neg];
	}
    }
    if ( [selectedPlot isCutPlot] ) {
        [graphTypMatrix setEnabled:NO];
    } else {
        [graphTypMatrix setEnabled:YES];
    }
    [graphTypMatrix display];
    
  /*
   * get the current bindings
   */
    tpAxisIndex[0] = h_getBinding(adisplay, XAXIS);
    tpAxisIndex[1] = h_getBinding(adisplay, YAXIS);
    tpAxisIndex[2] = h_getBinding(adisplay, WEIGHT);
    if (graphtype == THREEDSCATTER) { 
    	tpAxisIndex[2] = h_getBinding(adisplay, ZAXIS);
    } else {
    	tpAxisIndex[2] = h_getBinding(adisplay, WEIGHT);
    };
    tpAxisIndex[3] = h_getBinding(adisplay, XERROR);
    tpAxisIndex[4] = h_getBinding(adisplay, YERROR);
    	
 /*
  * enable buttons and show titles
  */
    [self setButtonsAndTitles];
 /*
  * click on the X Axis button as a starting default
  */
    row = [ tpAxisButtons selectedRow ];
    if ( row < 0 ) {
	[tpAxisButtons selectCellAt:AX_X :0];
    }
    if ( ![[tpAxisButtons cellAt:row :0] isEnabled] ) {
	[tpAxisButtons selectCellAt:AX_X :0];
    }
    
    [self axisMatrixClick:self];
    return self;
}
- updateFileForm
{
    Matrix		*browserMatrix;
    HTuple		*hTuple;
    id			cell;
    ntuple		nt;
    char		filename[FILENAME_MAX];
    float		mb;
    int			i, index, rows, cols;
    char *titles[5] = {"file", "mode", "cols", "rows", "size"};

    if ( popUpRow != 1 ) return self;
    
    for ( i = 0; i < 5; i++ ) {
    	cell = [tpAxisButtons cellAt:i :0];
	[cell setTitle:titles[i]];
	[cell setEnabled:YES];
    }
    browserMatrix = [tupleBrowser matrixInColumn:0];
    index = [browserMatrix selectedRow];
    if ( index < 0 ) {
        for ( i = 0; i < 5; i ++ ) {
	    [infoForm setStringValue:"" at:i];
	    [infoForm display];
	}
	[copyButton setEnabled:NO];
	return self;
    }
    hTuple = [tupleList objectAt:index];
    nt = [hTuple ntuple];
    rows = h_getNtNdata(nt);
    cols = h_getNtDim(nt);
    mb = (4*rows*cols)/1000000.;
    
    strcpy( filename, NXHomeDirectory() );
    index = strspn( [hTuple filename], filename );
    if ( index != (strlen(filename) + 1) ) {
    	strcpy( filename, [hTuple filename]); /* not in home directory */
    } else {
    	strcpy( filename, "~");		/* file in home directory */
	strcat( filename, [hTuple filename]+(index-1) );
    }
    if ( [hTuple isFakeFilename] ) {
        [copyButton setEnabled:NO];
    } else {
        [copyButton setEnabled:YES];
    }
    [ infoForm setStringValue:filename at:FN_INDEX];
    if ( [hTuple isRef ] ) {
        [infoForm setStringValue:"Imported by reference" at:BY_INDEX];
    } else {
        [infoForm setStringValue:"Imported by copy" at:BY_INDEX];
    }        
    [ infoForm setIntValue:cols at:COL_INDEX];
    [ infoForm setIntValue:rows at:ROW_INDEX];
    [ infoForm setFloatValue:mb at:MB_INDEX];
    [ infoForm display];
    return self;
}

@end
