
/* Generated by Interface Builder */

#import "ErrorBarHandler.h"
#import "Plot.h"
#import "defs.h"
#import <appkit/Font.h>
#import <appkit/Panel.h>
#import <appkit/Window.h>
#import <appkit/Matrix.h>
#import <appkit/View.h>
#import <appkit/ButtonCell.h>
#import <dpsclient/wraps.h>
#import <appkit/color.h>	/* only for "blanking out" parts of a matrix */
#import <appkit/graphics.h>	/* ditto */

/*
 * The job of this object is to keep the appearance of the Error Bar panel in
 * accord with the state of the program.  This is only needed if the Error Bar
 * panel is visible.  A lot of the code is the same as in ColumnSelectionHandler.m.
 */

@implementation ErrorBarHandler

- init
{
/*
 * This global variable keeps track of whether the error bar window
 * has ever been displayed.  We can avoid updating after the selective
 * file removal if we have never been displayed.
 */ 
  everVisible = NO;
  return self;
}

- eraseDisabledCells:sender
{
  int n, j, numrows, numcols, ncurves;
  NXRect r;
  BOOL exbars, eybars;

  [errorBarMatrix getNumRows:&numrows numCols:&numcols];
  [errorBarMatrix lockFocus];
  NXSetColor([errorBarMatrix backgroundColor]); /* cf. comment in ColumnSelectionHandler.m */
  for (n=0; n<[plotParam nFiles]; n++) {
    exbars = [plotParam has_exbars:n];
    eybars = [plotParam has_eybars:n];
    if (!exbars && !eybars) {
      /* if this file has no error bars, blank out everything */
      for (j=-1; j<numcols; j++) {
        [errorBarMatrix getCellFrame:&r at:n :j+1]; /* note the j+1 here and below */
        NXRectFill(&r);
      }
    }
    else if (exbars && !eybars) {
      /* x error bars, no y error bars, blank out some columns  */
      for (j=0; j<numcols; j++) {
        [errorBarMatrix getCellFrame:&r at:n :j+1];
        NXRectFill(&r);
      }
    }
    else if (!exbars && eybars) {
      /* no x error bars, has y error bars; blank out 1st and some more columns */
      [errorBarMatrix getCellFrame:&r at:n :0];
      NXRectFill(&r);
      ncurves = [plotParam nCurves:n];
      if (ncurves < numcols) {
	for (j=ncurves; j<numcols; j++) {
	  [errorBarMatrix getCellFrame:&r at:n :j+1];
	  NXRectFill(&r);
	}
      }
    }
    else {
      /* both x and y error bars; maybe blank out some columns */
      ncurves = [plotParam nCurves:n];
      if (ncurves < numcols) {
	for (j=ncurves; j<numcols; j++) {
	  [errorBarMatrix getCellFrame:&r at:n :j+1];
	  NXRectFill(&r);
	}
      }
    }
  }
  [errorBarMatrix unlockFocus];
  NXPing();
  return self;
}

- removeAll:sender
/* This method is called by the "remove all files" method of the Plot object. */
{
  int n, j;
  int numrows, numcols;
  NXSize cellsize, intercell;
  NXCoord dy;

  /* Do something only if the window has ever been displayed  */
  if (everVisible) {
    [errorBarMatrix getNumRows:&numrows numCols:&numcols];
    [errorBarMatrix getCellSize:&cellsize];
    [errorBarMatrix getIntercell:&intercell];
    /*
     * Wipe out everything.
     */
    dy = (NXCoord)(numrows - 1) * (cellsize.height + intercell.height);
    [errorBarMatrix moveBy:0.0 :dy];
    /*
     * Before doing renewRows, set the state of all the cells to 1; this is
     * because the renewRows method just recycles the cells, and later on
     * we may find ourselves using a cell which has state=0 when we don't
     * want such a beast.  Also disable all cells (paranoia strikes).
     */
    for (n=0; n<numrows; n++) {
      for (j=0; j<numcols; j++) {
	[[errorBarMatrix cellAt:n :j] setState:1];
	[[errorBarMatrix cellAt:n :j] setEnabled:YES];
      }
    }
    [errorBarMatrix renewRows:1 cols:1];
    [errorBarMatrix sizeToCells];
    [[errorBarMatrix cellAt:0 :0] setType:NX_TOGGLE];
    [filenameMatrix moveBy:0.0 :dy];
    [filenameMatrix renewRows:1 cols:1];
    [[filenameMatrix cellAt:0 :0] setStringValue:"filename"];
    [filenameMatrix sizeToCells];
    [errorBarText renewRows:1 cols:1];
    [errorBarText sizeToCells];
    /* Do some drawing now.  Display the view. */
    [[errorBarMatrix window] display];
    NXPing();			/* needed??? */
  }
  return self;
}


- updatePanel:sender
/*
 * The update button activates this method; it is also called from
 * windowDidResize and from Plot.m in postludeToReading.  We have to
 * fix up the error bar panel so that it's correct.
 */
{
  int n, j;
  int nfilestotal = [plotParam nFiles], maxcols = 0, ncurves;
  char title[80];
  int numrows, numcols;
  NXSize cellsize, intercell;
  NXCoord dy;
  int ** turned_on;
  BOOL  exbars, eybars;

  if (everVisible == NO) everVisible = YES;


  [errorBarMatrix getNumRows:&numrows numCols:&numcols];
  [errorBarMatrix getCellSize:&cellsize];
  [errorBarMatrix getIntercell:&intercell];

  /*
   * We have to create some additional rows and perhaps some additional columns.
   */
  // First figure out how many columns the matrix should have
  for (n=0; n<nfilestotal; n++) {
    maxcols = MAX(maxcols, [plotParam nCurves:n]);
  }
  maxcols++;			/* add one column for the "x" (zero) column */

  if (numrows != nfilestotal  ||  numcols != maxcols) {
    /* The renewRows:cols: message might have the side effect of setting
     * the state of all the cells of the matrix back to 0; this is undesired,
     * so before changing the number of rows and/or columns in the matrix,
     * make sure we remember which cells in each row were turned on.
     */
    turned_on = (int **)malloc(nfilestotal * sizeof(int)); /* should do error check */
    for (n=0; n<nfilestotal; n++) {
      *(turned_on + n) = (int *)malloc(maxcols * sizeof(int));
    }
    for (n=0; n<nfilestotal; n++) {
      for (j=0; j<maxcols; j++) {
	*(*(turned_on+n)+j) = 1;
      }
    }
    for (n=0; n<numrows; n++) {
      for (j=0; j<numcols; j++) {
	*(*(turned_on+n)+j) = [[errorBarMatrix cellAt:n :j] state];
      }
    }

    [errorBarMatrix renewRows:nfilestotal cols:maxcols];

    for (n=0; n<nfilestotal; n++) {
      for (j=0; j<maxcols; j++) {
	[[errorBarMatrix cellAt:n :j] setState:1]; /* everything turned on */
      }
    }
    /* Turn cells off if needed. */
    for (n=0; n<numrows; n++) {
      for (j=0; j<numcols; j++) {
	[[errorBarMatrix cellAt:n :j] setState:*(*(turned_on+n)+j)];
      }
    }
    for (n=0; n<nfilestotal; n++) {
      free((void *)*(turned_on + n));
    }
    free((void *)turned_on);
  }
  [errorBarMatrix sizeToCells];
  dy = (float)(nfilestotal - numrows) * (cellsize.height + intercell.height);
  [errorBarMatrix moveBy:0.0 :-dy];
  // Some of the cells may come in with type NULLCELL; fix them here
  for (n=0; n<nfilestotal; n++) {
    for (j=0; j<maxcols; j++) {
      [[errorBarMatrix cellAt:n :j] setEnabled:YES];
      [[errorBarMatrix cellAt:n :j] setType:NX_TOGGLE];
    }
  }
  // We surround the [errorBarMatrix display] and [self eraseDisabledCells]
  // calls with the disableFlushWindow, reenableFlushWindow, and flushWindow
  // calls to prevent the disabled matrix cells from being visible for a long
  // time while they're drawn and then erased.
  [errorBarPanel disableFlushWindow];
  [errorBarMatrix display];
  // Disable cells that shouldn't appear.
  for (n=0; n<nfilestotal; n++) {
    exbars = [plotParam has_exbars:n];
    eybars = [plotParam has_eybars:n];
    if (!exbars && !eybars) { 
      /* if this file has no error bars, disable everything */
      for (j=-1; j<maxcols; j++) {
        [[errorBarMatrix cellAt:n :j+1] setType:NX_NULLCELL];
	[[errorBarMatrix cellAt:n :j+1] setEnabled:NO];
      }
    }
    else if (exbars && !eybars) {
      /* x error bars, no y error bars, blank out some columns  */
      for (j=0; j<numcols; j++) {
	[[errorBarMatrix cellAt:n :j+1] setType:NX_NULLCELL];
	[[errorBarMatrix cellAt:n :j+1] setEnabled:NO];
      }
    }
    else if (!exbars && eybars) {
      /* no x error bars, has y error bars; blank out 1st and some more columns */
      [[errorBarMatrix cellAt:n :0] setType:NX_NULLCELL];
      [[errorBarMatrix cellAt:n :0] setEnabled:NO];
      ncurves = [plotParam nCurves:n];
      if (ncurves < numcols) {
	for (j=ncurves; j<maxcols; j++) {
	  [[errorBarMatrix cellAt:n :j+1] setType:NX_NULLCELL];
	  [[errorBarMatrix cellAt:n :j+1] setEnabled:NO];
	}
      }
    }
    else {
      /* both x and y error bars; maybe blank out some columns */
      ncurves = [plotParam nCurves:n];
      if (ncurves < numcols) {
	for (j=ncurves; j<numcols; j++) {
	  [[errorBarMatrix cellAt:n :j+1] setType:NX_NULLCELL];
	  [[errorBarMatrix cellAt:n :j+1] setEnabled:NO];
	}
      }
    }
  }

  // We have to put the makeKeyAndOrderFront before the eraseDisabledCells
  // method, because eraseDisabledCells does a lockFocus and lockFocus needs
  // a real window to draw into.  We put the makeKeyAndOrderFront here rather
  // than at the beginning of this method because this way the disabled cells
  // of the matrix are visible for a shorter period of time.
  [errorBarPanel makeKeyAndOrderFront:self];

  // Now erase those disabled cells.
  [self eraseDisabledCells:self];
  [errorBarPanel reenableFlushWindow];
  [errorBarPanel flushWindowIfNeeded];
  NXPing();			/* needed? */

  // That finishes with the errorBarMatrix; now for the filename Matrix.
  [filenameMatrix renewRows:nfilestotal cols:1];
  for (n=0; n<nfilestotal; n++) {
    if (!strncmp([plotParam filename:(unsigned)n], "pasteboard", 10))
      sprintf(title, "pasteboard");
    else
      sprintf(title, strrchr([plotParam filename:(unsigned)n], '/') + 1);
    [[filenameMatrix cellAt:n :0] setStringValue:title];
  }
  [filenameMatrix sizeToCells];
  [filenameMatrix moveBy:0.0 :-dy];
  [filenameMatrix display];

  // And now for the errorBarText matrix.
  [errorBarText renewRows:1 cols:maxcols];
  for (j=0; j<maxcols; j++) {
    sprintf(title, "%d", j);
    [[errorBarText cellAt:0 :j] setStringValue:title];
  }
  [errorBarText sizeToCells];
  [errorBarText display];
  return self;
}


/*
 * Apropos this, see the remarks in ColumnSelectionHandler.m
 */
- windowDidResize:sender
{
  [self perform:@selector(updatePanel:)
        with:self
        afterDelay:1		/* wait 0.001 second */
        cancelPrevious:YES];
  return self;
}


/*
 * This is called by the selective file removal code in Plot.m.
 */
- update:sender
{
  int n, nfilestotal = [plotParam nFiles], num_removed = 0;
  NXSize cellsize, intercell;
  NXCoord dy;

  /* Do something only if the window has ever been displayed  */
  if (everVisible) {
    [errorBarMatrix getCellSize:&cellsize];
    [errorBarMatrix getIntercell:&intercell];
    /*
     * Count down the file removal panel, removing rows as needed.
     */
    for (n = nfilestotal-1; n>=0; n--) {
      if ([[fileRemovalButtons cellAt:n :0] state] == 1) {
	/* This file is to be removed. */
	num_removed++;
	[errorBarMatrix removeRowAt:n andFree:YES];
	[filenameMatrix removeRowAt:n andFree:YES];
      }
    }
    [errorBarMatrix sizeToCells];
    [filenameMatrix sizeToCells];
    if (num_removed > 0) {
      dy = (NXCoord)(num_removed) * (cellsize.height + intercell.height);
      [errorBarMatrix moveBy:0.0 :dy];
      [filenameMatrix moveBy:0.0 :dy];
    }
    /* Do some drawing now.  Display the view. */
    [[errorBarMatrix window] display];
    NXPing();			/* needed??? */
  }


  return self;
}

@end
