/**************************************************
 * SynthBuilder
 * Copyright 1993 Nick Porcaro All Rights Reserved
 **************************************************/

/*
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 */


#import "CellScrollView.h"
#import "ViewController.h"
#import "GenericCell.h"
#import "GenericObject.h"
#import "Utilities.h"
#import "DragView.h"
#import "FakeSynthPatch.h"

extern char *getAppName();

@implementation CellScrollView

static char TempString[1024];

- init
{
  return [self initFrame:NULL];
}

- initFrame:(const NXRect *)frameRect
/* This is called automatically when the nib is loaded */
{
  [super initFrame:frameRect];
  theFrameRect = frameRect;
  return self;
}

- initCellList:aList
/* Call this to setup the initial emtpy list 
 * This makes each cell be a GenericCell, which is usually fine
 */
{
  theCellList = aList;
  [self initCellMatrixWithClass:[GenericCell class]];
  [self updateList];

  return self;
}

- setCellList:aList
/* Call this if theCellList ever changes for a given CellScrollView */
{
  theCellList = aList;
  return self;
}

- initCellMatrix
{
  [self initCellMatrixWithClass:[GenericCell class]];
  return self;
}

- initCellMatrixWithClass:(Class) aClass
/* Rarely called from the outside world */
{
  NXSize interCellSpacing = {0.0, 0.0}, docSize;
  cellMatrix = [[Matrix alloc] initFrame:theFrameRect
		mode:NX_LISTMODE
		cellClass:aClass
		numRows:0
		numCols:1];

  /*
   * In the following lines,
   * remember that "cellMatrix" is the matrix that will be installed
   * in the scrollview, "self" is the scrollview.
   */

  /* we don't want any space between the matrix's cells  */
  [cellMatrix setIntercell:&interCellSpacing];
  /* resize the matrix to contain the cells */
  [cellMatrix sizeToCells];
  [cellMatrix setAutosizeCells:YES];
  /*
   * when the user clicks in the matrix and then drags the mouse out of
   * scrollView's contentView, we want the matrix to scroll
   */
  [cellMatrix setAutoscroll:YES];
  /* let the matrix stretch horizontally */
  [cellMatrix setAutosizing:NX_WIDTHSIZABLE];  
  /* Install the matrix as the docview of the scrollview */
  [[self setDocView:cellMatrix] free];
  /* set up the visible attributes of the scrollview */
  [self setVertScrollerRequired:YES];
  [self setBorderType:NX_BEZEL];
  /* tell the subviews to resize along with the scrollview */
  [self setAutoresizeSubviews:YES];
  /* This is the only way to get the clipview to resize too */
  [[cellMatrix superview] setAutoresizeSubviews:YES];
  /* Allow the scrollview to stretch both horizontally and vertically */
  [self setAutosizing:NX_WIDTHSIZABLE|NX_HEIGHTSIZABLE];
  /* Resize the matrix to fill the inside of the scrollview */
  [self getContentSize:&docSize];
  [cellMatrix sizeTo:docSize.width :docSize.height];

  return self;
}

- reportCurrentSelection
{
  static char buf[1000];
  GenericCell *cell = [cellMatrix selectedCell];
  
  sprintf(buf, "The highlighted name: %s\n", 
	  [[cell genericObject]  getName]);
  
  NXRunAlertPanel("CellScrollView", buf,0,0,0);
  return self;
}

- currentlySelectedObject
{
  return [[cellMatrix selectedCell] genericObject];
}

- free
{
  [cellMatrix free];
  return [super free];
}

- cellMatrix
{
  return cellMatrix;
}

- deleteSelections
{
  int i;
  id cell;
  id anObj;

  for (i=[cellMatrix cellCount]-1; i>=0; i--) 
    {
      cell = [cellMatrix cellAt:i:0];
      if ([cell isHighlighted]) 
	{
	  /*
	   * If a cell is highlighted, remove (and free) the corresponding item
	   * from the list of theCellList.
	   */
	  sprintf(TempString, "Do you really want to delete %s",
		  [[cell genericObject] getName]);

	  if (NXRunAlertPanel(getAppName(),
			      TempString,
			      "OK","Cancel",NULL) != NX_ALERTDEFAULT)
	    {
	      return self;
	    }
	  else
	    {
	      anObj = [cell genericObject];
	      [anObj closeInspector];
	      [[theCellList removeObject:anObj] free];
	    }
	}
    }

  [self updateList];
  return self;
}

- editSelections
{
  int i;
  id cell;

  for (i=[cellMatrix cellCount]-1; i>=0; i--) 
    {
      cell = [cellMatrix cellAt:i:0];
      if ([cell isHighlighted]) 
	{
	  [[cell genericObject] edit];
	}
    }
  return self;
}

- objectsJustAdded
{
  /*
   * Assumptions in this next line:
   *	There are as many GenericCells as there are theCellList
   *	We've added the new genericObject at the end of the list.
   *	We want to display the genericObject we just added and highlight it.
   * In short, this is a hack.
   */
  [cellMatrix scrollCellToVisible:[theCellList count]-1 :0];
  [cellMatrix selectCellAt:[theCellList count]-1:0];
  return self;
}

static int defCompare(const void *def1, const void *def2)
{
  return strcmp([*(id *)def1 getName],[*(id *)def2 getName]);
}

- updateList
/*
 * Fill the matrix with GenericCells, associate each GenericCell with a GenericObject.
 *
 * Since we recycle the cells (via renewRows:cols:), we also set the state
 * of each cell to 0 and unhighlight it.  If we don't do that, the recycled
 * cells will display their previous state.
 */
{
  int i, cellCount;
  id utilities;
  
  cellCount = [theCellList count];
  utilities = [ [ [theViewController getDragView] getPatch] utilities];
  [utilities sortList:theCellList sortFunction:defCompare];

  /* tell the matrix there are 0 cells in it (but don't deallocate them) */
  [cellMatrix renewRows:0 cols:1];
  if ([theViewController getFocusLock])
    {
      [cellMatrix lockFocus];		/* for highlightCellAt::lit: */
    }

  for (i=0;i<cellCount;i++) {
    GenericCell *cell;
    /*
     * add a row to the matrix.  (This doesn't necessarily allocate a new
     * cell, thanks to renewRows:cols:).
     */
    [cellMatrix addRow];
    cell = [cellMatrix cellAt:i:0];
    /* make sure the cell is neither selected nor highlighted */
    [cellMatrix highlightCellAt:i:0 lit:NO];
    [cell setState:0];
    /* install the genericObject in that cell */
    [cell setGenericObject:[theCellList objectAt:i]];
  }

  if ([theViewController getFocusLock])
    {
      [cellMatrix unlockFocus];
    }
  [cellMatrix sizeToCells];
  [cellMatrix display];
  
  return self;
}

- getCurrentImage
{
  id anImage;
  int i, j;
  id firstSelected = nil;
  id cell;

  /* Just get the first one the was selected */
  j = 0;
  for (i=[cellMatrix cellCount]-1; i>=0; i--) 
    {
      cell = [cellMatrix cellAt:i:0];
      if ([cell isHighlighted])
	{
	  firstSelected = cell;
	  j++;
	}
    }

  switch (j)
    {
    case (0):
      {
	anImage = [NXImage findImageNamed:"NoIcon"];
        break;
      }
    case (1):
      {
	anImage = [self getImageFor:[firstSelected genericObject]];
	if (! anImage)
	  {
	    anImage = [NXImage findImageNamed:"NoIcon"];
	  }
        break;
      }
    default:
      {
	anImage = [NXImage findImageNamed:"MultipleSelection"];
      }
    }

  return anImage;
}

- (BOOL) haveSelection
{
  int i, j;
  id cell;

  /* Just get the first one the was selected */
  j = 0;
  for (i=[cellMatrix cellCount]-1; i>=0; i--) 
    {
      cell = [cellMatrix cellAt:i:0];
      if ([cell isHighlighted])
	{
	  j++;
	}
    }

  if (j == 0)
    {
      return NO;
    }
  else
    {
      return YES;
    }
}

- getImageFor:anObject
{
  id anImage;
  char *iconName;

  if (! anObject)
    {
      return nil;
    }

  if (! (iconName = [anObject getIconFile]) ||
      ! (anImage = [NXImage findImageNamed:iconName]))
    {
      anImage = [NXImage findImageNamed:"NoIcon"];
    }

  return anImage;
}


@end
