/* InspectAxes.m		by Paul Rensing Nov. 1992
 * Controls axis frills
 *
 * Copyright (C)  1992  The Board of Trustees of
 * The Leland Stanford Junior University.  All Rights Reserved.
 */

#import "HGraphicView.h"
#import "InspectAxes.h"

const char InspectAxes_h_rcsid[] = INSPECTAXES_H_ID;
const char InspectAxes_m_rcsid[] = "$Id: InspectAxes.m,v 2.30.2.3 1994/02/08 20:29:06 rensing Exp $";

#import "FineSlider.h"
#import "NewInspector.h"
#import "Plot.h"
#import <float.h>

#define RB_HIGH 	0
#define RB_LOW 		1
#define RB_WIDTH 	2
#define RB_OFF 		3

#define MAXBINS 	500
#define DEFAULT_BINS	50
#define SCALE_FACTOR 	5.0
 /*
  * bug fix: FLT_MIN=1.17549435e-38 is actually 0
  */
#undef FLT_MIN
#define FLT_MIN 	2e-38f

@implementation InspectAxes

static BOOL checkDisplay( display disp, binding_t axis, 
                          float *low, float *high, int *num )
{
    graphtype_t	type;
    BOOL	checkBins;
    
    type = h_getDispType( disp );

    if  (( type == COLORPLOT && (axis == XAXIS || axis == YAXIS))
    	|| type == LEGOPLOT
	|| (type == HISTOGRAM && axis == XAXIS)) {
	checkBins = YES;
    } else {
	checkBins = NO;
    }	

    if ( checkBins ) *num = h_getBinNum(disp, axis);
    
    h_getRange( disp, axis, low, high );
    return checkBins;
}
- initInspFor:aDraw
{
    NXBundle		*bundle;
    char		buffer[MAXPATHLEN+1];
    unsigned int	i;
    
    [super initInspFor:aDraw];
    bundle = [NXBundle bundleForClass:[self class]];
    if ( [bundle getPath:buffer forResource:"InspectAxes" ofType:"nib"] ) {
    	[NXApp loadNibFile:buffer owner:self
                 withNames:NO  fromZone:[self zone]];
    }
    [theInspector addView:[contentBox contentView]
                 withName:"Axis Options" withSupervisor:self];

    /* make sure the tag are set to something consistent */
    axis = XAXIS;
    [self setTags];
 /*
  * we've got to do the following nonsense because can't stretch out spacing
  * of slider from IB 
  */
    rbSlider[0] = rbSlider0;
    rbSlider[1] = rbSlider1;
    rbSlider[2] = rbSlider2;
    rbSlider[3] = rbSlider3;
    for ( i = 0; i < 4; i++ ) {
        [rbSlider[i] setScaleFactor:SCALE_FACTOR];
    }
    numLimitCheck = [limitCheckButton state];
    currentDim = 1;
    prevIndex = -1;
    prevValue = 999.999;
    return self;
}
 
/* Action methods */
- tickOption:sender
{
    plotloc_t loc=0;
    
    if ([[TickMatrix findCellWithTag:PLOTBOTTOM] state] == 1) 
	loc += PLOTBOTTOM;
    if ([[TickMatrix findCellWithTag:PLOTTOP] state] == 1) 
	loc += PLOTTOP;
    if ([[TickMatrix findCellWithTag:PLOTLEFT] state] == 1) 
	loc += PLOTLEFT;
    if ([[TickMatrix findCellWithTag:PLOTRIGHT] state] == 1) 
	loc += PLOTRIGHT;

    [graphicView graphicsPerform:@selector(setTickLocation:to:)
                           with:(id)&axis with:(id)&loc andDraw:YES];
    [[graphicView window] flushWindow];
    return self;
}

- scaleOption:sender
{
    plotloc_t loc;

    loc = [[sender selectedCell] tag ];
    
    [graphicView graphicsPerform:@selector(setScaleLocation:to:)
                           with:(id)&axis with:(id)&loc andDraw:YES];
    
    [[graphicView window] flushWindow];
    return self;
}

- labelOption:sender
{
    plotloc_t loc;

    loc = [[sender selectedCell] tag ];
    
    [graphicView graphicsPerform:@selector(setLabelLocation:to:)
                           with:(id)&axis with:(id)&loc andDraw:YES];
    
    [[graphicView window] flushWindow];
    return self;
}
 /* 
  * Respond to the appropriate x axis matrix
  */

- scaleFontSize:sender
{
    float        fontSize;

    fontSize = [sender floatValue];     
    
    [graphicView graphicsPerform:@selector(setScaleFontSize:to:)
                           with:(id)&axis with:(id)&fontSize andDraw:YES];
    
    [[graphicView window] flushWindow];
    return self;
}

- tickLength:sender
{
    float        len;

    len = [sender floatValue];     
    
    [graphicView graphicsPerform:@selector(setTickLength:to:)
                           with:(id)&axis with:(id)&len andDraw:YES];
    
    [[graphicView window] flushWindow];
    return self;
}

- axisSelection:sender
{
    axis = [[sender selectedCell] tag ];    
    
    [self setTags];
    [self updateView];
    return self;
}

- limitCheck:sender
{
    numLimitCheck = [limitCheckButton state];
    return self;
}

- rebin:sender
{
     NXPoint 	p;
     float 	svalue, toff, tlow, thigh, delta, deltaoff;
     float 	plo, phi;
     double   	dnum;
     int	i;
     int	slider_index;
     BOOL	re_display = NO;
     BOOL 	logAxis, checkBins=NO;
     display 	adisplay = [firstPlot histDisplay];
     BOOL 	zpmode;
     
     slider_index = -1;
     for (i = 0; i < 4; i++) {
	  if (sender == rbSlider[i]) {
	       slider_index = i;
	       break;
	  }
     }

     svalue = [sender floatValue];
     h_getRange(adisplay, axis, &plo, &phi );
     zpmode = [zpbutton state];
     if ( (i < 4) && ((slider_index != prevIndex) || (svalue != prevValue))) {
	  prevIndex = slider_index;
	  prevValue = svalue;

    	if  (( graphtype == COLORPLOT && (axis == XAXIS || axis == YAXIS))
    	    || graphtype == LEGOPLOT
	    || (graphtype == HISTOGRAM && axis == XAXIS)) {
	    checkBins = YES;
    	} else {
	    checkBins = NO;
    	}	

	  logAxis = h_getLogAxis(adisplay, axis);
	  
	  switch (slider_index) {
	  case RB_HIGH:
	       if (logAxis && svalue <= 0.0) svalue = 2*FLT_MIN;
	       if (checkBins) {
		    thigh = MAX( svalue, low + 2.0*width );
		    delta = thigh - high;
		    if (ABS(delta) > width) {
			 i = (delta > 0) ? delta / width : delta / width + 1;
			 num = num + i;
			 if ( zpmode ) num = num + i;
			 num = [self limitCheck:num against: MAXBINS ];
			 high = high + i * width;
			 if ( zpmode ) low = low - i*width;
			 high = MAX(high, low + width);
			 re_display = YES;
		    }
	       } else {
		    high = MAX(svalue,low*(1.0+2.0*FLT_EPSILON));
		    if (zpmode) low = plo - (high - phi);
		    re_display = YES;
	       }
	       if (re_display) [rbForm setFloatValue:high at:RB_HIGH];
	       break;
	  case RB_LOW:
	       if (logAxis && svalue <= 0.0) svalue = 2*FLT_MIN;
	       if (checkBins) {
		    tlow = MIN(svalue, high - 2 * width);
		    delta = low - tlow;
		    if (ABS(delta) > width) {
			 i = delta / width;
			 if ( !zpmode ) {
			     num = num + i;
			     num = [self limitCheck:num against: MAXBINS ];
			 }
			 low = low - i * width;
			 if (zpmode) high = high - i*width;
			 low = MIN(low, high - width);
			 re_display = YES;
		    }
	       } else {
		    low = MIN(svalue,high*(1.0-2.0*FLT_EPSILON));
		    if ( zpmode ) high = phi + ( low - plo );
		    re_display = YES;
	       }
	       if (re_display) [rbForm setFloatValue:low at:RB_LOW];
	       break;
	  case RB_WIDTH:
	       if (checkBins) {
		   /* 
		    * Given width, adjust nbins and high to closest values  
		    * giving an integral number of bins
		    */ 
		    width = [rbSlider[RB_WIDTH] floatValue];
		    dnum = (high - low) / width;
		    if (dnum > (((float)floor(dnum) + (float)ceil(dnum))/2.0)) 
		    {
		    	num = ceil(dnum);
		    } else {
		    	num = floor(dnum);
		    }
		    num = [self limitCheck:num against: MAXBINS ];
		    high = low + num * width;
		    [rbForm setFloatValue:width at:RB_WIDTH];
		    [rbForm setFloatValue:high at:RB_HIGH];
		    re_display = YES;
	       }
	       break;
	  case RB_OFF:
	       toff = svalue;		/* slider value */
	       deltaoff = toff - offset;
	       low  = low + width * deltaoff; 
	       high = high + width * deltaoff;
	       offset = toff;	       
	       [rbForm setFloatValue:high   at:RB_HIGH];
	       [rbForm setFloatValue:low    at:RB_LOW];
	       [rbForm setFloatValue:offset at:RB_OFF];
	       re_display = YES;
	       break;
	  }	
	  if (re_display == YES) {
	       p.x = low;
	       p.y = high;
	       [graphicView graphicsPerform:@selector(setNumBins:to:)
	                    with:(id)&axis with :(id)&num andDraw:NO];
	       [graphicView graphicsPerform:@selector(setRange:to:)
		            with:(id)&axis with:(id)&p andDraw:YES];
		[autoScaleButton setState:NO];
	       if (zpmode) {
	           if ( slider_index == RB_HIGH ) {
		       [rbForm setFloatValue:low at:RB_LOW];
		   } else if ( slider_index == RB_LOW ) {
		       [rbForm setFloatValue:high at:RB_HIGH];
		   }
	       }
	       width = (high - low) / (num);
	  }
     }

     if ( re_display == YES ) {
	  [[[graphicView window] flushWindow] makeKeyWindow];
     }
     return self;
}

- rebinForm:sender
{
    NXPoint	p;
    double   	dnum;
    float	thigh, tlow, twidth=0.0, toff;
    int		sel;
    BOOL	re_display = NO;
    BOOL	logAxis, checkBins;
    display	adisplay = [firstPlot histDisplay];

    sel = [rbForm selectedIndex];

    logAxis = h_getLogAxis(adisplay, axis);
    
    if  ( ((graphtype == COLORPLOT|| graphtype == LEGOPLOT)
            && (axis == XAXIS || axis == YAXIS))
    	 || (graphtype == HISTOGRAM && axis == XAXIS)) {
	checkBins = YES;
    } else {
	checkBins = NO;
    }	

    thigh = [rbForm floatValueAt:RB_HIGH];
    tlow  = [rbForm floatValueAt:RB_LOW];

    if (checkBins) {
	if (sel == RB_WIDTH) {
	    twidth = [rbForm floatValueAt:RB_WIDTH];
	    dnum = (thigh - tlow) / twidth;
	    if (dnum > ((floor(dnum) + ceil(dnum)) / 2.0)) {
		num = ceil(dnum);
	    } else {
		num = floor(dnum);
	    }
	    num = MAX(num, 2);
	    num = [self limitCheck:num against:MAXBINS];
	    if (sel == RB_HIGH)
		tlow = thigh - num * twidth;
	    else
		thigh = tlow + num * twidth;
	} else {
	    num = h_getBinNum(adisplay, axis);
	}
    }
    if (logAxis && tlow <= 0.0)
	tlow = 2 * FLT_MIN;

    if (tlow >= thigh) {
	if (checkBins)
	    thigh = tlow + twidth;
	else
	    thigh = tlow * (1.0 + 2.0 * FLT_EPSILON) + 2.0 * FLT_MIN;
    }
    
    if ((high != thigh) || (sel == RB_HIGH)) {
	re_display = YES;
	high = thigh;
    }
    if ((low != tlow) || (sel == RB_LOW)) {
	re_display = YES;
	low = tlow;
    }
    if ((twidth != width) || (sel == RB_WIDTH)) {
	if (checkBins) {
	    re_display = YES;
	    width = twidth;
	}
    }
    toff = [rbForm floatValueAt:RB_OFF];
    if (toff != offset) {
	re_display = YES;
	toff = MIN(toff, 1.0);
	toff = MAX(toff, -1.0);
	high -= offset * width;
	low  -= offset * width;
	offset = toff;
	high += offset * width;
	low  += offset * width;
    }
    if ( re_display == YES ) {
	p.x = low;
	p.y = high;
	[graphicView graphicsPerform:@selector(setRange:to:)
	                with:(id)&axis with:(id)&p andDraw:NO];
	[graphicView graphicsPerform:
	   @selector(setNumBins:to:) with:(id)&axis with:(id)&num andDraw:YES];
	width = (high - low)/num;
	[rbForm setFloatValue:width at:RB_WIDTH];
	
    }
    [[[graphicView window] flushWindow] makeKeyWindow];
    return self;
}

- scaleType:sender
{
 /* called when the AutoScale or logScale button is clicked */
	SEL		theMethod = 0;
	int		theState;
	
	theState = [sender state];
	
	switch ([sender tag])
	{
		case 0: /* AutoScale X-axis */
			theMethod = @selector(setAutoScale:to:);
			break;
		case 2: /* Log Scale X-axis */
			theMethod = @selector(setLogScale:to:);
			break;
			
	}
	
	[graphicView graphicsPerform:theMethod
		with:&axis with :(id)&theState andDraw:YES];

	[[[graphicView window] flushWindow] makeKeyWindow];
    
	return self;
}
    
- mouseMoved:(const NXPoint *)point in:sender withKey:(int)flag
{
    display		disp;
    binding_t		xaxis = XAXIS, yaxis = YAXIS, zaxis = ZAXIS;
    NXPoint		p;
    float		lo, hi;
    int			no;
    float               diff, range;
    BOOL		checkBins, logAxis;

    if ( selectedPlot ) {
        disp = [selectedPlot histDisplay];
    } else {
    	disp = [selectedPlot histDisplay];
    }
    if ( !disp ) return self;
    
    logAxis = h_getLogAxis(disp, XAXIS);
    checkBins = checkDisplay( disp, XAXIS, &lo, &hi, &no );
    range = hi - lo;
    diff  = 2.0 * range;
    p.y = hi - point->x * diff / 100;
    if ( logAxis && lo <= range ) {
        diff = 10.0 * lo - 0.1 * lo;
    }
    p.x = lo - point->x * diff / 100;
    [graphicView graphicsPerform:@selector(setRange:to:)
		with:(id)&xaxis with:(id)&p andDraw:YES];
    
    logAxis = h_getLogAxis(disp, YAXIS);
    checkBins = checkDisplay( disp, YAXIS, &lo, &hi, &no );
    range = hi - lo;
    diff = 2.0 * range;
    p.y = hi - point->y * diff / 100;
    if ( logAxis && lo <= range ) {
        diff = 10.0 * lo - 0.1 * lo;
    }
    p.x = lo - point->y * diff / 100;
    [graphicView graphicsPerform:@selector(setRange:to:)
		with:(id)&yaxis with:(id)&p andDraw:YES];
    
    logAxis = h_getLogAxis(disp, ZAXIS);
    checkBins = checkDisplay( disp, ZAXIS, &lo, &hi, &no );
    range = hi - lo;
    diff  = 2.0 * range;
    p.y = hi - point->x * diff / 100;
    if ( logAxis && lo <= range ) {
        diff = 10.0 * lo - 0.1 * lo;
    }
    p.x = lo - point->x * diff / 100;
    [graphicView graphicsPerform:@selector(setRange:to:)
		with:(id)&zaxis with:(id)&p andDraw:YES];
    
    [[[graphicView window] flushWindow] makeKeyWindow];
    
    return self;
}

/* Internal methods */
- setTags
{     
    if (axis == XAXIS)
    {
	[[TickMatrix cellAt:0 :0] setTitle:"Bottom"];
	[TickMatrix setTag:PLOTBOTTOM at:0 :0];
	[[ScaleMatrix cellAt:0 :0] setTitle:"Bottom"];
	[ScaleMatrix setTag:PLOTBOTTOM at:0 :0];
	[[LabelMatrix cellAt:0 :0] setTitle:"Bottom"];
	[LabelMatrix setTag:PLOTBOTTOM at:0 :0];
	[[TickMatrix cellAt:0 :1] setTitle:"Top"];
	[TickMatrix setTag:PLOTTOP at:0 :1];
	[[ScaleMatrix cellAt:0 :1] setTitle:"Top"];
	[ScaleMatrix setTag:PLOTTOP at:0 :1];
	[[LabelMatrix cellAt:0 :1] setTitle:"Top"];
	[LabelMatrix setTag:PLOTTOP at:0 :1];
    }
    else if (axis == YAXIS)
    {
	[[TickMatrix cellAt:0 :0] setTitle:"Left"];
	[TickMatrix setTag:PLOTLEFT at:0 :0];
	[[ScaleMatrix cellAt:0 :0] setTitle:"Left"];
	[ScaleMatrix setTag:PLOTLEFT at:0 :0];
	[[LabelMatrix cellAt:0 :0] setTitle:"Left"];
	[LabelMatrix setTag:PLOTLEFT at:0 :0];
	[[TickMatrix cellAt:0 :1] setTitle:"Right"];
	[TickMatrix setTag:PLOTRIGHT at:0 :1];
	[[ScaleMatrix cellAt:0 :1] setTitle:"Right"];
	[ScaleMatrix setTag:PLOTRIGHT at:0 :1];
	[[LabelMatrix cellAt:0 :1] setTitle:"Right"];
	[LabelMatrix setTag:PLOTRIGHT at:0 :1];
    }
    
    return self;
}
 
- titleForm:sender
{
    const char		*string;
    
    string = [titleForm stringValueAt:0];

    switch (axis) {
        case XAXIS:
    	    [graphicView graphicsPerform:@selector(NameAxisX:)
	                    with:(id)string andDraw:YES];
	    break;
        case YAXIS:
    	    [graphicView graphicsPerform:@selector(NameAxisY:)
	                    with:(id)string andDraw:YES];
	    break;
        case ZAXIS:
    	    [graphicView graphicsPerform:@selector(NameAxisZ:)
	                    with:(id)string andDraw:YES];
	    break;
	default :
	    string = NULL;
	    break;
    }
    [[graphicView window] flushWindow];
    return self;
}
- resetTitle:sender
{
    const char	*string;
    display	disp;
    
    if ( !selectedPlot ) {
        return self;
    }

    switch (axis) {
        case XAXIS:
	    string = "%x";
	    [graphicView graphicsPerform:@selector(NameAxisX:)
				    with:(id)string andDraw:YES];
	    break;
	    
	case YAXIS :
	    disp = [selectedPlot histDisplay];
	    if ( h_getDispType(disp) == HISTOGRAM ) {
	        string = "Entries / %dx bin";
	    } else {
		string = "%y";
	    }
	    [graphicView graphicsPerform:@selector(NameAxisY:)
				    with:(id)string andDraw:YES];
	    break;
	case ZAXIS :
	    string = "%z";
	    [graphicView graphicsPerform:@selector(NameAxisZ:)
				    with:(id)string andDraw:YES];
	    break;
	default :
	    string = NULL;
	    break;
	    
    }
    [titleForm setStringValue:string at:0];
    [[graphicView window] flushWindow];
    return self;
}
- updateView
{
    display disp;
    int pos;
    plotloc_t loc;
    float f;
    
    if ( selectedPlot ) 
    	disp = [ selectedPlot histDisplay ];
    else if (firstPlot )
    	disp = [ firstPlot histDisplay ];
    else {
        lastPlot = nil;
    	return self;
     }

    /*
     * set the axis title
     */
    switch (axis) {
        case XAXIS:
    	    [titleForm setStringValue:h_getAxisLabel(disp,XAXIS) at:0];
	    break;
        case YAXIS:
    	    [titleForm setStringValue:h_getAxisLabel(disp,YAXIS) at:0];
	    break;
        case ZAXIS:
    	    [titleForm setStringValue:h_getAxisLabel(disp,ZAXIS) at:0];
	    break;
	default :
    	    [titleForm setStringValue:"" at:0];
	    break;
    }

    /*
     * set the tick buttons
     */
    loc = h_getTickLocation( disp, axis );
    for (pos = 1; pos <= 16; pos <<= 1)
    {
       if (loc & pos)
	  [TickMatrix selectCellWithTag:pos];
    }

    /*
     * set the scale radio buttons
     */
    loc = h_getScaleLocation( disp, axis );
    [ScaleMatrix selectCellWithTag:(int)loc];

    /*
     * set the scale radio buttons
     */
    loc = h_getLabelLocation( disp, axis );
    [LabelMatrix selectCellWithTag:(int)loc];

    /*
     * set the font size field
     */
    f = h_getScaleFontSize( disp, axis );
    [ScaleFontSize setFloatValue: f];

    f = h_getTickLength( disp, axis );
    [TickLength setFloatValue: f];

    [self updateOptions];
    [self updateSliders];
    if ( lastPlot != firstPlot) {
        prevIndex = -1;
	prevValue = 999.999;
    }
    lastPlot = firstPlot;
    return self;
}
  
- updateOptions
{
    display     disp;
    BOOL	enable;
    
    if (!selectedPlot)
    	disp = [selectedPlot histDisplay];
    else
    	disp = [firstPlot histDisplay];
	
    graphtype = h_getDispType(disp);
    if (graphtype == HISTOGRAM)
	 currentDim = 1;
    else
	 currentDim = 2;

    if  (graphtype == LEGOPLOT
	|| graphtype == THREEDSCATTER
	|| graphtype == COLORPLOT
	|| (graphtype == HISTOGRAM && axis == XAXIS)) {
	enable = NO;
    } else {
	enable = YES;
    }	

    [logScaleButton  setEnabled:enable];
    [autoScaleButton setState:h_getAutoScale(disp, axis)];
    [logScaleButton  setState:h_getLogAxis(disp, axis)];

    return self;
}
- updateSliders
{
    float	range;
    BOOL	logAxis, checkBins;
    display	disp;
	
    if (!selectedPlot)
	disp = [selectedPlot histDisplay];
    else
	disp = [firstPlot histDisplay];
	
     logAxis = h_getLogAxis(disp, axis);
     checkBins = checkDisplay( disp, axis, &low, &high, &num );
     if (checkBins) {
	  width = (high - low) / num;
	  [rbForm setFloatValue:width at: RB_WIDTH];
	  [rbSlider[RB_WIDTH] setMinValue:(width * 0.5)];
	  [rbSlider[RB_WIDTH] setMaxValue:(width * 1.5)];
	  [rbSlider[RB_WIDTH] setFloatValue:width];
	  [rbSlider[RB_WIDTH] setEnabled:YES];
	  [[rbForm cellAt:RB_WIDTH :0] setEnabled:YES];
     } else {
	  width = 0.0;
	  [rbForm setStringValue:"" at:RB_WIDTH];
	  [rbSlider[RB_WIDTH] setEnabled:NO];
	  [[rbForm cellAt:RB_WIDTH :0] setEnabled:NO];
     }
     
     range = high - low;
     [rbForm setFloatValue:high at:RB_HIGH];
     [rbForm setFloatValue:low  at:RB_LOW];
     [rbSlider[RB_HIGH] setMinValue:(double) (low  + width)];
     [rbSlider[RB_HIGH] setMaxValue:(double) (high + range)];
     [rbSlider[RB_HIGH] setFloatValue:high];
     if (logAxis) {
	  if (low <= range) {
	       [rbSlider[RB_LOW] setMinValue:(double) low/10.0];
	       [rbSlider[RB_LOW] setMaxValue:(double) low*10.0];
	  } else {
	       [rbSlider[RB_LOW] setMinValue:(double) (low  - range)];
	       [rbSlider[RB_LOW] setMaxValue:(double) (high - width)];
	  }
     } else {
	  [rbSlider[RB_LOW] setMinValue:(double) (low  - range)];
	  [rbSlider[RB_LOW] setMaxValue:(double) (high - width)];
     }
     [rbSlider[RB_LOW]  setFloatValue:low];
     
     if (checkBins) {
	  [rbForm setFloatValue:0. at:RB_OFF ];
	  [rbSlider[RB_OFF] setMinValue:-1.];
	  [rbSlider[RB_OFF] setMaxValue: 1.];
	  [rbSlider[RB_OFF] setFloatValue: 0.];
	  [rbSlider[RB_OFF] setEnabled:YES];
	  [[rbForm cellAt:RB_OFF :0] setEnabled:YES];
     } else {
	  [rbForm setStringValue:"" at:RB_OFF];
	  [rbSlider[RB_OFF] setEnabled:NO];
	  [[rbForm cellAt:RB_OFF :0] setEnabled:NO];
     }
     offset = 0.0;
	  
     return self;
}

- (int) limitCheck:(int) nbins against:(int)maxbins
{
    int		irc;
    static int prevnbins=0;
        
    if ( numLimitCheck == NO )  return nbins;
    
    if ( nbins > maxbins && numLimitCheck == YES ) {
        if (nbins == prevnbins) { 
	    	nbins = maxbins;
		return nbins;     /* ignore double events */
    	}
	prevnbins = nbins;
	irc = NXRunAlertPanel( "Alert",
		    "Greater than %d bins may cause performace problems",
		    "Cancel", "Proceed", NULL, maxbins );
	if ( irc == NX_ALERTDEFAULT ) {
	    nbins = maxbins;
	} else {
	    numLimitCheck = NO;
	    [ limitCheckButton setState: NO ];
	}
    }
    return nbins;
}
@end
