/* Hippo Graphic View	by Paul Kunz	June 1991
 * a subclass of GraphicView in /NextDeveloper/Examples/Draw
 * to add or over-ride so that it can handle hippo Graphic objects
 *
 * Copyright (C)  1991  The Board of Trustees of
 * The Leland Stanford Junior University.  All Rights Reserved.
 */
 
#import "HGraphicView.h"

const char HGraphicView_h_rcsid[] = HGRAPHICVIEW_H_ID;
const char HGraphicView_m_rcsid[] = "$Id: HGraphicView.m,v 1.73 1992/04/25 18:56:24 pfkeb Rel $";

#import <appkit/Application.h>
#import <appkit/Form.h>
#import <appkit/Matrix.h>
#import <appkit/MenuCell.h>
#import <appkit/OpenPanel.h>
#import <appkit/Pasteboard.h>
#import <objc/List.h>
#import "Rectangle.h"

#import "DrawDocument.h"
#import "HDraw.h"
#import "HTuple.h"
#import "InspectCut.h"
#import "InspectPlot.h"
#import "InspectTuple.h"
#import "NewInspector.h"
#import "Plot.h"
#import "Graphic.h"
#import "Overlay.h"
#import "PageMarker.h"
#import <appkit/nextstd.h>
#import <defaults.h>
#import <stdlib.h>
#import <mach.h>
#import <zone.h>

#define DIRTY(condition) \
    if (condition && !gvFlags.dirty) { \
	gvFlags.dirty = YES; \
	[window setDocEdited:YES]; \
    }  
      
#define DEF_PLOT_SIZE 260.0

@implementation HGraphicView : GraphicView

static id currentGraphic = nil;	/* won't be used if NXApp knows how
				   to keep track of the currentGraphic */

+ initialize
{
    static NXDefaultsVector HippoDrawDefaults = {
	{ "NumPlotCols", "2" },
	{ "NumPlotRows", "3" },
	{ NULL, NULL }
    };
    [self setVersion:[[self superClass] version]];
    NXRegisterDefaults("HippoDraw", HippoDrawDefaults);
    return self;
}
+ convert:(NXTypedStream *)ts to:(const char *)type using:(SEL)writer toPasteboard:pb
/*
 * Over ride this method in order to make scrapper a HGraphicView
 */
{
    id w;
    char *data;
    NXZone *zone;
    NXStream *stream;
    int length, maxlen;
    HGraphicView *scrapper;
    NXRect origFrame;
    NXPoint movePoint;
    const NXRect scrapperFrame = {{0.0, 0.0}, {11.0*72.0, 14.0*72.0}};

    if (!ts) return self;

    zone = NXCreateZone(vm_page_size, vm_page_size, NO);
    NXNameZone(zone, "Scrapper");
    scrapper = [[HGraphicView allocFromZone:zone] initFrame:&scrapperFrame];
    w = [[Window allocFromZone:zone] initContent:&scrapperFrame
					   style:NX_PLAINSTYLE
					 backing:NX_NONRETAINED
				      buttonMask:0
					   defer:NO];
    [w reenableDisplay];
    [w setContentView:scrapper];

    stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
    NXSetTypedStreamZone(ts, zone);
    scrapper->glist = NXReadObject(ts);
 /* move graphics onto page */
    [scrapper getBBox:&origFrame of:scrapper->glist extended:NO];
    movePoint.x = - origFrame.origin.x;
    movePoint.y = - origFrame.origin.y;
    [scrapper selectAll:self];
    [scrapper graphicsPerform:@selector(moveBy:)
                        with:(id)&movePoint andDraw:NO];
    [scrapper deselectAll:self];
    [scrapper perform:writer with:(id)stream];
    NXGetMemoryBuffer(stream, &data, &length, &maxlen);
    [pb writeType:type data:data length:length];
    NXCloseMemory(stream, NX_FREEBUFFER);
    [w free];
    NXDestroyZone(zone);

    return self;
}
- initFrame:(const NXRect *)frameRect
{
    [super initFrame:frameRect];
    hDraw = NXGetNamedObject("HDrawInstance", NXApp);
    return self;
}

- (BOOL)validateCommand:menuCell
{
    id			g;
    SEL			action;
    unsigned int	i, count, pcount;
    BOOL		retval;
    
    retval = [super validateCommand:menuCell];
    if ( retval == NO ) {
        return NO;
    }
    action = [menuCell action];
    if ( action == @selector(overlay:) ||
         action == @selector(alignXRange:) ||
	 action == @selector(alignXNumBins:) ||
	 action == @selector(alignYRange:) ||
	 action == @selector(alignYNumBins:) ) {
	count = [slist count]; 
        if ( !count ) {
	    return NO;
	}
	pcount = 0;
	for ( i = 0; i < count; i ++ ) {
	    g = [slist objectAt:i];
	    if ( [g isKindOf:[Plot class]] ) {
	        pcount++;
	    }
	}
	if ( pcount < 2 ) {
	    return NO;
	}
    }
    if ( action == @selector(unoverlay:) ) {
        count = [slist count];
	if ( !count ) {
	    return NO;
	}
	for ( i = 0; i < count; i++ ) {
	    g = [slist objectAt:i];
	    if ( [g isKindOf:[Overlay class]] ) {
	        return YES;
	    }
	}
	return NO;
    }
    return YES;
}
- delete:sender
{
    if ( [self isAllowed] ) {
        return [super delete:sender];
    }
    return self;
}
- cut:sender
{
    if ( [self isAllowed] ) {
        return [super cut:sender];
    }
    return self;
}
- (BOOL) isAllowed
{
    id		inspectCut;
    id		g;
    int		i, irc;
    
    i = [slist count];
    while (i-- ) {
        g = [slist objectAt:i];
        if ( [g isKindOf:[Plot class]] ) {
	    if ( [g isCutHist] ) {
	        if ( [g dependCount] ) {
		    irc = NXRunAlertPanel("Can not delete",
			      "Selected graphic include plots that display"
			      " a cut.  Remove cut from target plot(s) first.",
			      "OK", NULL, NULL);
		    return NO;
		}
		inspectCut = [hDraw inspectCut];
		[inspectCut deleteCut:g];
	    }
	    if ( [g hasCut] ) {
	        [g removeAllCuts];
	    }
	}
    }
    return YES;
}
- setTuple:(ntuple) nt withNewDisplay:(int) dim
{
    graphtype_t		type;

    type = (dim==1) ? HISTOGRAM : COLORPLOT;
    [self addPlotOfType:type andSelect:YES];
    return self;
}
- addPlotOfType:(int) type
{
    return [self addPlotOfType:type andSelect:YES];
}
- addPlotOfType:(int) type andSelect:(BOOL) selFlag
{
    id			theInspector;
    id			inspectTuple;
    id			plot, htuple;
    display		disp;
    func_id		cutfunc;
    NXRect		bbox;
    int			index;
    
    currentGraphic = nil;
    [hDraw setCurrentGraphic:nil];
    inspectTuple = [hDraw inspectTuple];
    htuple = [inspectTuple currentHTuple];
    if ( !htuple ) {
        return self;
    }
    if ( (type != HISTOGRAM) && (h_getNtDim([htuple ntuple]) <= 1 ) ) {
        return self;
    }
    plot = [inspectTuple firstPlot];
    if ( plot ) {
        disp = [plot histDisplay];
	if ( type == h_getDispType(disp) ) {
	    disp = h_copyDisp(disp);
	    while ( (cutfunc = h_nextCut(disp, NULL)) ) {
	        h_deleteCut(disp, cutfunc);
	    }
	} else {
	    h_getDrawRect( disp, (rectangle *)&bbox );
    	    disp = h_newDisp(type);
	    h_setDrawRect(disp, (rectangle *) &bbox);
	}
    } else {
	disp = h_newDisp(type);
        [self calcDefaultPlotSize:&bbox];
	h_setDrawRect(disp, (rectangle *) &bbox);
    }
    
    plot = [[Plot allocFromZone:[self zone]] init];
    [plot setGraphicView:self];
    [plot setHTuple:htuple withDisplay:disp ];
    
    index = h_getBinding( disp, XAXIS );
    [plot bindAxisX:&index];
    if ( h_getDispType(disp) != HISTOGRAM ) {
        index = h_getBinding( disp, YAXIS );
	[plot bindAxisY:&index];
    }
    
    [plot setCutHistFlag: NO];
    if ( selFlag ) {
        [ self deselectAll:self];
    }
    [self placeDisplay: plot select:selFlag];
    [plot getBounds: &bbox ];
    [self scrollRectToVisible: &bbox];
    [window makeKeyWindow];
    if ( selFlag ) {
	[NXApp updateWindows]; 
	theInspector = [hDraw newInspector];
	[theInspector show:"Data Selection"];
	[theInspector orderFrontPanel:self];
    }
    return plot;
}
- hTupleForFile:(const char *)filename index:(int) iValue
{
    id		inspectTuple;
    
    inspectTuple = [hDraw inspectTuple];
    return [inspectTuple hTupleForFile:filename index:iValue];
}
- addCut:plot
{
    id	inspectCut;
    
    inspectCut = [hDraw inspectCut];
    [inspectCut addCut:plot];
    return self;
}
- placeDisplay:(id) plot select: (int) sel
{
     NXRect	curRect, resizeRect;
     NXRect	frameRect, box;
     NXRect	sliceRect, filledRect;
     float	pageHeight;
     id 	g, myglist;
     int 	below=1, i;
     
     /* resize the view and move every thing up to accomodate new Plot */
     [ plot getBounds:&resizeRect ];
     [ self getFrame: &frameRect ];

     myglist = [[List allocFromZone:[self zone]] init];
     for (i=[glist count]-1; i>=0; i--)
     {
	  g = [glist objectAt:i];
	  if (![g isLocked]) [myglist insertObject:g at:0];
     }
     [ self getBBox:&curRect of:myglist ];
     
     /*
      * when there is nothing in the plot, bounding box comes back with 
      *  all 0's. Reset to region at top of page.
      */
    if ( NXEmptyRect(&curRect) )  {
        curRect.origin.x = 5.0;
	curRect.origin.y = frameRect.size.height - 5.0;
    } else if (curRect.size.height >= resizeRect.size.height) {
	  /*
	   * see if there is room on right at bottom of bounding box.
	   */
	  NXDivideRect( &curRect, &sliceRect, NX_HEIGHT(&resizeRect), 1 );
	  NXSetRect( &filledRect, 0.0, 0.0, 0.0, 0.0 );
	  i = [myglist count];
	  while ( i-- ) {
	      g = [myglist objectAt:i];
	      [g getBounds:&box];
	      NXIntersectionRect( &sliceRect, &box );
	      NXUnionRect( &box, &filledRect );
	  }
	  resizeRect.origin.x = NX_WIDTH(&filledRect)+10.0;
	  resizeRect.origin.y = NX_Y(&filledRect);
	  below = ( (NX_MAXX(&resizeRect) + 5.0) > NX_WIDTH(&frameRect) );
	  NXUnionRect( &sliceRect, &curRect );
     }

    [[[self window] delegate] getPageFrame:&box];
    pageHeight = box.size.height;
     if (below) {
	  if ((curRect.origin.y-frameRect.origin.y) < resizeRect.size.height)
	  {
	       [ self addPage:self ];
	       /* 
		* everything was moved up by 1 page, so change the current
		* bounding box.
		*/
	       curRect.origin.y +=pageHeight;
	  }
	  resizeRect.origin.x = 5.0;
	  resizeRect.origin.y = curRect.origin.y - resizeRect.size.height;
     }

     /*
      * make sure plot does not cross page boundary.
      */
     if ((int)(resizeRect.origin.y/pageHeight) !=
	 (int)((resizeRect.origin.y+resizeRect.size.height)/pageHeight) ) {
	  resizeRect.origin.y = pageHeight*
	                        (int)(resizeRect.origin.y/pageHeight + 1) - 
		                resizeRect.size.height - 5.0;
     }
     NXIntegralRect( &resizeRect );
     [ plot setBounds: &resizeRect ];

     if (sel) {
	  [ self insertGraphic: plot];
     } else {
	  [ self insertGraphicNoSelect: plot];
          [self recacheSelection];
     }

     [myglist free];
     return self;
}

- addPage:sender
{
     NXRect 	box;
     int i;
     id g;
          
     /*
      * add a page marker 
      * it is inserted at the very top of the document.
      */
     [self notifyAncestorWhenFrameChanged:YES];
     [[[PageMarker new] init] addSelf: self];

     /*
      * add page. It gets add to TOP of document, so move everything up
      */
     [[[self window] delegate] getPageFrame:&box];
     box.size.width = 0.0;
     [ self sizeBy:box.size.width : box.size.height ];

     for (i=[glist count]-1; i>=0; i--)
     {
	  g = [glist objectAt:i];
	  if (![g isLocked]) [g moveBy: (const NXPoint *) &box.size];
     }

     [ self getFrame:&box ];
     [self cache:&box ];
     [window flushWindow];

     return self;
}


- saveAsExportFile:(const char *)filename
{
    id		inspectTuple;
    id		htuple;
    ntuple	nt;
    display	disp;
    const char	*ntFilename;
    int		i, irc;
    BOOL	refFlag;
    
    dispList = [self displayList];
    if ( dispList == NULL ) {
        return self;
    }
    inspectTuple = [hDraw inspectTuple];
    
 /* update the ntuple filename for referenced tuples before saving */
    for ( i = 0; (disp = dispList[i]) != NULL; i++) {
        nt = h_getNtuple( disp );
	htuple = [inspectTuple hTupleForTuple:nt];
	refFlag = [htuple isRef];
	ntFilename = [htuple filename];
	h_setNtByRef( disp, refFlag, ntFilename );
    }
    if ( h_write( filename, dispList, NULL ) ) {
	irc = NXRunAlertPanel("Alert", "Couldn't save export file",
				"OK", NULL, NULL);
    }
    return self;
}
- (display *) displayList;
{
    id		g;
    int		i, count, num_disp;
    
    plotList = [self plotList];
    count = [plotList count];
    if ( dispList != NULL ) {
	NX_ZONEREALLOC( [self zone], dispList, display, count+1 );
    } else {
        NX_ZONEMALLOC( [self zone], dispList, display, count+1 );
    }
    num_disp = 0;
    for ( i = 0; i < count; i ++ ) {
        g = [plotList objectAt:i];
	dispList[num_disp++] = [g histDisplay];
    }
    dispList[num_disp] = NULL;
    return dispList;
}
- plotList
{
    if ( !plotList ) {
        plotList = [[List allocFromZone:[self zone]] initCount:0];
    } else {
        [plotList empty];
    }
    [glist makeObjectsPerform:@selector(addPlotToList:) with:plotList];
    return plotList;
}
- cutList
{
    if ( !cutList ) {
        cutList = [[List allocFromZone:[self zone]] initCount:0];
    }
    return cutList;
}
- firstPlot
{
    id		g;
    
    if ([slist count] == 1) {
	g = [slist objectAt:0];
	if ( [g isKindOf:[Overlay class]] ) {
	    return [g firstPlot];
	}
	if ( [ g isKindOf:[Plot class]] ) {
	    return g;
	}
    }
    return nil;
}


- reDrawPlot
{
    [self selectAll:self];
    [self deselectAll:self];
    [window flushWindow];

    return self;
}
- graphicsPerformNOP: g
{
    NXRect              affectedBounds;

    [g getExtendedBounds:&affectedBounds];
    [self cache:&affectedBounds];

    return self;
}
- graphicsPerform:(SEL)aSelector with:(void *)argument
                   andDraw:(BOOL)flag inList:aList
{
    id		savesList;
    
    savesList = slist;
    slist = aList;
    [self graphicsPerform:aSelector with:argument andDraw:flag];
    slist = savesList;
    return self;
}
- graphicsPerform:(SEL)aSelector with:arg1 with:arg2
{
    id		g;
    unsigned	i, count;
    
    count = [slist count];
    for ( i = 0; i < count; i++ ) {
        g = [slist objectAt:i];
	[g perform:aSelector with:arg1 with:arg2];
    }
    return self;
}

- currentGraphic
{
    return [hDraw currentGraphic];
}
- alignAll:sender
{
 // This method is obsolete and kept in the 1.1 release for
 // backward compatability.
    return [self overlay:sender];
}
- alignSize:sender
{
 // backward compatible code.   Remove in next release
    return [self overlay:sender];
}
- overlay:sender
/*
 * Creates a new Group object with the current slist as its member list.
 * See the Group class for more info.
 */
{
    id		g, plotInsp, firstPlot;
    NXRect	eb;
    graphtype_t	firstType, plotType;
    int		i, irc;

    plotInsp = [hDraw inspectPlot];
    if ( plotInsp == nil ) return self;
    firstPlot = [plotInsp firstPlot];
    
 /* If firstPlot is nil, which can happen if user just did a unoverlay,
  * then pick a plot in the list to be the firstPlot.
  */
    if ( firstPlot == nil ) {
        i = [slist count];
	while( i-- ) {
	    g = [slist objectAt:i];
	    if ( [g isKindOf:[Plot class]] ) {
	        firstPlot = g;
		break;
	    }
	}
    }
    
 /* See if all Plots are of same type */
    [firstPlot getDispType:&firstType];
    i = [slist count];
    while( i-- ) {
        g = [slist objectAt:i];
	if ( [g isKindOf:[Plot class]] ) {
	    [g getDispType:&plotType];
	    if ( plotType != firstType ) {
		irc = NXRunAlertPanel("Alert",
			"Overlaying Plots of different types not allowed",
			"OK", NULL, NULL);
		return self;
	    }
	}
    }
    i = [slist count];
    if (i > 1) {
	DIRTY(YES);
	while (i--) [glist removeObject:[slist objectAt:i]];
	g = [[Overlay allocFromZone:[self zone]] initList:slist
						 with:firstPlot];
	[glist insertObject:g at:0];
	slist = [[List allocFromZone:[self zone]] init];
	[slist addObject:g];
	gvFlags.groupInSlist = YES;
	[self cache:[g getInitialBounds:&eb]];
	if (sender != self) [window flushWindow];
    }
    [ window flushWindow];
    return self;
}
- unoverlay:sender
/*
 * Goes through the slist and ungroups any Group objects in it.
 * Does not descend any further than that (i.e. all the Group objects
 * in the slist are ungrouped, but any Group objects in those ungrouped
 * objects are NOT ungrouped).
 */
{
    id	g, tlist;
    int i, j, k;
    NXRect sbounds, tbounds;
    BOOL found = NO;

    [self getBBox:&sbounds of:slist];
    i = [slist count];
    while (i--) {
	g = [slist objectAt:i];
	if ([g isKindOf:[Overlay class]]) {
	    k = [glist indexOf:g];
	    [glist removeObjectAt:k];
	    found = YES;
	    tlist = [[List allocFromZone:[self zone]] initCount:0];
	    [[g transferSubGraphicsTo:tlist at:0] free];
	    [self getBBox:&tbounds of:tlist];
	    NXUnionRect( &tbounds, &sbounds );
	    j = [tlist count];
	    while ( j-- ) {
	        g = [tlist objectAt:j];
		[glist insertObject:g at:k];
	    }
	    [tlist free];
	}
    }

    if (found) {
	DIRTY(YES);
	[self cache:&sbounds];
	if (sender != self) [window flushWindow];
	[self getSelection];
    }

    return self;
}

- alignXRange:sender
{
    id		plotInsp, firstPlot;
    NXPoint	theRange;
    
    plotInsp = [hDraw inspectPlot];
    if ( plotInsp == nil ) return self;
    firstPlot = [plotInsp firstPlot];
    if ( firstPlot == nil ) return self;
    [firstPlot getRangeForAxisX:&theRange];
    [ self graphicsPerform:@selector(setRangeForAxisX:)
	    with: (id)&theRange andDraw:YES ];
    [ window flushWindow];
    return self;
}
- alignYRange:sender
{
    id		plotInsp, firstPlot;
    NXPoint	theRange;
    
    plotInsp = [hDraw inspectPlot];
    if ( plotInsp == nil ) return self;
    firstPlot = [plotInsp firstPlot];
    if ( firstPlot == nil ) return self;
    [firstPlot getRangeForAxisY:&theRange];
    [ self graphicsPerform:@selector(setRangeForAxisY:)
	    with: (id)&theRange andDraw:YES ];
    [ window flushWindow];
    return self;
}
- alignXNumBins:sender
{
    id		plotInsp, firstPlot;
    int		numBins;
    
    plotInsp = [hDraw inspectPlot];
    if ( plotInsp == nil ) return self;
    firstPlot = [plotInsp firstPlot];
    if ( firstPlot == nil ) return self;
    numBins = [firstPlot numBinsForAxis: XAXIS ];
    [ self graphicsPerform:@selector(setNumBinsForAxisX:)
	    with: (id)&numBins andDraw:YES ];
    [ window flushWindow];
    return self;
}
- alignYNumBins:sender
{
    id		plotInsp, firstPlot;
    int	        numBins;
    graphtype_t gt;
    
    plotInsp = [hDraw inspectPlot];
    if ( plotInsp == nil ) return self;
    firstPlot = [plotInsp firstPlot];
    if ( firstPlot == nil ) return self;
    [firstPlot getDispType: &gt];
    if ( gt == HISTOGRAM ) return self;
    numBins = [firstPlot numBinsForAxis: YAXIS ];
    [ self graphicsPerform:@selector(setNumBinsForAxisY:)
	    with: (id)&numBins andDraw:YES ];
    [ window flushWindow];
    return self;
}
- copyPSToPasteboard:pboard
{
    char *data;
    NXStream *stream;   
    const char *types[1];
    int length, maxlen;

    if ([slist count]) {
	types[0] = NXPostScriptPboardType;
	stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
	[ self copySelectionAsPS:stream];
	NXGetMemoryBuffer(stream, &data, &length, &maxlen);
	[pboard declareTypes:types num:1 owner:[self class]];
	[pboard writeType:NXPostScriptPboardType data:data length:length];
	NXCloseMemory(stream, NX_FREEBUFFER);
	return self;
    } else {
	return nil;
    }
}
- pasteFromPasteboard:pboard
{
    id		pblist;
    NXRect	bbox;
    
    pblist = [ super pasteFromPasteboard:pboard ];
    if ( !pblist ) return nil;
    [self getBBox:&bbox of:pblist extended:NO];
    if ( !NXContainsRect( &bounds, &bbox ) ) {
        if ( NX_MAXX(&bbox) > NX_WIDTH(&bounds) ) {
	    bbox.origin.x = -( (bbox.origin.x + bbox.size.width) 
	                   - bounds.size.width);
	}
	if ( NX_MAXY(&bbox) > NX_HEIGHT(&bounds) ) {
	    bbox.origin.y = -( (bbox.origin.y + bbox.size.height)
	                    - bounds.size.height) -5.0;
	}
	[pblist makeObjectsPerform:@selector(moveBy:) with:(id)&bbox];
    }
    [ self bindDisplaysInList:pblist];
    return pblist;
}
- writePSToStream:(NXStream *)stream
 /* over-ride GraphicView method to add one line */
{
    NXRect bbox;

    if (stream) {
	[self getBBox:&bbox of:glist];
	[self bindDisplaysInList:glist]; /* Added line */
	[self copyPSCodeInside:&bbox to:stream];
    }

    return self;
}
- bindDisplays
{
    [ self bindDisplaysInList:glist];
    return self;
}
- bindDisplaysInList:list
{
    [list makeObjectsPerform:@selector(setGraphicView:) with:self];
    [list makeObjectsPerform:@selector(bindReference)];
    [list makeObjectsPerform:@selector(bindCuts)];
    return self;
}
- tupleList
{
    if ( !tupleList ) {
        tupleList = [[List allocFromZone:[self zone]] initCount:0];
    } else {
        [tupleList empty];
    }
    [glist makeObjectsPerform:@selector(addHTupleToList:) with:tupleList];
    return tupleList;
}
- replace:oldTuple with:newTuple
{
    id			g;
    unsigned int	i;
    
    i = [glist count];
    while ( i-- ) {
        g = [glist objectAt:i];
	[g replace:oldTuple with:newTuple];
    }
    return self;
}
- closeTupleFile:(const char *) filename
{
    id		inspectTuple;
    id		htuple, plot;
    int		i, j, pcount;
    
    plotList = [self plotList];
    pcount = [plotList count];
    if ( !pcount ) {
        return self;
    }
    inspectTuple = [hDraw inspectTuple];
    tupleList = [inspectTuple tupleListForFile:filename];
    if ( ![tupleList count] ) {
        return self;
    }
    for ( i = 0; i < pcount; i++) {
        plot = [plotList objectAt:i];
	htuple = [plot hTuple];
	j = [tupleList indexOf:htuple];
	if ( j != NX_NOT_IN_LIST ) {
	    [ plot closeTuple ];
        }
    }
    return self;
}
	       
- openTuple:pasteBoard
    userData:(const char *)args
    error:(char **)errorMsg
{
    id		inspectTuple;
    id		retval;
    int         graphtype;

    inspectTuple = [hDraw inspectTuple];
    retval = [inspectTuple openTuple:pasteBoard
                           userData:args
			   error:errorMsg];
    if ( retval == nil ) {
        return self;
    }
    sscanf(args, "%i", &graphtype );
    [self addPlotOfType:graphtype];
    return self;
}
- calcDefaultPlotSize:(NXRect *) bbox
{
    NXRect		frameRect;
    const char		*defaultValue;
    unsigned int	cols, rows;
    float		h, w;
    
    defaultValue = NXGetDefaultValue("HippoDraw", "NumPlotCols" );
    sscanf( defaultValue, "%u", &cols );
    defaultValue = NXGetDefaultValue("HippoDraw", "NumPlotRows" );
    sscanf( defaultValue, "%u", &rows );
    [[[self window] delegate] getPageFrame:&frameRect];
    if ( NX_HEIGHT(&frameRect) > NX_WIDTH(&frameRect) ) {
	w = (NX_WIDTH(&frameRect) - 5.0*cols)/cols - 5.0;
	h = (NX_HEIGHT(&frameRect) - 5.0*rows)/ rows - 5.0;
    } else {
	w = (NX_WIDTH(&frameRect) - 5.0*rows)/rows - 5.0;
	h = (NX_HEIGHT(&frameRect) - 5.0*cols)/cols - 5.0;
    }
    NXSetRect( bbox, 0, 0, w, h );
    return self;
}
- prefPanel:sender
{
    const char	*defaultValue;
    
    if( ! prefPanel ){
        [NXApp loadNibSection:"HGraphicView.nib" owner:self
               withNames:NO fromZone:[self zone]];
    }
    defaultValue = NXGetDefaultValue("HippoDraw", "NumPlotCols" );
    [prefForm setStringValue:defaultValue at:0];
    defaultValue = NXGetDefaultValue("HippoDraw", "NumPlotRows" );
    [prefForm setStringValue:defaultValue at:1];
    [prefPanel makeKeyAndOrderFront:sender];
    return self;    
}
- prefButton:sender
{
    char	string[80];
    int		cols, rows;
    
    [prefPanel orderOut:self];
    if ( [sender selectedCol] == 1 ) {
        cols = [prefForm intValueAt:0];
	rows = [prefForm intValueAt:1];
	sprintf( string, "%u", cols );
	NXWriteDefault("HippoDraw", "NumPlotCols", string );
	sprintf( string, "%u", rows );
	NXWriteDefault("HippoDraw", "NumPlotRows", string );
	[self deselectAll:self];
	[window flushWindow];
    }
    NXUpdateDefaults();
    return self;
}
- write:(NXTypedStream *)stream
{
    id			cut;
    unsigned int	i, count;
    
    [super write:stream];
    tupleList = [self tupleList];
    NXWriteObject( stream, tupleList );
    if ( cutList ) {
        count = [cutList count];
    } else {
        count = 0;
    }
    NXWriteType( stream, "i", &count );
    for ( i = 0; i < count; i++ ) {
        cut = [cutList objectAt:i];
	NXWriteObjectReference( stream, cut );
    }
    return self;
}
- read:(NXTypedStream *)stream
{
    id			cut;
    unsigned int	i, count;
    
    [super read:stream];
    
    tupleList = NXReadObject( stream );
    NXReadType( stream, "i", &count );
    if ( count ) {
        cutList = [[List allocFromZone:[self zone]] initCount:0];
    }
    for ( i = 0; i < count; i++ ) {
        cut = NXReadObject( stream );
	if ( cut ) {
	    [cutList addObject:cut];
	}
    }
    return self;
}
- awake
{
    id		inspectTuple;
    
    hDraw = NXGetNamedObject("HDrawInstance", NXApp);
    if ( tupleList && [tupleList count] ) {
	[ hDraw orderFrontTupleInsp:self];
	inspectTuple = [hDraw inspectTuple];
	[inspectTuple addTuplesIfAbsent:tupleList for:self];
	[ tupleList empty];
    }
    [self bindDisplays];
    return [super awake];
}
@end
