/** <title>NSTableView</title>

   Copyright (C) 2000 Free Software Foundation, Inc.

   Author: Nicola Pero <n.pero@mi.flashnet.it>
   Date: March 2000, June 2000, August 2000, September 2000
   
   Author: Pierre-Yves Rivaille <pyrivail@ens-lyon.fr>
   Date: August 2001, January 2002

   Author: Fred Kiefer <fredkiefer@gmx.de>
   Date: March 2004

   This file is part of the GNUstep GUI Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/ 

#include <Foundation/NSAutoreleasePool.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFormatter.h>
#include <Foundation/NSIndexSet.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSSet.h>
#include <Foundation/NSUserDefaults.h>
#include <Foundation/NSValue.h>

#include "AppKit/NSTableView.h"
#include "AppKit/NSApplication.h"
#include "AppKit/NSCell.h"
#include "AppKit/NSClipView.h"
#include "AppKit/NSColor.h"
#include "AppKit/NSEvent.h"
#include "AppKit/NSImage.h"
#include "AppKit/NSGraphics.h"
#include "AppKit/NSScroller.h"
#include "AppKit/NSTableColumn.h"
#include "AppKit/NSTableHeaderView.h"
#include "AppKit/NSText.h"
#include "AppKit/NSTextFieldCell.h"
#include "AppKit/NSWindow.h"
#include "AppKit/PSOperators.h"
#include "AppKit/NSCachedImageRep.h"
#include "AppKit/NSPasteboard.h"
#include "AppKit/NSDragging.h"
#include "AppKit/NSCustomImageRep.h"

#include <math.h>
static NSNotificationCenter *nc = nil;

static const int currentVersion = 3;

static NSRect oldDraggingRect;
static int oldDropRow;
static NSTableViewDropOperation oldDropOperation;
static NSTableViewDropOperation currentDropOperation;
static int currentDropRow;
static int lastQuarterPosition;
static unsigned currentDragOperation;


#define ALLOWS_MULTIPLE (1)
#define ALLOWS_EMPTY (1 << 1)
#define SHIFT_DOWN (1 << 2)
#define CONTROL_DOWN (1 << 3)
#define ADDING_ROW (1 << 4)


@interface NSTableView (NotificationRequestMethods)
- (void) _postSelectionIsChangingNotification;
- (void) _postSelectionDidChangeNotification;
- (void) _postColumnDidMoveNotificationWithOldIndex: (int) oldIndex
					   newIndex: (int) newIndex;
- (void) _postColumnDidResizeNotification;
- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn;
- (BOOL) _shouldSelectRow: (int)rowIndex;

- (BOOL) _shouldSelectionChange;
- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
			    row: (int) rowIndex;

- (BOOL) _writeRows: (NSArray *) rows
       toPasteboard: (NSPasteboard *)pboard;
- (BOOL) _isDraggingSource;
- (id)_objectValueForTableColumn: (NSTableColumn *)tb
			     row: (int)index;
- (void)_setObjectValue: (id)value
	 forTableColumn: (NSTableColumn *)tb
		    row: (int)index;
@end

@interface NSTableView (SelectionHelper)
- (NSArray *) _selectedRowArray;
- (BOOL) _selectRow: (int)rowIndex;
- (BOOL) _selectUnselectedRow: (int)rowIndex;
- (BOOL) _unselectRow: (int)rowIndex;
- (void) _unselectAllRows;
- (NSArray *) _selectedColumArray;
- (void) _unselectAllColumns;
@end



/*
 *  A specific struct and its associated quick sort function
 *  This is used by the -sizeToFit method
 */
typedef struct {
  float width;
  BOOL isMax;
} columnSorting;


static
void quick_sort_internal(columnSorting *data, int p, int r)
{
  if (p < r)
    {
      int q;
      {
	float x = data[p].width;
	BOOL y = data[p].isMax;
	int i = p - 1;
	int j = r + 1;
	columnSorting exchange;
	while(1)
	  {
	    j--;
	    for(; 
		(data[j].width > x)
		  || ((data[j].width == x) 
		      && (data[j].isMax == YES)
		      && (y == NO));
		j--)
	      ;

	    i++;
	    for(;
		(data[i].width < x)
		  || ((data[i].width == x) 
		      && (data[i].isMax == NO)
		      && (y == YES));
		i++)
	      ;
	    if ( i < j )
	      {
		exchange = data[j];
		data[j] = data[i];
		data[i] = exchange;
	      }
	    else
	      {
		q = j;
		break;
	      }
	  }
      }
      quick_sort_internal(data, p, q);
      quick_sort_internal(data, q + 1, r);
    }
}

/* 
 * Now some auxiliary functions used to manage real-time user selections. 
 *
 */

static void computeNewSelection
(NSTableView *tv,
 NSIndexSet *_oldSelectedRows,
 NSMutableIndexSet *_selectedRows,
 int _originalRow,
 int _oldRow,
 int _currentRow,
 int *_selectedRow,
 unsigned selectionMode)
{
  if (!(selectionMode & ALLOWS_MULTIPLE))
    {
      if ((selectionMode & SHIFT_DOWN) && 
	  (selectionMode & ALLOWS_EMPTY) && 
	  !(selectionMode & ADDING_ROW))
	  // we will unselect the selected row
	  // ic, sc : ok
        {
	  int count = [_selectedRows count];

	  if ((count == 0) && (_oldRow == -1))
	    {
	      NSLog(@"how did you get there ?");
	      NSLog(@"you're supposed to have clicked on a selected row,");
	      NSLog(@"but there's no selected row!");
	      return;
	    }
	  else if (count > 1)
	    {
	      [tv _unselectAllRows];
	      [tv _postSelectionIsChangingNotification];
	    }
	  else if (_currentRow != _originalRow)
	    {
	      if (*_selectedRow == _originalRow)
	        {
		  // we are already selected, don't do anything
		}
	      else
	        {
		  //begin checking code
		  if (count > 0)
		    {
		      NSLog(@"There should not be any row selected");
		    }
		  //end checking code
		  
		  if ([tv _selectRow: _originalRow])
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (_currentRow == _originalRow)
	    {
	      if (count == 0)
	        {
		  // the row is already deselected
		  // nothing to do !
		}
	      else
	        {
		  [tv _unselectRow: _originalRow];
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
      else  //(!(selectionMode & ALLOWS_MULTIPLE) && 
	    //(!(selectionMode & SHIFT_DOWN) || 
	    //!(selectionMode & ALLOWS_EMPTY) ||
	    //(selectionMode & ADDING_ROW)))
	  // we'll be selecting exactly one row
	  // ic, sc : ok
        {
	  int count = [_selectedRows count];
	  BOOL notified = NO;
	  
	  if ([tv _shouldSelectRow: _currentRow] == NO)
	    {
	      return;
	    }
	  
	  if ((count != 1) || (_oldRow == -1))
	    {
	      // this is the first call that goes thru shouldSelectRow
	      // Therefore we don't know anything about the selection
	      notified = ![_selectedRows containsIndex: _currentRow];
	      [tv _unselectAllRows];
	      [_selectedRows addIndex: _currentRow];
	      *_selectedRow = _currentRow;
	      
	      if (notified == YES)
	        {
		  [tv setNeedsDisplayInRect: [tv rectOfRow: _currentRow]];
		}
	      else
	        {
		  if (count > 1)
		    {
		      notified = YES;
		    }
		}
	      if (notified == YES)
	        {
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else
	    {
	      // we know there is only one column selected
	      // this column is *_selectedRow

	      //begin checking code
	      if (![_selectedRows containsIndex: *_selectedRow])
	        {
		  NSLog(@"*_selectedRow is not the only selected row!");
		}
	      //end checking code
	      
	      if (*_selectedRow == _currentRow)
	        {
		  // currentRow is already selecteed
		  return;
		}
	      else
	        {
		  [tv _unselectRow: *_selectedRow];
		  // CHANGE: This does a check more
		  [tv _selectRow: _currentRow];
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
    }
  else if ((selectionMode & ALLOWS_MULTIPLE)
	   && (selectionMode & SHIFT_DOWN)
	   && (selectionMode & ADDING_ROW))
    // we add new row to the current selection
    {
      if (_oldRow == -1)
	// this is the first pass
	{
	  BOOL notified = NO;
	  int i;
	  int diff = _currentRow - _originalRow;

	  if (diff >= 0)
	    {
	      for (i = _originalRow; i <= _currentRow; i++)
		{
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}
	    }
	  else
	    {
	      // this case does happen, (sometimes)
	      for (i = _originalRow; i >= _currentRow; i--)
		{
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}
	    }

	  if (notified == YES)
	    {
	      [tv _postSelectionIsChangingNotification];
	    }
	}
      else // new multiple selection, after first pass
	{ 
	  int oldDiff, newDiff, i;
	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;
	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ([_selectedRows containsIndex: i] ||
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // leave it selected
			  continue;
			}
		      
		      if ([tv _unselectRow: i])
		        {
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int last = [_selectedRows lastIndex];

		      if (last == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = last;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;
		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ([_selectedRows containsIndex: i] ||
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // leave it selected
			  continue;
			}

		      if ([tv _unselectRow: i])
			{
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int first = [_selectedRows firstIndex];

		      if (first == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = first;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      {
		for (i = _oldRow; i < _originalRow; i++)
		  {
		    if ([_oldSelectedRows containsIndex: i])
		      {
			// this row was in the old selection
			// leave it selected
			continue;
		      }
		    
		    if ([tv _unselectRow: i])
		      {
			notified = YES;
		      }
		  }
	      }
	      // then we're extending it
	      for (i = _originalRow + 1; i <= _currentRow; i++)
	        {
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int first = [_selectedRows firstIndex];

		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;

	      // we're reducing the selection
	      for (i = _oldRow; i > _originalRow; i--)
	        {
		  if ([_oldSelectedRows containsIndex: i])
		    {
		      // this row was in the old selection
		      // leave it selected
		      continue;
		    }
		    
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow - 1; i >= _currentRow; i--)
	        {
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int last = [_selectedRows lastIndex];

		  if (last == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = last;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }

	}
    }
  else if (   (selectionMode & ALLOWS_MULTIPLE)
	   && ((selectionMode & SHIFT_DOWN) == 0)
	   && (selectionMode & ALLOWS_EMPTY)
	      )
    // ic, sr : ok
    // new multiple selection (empty possible)
    {
      if (_oldRow == -1)
	// this is the first pass
	// we'll clear the selection first
	{
	  int diff, i;
	  int count = [_selectedRows count];
	  BOOL notified = NO;
      	  diff = _currentRow - _originalRow;

	  if (count > 0)
	    {
	      notified = YES;
	    }

	  [tv _unselectAllRows];

	  if (diff >= 0)
	    {
	      for (i = _originalRow; i <= _currentRow; i++)
		{
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}
	    }
	  else
	    {
	      // this case does happen (sometimes)
	      for (i = _originalRow; i >= _currentRow; i--)
		{
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}
	    }
	  if (notified == YES)
	    {
	      [tv _postSelectionIsChangingNotification];
	    }
	}
      else // new multiple selection, after first pass
	{ 
	  int oldDiff, newDiff, i;
	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;
	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		{
		  BOOL notified = NO;
		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ([tv _selectRow: i])
			{
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if ([tv _unselectRow: i])
			{
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int last = [_selectedRows lastIndex];

		      if (last == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = last;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ([tv _selectRow: i])
			{
			  notified = YES;
			}
		    } 
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([tv _unselectRow: i])
			{
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int first = [_selectedRows firstIndex];

		      if (first == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = first;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      for (i = _oldRow; i < _originalRow; i++)
	        {
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow + 1; i <= _currentRow; i++)
	        {
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int first = [_selectedRows firstIndex];

		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;

	      // we're reducing the selection
	      for (i = _oldRow; i > _originalRow; i--)
	        {
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow - 1; i >= _currentRow; i--)
	        {
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int last = [_selectedRows lastIndex];

		  if (last == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = last;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
    }
  else if (((selectionMode & ALLOWS_MULTIPLE)
	    && ((selectionMode & SHIFT_DOWN) == 0)
	    && ((selectionMode & ALLOWS_EMPTY) == 0)
	    && (selectionMode & ADDING_ROW))
	   // the following case can be assimilated to the 
	   // one before, although it will lead to an
	   // extra redraw
	   // TODO: solve this issue
	   ||
	   ((selectionMode & ALLOWS_MULTIPLE)
	    && ((selectionMode & SHIFT_DOWN) == 0)
	    && ((selectionMode & ALLOWS_EMPTY) == 0)
	    && ((selectionMode & ADDING_ROW) == 0))
	   )
    {
      if (_oldRow == -1)
	{
	  // if we can select the _originalRow, we'll clear the old selection
	  // else we'll add to the old selection
	  if ([tv _shouldSelectRow: _currentRow] == YES)
	    {
	      // let's clear the old selection
	      // this code is copied from another case
	      // (AM = 1, SD=0, AE=1, AR=*, first pass)
	      int diff, i;
	      int count = [_selectedRows count];
	      BOOL notified = NO;
	      diff = _currentRow - _originalRow;
	      
	      if (count > 0)
		{
		  notified = YES;
		}
	      
	      [tv _unselectAllRows];
	      
	      if (diff >= 0)
		{
		  for (i = _originalRow; i <= _currentRow; i++)
		    {
		      if ([tv _selectRow: i])
		        {
			  notified = YES;
			}
		    }	      
		}
	      else
		{
		  for (i = _originalRow; i >= _currentRow; i--)
		    {
		      if ([tv _selectRow: i])
		        {
			  notified = YES;
			}
		    }	      
		}
	    }
	  else
	    {
	      // let's add to the old selection
	      // this code is copied from another case
	      // (AM=1, SD=1, AE=*, AR=1)
	      int diff, i;
	      BOOL notified = NO;
	      diff = _currentRow - _originalRow;
	      
	      if (diff >= 0)
		{
		  for (i = _originalRow; i <= _currentRow; i++)
		    {
		      if ([_selectedRows containsIndex: i] || 
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		}
	      else
		{
		  // this case does happen (sometimes)
		  for (i = _originalRow; i >= _currentRow; i--)
		    {
		      if ([_selectedRows containsIndex: i] || 
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		}
	      if (notified == YES)
		{
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
      else if ([_selectedRows containsIndex: _originalRow])
	// as the originalRow is selected,
	// we are in a new selection
	{
	  // this code is copied from another case
	  // (AM=1, SD=0, AE=1, AR=*, after first pass)
	  int oldDiff, newDiff, i;
	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;
	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		{
		  BOOL notified = NO;
		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ([tv _selectRow: i])
		        {
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if ([tv _unselectRow: i])
		        {
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int last = [_selectedRows lastIndex];
		      
		      if (last == NSNotFound)
		        {
			  *_selectedRow = -1;
			}
		      else
		        {
			  *_selectedRow = last;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ([tv _selectRow: i])
		        {
			  notified = YES;
			}
		    } 
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([tv _unselectRow: i])
		        {
			  notified = YES;
			}
		    }

		  if (notified == YES)
		    {
		      unsigned int first = [_selectedRows firstIndex];

		      if (first == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = first;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      for (i = _oldRow; i < _originalRow; i++)
	        {
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow + 1; i <= _currentRow; i++)
	        {
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int first = [_selectedRows firstIndex];

		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;

	      // we're reducing the selection
	      for (i = _oldRow; i > _originalRow; i--)
	        {
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow - 1; i >= _currentRow; i--)
	        {
		  if ([tv _selectRow: i])
		    {
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int last = [_selectedRows lastIndex];

		  if (last == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = last;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
      else
	// as the originalRow is not selection, 
	// we are adding to the old selection
	{
	  // this code is copied from another case
	  // (AM=1, SD=1, AE=*, AR=1, after first pass)
	  int oldDiff, newDiff, i;
	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;

	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;
		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ([_selectedRows containsIndex: i] || 
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // leave it selected
			  continue;
			}
		      
		      if ([tv _unselectRow: i])
		        {
			  notified = YES;
			}
		    }

		  if (notified == YES)
		    {
		      unsigned int last = [_selectedRows lastIndex];

		      if (last == NSNotFound)
		        {
			  *_selectedRow = -1;
			}
		      else
		        {
			  *_selectedRow = last;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;
		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ([_selectedRows containsIndex: i] || 
			  [tv _selectRow: i])
			{
			  *_selectedRow = i;
			  notified = YES;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // leave it selected
			  continue;
			}
		      if ([tv _unselectRow: i])
		        {
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      unsigned int first = [_selectedRows firstIndex];

		      if (first == NSNotFound)
			{
			  *_selectedRow = -1;
			}
		      else
			{
			  *_selectedRow = first;
			}
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      for (i = _oldRow; i < _originalRow; i++)
	        {
		  if ([_oldSelectedRows containsIndex: i])
		    {
		      // this row was in the old selection
		      // leave it selected
		      continue;
		    }
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      // then we're extending it
	      for (i = _originalRow + 1; i <= _currentRow; i++)
	        {
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;

	      // we're reducing the selection
	      for (i = _oldRow; i > _originalRow; i--)
	        {
		  if ([_oldSelectedRows containsIndex: i])
		    {
		      // this row was in the old selection
		      // leave it selected
		      continue;
		    }
		    
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}

	      // then we're extending it
	      for (i = _originalRow - 1; i >= _currentRow; i--)
	        {
		  if ([_selectedRows containsIndex: i] ||
		      [tv _selectRow: i])
		    {
		      *_selectedRow = i;
		      notified = YES;
		    }
		}

	      if (notified == YES)
		{
		  unsigned int last = [_selectedRows lastIndex];

		  if (last == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = last;
		    }
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
    }
  else if ((selectionMode & ALLOWS_MULTIPLE)
	   && (selectionMode & SHIFT_DOWN)
	   && (selectionMode & ALLOWS_EMPTY)
	   && ((selectionMode & ADDING_ROW) == 0))
    {
      if (_oldRow == -1)
	// this is the first pass
	{
	  int diff, i;
	  BOOL notified = NO;

      	  diff = _currentRow - _originalRow;

	  if (diff >= 0)
	    {
	      for (i = _originalRow; i <= _currentRow; i++)
		{
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      if (   notified 
		  && (*_selectedRow >= _originalRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}
	    }
	  else
	    {
	      // this case does happen (sometimes)
	      for (i = _originalRow; i >= _currentRow; i--)
		{
		  if ([tv _unselectRow: i])
		    {
		      notified = YES;
		    }
		}
	      if (   notified 
		  && (*_selectedRow >= _originalRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}
	    }
	  if (notified == YES)
	    {
	      [tv _postSelectionIsChangingNotification];
	    }
	}
      else // new multiple antiselection, after first pass
	{ 
	  int oldDiff, newDiff, i;

	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;
	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		// we're extending the antiselection
		{
		  BOOL notified = NO;
		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ([tv _unselectRow: i])
			{
			  notified = YES;
			}
		    }

		  if (   notified 
		      && (*_selectedRow > _oldRow)
		      && (*_selectedRow <= _currentRow))
		    {
			unsigned int first = [_selectedRows firstIndex];
			
			if (first == NSNotFound)
			  {
			    *_selectedRow = -1;
			  }
			else
			  {
			    *_selectedRow = first;
			  }
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // select it
			  [tv setNeedsDisplayInRect: [tv rectOfRow: i]];
			  [_selectedRows addIndex: i];
			  *_selectedRow = i;
			  notified = YES;
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;
		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ([tv _unselectRow: i])
			{
			  notified = YES;
			}
		    }

		  if (   notified 
		      && (*_selectedRow < _oldRow)
		      && (*_selectedRow >= _currentRow))
		    {
		      unsigned int first = [_selectedRows firstIndex];
		  
		      if (first == NSNotFound)
		        {
			  *_selectedRow = -1;
			}
		      else
		        {
			  *_selectedRow = first;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // select it
			  [tv setNeedsDisplayInRect:
				[tv rectOfRow: i]];
			  [_selectedRows addIndex: i];
			  *_selectedRow = i;
			  notified = YES;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      {
		for (i = _oldRow; i < _originalRow; i++)
		  {
		    if ([_oldSelectedRows containsIndex: i])
		      {
			// this row was in the old selection
			// select it
			[tv setNeedsDisplayInRect:
			      [tv rectOfRow: i]];
			[_selectedRows addIndex: i];
			*_selectedRow = i;
			notified = YES;
		      }
		  }
	      }
	      // then we're extending it
	      {
		for (i = _originalRow + 1; i <= _currentRow; i++)
		  {
		    if ([tv _unselectRow: i])
		      {
			notified = YES;
		      }
		  }
	      }

	      if (   notified 
		  && (*_selectedRow > _oldRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}

	      if (notified == YES)
		{
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      {
		for (i = _oldRow; i > _originalRow; i--)
		  {
		    if ([_oldSelectedRows containsIndex: i])
		      {
			// this row was in the old selection
			// select it
			[tv setNeedsDisplayInRect:
			      [tv rectOfRow: i]];
			[_selectedRows addIndex: i];
			*_selectedRow = i;
			notified = YES;
		      }
		  }
	      }
	      // then we're extending it
	      {
		for (i = _originalRow - 1; i >= _currentRow; i--)
		  {
		    if ([tv _unselectRow: i])
		      {
			notified = YES;
		      }
		  }
	      }

	      if (   notified 
		  && (*_selectedRow > _oldRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}

	      if (notified == YES)
		{
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
    }
  else if ((selectionMode & ALLOWS_MULTIPLE)
	   && (selectionMode & SHIFT_DOWN)
	   && ((selectionMode & ALLOWS_EMPTY) == 0)
	   && ((selectionMode & ADDING_ROW) == 0))
    {
      if (_oldRow == -1)
	// this is the first pass
	{
	  int diff, i;
	  int count = [_selectedRows count];
	  BOOL notified = NO;
      	  diff = _currentRow - _originalRow;

	  if (diff >= 0)
	    {
	      for (i = _originalRow; i <= _currentRow; i++)
		{
		  if ((count > 1) && [tv  _unselectRow: i])
		    {
		      notified = YES;
		      count--;
		    }
		}
	      if (   notified 
		  && (*_selectedRow >= _originalRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      NSLog(@"error!");
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}
	    }
	  else
	    {
	      // this case does happen (sometimes)
	      for (i = _originalRow; i >= _currentRow; i--)
		{
		  if ((count > 1) && [tv  _unselectRow: i])
		    {
		      notified = YES;
		      count--;
		    }
		}
	      if (   notified 
		  && (*_selectedRow >= _originalRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		  
		  if (first == NSNotFound)
		    {
		      NSLog(@"error!");
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}
	    }
	  if (notified == YES)
	    {
	      [tv _postSelectionIsChangingNotification];
	    }
	}
      else // new multiple antiselection, after first pass
	{ 
	  int oldDiff, newDiff, i;
	  int count = [_selectedRows count];
	  oldDiff = _oldRow - _originalRow;
	  newDiff = _currentRow - _originalRow;
	  if (oldDiff >= 0 && newDiff >= 0)
	    {
	      if (newDiff >= oldDiff)
		// we're extending the antiselection
		{
		  BOOL notified = NO;
		  for (i = _oldRow + 1; i <= _currentRow; i++)
		    {
		      if ((count > 1) && [tv  _unselectRow: i])
		        {
			  notified = YES;
			  count--;
			}
		    }

		  if (   notified 
		      && (*_selectedRow > _oldRow)
		      && (*_selectedRow <= _currentRow))
		    {
		      unsigned int first = [_selectedRows firstIndex];
		  
		      if (first == NSNotFound)
		        {
			  NSLog(@"error!");
			  *_selectedRow = -1;
			}
		      else
		        {
			  *_selectedRow = first;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i > _currentRow; i--)
		    {
		      if (([_oldSelectedRows containsIndex: i]))
			{
			  // this row was in the old selection
			  // select it
			  if ([tv _selectUnselectedRow: i])
			    {
			      notified = YES;
			    }
			}
		    }
		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff <= 0)
	    {
	      if (newDiff <= oldDiff)
		// we're extending the selection
		{
		  BOOL notified = NO;
		  for (i = _oldRow - 1; i >= _currentRow; i--)
		    {
		      if ((count > 1) && [tv  _unselectRow: i])
		        {
			  notified = YES;
			  count--;
			}
		    }

		  if (   notified 
		      && (*_selectedRow < _oldRow)
		      && (*_selectedRow >= _currentRow))
		    {
		      unsigned int first = [_selectedRows firstIndex];
		      
		      if (first == NSNotFound)
		        {
			  NSLog(@"error!");
			  *_selectedRow = -1;
			}
		      else
		        {
			  *_selectedRow = first;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	      else
		// we're reducing the selection
		{
		  BOOL notified = NO;

		  for (i = _oldRow; i < _currentRow; i++)
		    {
		      if ([_oldSelectedRows containsIndex: i])
			{
			  // this row was in the old selection
			  // select it
			  if ([tv _selectUnselectedRow: i])
			    {
			      notified = YES;
			    }
			  count++;
			}
		    }

		  if (notified == YES)
		    {
		      [tv _postSelectionIsChangingNotification];
		    }
		}
	    }
	  else if (oldDiff <= 0 && newDiff >= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      {
		for (i = _oldRow; i < _originalRow; i++)
		  {
		    if ([_oldSelectedRows containsIndex: i])
		      {
			// this row was in the old selection
			// select it
			if ([tv _selectUnselectedRow: i])
			  {
			    notified = YES;
			  }
		      }
		  }
	      }
	      // then we're extending it
	      {
		for (i = _originalRow + 1; i <= _currentRow; i++)
		  {
		    if ((count > 1) && [tv  _unselectRow: i])
		      {
			notified = YES;
			count--;
		      }
		  }
	      }

	      if (   notified 
		  && (*_selectedRow > _oldRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		    
		  if (first == NSNotFound)
		    {
		      NSLog(@"error!");
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}

	      if (notified == YES)
		{
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	  else if (oldDiff >= 0 && newDiff <= 0)
	    {
	      BOOL notified = NO;
	      
	      // we're reducing the selection
	      {
		for (i = _oldRow; i > _originalRow; i--)
		  {
		    if ([_oldSelectedRows containsIndex: i])
		      {
			// this row was in the old selection
			// select it
			if ([tv _selectUnselectedRow: i])
			  {
			    notified = YES;
			  }
		      }
		  }
	      }
	      // then we're extending it
	      {
		for (i = _originalRow - 1; i >= _currentRow; i--)
		  {
		    if ((count > 1) && [tv  _unselectRow: i])
		      {
			notified = YES;
			count--;
		      }
		  }
	      }

	      if (   notified 
		  && (*_selectedRow > _oldRow)
		  && (*_selectedRow <= _currentRow))
		{
		  unsigned int first = [_selectedRows firstIndex];
		    
		  if (first == NSNotFound)
		    {
		      NSLog(@"error!");
		      *_selectedRow = -1;
		    }
		  else
		    {
		      *_selectedRow = first;
		    }
		}

	      if (notified == YES)
		{
		  [tv _postSelectionIsChangingNotification];
		}
	    }
	}
    }
}

static inline BOOL 
_isCellEditable (id delegate, NSArray *tableColumns, 
		 NSTableView *tableView, int row, int column)
{
  {
    NSTableColumn *tb;
    tb = [tableColumns objectAtIndex: column];
    if ([tableView _shouldEditTableColumn: tb 
		   row: row] == NO)
      {
	return NO;
	}
  }
  
  return YES;
}

@interface GSTableCornerView : NSView
{}
@end

@implementation GSTableCornerView

- (void) drawRect: (NSRect)aRect
{
  NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, 
			   NSMinXEdge, NSMaxYEdge, 
			   NSMinYEdge};
  NSRectEdge down_sides[] = {NSMaxXEdge, NSMaxYEdge, 
			     NSMinXEdge, NSMinYEdge, 
			     NSMaxYEdge};
  float grays[] = {NSBlack, NSBlack, 
		   NSLightGray, NSLightGray, 
		   NSBlack};
  NSRect rect;
  NSGraphicsContext *ctxt = GSCurrentContext();

  if (GSWViewIsFlipped(ctxt) == YES)
    {
      rect = NSDrawTiledRects(_bounds, aRect,
			       down_sides, grays, 5);
    }
  else
    {
      rect = NSDrawTiledRects(_bounds, aRect,
			       up_sides, grays, 5);
    }

  DPSsetgray(ctxt, NSDarkGray);
  DPSrectfill(ctxt, NSMinX(rect), NSMinY(rect), 
	      NSWidth(rect), NSHeight(rect));


  /*
  NSRect rect = _bounds;

  NSDrawButton (rect, aRect);
  [[NSColor controlShadowColor] set];
  rect.size.width -= 4;
  rect.size.height -= 4;
  rect.origin.x += 2;
  rect.origin.y += 2;
  NSRectFill (rect);*/
}

@end

@interface NSTableView (TableViewInternalPrivate)
- (void) _setSelectingColumns: (BOOL)flag;
- (BOOL) _editNextEditableCellAfterRow: (int)row
				column: (int)column;
- (BOOL) _editPreviousEditableCellBeforeRow: (int)row
				     column: (int)column;
- (void) _autosaveTableColumns;
- (void) _autoloadTableColumns;
@end


@implementation NSTableView 

+ (void) initialize
{
  if (self == [NSTableView class])
    {
      [self setVersion: currentVersion];
      nc = [NSNotificationCenter defaultCenter];
    }
}

/*
 * Initializing/Releasing 
 */

- (id) initWithFrame: (NSRect)frameRect
{
  self = [super initWithFrame: frameRect];
  _drawsGrid        = YES;
  _rowHeight        = 16.0;
  _intercellSpacing = NSMakeSize (2.0, 3.0);
  ASSIGN (_gridColor, [NSColor gridColor]); 
  ASSIGN (_backgroundColor, [NSColor controlBackgroundColor]); 
  ASSIGN (_tableColumns, [NSMutableArray array]);
  ASSIGN (_selectedColumns, [NSMutableIndexSet indexSet]);
  ASSIGN (_selectedRows, [NSMutableIndexSet indexSet]);
  _allowsEmptySelection = YES;
  _allowsMultipleSelection = NO;
  _allowsColumnSelection = YES;
  _allowsColumnResizing = YES;
  _allowsColumnReordering = YES;
  _autoresizesAllColumnsToFit = NO;
  _editedColumn = -1;
  _editedRow = -1;
  _selectedColumn = -1;
  _selectedRow = -1;
  _highlightedTableColumn = nil;
  _headerView = [NSTableHeaderView new];
  [_headerView setFrameSize: NSMakeSize (frameRect.size.width, 22.0)];
  [_headerView setTableView: self];
  _cornerView = [GSTableCornerView new];
  [self tile];
  return self;
}

- (void) dealloc
{
  [self abortEditing];

  RELEASE (_gridColor);
  RELEASE (_backgroundColor);
  RELEASE (_tableColumns);
  RELEASE (_selectedColumns);
  RELEASE (_selectedRows);
  TEST_RELEASE (_headerView);
  TEST_RELEASE (_cornerView);
  if (_autosaveTableColumns == YES)
    {
      [nc removeObserver: self 
	  name: NSTableViewColumnDidResizeNotification
	  object: self];
    }
  TEST_RELEASE (_autosaveName);
  if (_numberOfColumns > 0)
    {
      NSZoneFree (NSDefaultMallocZone (), _columnOrigins);
    }
  if (_delegate != nil)
    {
      [nc removeObserver: _delegate  name: nil  object: self];
      _delegate = nil;
    }
  [super dealloc];
}

- (BOOL) isFlipped
{
  return YES;
}

/*
 * Table Dimensions 
 */

- (int) numberOfColumns
{
  return _numberOfColumns;
}

- (int) numberOfRows
{
  return _numberOfRows;
}

/* 
 * Columns 
 */

- (void) addTableColumn: (NSTableColumn *)aColumn
{
  [aColumn setTableView: self];
  [_tableColumns addObject: aColumn];
  _numberOfColumns++;
  if (_numberOfColumns > 1)
    {
      _columnOrigins = NSZoneRealloc (NSDefaultMallocZone (), _columnOrigins,
				      (sizeof (float)) * _numberOfColumns);
    }
  else 
    {
      _columnOrigins = NSZoneMalloc (NSDefaultMallocZone (), sizeof (float));
    }      
  [self tile];
}

- (void) removeTableColumn: (NSTableColumn *)aColumn
{
  int columnIndex = [self columnWithIdentifier: [aColumn identifier]];

  if (columnIndex == -1)
    {
      NSLog (@"Warning: Tried to remove not-existent column from table");
      return;
    }

  /* Remove selection on this column */
  [self deselectColumn: columnIndex];
  /* Shift column indexes on the right by one */
  if (_selectedColumn > columnIndex)
    {
      _selectedColumn--;
    }

  [_selectedColumns removeIndex: columnIndex];

  /* Now really remove the column */

  /* NB: Set table view to nil before removing the column from the
     array, because removing it from the array could deallocate it !  */
  [aColumn setTableView: nil];
  [_tableColumns removeObject: aColumn];
  _numberOfColumns--;
  if (_numberOfColumns > 0)
    {
      _columnOrigins = NSZoneRealloc (NSDefaultMallocZone (), _columnOrigins,
				      (sizeof (float)) * _numberOfColumns);
    }
  else 
    {
      NSZoneFree (NSDefaultMallocZone (), _columnOrigins);
    }      
  [self tile];
}

- (void) moveColumn: (int)columnIndex toColumn: (int)newIndex
{
  /* The range of columns which need to be shifted, 
     extremes included */
  int minRange, maxRange;
  /* Amount of shift for these columns */
  int shift;
  BOOL selected = NO;

  if ((columnIndex < 0) || (columnIndex > (_numberOfColumns - 1)))
    {
      NSLog (@"Attempt to move column outside table");
      return;
    }
  if ((newIndex < 0) || (newIndex > (_numberOfColumns - 1)))
    {
      NSLog (@"Attempt to move column to outside table");
      return;
    }

  if (columnIndex == newIndex)
    return;

  if (columnIndex > newIndex)
    {
      minRange = newIndex;
      maxRange = columnIndex - 1;
      shift = +1;
    }
  else // columnIndex < newIndex
    {
      minRange = columnIndex + 1;
      maxRange = newIndex;
      shift = -1;
    }

  /* Rearrange selection */
  if (_selectedColumn == columnIndex)
    {
      _selectedColumn = newIndex;
    }
  else if ((_selectedColumn >= minRange) && (_selectedColumn <= maxRange)) 
    {
      _selectedColumn += shift;
    }

  if ([_selectedColumns containsIndex: columnIndex])
    {
      selected = YES;
    }
  [_selectedColumns shiftIndexesStartingAtIndex: columnIndex + 1 by: -1];
  [_selectedColumns shiftIndexesStartingAtIndex: newIndex by: 1];
  if (selected)
    {
      [_selectedColumns addIndex: newIndex];
    }

  /* Now really move the column */
  if (columnIndex < newIndex)
    {
      [_tableColumns insertObject: [_tableColumns objectAtIndex: columnIndex]
		     atIndex: newIndex + 1];
      [_tableColumns removeObjectAtIndex: columnIndex];
    }
  else
    {
      [_tableColumns insertObject: [_tableColumns objectAtIndex: columnIndex]
		     atIndex: newIndex];
      [_tableColumns removeObjectAtIndex: columnIndex + 1];
    }
  /* Tile */
  [self tile];

  /* Post notification */

  [self _postColumnDidMoveNotificationWithOldIndex: columnIndex
	newIndex: newIndex];

  [self _autosaveTableColumns];
}

- (NSArray *) tableColumns
{
  return AUTORELEASE ([_tableColumns mutableCopyWithZone: 
				       NSDefaultMallocZone ()]);
}

- (int) columnWithIdentifier: (id)identifier
{
  NSEnumerator	*enumerator = [_tableColumns objectEnumerator];
  NSTableColumn	*tb;
  int           return_value = 0;
  
  while ((tb = [enumerator nextObject]) != nil)
    {
      if ([[tb identifier] isEqual: identifier])
	return return_value;
      else
	return_value++;
    }
  return -1;
}

- (NSTableColumn *) tableColumnWithIdentifier:(id)anObject
{
  int indexOfColumn = [self columnWithIdentifier: anObject];

  if (indexOfColumn == -1)
    return nil;
  else 
    return [_tableColumns objectAtIndex: indexOfColumn];
}

/* 
 * Data Source 
 */

- (id) dataSource
{
  return _dataSource;
}

- (void) setDataSource: (id)anObject
{
  /* Used only for readability */
  const SEL sel_a = @selector (numberOfRowsInTableView:);
  const SEL sel_b = @selector (tableView:objectValueForTableColumn:row:);
  const SEL sel_c = @selector(tableView:setObjectValue:forTableColumn:row:);
  if (anObject && [anObject respondsToSelector: sel_a] == NO) 
    {
      [NSException 
	raise: NSInternalInconsistencyException 
	format: @"Data Source doesn't respond to numberOfRowsInTableView:"];
    }
  
  if (anObject && [anObject respondsToSelector: sel_b] == NO) 
    {
      [NSException raise: NSInternalInconsistencyException 
		   format: @"Data Source doesn't respond to "
		   @"tableView:objectValueForTableColumn:row:"];
    }
  
  
  _dataSource_editable = [anObject respondsToSelector: sel_c];

  /* We do *not* retain the dataSource, it's like a delegate */
  _dataSource = anObject;
  [self tile];
  [self reloadData];
}

/* 
 * Loading data 
 */

- (void) reloadData
{
  [self noteNumberOfRowsChanged];
  [self setNeedsDisplay: YES];
}

/* 
 * Target-action 
 */

- (void) setAction: (SEL)aSelector
{
  _action = aSelector;
}

- (SEL) action
{
  return _action;
}

- (void) setDoubleAction: (SEL)aSelector
{
  _doubleAction = aSelector;
}

- (SEL) doubleAction
{
  return _doubleAction;
}

- (void) setTarget:(id)anObject
{
  _target = anObject;
}

- (id) target
{
  return _target;
}

- (int) clickedColumn
{
  return _clickedColumn;
}

- (int) clickedRow
{
  return _clickedRow;
}

/*
 * The NSTableHeaderView calls this method when it receives a double click.
 */

- (void) _sendDoubleActionForColumn: (int)columnIndex
{
  _clickedColumn = columnIndex;
  _clickedRow = -1;
  [self sendAction: _doubleAction  to: _target]; 
}

/*
 * And this when it gets a simple click which turns out to be for 
 * selecting/deselecting a column.
 */

- (void) _selectColumn: (int)columnIndex
	     modifiers: (unsigned int)modifiers
{
  if (_allowsColumnSelection == NO)
    {
      return;
    }

  if ([self isColumnSelected: columnIndex] == YES)
    {
      if (([_selectedColumns count] == 1) && (_allowsEmptySelection == NO))
	{
	  return;
	}
	  
      if ([self _shouldSelectionChange] == NO)
	{
	  return;
	}

      if (_selectingColumns == NO)
	{
	  [self _setSelectingColumns: YES];
	}

      [self deselectColumn: columnIndex];
      //      [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
      return;
    }
  else // column is not selected 
    {
      BOOL newSelection;
      
      if ((modifiers & (NSShiftKeyMask | NSAlternateKeyMask)) 
	  && _allowsMultipleSelection)
	{
	  newSelection = NO;
	}
      else
	{
	  newSelection = YES;
	}

      if (([_selectedColumns count] > 0) && (_allowsMultipleSelection == NO)
	  && (newSelection == NO))
	{
	  return;
	}
      
      if ([self _shouldSelectionChange] == NO)
	{
	  return;
	}

      
      {
	NSTableColumn *tc = [_tableColumns objectAtIndex: columnIndex];
	if ([self _shouldSelectTableColumn: tc] == NO)
	  {
	    return;
	  }
      }

      if (_selectingColumns == NO)
	{
	  [self _setSelectingColumns: YES];
	}

      if (newSelection == YES)
	{
	  /* No shift or alternate key pressed: clear the old selection */
	  
	  /* Compute rect to redraw to clear the old selection */
	  /*
	  int column, i, count = [_selectedColumns count];
	  
	  for (i = 0; i < count; i++)
	    {
	      column = [[_selectedColumns objectAtIndex: i] intValue];
	      [self setNeedsDisplayInRect: [self rectOfColumn: column]];
	    }
	  */
	  /* Draw the new selection */
	  [self selectColumn: columnIndex  byExtendingSelection: NO];
	  //	  [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
	}
      else /* Simply add to the old selection */
	{
	  [self selectColumn: columnIndex  byExtendingSelection: YES];
	  //	  [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
	}
    }
}


/*
 *Configuration 
 */ 

- (void) setAllowsColumnReordering: (BOOL)flag
{
  _allowsColumnReordering = flag;
}

- (BOOL) allowsColumnReordering
{
  return _allowsColumnReordering;
}

- (void) setAllowsColumnResizing: (BOOL)flag
{
  _allowsColumnResizing = flag;
}

- (BOOL) allowsColumnResizing
{
  return _allowsColumnResizing;
}

- (void) setAllowsMultipleSelection: (BOOL)flag
{
  _allowsMultipleSelection = flag;
}

- (BOOL) allowsMultipleSelection
{
  return _allowsMultipleSelection;
}

- (void) setAllowsEmptySelection: (BOOL)flag
{
  _allowsEmptySelection = flag;
}

- (BOOL) allowsEmptySelection
{
  return _allowsEmptySelection;
}

- (void) setAllowsColumnSelection: (BOOL)flag
{
  _allowsColumnSelection = flag;
}

- (BOOL) allowsColumnSelection
{
  return _allowsColumnSelection;
}

/* 
 * Drawing Attributes 
 */

- (void) setIntercellSpacing: (NSSize)aSize
{
  _intercellSpacing = aSize;
  [self setNeedsDisplay: YES];
}

- (NSSize) intercellSpacing
{
  return _intercellSpacing;
}

- (void) setRowHeight: (float)rowHeight
{
  _rowHeight = rowHeight;
  [self tile];
}

- (float) rowHeight
{
  return _rowHeight;
}

- (void) setBackgroundColor: (NSColor *)aColor
{
  ASSIGN (_backgroundColor, aColor);
}

- (NSColor *) backgroundColor
{
  return _backgroundColor;
}
- (void) setUsesAlternatingRowBackgroundColors: (BOOL)useAlternatingRowColors
{
  // FIXME
}

- (BOOL) usesAlternatingRowBackgroundColors
{
  // FIXME
  return NO;
}

/*
 * Selecting Columns and Rows
 */
- (void) selectColumn: (int)columnIndex 
 byExtendingSelection: (BOOL)flag
{
  if (columnIndex < 0 || columnIndex > _numberOfColumns)
    {
      [NSException raise: NSInvalidArgumentException
		   format: @"Column index out of table in selectColumn"];
    }

  _selectingColumns = YES;

  if (flag == NO)
    {
      /* If the current selection is the one we want, just ends editing
       * This is not just a speed up, it prevents us from sending
       * a NSTableViewSelectionDidChangeNotification.
       * This behaviour is required by the specifications */
      if ([_selectedColumns count] == 1
	  && [_selectedColumns containsIndex: columnIndex] == YES)
	{
	  /* Stop editing if any */
	  if (_textObject != nil)
	    {
	      [self validateEditing];
	      [self abortEditing];
	    }  
	  return;
	} 

      /* If _numberOfColumns == 1, we can skip trying to deselect the
	 only column - because we have been called to select it. */
      if (_numberOfColumns > 1)
	{
	  [_selectedColumns removeAllIndexes];
	  [self setNeedsDisplay: YES];
	  if (_headerView)
	    {
	      [_headerView setNeedsDisplay: YES];
	    }
	  _selectedColumn = -1;
	}
    }
  else // flag == YES
    {
      if (_allowsMultipleSelection == NO)
	{
	  [NSException raise: NSInternalInconsistencyException
		       format: @"Can not extend selection in table view when multiple selection is disabled"];  
	}
    }

  /* Stop editing if any */
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }

  /* Now select the column and post notification only if needed */ 
  if ([_selectedColumns containsIndex: columnIndex] == NO)
    {
      [_selectedColumns addIndex: columnIndex];
      _selectedColumn = columnIndex;

      [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
      if (_headerView)
	{
	  [_headerView setNeedsDisplayInRect: 
			 [_headerView headerRectOfColumn: columnIndex]];
	}
      [self _postSelectionDidChangeNotification];
    }
  else /* Otherwise simply change the last selected column */
    {
      _selectedColumn = columnIndex;
    }
}

- (void) selectRow: (int)rowIndex
byExtendingSelection: (BOOL)flag
{
  if (rowIndex < 0 || rowIndex >= _numberOfRows)
    {
      [NSException raise: NSInvalidArgumentException
		   format: @"Row index out of table in selectRow"];
    }

  if (_selectingColumns)
    {
      _selectingColumns = NO;
      if (_headerView)
	{
	  [_headerView setNeedsDisplay: YES];
	}
    }

  if (flag == NO)
    {
      /* If the current selection is the one we want, just ends editing
       * This is not just a speed up, it prevents us from sending
       * a NSTableViewSelectionDidChangeNotification.
       * This behaviour is required by the specifications */
      if ([_selectedRows count] == 1
	  && [_selectedRows containsIndex: rowIndex] == YES)
	{
	  /* Stop editing if any */
	  if (_textObject != nil)
	    {
	      [self validateEditing];
	      [self abortEditing];
	    }
	  return;
	} 

      /* If _numberOfRows == 1, we can skip trying to deselect the
	 only row - because we have been called to select it. */
      if (_numberOfRows > 1)
	{
	  [_selectedRows removeAllIndexes];
	  _selectedRow = -1;
	  [self setNeedsDisplay: YES];
	}
    }
  else // flag == YES
    {
      if (_allowsMultipleSelection == NO)
	{
	  [NSException raise: NSInternalInconsistencyException
		       format: @"Can not extend selection in table view when multiple selection is disabled"];  
	}
    }

  /* Stop editing if any */
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }  

  /* Now select the row and post notification only if needed */ 
  if ([self _selectUnselectedRow: rowIndex])
    {
      [self _postSelectionDidChangeNotification];
    }
  else /* Otherwise simply change the last selected row */
    {
      _selectedRow = rowIndex;
    }
}

- (void) selectColumnIndexes: (NSIndexSet *)indexes byExtendingSelection: (BOOL)extend
{
  BOOL empty = ([indexes firstIndex] == NSNotFound);
  BOOL changed = NO;
  unsigned int col;
  
  if (!_selectingColumns)
    {
      _selectingColumns = YES;
      if (_headerView)
	{
	  [_headerView setNeedsDisplay: YES];
	}
    }

  /* Stop editing if any */
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }
  
  if (extend == NO)
    {
      /* If the current selection is the one we want, just ends editing
       * This is not just a speed up, it prevents us from sending
       * a NSTableViewSelectionDidChangeNotification.
       * This behaviour is required by the specifications */
      if ([_selectedColumns isEqualToIndexSet: indexes])
        {
	  if (!empty)
	    {
	      _selectedColumn = [indexes lastIndex];
	    }
	  return;
	}

      [self _unselectAllColumns];
      changed = YES;
    }

  if (!empty)
    {
      if ([indexes lastIndex] >= _numberOfColumns)
        {
	  [NSException raise: NSInvalidArgumentException
		       format: @"Column index out of table in selectColumn"];
	}

      /* This check is not fully correct, as both sets may contain just 
	 the same entry, but works according to the old specification. */
      if (_allowsMultipleSelection == NO && 
	  [_selectedColumns count] + [indexes count] > 1)
        {
	  [NSException raise: NSInternalInconsistencyException
		       format: @"Can not set multiple selection in table view when multiple selection is disabled"];  
	}

      col = [indexes firstIndex];
      while (col != NSNotFound)
        {
	  if (![_selectedColumns containsIndex: col])
	    {
	      [self setNeedsDisplayInRect: [self rectOfColumn: col]];
	      if (_headerView)
	        {
		  [_headerView setNeedsDisplayInRect: 
				   [_headerView headerRectOfColumn: col]];
		}
	      changed = YES;
	    }
	  col = [indexes indexGreaterThanIndex: col];
	}
      [_selectedColumns addIndexes: indexes];
      _selectedColumn = [indexes lastIndex];
    }

  if (changed)
    {
      [self _postSelectionDidChangeNotification];
    }
}

- (void) selectRowIndexes: (NSIndexSet *)indexes byExtendingSelection: (BOOL)extend
{
  BOOL empty = ([indexes firstIndex] == NSNotFound);
  BOOL changed = NO;
  unsigned int row;
  
  if (_selectingColumns)
    {
      _selectingColumns = NO;
      if (_headerView)
	{
	  [_headerView setNeedsDisplay: YES];
	}
    }

  /* Stop editing if any */
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }
  
  if (extend == NO)
    {
      /* If the current selection is the one we want, just ends editing
       * This is not just a speed up, it prevents us from sending
       * a NSTableViewSelectionDidChangeNotification.
       * This behaviour is required by the specifications */
      if ([_selectedRows isEqualToIndexSet: indexes])
        {
	  if (!empty)
	    {
	      _selectedRow = [indexes lastIndex];
	    }
	  return;
	}

      [self _unselectAllRows];
      changed = YES;
    }

  if (!empty)
    {
      if ([indexes lastIndex] >= _numberOfRows)
        {
	  [NSException raise: NSInvalidArgumentException
		       format: @"Row index out of table in selectRow"];
	}

      /* This check is not fully correct, as both sets may contain just 
	 the same entry, but works according to the old specification. */
      if (_allowsMultipleSelection == NO && 
	  [_selectedRows count] + [indexes count] > 1)
        {
	  [NSException raise: NSInternalInconsistencyException
		       format: @"Can not set multiple selection in table view when multiple selection is disabled"];  
	}

      row = [indexes firstIndex];
      while (row != NSNotFound)
        {
	  if (![_selectedRows containsIndex: row])
	    {
	      [self setNeedsDisplayInRect: [self rectOfRow: row]];
	    }
	  row = [indexes indexGreaterThanIndex: row];
	}
      [_selectedRows addIndexes: indexes];
      _selectedRow = [indexes lastIndex];
      changed = YES;
    }

  if (changed)
    {
      [self _postSelectionDidChangeNotification];
    }
}

- (NSIndexSet *) selectedColumnIndexes;
{
  return _selectedColumns;  
}

- (NSIndexSet *) selectedRowIndexes;
{
  return _selectedRows;  
}

- (void) deselectColumn: (int)columnIndex
{
  if ([_selectedColumns containsIndex: columnIndex] == NO)
    {
      return;
    }

  /* Now by internal consistency we assume columnIndex is in fact a
     valid column index, since it was the index of a selected column */

  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }
  
  _selectingColumns = YES;

  [_selectedColumns removeIndex: columnIndex];

  if (_selectedColumn == columnIndex)
    {
      unsigned int less = [_selectedColumns indexLessThanIndex: columnIndex];
      unsigned int greater = [_selectedColumns indexGreaterThanIndex: columnIndex];

      if (less == NSNotFound)
        {
	  if (greater == NSNotFound)
	    {
	      _selectedColumn = -1;
	    }
	  else
	    {
	      _selectedColumn = greater;		
	    }  
	}
      else if (greater == NSNotFound)
        {
	  _selectedColumn = less;
	}
      else if (columnIndex - less > greater - columnIndex)
        {
	  _selectedColumn = greater;		
	}
      else 
        {
	  _selectedColumn = less;
	}
    }
      
  [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
  if (_headerView)
    {
      [_headerView setNeedsDisplayInRect: 
		     [_headerView headerRectOfColumn: columnIndex]];
    }

  [self _postSelectionDidChangeNotification];
}

- (void) deselectRow: (int)rowIndex
{
  if ([_selectedRows containsIndex: rowIndex] == NO)
    {
      return;
    }

  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }

  _selectingColumns = NO;

  [_selectedRows removeIndex: rowIndex];

  if (_selectedRow == rowIndex)
    {
      unsigned int less = [_selectedRows indexLessThanIndex: rowIndex];
      unsigned int greater = [_selectedRows indexGreaterThanIndex: rowIndex];

      if (less == NSNotFound)
        {
	  if (greater == NSNotFound)
	    {
	      _selectedRow = -1;
	    }
	  else
	    {
	      _selectedRow = greater;		
	    }  
	}
      else if (greater == NSNotFound)
        {
	  _selectedRow = less;
	}
      else if (rowIndex - less > greater - rowIndex)
        {
	  _selectedRow = greater;		
	}
      else 
        {
	  _selectedRow = less;
	}
    }

  [self _postSelectionDidChangeNotification];
}

- (int) numberOfSelectedColumns
{
  return [_selectedColumns count];
}

- (int) numberOfSelectedRows
{
  return [_selectedRows count];
}

- (int) selectedColumn
{
  return _selectedColumn;
}

- (int) selectedRow
{
  return _selectedRow;
}

- (BOOL) isColumnSelected: (int)columnIndex
{
  return [_selectedColumns containsIndex: columnIndex];
}

- (BOOL) isRowSelected: (int)rowIndex
{
  return [_selectedRows containsIndex: rowIndex];
}

- (NSEnumerator *) selectedColumnEnumerator
{
  return [[self _selectedColumArray] objectEnumerator];
}

- (NSEnumerator *) selectedRowEnumerator
{
  return [[self _selectedRowArray] objectEnumerator];
}

- (void) selectAll: (id) sender
{
  if (_allowsMultipleSelection == NO)
    return;

  /* Ask the delegate if we can select all columns or rows */
  if (_selectingColumns == YES)
    {
      if ([_selectedColumns count] == (unsigned)_numberOfColumns)
	{
	  // Nothing to do !
	  return;
	}

      {
	NSEnumerator *enumerator = [_tableColumns objectEnumerator];
	NSTableColumn *tb;
	while ((tb = [enumerator nextObject]) != nil)
	  {
	    if ([self _shouldSelectTableColumn: tb] == NO)
	      {
		return;
	      }
	  }
      }
    }
  else // selecting rows
    {
      if ([_selectedRows count] == (unsigned)_numberOfRows)
	{
	  // Nothing to do !
	  return;
	}

      {
	int row; 
	
	for (row = 0; row < _numberOfRows; row++)
	  {
	    if ([self _shouldSelectRow: row] == NO)
	      return;
	  }
      }
    }

  /* Stop editing if any */
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }  

  /* Do the real selection */
  if (_selectingColumns == YES)
    {
      [_selectedColumns removeAllIndexes];
      [_selectedColumns addIndexesInRange: NSMakeRange(0, _numberOfColumns)];
    }
  else // selecting rows
    {
      [_selectedRows removeAllIndexes];
      [_selectedRows addIndexesInRange: NSMakeRange(0, _numberOfRows)];
    }
  
  [self _postSelectionDidChangeNotification];
}



- (void) deselectAll: (id) sender
{
  if (_allowsEmptySelection == NO)
    return;

  if ([self _shouldSelectionChange] == NO)
    {
      return;
    }
  
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }
	  
  if (([_selectedColumns count] > 0) || ([_selectedRows count] > 0))
    {
      [_selectedColumns removeAllIndexes];
      [_selectedRows removeAllIndexes];
      _selectedColumn = -1;
      _selectedRow = -1;
      _selectingColumns = NO;
      [self _postSelectionDidChangeNotification];
    }
  else
    {
      _selectedColumn = -1;
      _selectedRow = -1;
      _selectingColumns = NO;
    }
}

/* 
 * Grid Drawing attributes 
 */

- (void) setDrawsGrid: (BOOL)flag
{
  _drawsGrid = flag;
}

- (BOOL) drawsGrid
{
  return _drawsGrid;
}

- (void) setGridColor: (NSColor *)aColor
{
  ASSIGN (_gridColor, aColor);
}

- (NSColor *) gridColor
{
  return _gridColor;
}

- (void) setGridStyleMask: (unsigned int)gridType
{
  // FIXME
}

- (unsigned int) gridStyleMask;
{
  // FIXME
  return 0;
}

/* 
 * Editing Cells 
 */

- (BOOL) abortEditing
{ 
  if (_textObject)
    {
      [_textObject setString: @""];
      [_editedCell endEditing: _textObject];
      RELEASE (_editedCell);
      [self setNeedsDisplayInRect: 
	      [self frameOfCellAtColumn: _editedColumn row: _editedRow]];
      _editedRow = -1;
      _editedColumn = -1;
      _editedCell = nil;
      _textObject = nil;
      return YES;
    }
  else
    return NO;
}

- (NSText *) currentEditor
{
  if (_textObject && ([_window firstResponder] == _textObject))
    return _textObject;
  else
    return nil;
}

- (void) validateEditing
{
  if (_textObject)
    {
      NSFormatter *formatter;
      NSString *string;
      id newObjectValue;
      BOOL validatedOK = YES;

      formatter = [_editedCell formatter];
      string = AUTORELEASE ([[_textObject text] copy]);

      if (formatter == nil)
	{
	  newObjectValue = string;
	}
      else
	{
	  NSString *error;
	  
	  if ([formatter getObjectValue: &newObjectValue 
			 forString: string 
			 errorDescription: &error] == NO)
	    {
	      if ([_delegate control: self 
			     didFailToFormatString: string 
			     errorDescription: error] == NO)
		{
		  validatedOK = NO;
		}
	      else
		{
		  newObjectValue = string;
		}
	    }
	}
      if (validatedOK == YES)
	{
	  [_editedCell setObjectValue: newObjectValue];
	  
	  if (_dataSource_editable)
	    {
	      NSTableColumn *tb;
	      
	      tb = [_tableColumns objectAtIndex: _editedColumn];
	      
	      [self _setObjectValue: newObjectValue
		    forTableColumn: tb
		    row: _editedRow];

	      //[_dataSource tableView: self  setObjectValue: newObjectValue
	      //	 forTableColumn: tb  row: _editedRow];
	    }
	}
    }
}

- (void) editColumn: (int) columnIndex 
		row: (int) rowIndex 
	  withEvent: (NSEvent *) theEvent 
	     select: (BOOL) flag
{
  NSText *t;
  NSTableColumn *tb;
  NSRect drawingRect;
  unsigned length = 0;

  // We refuse to edit cells if the delegate can not accept results 
  // of editing.
  if (_dataSource_editable == NO)
    {
      return;
    }
  
  [self scrollRowToVisible: rowIndex];
  [self scrollColumnToVisible: columnIndex];

  if (rowIndex < 0 || rowIndex >= _numberOfRows 
      || columnIndex < 0 || columnIndex >= _numberOfColumns)
    {
      [NSException raise: NSInvalidArgumentException
		   format: @"Row/column out of index in edit"];
    }
  
  if (_textObject != nil)
    {
      [self validateEditing];
      [self abortEditing];
    }

  // Now (_textObject == nil)

  t = [_window fieldEditor: YES  forObject: self];

  if ([t superview] != nil)
    {
      if ([t resignFirstResponder] == NO)
	{
	  return;
	}
    }
  
  _editedRow = rowIndex;
  _editedColumn = columnIndex;

  // Prepare the cell
  tb = [_tableColumns objectAtIndex: columnIndex];
  // NB: need to be released when no longer used
  _editedCell = [[tb dataCellForRow: rowIndex] copy];

  [_editedCell setEditable: YES];
  [_editedCell setObjectValue: [self _objectValueForTableColumn: tb
				     row: rowIndex]];
  /* [_dataSource tableView: self
     objectValueForTableColumn: tb
     row: rowIndex]]; */

  // We really want the correct background color!
  if ([_editedCell respondsToSelector: @selector(setBackgroundColor:)])
    {
      [(NSTextFieldCell *)_editedCell setBackgroundColor: _backgroundColor];
    }
  else
    {
      [t setBackgroundColor: _backgroundColor];
    }
  
  // But of course the delegate can mess it up if it wants
  if (_del_responds)
    {
      [_delegate tableView: self   willDisplayCell: _editedCell 
		 forTableColumn: tb   row: rowIndex];
    }

  /* Please note the important point - calling stringValue normally
     causes the _editedCell to call the validateEditing method of its
     control view ... which happens to be this NSTableView object :-)
     but we don't want any spurious validateEditing to be performed
     before the actual editing is started (otherwise you easily end up
     with the table view picking up the string stored in the field
     editor, which is likely to be the string resulting from the last
     edit somewhere else ... getting into the bug that when you TAB
     from one cell to another one, the string is copied!), so we must
     call stringValue when _textObject is still nil.  */
  if (flag)
    {
      length = [[_editedCell stringValue] length];
    }

  _textObject = [_editedCell setUpFieldEditorAttributes: t];

  drawingRect = [self frameOfCellAtColumn: columnIndex  row: rowIndex];
  if (flag)
    {
      [_editedCell selectWithFrame: drawingRect
		   inView: self
		   editor: _textObject
		   delegate: self
		   start: 0
		   length: length];
    }
  else
    {
      [_editedCell editWithFrame: drawingRect
		   inView: self
		   editor: _textObject
		   delegate: self
		   event: theEvent];
    }
  return;    
}

- (int) editedRow
{
  return _editedRow;  
}

- (int) editedColumn
{
  return _editedColumn;
}

- (void) _setSelectingColumns: (BOOL)flag
{
  if (flag == _selectingColumns)
    return;
  
  if (flag == NO)
    {
      [self _unselectAllColumns];
      _selectingColumns = NO;
    }
  else
    {
      [self _unselectAllRows];
      _selectingColumns = YES;
    }
}

- (void) mouseDown: (NSEvent *)theEvent
{
  NSPoint location = [theEvent locationInWindow];
  NSPoint initialLocation = [theEvent locationInWindow];
  NSTableColumn *tb;
  int clickCount;
  BOOL shouldEdit;
  int _originalRow;
  int _oldRow = -1;
  int _currentRow = -1;

  //
  // Pathological case -- ignore mouse down
  //
  if ((_numberOfRows == 0) || (_numberOfColumns == 0))
    {
      [super mouseDown: theEvent];
      return; 
    }
  
  clickCount = [theEvent clickCount];

  if (clickCount > 2)
    return;

  // Determine row and column which were clicked
  location = [self convertPoint: location  fromView: nil];
  _clickedRow  = [self rowAtPoint: location];
  _clickedColumn = [self columnAtPoint: location];
  _originalRow = _clickedRow;

  // Selection
  if (clickCount == 1)
    {
      unsigned int modifiers = [theEvent modifierFlags];
      BOOL startedPeriodicEvents = NO;
      unsigned int eventMask = (NSLeftMouseUpMask 
				| NSLeftMouseDownMask
				| NSLeftMouseDraggedMask 
				| NSPeriodicMask);
      unsigned selectionMode;
      NSPoint mouseLocationWin;
      NSDate *distantFuture = [NSDate distantFuture];
      NSEvent *lastEvent;
      NSIndexSet *_oldSelectedRows;
      BOOL mouseUp = NO;
      BOOL done = NO;
      BOOL draggingPossible = [self _isDraggingSource];
      NSRect visibleRect = [self convertRect: [self visibleRect]
				 toView: nil];
      float minYVisible = NSMinY (visibleRect);
      float maxYVisible = NSMaxY (visibleRect);
      BOOL mouseMoved = NO;

      /* We have three zones of speed. 
	 0   -  50 pixels: period 0.2  <zone 1>
	 50  - 100 pixels: period 0.1  <zone 2>
	 100 - 150 pixels: period 0.01 <zone 3> */
      float oldPeriod = 0;
      inline float computePeriod(void)
	{
	  float distance = 0;
	  
	  if (mouseLocationWin.y < minYVisible) 
	    {
	      distance = minYVisible - mouseLocationWin.y; 
	    }
	  else if (mouseLocationWin.y > maxYVisible)
	    {
	      distance = mouseLocationWin.y - maxYVisible;
	    }
	  
	  if (distance < 50)
	    return 0.2;
	  else if (distance < 100)
	    return 0.1;
	  else 
	    return 0.01;
	}
      
      selectionMode = 0;
      if (_allowsMultipleSelection == YES)
	{
	  selectionMode |= ALLOWS_MULTIPLE;
	}

      if (_allowsEmptySelection == YES)
	{
	  selectionMode |= ALLOWS_EMPTY;
	}
      
      if (modifiers & NSShiftKeyMask)
	{
	  selectionMode |= SHIFT_DOWN;
	}
      
      if (![_selectedRows containsIndex: _clickedRow])
	{
	  selectionMode |= ADDING_ROW;
	}

      if (modifiers & NSControlKeyMask)
	{
	  selectionMode |= CONTROL_DOWN;
	  if (_allowsMultipleSelection == YES)
	    {
	      _originalRow = _selectedRow;
	      selectionMode |= SHIFT_DOWN;
	      selectionMode |= ADDING_ROW;
	    }
	}
      
      // is the delegate ok for a new selection ?
      if ([self _shouldSelectionChange] == NO)
	{
	  return;
	}

      // if we are in column selection mode, stop it
      if (_selectingColumns == YES)
	{
	  if (_headerView)
	    {
	      int i;
	      for (i = 0; i < _numberOfColumns; i++)
		{
		  if ([self isColumnSelected: i])
		    [_headerView setNeedsDisplayInRect: 
				   [_headerView headerRectOfColumn: i]];
		}
	    }
	  [self _setSelectingColumns: NO];
	}

      // let's sort the _selectedRows
      _oldSelectedRows = [_selectedRows copy];

      lastEvent = theEvent;
      while (done != YES)
	{
	  /*
	  Wrap each iteration in an autorelease pool. Otherwise, we end
	  up allocating huge amounts of objects if the button is held
	  down for a long time.
	  */
	  CREATE_AUTORELEASE_POOL(arp);
	  BOOL shouldComputeNewSelection = NO;
	  switch ([lastEvent type])
	    {
	    case NSLeftMouseUp:
	      mouseLocationWin = [lastEvent locationInWindow]; 
	      if ((mouseLocationWin.y > minYVisible) 
		  && (mouseLocationWin.y < maxYVisible))
		// mouse dragged within table
		{
		  NSPoint mouseLocationView;
		  
		  if (startedPeriodicEvents == YES)
		    {
		      [NSEvent stopPeriodicEvents];
		      startedPeriodicEvents = NO;
		    }
		  mouseLocationView = [self convertPoint: 
					      mouseLocationWin 
					    fromView: nil];
		  mouseLocationView.x = _bounds.origin.x;
		  _oldRow = _currentRow;
		  _currentRow = [self rowAtPoint: mouseLocationView];
		  if (_oldRow != _currentRow)
		    {
		      shouldComputeNewSelection = YES;
		    }
		}
	      else
		// Mouse dragged out of the table
		{
		  // we don't care
		}
	      done = YES;
	      break;

	    case NSLeftMouseDown:
	    case NSLeftMouseDragged:
	      mouseLocationWin = [lastEvent locationInWindow];

	      if (fabs(mouseLocationWin.x - initialLocation.x) > 1
	          || fabs(mouseLocationWin.y - initialLocation.y) > 1)
		{
		  mouseMoved = YES;
		}

	      if (draggingPossible == YES)
		{
		  if (mouseLocationWin.y - initialLocation.y > 2
		      || mouseLocationWin.y - initialLocation.y < -2)
		    {
		      draggingPossible = NO;
		    }
		  else if (mouseLocationWin.x - initialLocation.x >=4
			   || mouseLocationWin.x - initialLocation.x <= -4)
		    {
		      NSPoint mouseLocationView;
		      NSPasteboard *pboard;
		      NSArray *rows;

		      pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
		      mouseLocationView = [self convertPoint: 
					      mouseLocationWin 
					    fromView: nil];
		      mouseLocationView.x = _bounds.origin.x;
		      _oldRow = _currentRow;
		      _currentRow = [self rowAtPoint: mouseLocationView];
		      if (_oldRow != _currentRow)
			{
			  /* Mouse drag in a row that wasn't selected.
			     select the new row before dragging */
			  computeNewSelection(self,
					      _oldSelectedRows, 
					      _selectedRows,
					      _originalRow,
					      _oldRow,
					      _currentRow,
					      &_selectedRow,
					      selectionMode);
			  
			}
		      rows = [self _selectedRowArray];
		      if ([self _writeRows: rows
				toPasteboard: pboard] == YES)
			{
			  NSPoint p = NSZeroPoint;
			  NSImage *dragImage =
			    [self dragImageForRows: rows
				  event: theEvent
				  dragImageOffset: &p];

			  [self dragImage: dragImage
				at: NSZeroPoint
				offset: NSMakeSize(0, 0)
				event: theEvent
				pasteboard: pboard
				source: self
				slideBack: YES];
			  return;
			}
		      else
			{
			  draggingPossible = NO;
			}
		    }
		}
	      else if ((mouseLocationWin.y > minYVisible) 
		       && (mouseLocationWin.y < maxYVisible))
		// mouse dragged within table
		{
		  NSPoint mouseLocationView;
		  
		  if (startedPeriodicEvents == YES)
		    {
		      [NSEvent stopPeriodicEvents];
		      startedPeriodicEvents = NO;
		    }
		  mouseLocationView = [self convertPoint: 
					      mouseLocationWin 
					    fromView: nil];
		  mouseLocationView.x = _bounds.origin.x;
		  _oldRow = _currentRow;
		  _currentRow = [self rowAtPoint: mouseLocationView];

		  if (_oldRow != _currentRow)
		    {
		      shouldComputeNewSelection = YES;
		    }
		}
	      else
		// Mouse dragged out of the table
		{
		  if (startedPeriodicEvents == YES)
		    {
		      /* Check - if the mouse did not change zone, 
			 we do nothing */
		      if (computePeriod () == oldPeriod)
			break;

		      [NSEvent stopPeriodicEvents];
		    }
		  /* Start periodic events */
		  oldPeriod = computePeriod ();
			
		  [NSEvent startPeriodicEventsAfterDelay: 0
			   withPeriod: oldPeriod];
		  startedPeriodicEvents = YES;
		  if (mouseLocationWin.y <= minYVisible) 
		    mouseUp = NO;
		  else
		    mouseUp = YES;
		}
	      break;
	    case NSPeriodic:
	      if (mouseUp == NO)
		{
		  /* mouse below the table */
		  if (_currentRow < _numberOfRows - 1)
		    {
		      _oldRow = _currentRow;
		      _currentRow++;
		      [self scrollRowToVisible: _currentRow];
		      if (draggingPossible == NO)
			shouldComputeNewSelection = YES;
		    }
		}
	      else
		{
		  if (_currentRow > 0)
		    {
		      /* mouse above the table */
		      _oldRow = _currentRow;
		      _currentRow--;
		      [self scrollRowToVisible: _currentRow];
		      if (draggingPossible == NO)
			shouldComputeNewSelection = YES;
		    }
		}
	      break;
	    default:
	      break;
	    }
	  if (shouldComputeNewSelection == YES)
	    {
	      computeNewSelection(self,
				  _oldSelectedRows, 
				  _selectedRows,
				  _originalRow,
				  _oldRow,
				  _currentRow,
				  &_selectedRow,
				  selectionMode);
	      [self displayIfNeeded];
	    }
	  if (done == NO)
	    {
	      lastEvent = [NSApp nextEventMatchingMask: eventMask 
				 untilDate: distantFuture
				 inMode: NSEventTrackingRunLoopMode 
				 dequeue: YES]; 
	    }
	  DESTROY(arp);
	}

      if (startedPeriodicEvents == YES)
	[NSEvent stopPeriodicEvents];

      if (![_selectedRows  isEqualToIndexSet: _oldSelectedRows])
	{
	  [self _postSelectionDidChangeNotification];
	}

      /* If this was a simple click (ie. no dragging), we send our action. */
      if (!mouseMoved)
	{
	  /*
	  _clickedRow and _clickedColumn are already set at the start of
	  this function.

	  TODO: should we ask the data source/column for the cell for this
	  row/column and check whether it has its own action/target?
	  */
	  [self sendAction: _action  to: _target];
	}
      return;
    }

  // Double-click events

  if ([self isRowSelected: _clickedRow] == NO)
    return;

  tb = [_tableColumns objectAtIndex: _clickedColumn];

  shouldEdit = YES;

  if ([tb isEditable] == NO)
    {
      shouldEdit = NO;
    }
  else if ([self _shouldEditTableColumn: tb 
		 row: _clickedRow] == NO)
    {
      shouldEdit = NO;
    }
  if (shouldEdit == NO)
    {
      // Send double-action but don't edit
      [self sendAction: _doubleAction to: _target];
      return;
    }

  // It is OK to edit column.  Go on, do it.
  [self editColumn: _clickedColumn  row: _clickedRow
	withEvent: theEvent  select: NO];
}

/* 
 * Auxiliary Components 
 */

- (void) setHeaderView: (NSTableHeaderView*)aHeaderView
{
  if (_super_view != nil)
    {
      /* Changing the headerView after the table has been linked to a
	 scrollview is not yet supported - the doc is not clear
	 whether it should be supported at all.  If it is, perhaps
	 it's going to be done through a private method between the
	 tableview and the scrollview. */
      NSLog (@"setHeaderView: called after NSTableView has been put "
	     @"in the view tree!"); 
    }
  [_headerView setTableView: nil];
  ASSIGN (_headerView, aHeaderView);
  [_headerView setTableView: self];
  [self tile];
}

- (NSTableHeaderView*) headerView
{
  return _headerView;
}

- (void) setCornerView: (NSView*)aView
{
  ASSIGN (_cornerView, aView);
  [self tile];
}

- (NSView*) cornerView
{
  return _cornerView;
}

/* 
 * Layout 
 */

- (NSRect) rectOfColumn: (int)columnIndex
{
  NSRect rect;

  if (columnIndex < 0)
    {
      [NSException 
	raise: NSInternalInconsistencyException 
	format: @"ColumnIndex < 0 in [NSTableView -rectOfColumn:]"];
    }
  if (columnIndex >= _numberOfColumns)
    {
      [NSException 
	raise: NSInternalInconsistencyException 
	format: @"ColumnIndex => _numberOfColumns in [NSTableView -rectOfColumn:]"];
    }

  rect.origin.x = _columnOrigins[columnIndex];
  rect.origin.y = _bounds.origin.y;
  rect.size.width = [[_tableColumns objectAtIndex: columnIndex] width];
  rect.size.height = _bounds.size.height;
  return rect;
}

- (NSRect) rectOfRow: (int)rowIndex
{
  NSRect rect;

  if (rowIndex < 0)
    {
      [NSException 
	raise: NSInternalInconsistencyException 
	format: @"RowIndex < 0 in [NSTableView -rectOfRow:]"];
    }
  if (rowIndex >= _numberOfRows)
    {
      [NSException 
	raise: NSInternalInconsistencyException 
	format: @"RowIndex => _numberOfRows in [NSTableView -rectOfRow:]"];
    }
  rect.origin.x = _bounds.origin.x;
  rect.origin.y = _bounds.origin.y + (_rowHeight * rowIndex);
  rect.size.width = _bounds.size.width;
  rect.size.height = _rowHeight;
  return rect;
}

- (NSRange) columnsInRect: (NSRect)aRect
{
  NSRange range;

  range.location = [self columnAtPoint: aRect.origin];
  range.length = [self columnAtPoint: 
			 NSMakePoint (NSMaxX (aRect), _bounds.origin.y)];
  range.length -= range.location;
  range.length += 1;
  return range;
}

- (NSRange) rowsInRect: (NSRect)aRect
{
  NSRange range;

  range.location = [self rowAtPoint: aRect.origin];
  range.length = [self rowAtPoint: 
			 NSMakePoint (_bounds.origin.x, NSMaxY (aRect))];
  range.length -= range.location;
  range.length += 1;
  return range;
}

- (int) columnAtPoint: (NSPoint)aPoint
{
  if ((NSMouseInRect (aPoint, _bounds, YES)) == NO)
    {
      return -1;
    }
  else
    {
      int i = 0;
      
      while ((i < _numberOfColumns) && (aPoint.x >= _columnOrigins[i]))
	{
	  i++;
	}
      return i - 1;
    }
}

- (int) rowAtPoint: (NSPoint)aPoint
{
  /* NB: Y coordinate system is flipped in NSTableView */
  if ((NSMouseInRect (aPoint, _bounds, YES)) == NO)
    {
      return -1;
    }
  else
    {
      int return_value;

      aPoint.y -= _bounds.origin.y;
      return_value = (int) (aPoint.y / _rowHeight);
      /* This could happen if point lies on the grid line below the last row */
      if (return_value == _numberOfRows)
	{
	  return_value--;
	}
      return return_value;
    }
}

- (NSRect) frameOfCellAtColumn: (int)columnIndex 
			   row: (int)rowIndex
{
  NSRect frameRect;

  if ((columnIndex < 0) 
      || (rowIndex < 0)
      || (columnIndex > (_numberOfColumns - 1))
      || (rowIndex > (_numberOfRows - 1)))
    return NSZeroRect;
      
  frameRect.origin.y  = _bounds.origin.y + (rowIndex * _rowHeight);
  frameRect.origin.y += _intercellSpacing.height / 2;
  frameRect.size.height = _rowHeight - _intercellSpacing.height;

  frameRect.origin.x = _columnOrigins[columnIndex];
  frameRect.origin.x  += _intercellSpacing.width / 2;
  frameRect.size.width = [[_tableColumns objectAtIndex: columnIndex] width];
  frameRect.size.width -= _intercellSpacing.width;

  // We add some space to separate the cell from the grid
  if (_drawsGrid)
    {
      frameRect.size.width -= 4;
      frameRect.origin.x += 2;
    }

  // Safety check
  if (frameRect.size.width < 0)
    frameRect.size.width = 0;
  
  return frameRect;
}

- (void) setAutoresizesAllColumnsToFit: (BOOL)flag
{
  _autoresizesAllColumnsToFit = flag;
}

- (BOOL) autoresizesAllColumnsToFit
{
  return _autoresizesAllColumnsToFit;
}

- (void) sizeLastColumnToFit
{
  if ((_super_view != nil) && (_numberOfColumns > 0))
    {
      float excess_width;
      float last_column_width;
      NSTableColumn *lastColumn;

      lastColumn = [_tableColumns objectAtIndex: (_numberOfColumns - 1)];
      if ([lastColumn isResizable] == NO)
	return;
      excess_width = NSMaxX ([self convertRect: [_super_view bounds] 
				      fromView: _super_view]);
      excess_width -= NSMaxX (_bounds);
      last_column_width = [lastColumn width];
      last_column_width += excess_width;
      _tilingDisabled = YES;
      if (last_column_width < [lastColumn minWidth])
	{
	  [lastColumn setWidth: [lastColumn minWidth]];
	}
      else if (last_column_width > [lastColumn maxWidth])
	{
	  [lastColumn setWidth: [lastColumn maxWidth]];
	}
      else
	{
	  [lastColumn setWidth: last_column_width];
	}
      _tilingDisabled = NO;
      [self tile];
    }
}

- (void) setFrame: (NSRect)frameRect
{
  [super setFrame: frameRect];
}

- (void) sizeToFit
{
  NSTableColumn *tb;
  int i, j;
  float remainingWidth;
  float availableWidth;
  columnSorting *columnInfo;
  float *currentWidth;
  float *maxWidth;
  float *minWidth;
  BOOL *isResizable;
  int numberOfCurrentColumns = 0;
  float previousPoint;
  float nextPoint;
  float difference;
  float toAddToCurrentColumns;


  if ((_super_view == nil) || (_numberOfColumns == 0))
    return;

  columnInfo = NSZoneMalloc(NSDefaultMallocZone(),
			    sizeof(columnSorting) * 2 
			    * _numberOfColumns);
  currentWidth = NSZoneMalloc(NSDefaultMallocZone(),
			      sizeof(float) * _numberOfColumns);
  maxWidth = NSZoneMalloc(NSDefaultMallocZone(),
			  sizeof(float) * _numberOfColumns);
  minWidth = NSZoneMalloc(NSDefaultMallocZone(),
			  sizeof(float) * _numberOfColumns);
  isResizable = NSZoneMalloc(NSDefaultMallocZone(),
			     sizeof(BOOL) * _numberOfColumns);

  availableWidth = NSMaxX([self convertRect: [_super_view bounds] 
				fromView: _super_view]);
  remainingWidth = availableWidth;

  /*
   *  We store the minWidth and the maxWidth of every column
   *  because we'll use those values *a lot*
   *  At the same time we set every column to its mininum width
   */
  for (i = 0; i < _numberOfColumns; i++)
    {
      tb = [_tableColumns objectAtIndex: i];
      isResizable[i] = [tb isResizable];
      if (isResizable[i] == YES)
	{
	  minWidth[i] = [tb minWidth];
	  maxWidth[i] = [tb maxWidth];
	  
	  if (minWidth[i] < 0)
	    minWidth[i] = 0;
	  if (minWidth[i] > maxWidth[i])
	    {
	      minWidth[i] = [tb width];
	      maxWidth[i] = minWidth[i];
	    }
	  columnInfo[i * 2].width = minWidth[i];
	  columnInfo[i * 2].isMax = 0;
	  currentWidth[i] = minWidth[i];
	  remainingWidth -= minWidth[i];
	  
	  columnInfo[i * 2 + 1].width = maxWidth[i];
	  columnInfo[i * 2 + 1].isMax = 1;
	}
      else
	{
	  minWidth[i] = [tb width];
	  columnInfo[i * 2].width = minWidth[i];
	  columnInfo[i * 2].isMax = 0;
	  currentWidth[i] = minWidth[i];
	  remainingWidth -= minWidth[i];
	  
	  maxWidth[i] = minWidth[i];
	  columnInfo[i * 2 + 1].width = maxWidth[i];
	  columnInfo[i * 2 + 1].isMax = 1;
	}
    } 

  // sort the info we have
  quick_sort_internal(columnInfo, 0, 2 * _numberOfColumns - 1);

  previousPoint = columnInfo[0].width;
  numberOfCurrentColumns = 1;
  
  if (remainingWidth >= 0.)
    {
      for (i = 1; i < 2 * _numberOfColumns; i++)
	{
	  nextPoint = columnInfo[i].width;
	  
	  if (numberOfCurrentColumns > 0 && 
	      (nextPoint - previousPoint) > 0.)
	    {
	      int verification = 0;
	      
	      if ((nextPoint - previousPoint) * numberOfCurrentColumns
		  <= remainingWidth)
		{
		  toAddToCurrentColumns = nextPoint - previousPoint;
		  remainingWidth -= 
		    (nextPoint - previousPoint) * numberOfCurrentColumns;

		  for(j = 0; j < _numberOfColumns; j++)
		    {
		      if (minWidth[j] <= previousPoint
			  && maxWidth[j] >= nextPoint)
			{
			  verification++;
			  currentWidth[j] += toAddToCurrentColumns;
			}
		    }
		  if (verification != numberOfCurrentColumns)
		    {
		      NSLog(@"[NSTableView sizeToFit]: unexpected error");
		    }
		}
	      else
		{
		  int remainingInt = floor(remainingWidth);
		  int quotient = remainingInt / numberOfCurrentColumns;
		  int remainder = remainingInt - quotient * numberOfCurrentColumns;
		  int oldRemainder = remainder;

		  for(j = _numberOfColumns - 1; j >= 0; j--)
		    {
		      if (minWidth[j] <= previousPoint
			  && maxWidth[j] >= nextPoint)
			{
			  currentWidth[j] += quotient;
			  if (remainder > 0 
			      && maxWidth[j] >= currentWidth[j] + 1)
			    {
			      remainder--;
			      currentWidth[j]++;
			    }
			}
		    }
		  while (oldRemainder > remainder && remainder > 0)
		    {
		      oldRemainder = remainder;
		      for(j = 0; j < _numberOfColumns; j++)
			{
			  if (minWidth[j] <= previousPoint
			      && maxWidth[j] >= nextPoint)
			    {
			      if (remainder > 0 
				  && maxWidth[j] >= currentWidth[j] + 1)
				{
				  remainder--;
				  currentWidth[j]++;
				}
			    }
			  
			}
		    }
		  if (remainder > 0)
		    NSLog(@"There is still free space to fill.\
 However it seems better to use integer width for the columns");
		  else
		    remainingWidth = 0.;
		}
	      
	      
	    }
	  else if (numberOfCurrentColumns < 0)
	    {
	      NSLog(@"[NSTableView sizeToFit]: unexpected error");
	    }
	  
	  if (columnInfo[i].isMax)
	    numberOfCurrentColumns--;
	  else
	    numberOfCurrentColumns++;
	  previousPoint = nextPoint;
	  
	  if (remainingWidth == 0.)
	    {
	      break;
	    }
	}
    }

  _tilingDisabled = YES;

  remainingWidth = 0.;
  for (i = 0; i < _numberOfColumns; i++)
    {
      if (isResizable[i] == YES)
	{
	  tb = [_tableColumns objectAtIndex: i];
	  remainingWidth += currentWidth[i];
	  [tb setWidth: currentWidth[i]];
	}
      else
	{
	  remainingWidth += minWidth[i];
	}
    }


  difference = availableWidth - remainingWidth;


  _tilingDisabled = NO;
  NSZoneFree(NSDefaultMallocZone(), columnInfo);
  NSZoneFree(NSDefaultMallocZone(), currentWidth);
  NSZoneFree(NSDefaultMallocZone(), maxWidth);
  NSZoneFree(NSDefaultMallocZone(), minWidth);
  NSZoneFree(NSDefaultMallocZone(), isResizable);

  [self tile];
}
/*
- (void) sizeToFit
{
  NSCell *cell;
  NSEnumerator	*enumerator;
  NSTableColumn	*tb;
  float table_width;
  float width;
  float candidate_width;
  int row;

  _tilingDisabled = YES;

  // First Step
  // Resize Each Column to its Minimum Width
  table_width = _bounds.origin.x;
  enumerator = [_tableColumns objectEnumerator];
  while ((tb = [enumerator nextObject]) != nil)
    {
      // Compute min width of column 
      width = [[tb headerCell] cellSize].width;
      for (row = 0; row < _numberOfRows; row++)
	{
	  cell = [tb dataCellForRow: row];
	  [cell setObjectValue: [_dataSource tableView: self
					     objectValueForTableColumn: tb
					     row: row]]; 
	  if (_del_responds)
	    {
	      [_delegate tableView: self   willDisplayCell: cell 
			 forTableColumn: tb   row: row];
	    }
	  candidate_width = [cell cellSize].width;

	  if (_drawsGrid)
	    candidate_width += 4;

	  if (candidate_width > width)
	    {
	      width = candidate_width;
	    }
	}
      width += _intercellSpacing.width;
      [tb setWidth: width];
      // It is necessary to ask the column for the width, since it might have 
      // been changed by the column to constrain it to a min or max width
      table_width += [tb width];
    }

  // Second Step
  // If superview (clipview) is bigger than that, divide remaining space 
  // between all columns
  if ((_super_view != nil) && (_numberOfColumns > 0))
    {
      float excess_width;

      excess_width = NSMaxX ([self convertRect: [_super_view bounds] 
				      fromView: _super_view]);
      excess_width -= table_width;
      // Since we resized each column at its minimum width, 
      // it's useless to try shrinking more: we can't
      if (excess_width <= 0)
	{
	  _tilingDisabled = NO;
	  [self tile];
	  NSLog(@"exiting sizeToFit");
	  return;
	}
      excess_width = excess_width / _numberOfColumns;

      enumerator = [_tableColumns objectEnumerator];
      while ((tb = [enumerator nextObject]) != nil)
	{
	  [tb setWidth: ([tb width] + excess_width)];
	}
    }

  _tilingDisabled = NO;
  [self tile];
  NSLog(@"exiting sizeToFit");
}
*/
- (void) noteNumberOfRowsChanged
{
  _numberOfRows = [_dataSource numberOfRowsInTableView: self];
  
  /* If we are selecting rows, we have to check that we have no
     selected rows below the new end of the table */
  if (!_selectingColumns)
    {
      int row = [_selectedRows lastIndex];
      
      /* Check that all selected rows are in the new range of rows */
      if ((row != NSNotFound) && (row >= _numberOfRows))
        {
	  [_selectedRows removeIndexesInRange: 
			     NSMakeRange(_numberOfRows,  row - _numberOfRows)];
	  if (_selectedRow >= _numberOfRows)
	    {
	      row = [_selectedRows lastIndex];

	      if (row != NSNotFound)
		{
		  _selectedRow = row;
		}
	      else
		{
		  /* Argh - all selected rows were outside the table */
		  if (_allowsEmptySelection)
		    {
		      _selectedRow = -1;
		    }
		  else
		    {
		      /* We shouldn't allow empty selection - try
                         selecting the last row */
		      int lastRow = _numberOfRows - 1;
		      
		      if (lastRow > -1)
			{
			  [_selectedRows addIndex: lastRow];
			  _selectedRow = lastRow;
			}
		      else
			{
			  /* problem - there are no rows at all */
			  _selectedRow = -1;
			}
		    }
		}
	    }
	}
    }

  [self setFrame: NSMakeRect (_frame.origin.x, 
			      _frame.origin.y,
			      _frame.size.width, 
			      (_numberOfRows * _rowHeight) + 1)];
  
  /* If we are shorter in height than the enclosing clipview, we
     should redraw us now. */
  if (_super_view != nil)
    {
      NSRect superviewBounds; // Get this *after* [self setFrame:]
      superviewBounds = [_super_view bounds];
      if ((superviewBounds.origin.x <= _frame.origin.x) 
          && (NSMaxY (superviewBounds) >= NSMaxY (_frame)))
	{
	  [self setNeedsDisplay: YES];
	}
    }
}

- (void) tile
{
  float table_width = 0;
  float table_height;

  if (_tilingDisabled == YES)
    return;

  if (_numberOfColumns > 0)
    {
      int i;
      float width;
  
      _columnOrigins[0] = _bounds.origin.x;
      width = [[_tableColumns objectAtIndex: 0] width];
      table_width += width;
      for (i = 1; i < _numberOfColumns; i++)
	{
	  _columnOrigins[i] = _columnOrigins[i - 1] + width;
	  width = [[_tableColumns objectAtIndex: i] width];
	  table_width += width;
	}
    }
  /* + 1 for the last grid line */
  table_height = (_numberOfRows * _rowHeight) + 1;
  [self setFrameSize: NSMakeSize (table_width, table_height)];
  [self setNeedsDisplay: YES];

  if (_headerView != nil)
    {
      [_headerView setFrameSize: 
		     NSMakeSize (_frame.size.width,
				 [_headerView frame].size.height)];
      [_cornerView setFrameSize: 
		     NSMakeSize ([NSScroller scrollerWidth] + 1,
				 [_headerView frame].size.height)];
      [_headerView setNeedsDisplay: YES];
      [_cornerView setNeedsDisplay: YES];
    }  
}

/* 
 * Drawing 
 */

- (void) drawRow: (int)rowIndex clipRect: (NSRect)clipRect
{
  int startingColumn; 
  int endingColumn;
  NSTableColumn *tb;
  NSRect drawingRect;
  NSCell *cell;
  int i;
  float x_pos;

  if (_dataSource == nil)
    {
      return;
    }

  /* Using columnAtPoint: here would make it called twice per row per drawn 
     rect - so we avoid it and do it natively */

  /* Determine starting column as fast as possible */
  x_pos = NSMinX (clipRect);
  i = 0;
  while ((i < _numberOfColumns) && (x_pos > _columnOrigins[i]))
    {
      i++;
    }
  startingColumn = (i - 1);

  if (startingColumn == -1)
    startingColumn = 0;

  /* Determine ending column as fast as possible */
  x_pos = NSMaxX (clipRect);
  // Nota Bene: we do *not* reset i
  while ((i < _numberOfColumns) && (x_pos > _columnOrigins[i]))
    {
      i++;
    }
  endingColumn = (i - 1);

  if (endingColumn == -1)
    endingColumn = _numberOfColumns - 1;

  /* Draw the row between startingColumn and endingColumn */
  for (i = startingColumn; i <= endingColumn; i++)
    {
      if (i != _editedColumn || rowIndex != _editedRow)
	{
	  tb = [_tableColumns objectAtIndex: i];
	  cell = [tb dataCellForRow: rowIndex];
	  if (_del_responds)
	    {
	      [_delegate tableView: self   willDisplayCell: cell 
			 forTableColumn: tb   row: rowIndex];
	    }
	  [cell setObjectValue: [_dataSource tableView: self
					     objectValueForTableColumn: tb
					     row: rowIndex]]; 
	  drawingRect = [self frameOfCellAtColumn: i
			      row: rowIndex];
	  [cell drawWithFrame: drawingRect inView: self];
	}
    }
}


- (void) drawGridInClipRect: (NSRect)aRect
{
  float minX = NSMinX (aRect);
  float maxX = NSMaxX (aRect);
  float minY = NSMinY (aRect);
  float maxY = NSMaxY (aRect);
  int i;
  float x_pos;
  int startingColumn; 
  int endingColumn;

  NSGraphicsContext *ctxt = GSCurrentContext ();
  float position;

  int startingRow    = [self rowAtPoint: 
			       NSMakePoint (_bounds.origin.x, minY)];
  int endingRow      = [self rowAtPoint: 
			       NSMakePoint (_bounds.origin.x, maxY)];

  /* Using columnAtPoint:, rowAtPoint: here calls them only twice 

     per drawn rect */
  x_pos = minX;
  i = 0;
  while ((i < _numberOfColumns) && (x_pos > _columnOrigins[i]))
    {
      i++;
    }
  startingColumn = (i - 1);

  x_pos = maxX;
  // Nota Bene: we do *not* reset i
  while ((i < _numberOfColumns) && (x_pos > _columnOrigins[i]))
    {
      i++;
    }
  endingColumn = (i - 1);

  if (endingColumn == -1)
    endingColumn = _numberOfColumns - 1;
  /*
  int startingColumn = [self columnAtPoint: 
			       NSMakePoint (minX, _bounds.origin.y)];
  int endingColumn   = [self columnAtPoint: 
			       NSMakePoint (maxX, _bounds.origin.y)];
  */

  DPSgsave (ctxt);
  DPSsetlinewidth (ctxt, 1);
  [_gridColor set];

  if (_numberOfRows > 0)
    {
      /* Draw horizontal lines */
      if (startingRow == -1)
	startingRow = 0;
      if (endingRow == -1)
	endingRow = _numberOfRows - 1;
      
      position = _bounds.origin.y;
      position += startingRow * _rowHeight;
      for (i = startingRow; i <= endingRow + 1; i++)
	{
	  DPSmoveto (ctxt, minX, position);
	  DPSlineto (ctxt, maxX, position);
	  DPSstroke (ctxt);
	  position += _rowHeight;
	}
    }
  
  if (_numberOfColumns > 0)
    {
      /* Draw vertical lines */
      if (startingColumn == -1)
	startingColumn = 0;
      if (endingColumn == -1)
	endingColumn = _numberOfColumns - 1;

      for (i = startingColumn; i <= endingColumn; i++)
	{
	  DPSmoveto (ctxt, _columnOrigins[i], minY);
	  DPSlineto (ctxt, _columnOrigins[i], maxY);
	  DPSstroke (ctxt);
	}
      position =  _columnOrigins[endingColumn];
      position += [[_tableColumns objectAtIndex: endingColumn] width];  
      /* Last vertical line must moved a pixel to the left */
      if (endingColumn == (_numberOfColumns - 1))
	position -= 1;
      DPSmoveto (ctxt, position, minY);
      DPSlineto (ctxt, position, maxY);
      DPSstroke (ctxt);
    }

  DPSgrestore (ctxt);
}

- (void) highlightSelectionInClipRect: (NSRect)clipRect
{
  if (_selectingColumns == NO)
    {
      int selectedRowsCount;
      int row;
      int startingRow, endingRow;
      
      selectedRowsCount = [_selectedRows count];
      
      if (selectedRowsCount == 0)
	return;
      
      /* highlight selected rows */
      startingRow = [self rowAtPoint: NSMakePoint(0, NSMinY(clipRect))];
      endingRow   = [self rowAtPoint: NSMakePoint(0, NSMaxY(clipRect))];
      
      if (startingRow == -1)
	startingRow = 0;
      if (endingRow == -1)
	endingRow = _numberOfRows - 1;
      
      row = [_selectedRows indexGreaterThanOrEqualToIndex: startingRow];
      while ((row != NSNotFound) && (row <= endingRow))
	{
	  //NSHighlightRect(NSIntersectionRect([self rectOfRow: row],
	    //						 clipRect));
	  [[NSColor whiteColor] set];
	  NSRectFill(NSIntersectionRect([self rectOfRow: row], clipRect));
	  row = [_selectedRows indexGreaterThanIndex: row];
	}	  
    }
  else // Selecting columns
    {
      unsigned int selectedColumnsCount;
      unsigned int column;
      int startingColumn, endingColumn;
      
      selectedColumnsCount = [_selectedColumns count];
      
      if (selectedColumnsCount == 0)
	return;
      
      /* highlight selected columns */
      startingColumn = [self columnAtPoint: NSMakePoint(NSMinX(clipRect), 0)];
      endingColumn = [self columnAtPoint: NSMakePoint(NSMaxX(clipRect), 0)];

      if (startingColumn == -1)
	startingColumn = 0;
      if (endingColumn == -1)
	endingColumn = _numberOfColumns - 1;

      column = [_selectedColumns indexGreaterThanOrEqualToIndex: startingColumn];
      while ((column != NSNotFound) && (column <= endingColumn))
	{
	  NSHighlightRect(NSIntersectionRect([self rectOfColumn: column],
					     clipRect));
	  column = [_selectedColumns indexGreaterThanIndex: column];
	}	  
    }
}

- (void) drawBackgroundInClipRect: (NSRect)clipRect
{
  // FIXME
 /*  
  [_backgroundColor set];
  NSRectFill (clipRect);
  */
} 

- (void) drawRect: (NSRect)aRect
{
  int startingRow;
  int endingRow;
  int i;

  /* Draw background */
  [self drawBackgroundInClipRect: aRect];

  if ((_numberOfRows == 0) || (_numberOfColumns == 0))
    {
      return;
    }

  /* Draw selection */
  //    [self highlightSelectionInClipRect: aRect];

  /* Draw grid */
  if (_drawsGrid)
    {
      [self drawGridInClipRect: aRect];
    }
  
  /* Draw visible cells */
  /* Using rowAtPoint: here calls them only twice per drawn rect */
  startingRow = [self rowAtPoint: NSMakePoint (0, NSMinY (aRect))];
  endingRow   = [self rowAtPoint: NSMakePoint (0, NSMaxY (aRect))];

  if (startingRow == -1)
    {
      startingRow = 0;
    }
  if (endingRow == -1)
    {
      endingRow = _numberOfRows - 1;
    }
  //  NSLog(@"drawRect : %d-%d", startingRow, endingRow);
  {
    SEL sel = @selector(drawRow:clipRect:);
    IMP imp = [self methodForSelector: sel];
    
    NSRect localBackground;
    localBackground = aRect;
    localBackground.size.height = _rowHeight;
    localBackground.origin.y = _bounds.origin.y + (_rowHeight * startingRow);
    
    for (i = startingRow; i <= endingRow; i++)
      {
	[_backgroundColor set];
	NSRectFill (localBackground);
	[self highlightSelectionInClipRect: localBackground];
	if (_drawsGrid)
	  {
	    [self drawGridInClipRect: localBackground];
	  }
	localBackground.origin.y += _rowHeight;
	(*imp)(self, sel, i, aRect);
      }

    if (NSMaxY(aRect) > NSMaxY(localBackground) - _rowHeight)
      {
	[_backgroundColor set];
	localBackground.size.height =
	  aRect.size.height - aRect.origin.y + localBackground.origin.y;
	NSRectFill (localBackground);
      }
  }
}

- (BOOL) isOpaque
{
  return YES;
}

/* 
 * Scrolling 
 */

- (void) scrollRowToVisible: (int)rowIndex
{
  if (_super_view != nil)
    {
      NSRect rowRect = [self rectOfRow: rowIndex];
      NSRect visibleRect = [self visibleRect];
      
      // If the row is over the top, or it is partially visible 
      // on top,
      if ((rowRect.origin.y < visibleRect.origin.y))	
	{
	  // Then make it visible on top
	  NSPoint newOrigin;  
	  
	  newOrigin.x = visibleRect.origin.x;
	  newOrigin.y = rowRect.origin.y;
	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
	  return;
	}
      // If the row is under the bottom, or it is partially visible on
      // the bottom,
      if (NSMaxY (rowRect) > NSMaxY (visibleRect))
	{
	  // Then make it visible on bottom
	  NSPoint newOrigin;  
	  
	  newOrigin.x = visibleRect.origin.x;
	  newOrigin.y = visibleRect.origin.y;
	  newOrigin.y += NSMaxY (rowRect) - NSMaxY (visibleRect);
	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
	  return;
	}
    }
}

- (void) scrollColumnToVisible: (int)columnIndex
{
  if (_super_view != nil)
    {
      NSRect columnRect = [self rectOfColumn: columnIndex];
      NSRect visibleRect = [self visibleRect];
      float diff;

      // If the row is out on the left, or it is partially visible 
      // on the left
      if ((columnRect.origin.x < visibleRect.origin.x))	
	{
	  // Then make it visible on the left
	  NSPoint newOrigin;  
	  
	  newOrigin.x = columnRect.origin.x;
	  newOrigin.y = visibleRect.origin.y;
	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
	  return;
	}
      diff = NSMaxX (columnRect) - NSMaxX (visibleRect);
      // If the row is out on the right, or it is partially visible on
      // the right,
      if (diff > 0)
	{
	  // Then make it visible on the right
	  NSPoint newOrigin;

	  newOrigin.x = visibleRect.origin.x;
	  newOrigin.y = visibleRect.origin.y;
	  newOrigin.x += diff;
	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
	  return;
	}
    }
}


/* 
 * Text delegate methods 
 */

- (void) textDidBeginEditing: (NSNotification *)aNotification
{
  NSMutableDictionary *d;

  d = [NSMutableDictionary dictionaryWithDictionary: 
				[aNotification userInfo]];
  [d setObject: [aNotification object] forKey: @"NSFieldEditor"];
  [nc postNotificationName: NSControlTextDidBeginEditingNotification
      object: self
      userInfo: d];
}

- (void) textDidChange: (NSNotification *)aNotification
{
  NSMutableDictionary *d;

  // MacOS-X asks us to inform the cell if possible.
  if ((_editedCell != nil) && [_editedCell respondsToSelector: 
						 @selector(textDidChange:)])
    [_editedCell textDidChange: aNotification];

  d = [NSMutableDictionary dictionaryWithDictionary: 
				     [aNotification userInfo]];
  [d setObject: [aNotification object] forKey: @"NSFieldEditor"];
  [nc postNotificationName: NSControlTextDidChangeNotification
      object: self
      userInfo: d];
}

- (void) textDidEndEditing: (NSNotification *)aNotification
{
  NSMutableDictionary *d;
  id textMovement;
  int row, column;

  [self validateEditing];

  [_editedCell endEditing: [aNotification object]];
  [self setNeedsDisplayInRect: 
	  [self frameOfCellAtColumn: _editedColumn row: _editedRow]];
  _textObject = nil;
  RELEASE (_editedCell);
  _editedCell = nil;
  /* Save values */
  row = _editedRow;
  column = _editedColumn;
  /* Only then Reset them */
  _editedColumn = -1;
  _editedRow = -1;

  d = [NSMutableDictionary dictionaryWithDictionary: 
				     [aNotification userInfo]];
  [d setObject: [aNotification object] forKey: @"NSFieldEditor"];
  [nc postNotificationName: NSControlTextDidEndEditingNotification
      object: self
      userInfo: d];

  textMovement = [[aNotification userInfo] objectForKey: @"NSTextMovement"];
  if (textMovement)
    {
      switch ([(NSNumber *)textMovement intValue])
	{
	case NSReturnTextMovement:
	  // Send action ?
	  break;
	case NSTabTextMovement:
	  if([self _editNextEditableCellAfterRow: row  column: column] == YES)
	    {
	      break;
	    }
	  [_window selectKeyViewFollowingView: self];
	  break;
	case NSBacktabTextMovement:
	  if([self _editPreviousEditableCellBeforeRow: row  column: column] == YES)
	    {
	      break;
	    }
	  [_window selectKeyViewPrecedingView: self];
	  break;
	}
    }
}

- (BOOL) textShouldBeginEditing: (NSText *)textObject
{
  if (_delegate && [_delegate respondsToSelector:
				@selector(control:textShouldBeginEditing:)])
    return [_delegate control: self
		      textShouldBeginEditing: textObject];
  else
    return YES;
}

- (BOOL) textShouldEndEditing: (NSText*)textObject
{
  if ([_delegate respondsToSelector:
		   @selector(control:textShouldEndEditing:)])
    {
      if ([_delegate control: self
		     textShouldEndEditing: textObject] == NO)
	{
	  NSBeep ();
	  return NO;
	}
      
      return YES;
    }

  if ([_delegate respondsToSelector: 
		   @selector(control:isValidObject:)] == YES)
    {
      NSFormatter *formatter;
      id newObjectValue;
      
      formatter = [_cell formatter];
      
      if ([formatter getObjectValue: &newObjectValue 
		     forString: [_textObject text] 
		     errorDescription: NULL] == YES)
	{
	  if ([_delegate control: self
			 isValidObject: newObjectValue] == NO)
	    return NO;
	}
    }

  return [_editedCell isEntryAcceptable: [textObject text]];
}

/* 
 * Persistence 
 */

- (NSString *) autosaveName
{
  return _autosaveName;
}

- (BOOL) autosaveTableColumns
{
  return _autosaveTableColumns;
}

- (void) setAutosaveName: (NSString *)name
{
  ASSIGN (_autosaveName, name);
  [self _autoloadTableColumns];
}

- (void) setAutosaveTableColumns: (BOOL)flag
{
  if (flag == _autosaveTableColumns)
    {
      return;
    }

  _autosaveTableColumns = flag;
  if (flag)
    {
      [self _autoloadTableColumns];
      [nc addObserver: self 
          selector: @selector(_autosaveTableColumns)
	  name: NSTableViewColumnDidResizeNotification
	  object: self];
    }
  else
    {
      [nc removeObserver: self 
	  name: NSTableViewColumnDidResizeNotification
	  object: self];    
    }
}

/* 
 * Delegate 
 */

- (void) setDelegate: (id)anObject
{
  const SEL sel = @selector(tableView:willDisplayCell:forTableColumn:row:);

  if (_delegate)
    [nc removeObserver: _delegate name: nil object: self];
  _delegate = anObject;
  
#define SET_DELEGATE_NOTIFICATION(notif_name) \
  if ([_delegate respondsToSelector: @selector(tableView##notif_name:)]) \
    [nc addObserver: _delegate \
      selector: @selector(tableView##notif_name:) \
      name: NSTableView##notif_name##Notification object: self]

  SET_DELEGATE_NOTIFICATION(ColumnDidMove);
  SET_DELEGATE_NOTIFICATION(ColumnDidResize);
  SET_DELEGATE_NOTIFICATION(SelectionDidChange);
  SET_DELEGATE_NOTIFICATION(SelectionIsChanging);
  
  /* Cache */
  _del_responds = [_delegate respondsToSelector: sel];
}

- (id) delegate
{
  return _delegate;
}


/* indicator image */
- (NSImage *) indicatorImageInTableColumn: (NSTableColumn *)aTableColumn
{
  // TODO
  NSLog(@"Method %s is not implemented for class %s",
	"indicatorImageInTableColumn:", "NSTableView");
  return nil;
}

- (void) setIndicatorImage: (NSImage *)anImage
	     inTableColumn: (NSTableColumn *)aTableColumn
{
  // TODO
  NSLog(@"Method %s is not implemented for class %s",
	"setIndicatorImage:inTableColumn:", "NSTableView");
}

/* highlighting columns */
- (NSTableColumn *) highlightedTableColumn
{
  return _highlightedTableColumn;
}

- (void) setHighlightedTableColumn: (NSTableColumn *)aTableColumn
{
  int tableColumnIndex;

  tableColumnIndex = [_tableColumns indexOfObject: aTableColumn];

  if (tableColumnIndex == NSNotFound)
    {
      NSLog(@"setHighlightedTableColumn received an invalid\
 NSTableColumn object");
      return;
    }

  // we do not need to retain aTableColumn as it is already in
  // _tableColumns array
  _highlightedTableColumn = aTableColumn;

  [_headerView setNeedsDisplay: YES];
}

/* dragging rows */
- (NSImage*) dragImageForRows: (NSArray*)dragRows
			event: (NSEvent*)dragEvent
	      dragImageOffset: (NSPoint*)dragImageOffset
{

  NSImage *dragImage = [[NSImage alloc]
			 initWithSize: NSMakeSize(8, 8)];

  return AUTORELEASE(dragImage);
}

- (void) setDropRow: (int)row
      dropOperation: (NSTableViewDropOperation)operation
{
  
  if (row < 0)
    {
      currentDropRow = 0;
    }
  else if (operation == NSTableViewDropOn)
    {
      if (row >= _numberOfRows)
	currentDropRow = _numberOfRows;
    }
  else if (row > _numberOfRows)
    {
      currentDropRow = _numberOfRows;
    }
  else
    {
      currentDropRow = row;
    }
  
  currentDropOperation = operation;
}

- (void) setVerticalMotionCanBeginDrag: (BOOL)flag
{
  // TODO
  NSLog(@"Method %s is not implemented for class %s",
	"setVerticalMotionCanBeginDrag:", "NSTableView");
}

- (BOOL) verticalMotionCanBeginDrag
{
  // TODO
  NSLog(@"Method %s is not implemented for class %s",
	"verticalMotionCanBeginDrag", "NSTableView");
  return NO;
}

/*
 * Encoding/Decoding
 */

- (void) encodeWithCoder: (NSCoder*)aCoder
{
  [super encodeWithCoder: aCoder];

  [aCoder encodeConditionalObject: _dataSource];
  [aCoder encodeObject: _tableColumns];
  [aCoder encodeObject: _gridColor];
  [aCoder encodeObject: _backgroundColor];
  [aCoder encodeObject: _headerView];
  [aCoder encodeObject: _cornerView];
  [aCoder encodeConditionalObject: _delegate];
  [aCoder encodeConditionalObject: _target];

  [aCoder encodeValueOfObjCType: @encode(int) at: &_numberOfRows];
  [aCoder encodeValueOfObjCType: @encode(int) at: &_numberOfColumns];

  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_drawsGrid];
  [aCoder encodeValueOfObjCType: @encode(float) at: &_rowHeight];
  [aCoder encodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
  [aCoder encodeSize: _intercellSpacing];

  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnSelection];
  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnResizing];
  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnReordering];
  [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autoresizesAllColumnsToFit];

}

- (id) initWithCoder: (NSCoder*)aDecoder
{
  self = [super initWithCoder: aDecoder];

  if ([aDecoder allowsKeyedCoding])
    {
      NSSize intercellSpacing = [self intercellSpacing];
      NSArray *columns;
      NSEnumerator *e;
      NSTableColumn *col;

      [self setDataSource: [aDecoder decodeObjectForKey: @"NSDataSource"]];
      [self setDelegate: [aDecoder decodeObjectForKey: @"NSDelegate"]];

      [self setTarget: [aDecoder decodeObjectForKey: @"NSTarget"]];
      if ([aDecoder containsValueForKey: @"NSAction"])
        {
	  NSString *action = [aDecoder decodeObjectForKey: @"NSAction"];
	  [self setAction: NSSelectorFromString(action)];
	}
      if ([aDecoder containsValueForKey: @"NSBackgroundColor"])
        {
	  [self setBackgroundColor: [aDecoder decodeObjectForKey: @"NSBackgroundColor"]];
	}
      if ([aDecoder containsValueForKey: @"NSGridColor"])
        {
	  [self setGridColor: [aDecoder decodeObjectForKey: @"NSGridColor"]];
	}
      if ([aDecoder containsValueForKey: @"NSIntercellSpacingHeight"])
        {
	  intercellSpacing.height = [aDecoder decodeFloatForKey: @"NSIntercellSpacingHeight"];
	}
      if ([aDecoder containsValueForKey: @"NSIntercellSpacingWidth"])
        {
	  intercellSpacing.width = [aDecoder decodeFloatForKey: @"NSIntercellSpacingWidth"];
	}
      [self setIntercellSpacing: intercellSpacing];
      if ([aDecoder containsValueForKey: @"NSRowHeight"])
        {
	  [self setRowHeight: [aDecoder decodeFloatForKey: @"NSRowHeight"]];
	}

      columns = [aDecoder decodeObjectForKey: @"NSTableColumns"];
      e = [columns objectEnumerator];
      while ((col = [e nextObject]) != nil)
        {
	  [self addTableColumn: col];
	}

      if ([aDecoder containsValueForKey: @"NSTvFlags"])
        {
	  //int vFlags = [aDecoder decodeIntForKey: @"NSTvFlags"];
	  // FIXME set the flags
	}
    }
  else
    {
      int version = [aDecoder versionForClassName: 
				  @"NSTableView"];
      id aDelegate;

      _dataSource      = [aDecoder decodeObject];
      _tableColumns    = RETAIN([aDecoder decodeObject]);
      _gridColor       = RETAIN([aDecoder decodeObject]);
      _backgroundColor = RETAIN([aDecoder decodeObject]);
      _headerView      = RETAIN([aDecoder decodeObject]);
      _cornerView      = RETAIN([aDecoder decodeObject]);
      aDelegate        = [aDecoder decodeObject];
      _target          = [aDecoder decodeObject];
      
      [self setDelegate: aDelegate];
      [_headerView setTableView: self];
      [_tableColumns makeObjectsPerformSelector: @selector(setTableView:)
		     withObject: self];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_numberOfRows];
      [aDecoder decodeValueOfObjCType: @encode(int) at: &_numberOfColumns];
      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_drawsGrid];
      [aDecoder decodeValueOfObjCType: @encode(float) at: &_rowHeight];
      [aDecoder decodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
      _intercellSpacing = [aDecoder decodeSize];
      
      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnSelection];
      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnResizing];
      if (version == currentVersion)
        {
	  [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnReordering];
	}
      if (version >= 2)
        {
	  [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autoresizesAllColumnsToFit];
	} 
      
      ASSIGN (_selectedColumns, [NSMutableIndexSet indexSet]);
      ASSIGN (_selectedRows, [NSMutableIndexSet indexSet]);
      if (_numberOfColumns)
	_columnOrigins = NSZoneMalloc (NSDefaultMallocZone (), 
				       sizeof(float) * _numberOfColumns);
      
      _clickedRow = -1;
      _clickedColumn = -1;
      _selectingColumns = NO;
      _selectedColumn = -1;
      _selectedRow = -1;
      _editedColumn = -1;
      _editedRow = -1;
      
      if (version == 2)
        {
	  [self tile];
	}
      /*
	NSLog(@"frame %@", NSStringFromRect([self frame]));
	NSLog(@"dataSource %@", _dataSource);
	if (_dataSource != nil)
	  {
	    [self setDataSource: _dataSource];
	    NSLog(@"dataSource set");
	  }
      */
    }
  
  return self;
}

- (void) updateCell: (NSCell*)aCell
{
  int i, j;
  NSTableColumn *tb;
  if (aCell == nil)
    return;
  return;
  for (i = 0; i < _numberOfColumns; i++)
    {
      tb = [_tableColumns objectAtIndex: i];
      if ([tb dataCellForRow: -1] == aCell)
	{
	  [self setNeedsDisplayInRect: [self rectOfColumn: i]];
	}
      else
	{
	  NSRect columnRect = [self rectOfColumn: i];
	  NSRect rowRect;
	  NSRect visibleRect = [self convertRect: [_super_view bounds]
				     toView: self];
	  NSPoint top = NSMakePoint(NSMinX(visibleRect),
				    NSMinY(visibleRect));
	  NSPoint bottom = NSMakePoint(NSMinX(visibleRect),
				       NSMaxY(visibleRect));
	  int firstVisibleRow = [self rowAtPoint: top];
	  int lastVisibleRow = [self rowAtPoint: bottom];

	  if (firstVisibleRow == -1)
	    firstVisibleRow = 0;

	  if (lastVisibleRow == -1)
	    lastVisibleRow = _numberOfColumns - 1;

	  for (j = firstVisibleRow; j < lastVisibleRow; j++)
	    {
	      if ([tb dataCellForRow: j] == aCell)
		{
		  rowRect = [self rectOfRow: j];
		  [self setNeedsDisplayInRect:
			  NSIntersectionRect(columnRect, rowRect)];
		}
	    }
	}
    }
}

- (void) _userResizedTableColumn: (int)index
			   width: (float)width
{
  [[_tableColumns objectAtIndex: index] setWidth: width];
}

- (float *) _columnOrigins
{
  return _columnOrigins;
}

- (void) _mouseDownInHeaderOfTableColumn: (NSTableColumn *)tc
{
  if ([_delegate 
	respondsToSelector:
	  @selector(tableView:mouseDownInHeaderOfTableColumn:)])
    {
      [_delegate tableView: self
		 mouseDownInHeaderOfTableColumn: tc];
    }
}

- (void) _didClickTableColumn: (NSTableColumn *)tc
{
  if ([_delegate 
	respondsToSelector:
	  @selector(tableView:didClickTableColumn:)])
    {
      [_delegate tableView: self
		 didClickTableColumn: tc];
    }
}

-(BOOL) _editNextEditableCellAfterRow: (int)row
			       column: (int)column
{
  int i, j;
  
  if (row > -1)
    {
      // First look for cells in the same row
      for (j = column + 1; j < _numberOfColumns; j++)
	{
	  if (_isCellEditable (_delegate, _tableColumns, self, row, j) == YES)
	    {
	      [self editColumn: j  row: row  withEvent: nil  select: YES];
	      return YES;
	    }
	}
    }
  // Otherwise, make the big cycle.
  for (i = row + 1; i < _numberOfRows; i++)
    {
      for (j = 0; j < _numberOfColumns; j++)
	{
	  if (_isCellEditable (_delegate, _tableColumns, self, i, j) == YES)
	    {
	      [self editColumn: j  row: i  withEvent: nil  select: YES];
	      return YES;
	    }
	}
    }
  return NO;
}
-(BOOL) _editPreviousEditableCellBeforeRow: (int)row
				    column: (int)column
{
  int i,j;
  if (row < _numberOfColumns)
    {
      // First look for cells in the same row
      for (j = column - 1; j > -1; j--)
	{
	  if (_isCellEditable (_delegate, _tableColumns, self, row, j) == YES)
	    {
	      [self editColumn: j  row: row  withEvent: nil  select: YES];
	      return YES;
	    }
	}
    }
  // Otherwise, make the big cycle.
  for (i = row - 1; i > -1; i--)
    {
      for (j = _numberOfColumns - 1; j > -1; j--)
	{
	  if (_isCellEditable (_delegate, _tableColumns, self, i, j) == YES)
	    {
	      [self editColumn: j  row: i  withEvent: nil  select: YES];
	      return YES;
	    }
	}
    }
  return NO;
}
- (void) _autosaveTableColumns
{
  if (_autosaveTableColumns && _autosaveName != nil) 
    {
      NSUserDefaults      *defaults;
      NSString            *tableKey;
      NSMutableDictionary *config;
      NSTableColumn       *column;
      id                  en;

      defaults  = [NSUserDefaults standardUserDefaults];
      tableKey = [NSString stringWithFormat: @"NSTableView Columns %@", 
			   _autosaveName];
      config = [NSMutableDictionary new];
      
      en = [[self tableColumns] objectEnumerator];
      while ((column = [en nextObject]) != nil)
	{
	  NSArray *array;
	  NSNumber *width, *identNum;
	  NSObject *ident;
	  
	  width = [NSNumber numberWithInt: [column width]];
	  ident = [column identifier];
	  identNum = [NSNumber numberWithInt: [self columnWithIdentifier: 
						      ident]];
	  array = [NSArray arrayWithObjects: width, identNum, nil];  
	  [config setObject: array  forKey: ident];      
	} 
      [defaults setObject: config  forKey: tableKey];
      [defaults synchronize];
      RELEASE (config);
    }
}

- (void) _autoloadTableColumns
{
  if (_autosaveTableColumns && _autosaveName != nil) 
    { 
      NSUserDefaults     *defaults;
      NSDictionary       *config;
      NSString           *tableKey;

      defaults  = [NSUserDefaults standardUserDefaults];
      tableKey = [NSString stringWithFormat: @"NSTableView Columns %@", 
			   _autosaveName];
      config = [defaults objectForKey: tableKey];
      if (config != nil) 
	{
	  NSEnumerator *en = [[config allKeys] objectEnumerator];
	  NSString *colKey;
	  NSArray *colDesc; 
	  NSTableColumn *col;
	  
	  while ((colKey = [en nextObject]) != nil) 
	    {
	      col = [self tableColumnWithIdentifier: colKey];
	      
	      if (col != nil)
		{
		  colDesc = [config objectForKey: colKey];
		  [col setWidth: [[colDesc objectAtIndex: 0] intValue]];
		  [self moveColumn: [self columnWithIdentifier: colKey]
			toColumn: [[colDesc objectAtIndex: 1] intValue]];
		}
	    }
	}
    }
}

- (void) superviewFrameChanged: (NSNotification*)aNotification
{
  if (_autoresizesAllColumnsToFit == YES)
    {
      float visible_width = [self convertRect: [_super_view bounds] 
				  fromView: _super_view].size.width;
      float table_width = 0;

      if(_numberOfColumns > 0)
        {
          table_width = 
            _columnOrigins[_numberOfColumns - 1] +
            [[_tableColumns objectAtIndex: _numberOfColumns - 1] width];
        }
      
      /*
	NSLog(@"columnOrigins[0] %f", _columnOrigins[0]);
	NSLog(@"superview.bounds %@", 
	      NSStringFromRect([_super_view bounds]));
	NSLog(@"superview.frame %@", 
	      NSStringFromRect([_super_view frame]));
	NSLog(@"table_width %f", table_width);
	NSLog(@"width %f", visible_width);
	NSLog(@"_superview_width %f", _superview_width);
      */

      if ( table_width - _superview_width <= 0.001
	   && table_width - _superview_width >= -0.001 )
	{
	  // the last column had been sized to fit
	  [self sizeToFit];
	}
      else if ( table_width <= _superview_width
		&& table_width >= visible_width )
	{
	  // the tableView was too small and is now too large
	  [self sizeToFit];
	}
      else if (table_width >= _superview_width
	       && table_width <= visible_width )
	{
	  // the tableView was too large and is now too small
	  if (_numberOfColumns > 0)
	    [self scrollColumnToVisible: 0];
	  [self sizeToFit];
	}
      _superview_width = visible_width;
    }
  else
    {
      float visible_width = [self convertRect: [_super_view bounds] 
				  fromView: _super_view].size.width;
      float table_width = 0;

      if(_numberOfColumns > 0)
        {
          table_width = 
            _columnOrigins[_numberOfColumns - 1] +
            [[_tableColumns objectAtIndex: _numberOfColumns - 1] width];
        }
      
      /*
	NSLog(@"columnOrigins[0] %f", _columnOrigins[0]);
	NSLog(@"superview.bounds %@", 
	      NSStringFromRect([_super_view bounds]));
	NSLog(@"superview.frame %@", 
	      NSStringFromRect([_super_view frame]));
	NSLog(@"table_width %f", table_width);
	NSLog(@"width %f", visible_width);
	NSLog(@"_superview_width %f", _superview_width);
      */

      if ( table_width - _superview_width <= 0.001
	   && table_width - _superview_width >= -0.001 )
	{
	  // the last column had been sized to fit
	  [self sizeLastColumnToFit];
	}
      else if ( table_width <= _superview_width
		&& table_width >= visible_width )
	{
	  // the tableView was too small and is now too large
	  [self sizeLastColumnToFit];
	}
      else if (table_width >= _superview_width
	       && table_width <= visible_width )
	{
	  // the tableView was too large and is now too small
	  if (_numberOfColumns > 0)
	    [self scrollColumnToVisible: 0];
	  [self sizeLastColumnToFit];
	}
      _superview_width = visible_width;
    }
}


- (unsigned int) draggingSourceOperationMaskForLocal: (BOOL) flag
{
  return (NSDragOperationAll);
}


- (unsigned int) draggingEntered: (id <NSDraggingInfo>) sender
{
  currentDropRow = -1;
  currentDropOperation = -1;
  oldDropRow = -1;
  lastQuarterPosition = -1;
  oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
  currentDragOperation = NSDragOperationAll;
  return currentDragOperation;
}


- (void) draggingExited: (id <NSDraggingInfo>) sender
{
  [self setNeedsDisplayInRect: oldDraggingRect];
  [self displayIfNeeded];
}

- (unsigned int) draggingUpdated: (id <NSDraggingInfo>) sender
{
  NSPoint p = [sender draggingLocation];
  NSRect newRect;
  int row;
  int quarterPosition, positionInRow;
  int currentRow;
  unsigned dragOperation;

  p = [self convertPoint: p fromView: nil];
  /* This is a crude method of scrolling the view while dragging so
     the user can drag to any cell even if it's not
     visible. Unfortunately we don't receive events when the drag is
     outside the view, so the pointer must still be in the view to
     drag.
  */
  if (p.y < NSMinY([self visibleRect])+3)
    {
      currentRow = [self rowAtPoint: p] - 1;
      if (currentRow > 0)
	[self scrollRowToVisible: currentRow];
    }
  else if (p.y > NSMaxY([self visibleRect])-3)
    {
      currentRow = [self rowAtPoint: p] + 1;
      if (currentRow < _numberOfRows)
	[self scrollRowToVisible: currentRow];
    }

  positionInRow = (int)(p.y - _bounds.origin.y) % (int)_rowHeight;
  quarterPosition = (p.y - _bounds.origin.y) / _rowHeight * 4.;

  if ((quarterPosition - oldDropRow * 4 <= 2) &&
      (quarterPosition - oldDropRow * 4 >= -3) )
    {
      row = oldDropRow;
    }
  else
    {
      row = (quarterPosition + 2) / 4;
    }

  // Are we in the two middle quarters of the row? Use TableViewDropOn
  if(positionInRow > _rowHeight/4 && positionInRow <= (3*_rowHeight)/4)
    {
      currentDropRow  = (int)(p.y - _bounds.origin.y) / (int)_rowHeight;
      currentDropOperation = NSTableViewDropOn;
    }
  else // drop above
    {
      currentDropRow = row;
      currentDropOperation = NSTableViewDropAbove;
    }

  dragOperation = [sender draggingSourceOperationMask];
  if ((lastQuarterPosition != quarterPosition)
      || (currentDragOperation != dragOperation))
    {
      currentDragOperation = dragOperation;
      if ([_dataSource respondsToSelector: 
			 @selector(tableView:validateDrop:proposedRow:proposedDropOperation:)])
	{
	  currentDragOperation = [_dataSource tableView: self
					      validateDrop: sender
					      proposedRow: currentDropRow
					      proposedDropOperation: NSTableViewDropAbove];
	}
      
      lastQuarterPosition = quarterPosition;
      
      if ((currentDropRow != oldDropRow) || (currentDropOperation != oldDropOperation))
	{
	  [self lockFocus];
	  
	  [self setNeedsDisplayInRect: oldDraggingRect];
	  [self displayIfNeeded];
	  
	  [[NSColor darkGrayColor] set];
      
	  if (currentDropOperation == NSTableViewDropAbove)
	    {
	      if (currentDropRow == 0)
		{
		  newRect = NSMakeRect([self visibleRect].origin.x,
				       currentDropRow * _rowHeight,
				       [self visibleRect].size.width,
				       3);
		}
	      else if (currentDropRow == _numberOfRows)
		{
		  newRect = NSMakeRect([self visibleRect].origin.x,
				       currentDropRow * _rowHeight - 2,
				       [self visibleRect].size.width,
				       3);
		}
	      else
		{
		  newRect = NSMakeRect([self visibleRect].origin.x,
				       currentDropRow * _rowHeight - 1,
				       [self visibleRect].size.width,
				       3);
		}
	      NSRectFill(newRect);
	      oldDraggingRect = newRect;
	    }
	  else
	    {
	      newRect = [self frameOfCellAtColumn: 0
			      row: currentDropRow];
	      newRect.origin.x = _bounds.origin.x;
	      newRect.size.width = _bounds.size.width + 2;
	      newRect.origin.x -= _intercellSpacing.height / 2;
	      newRect.size.height += _intercellSpacing.height;
	      oldDraggingRect = newRect;
	      oldDraggingRect.origin.y -= 1;
	      oldDraggingRect.size.height += 2;

	      newRect.size.height -= 1;

	      newRect.origin.x += 3;
	      newRect.size.width -= 3;
		
	      if (_drawsGrid)
		{
		  //newRect.origin.y += 1;
		  //newRect.origin.x += 1;
		  //newRect.size.width -= 2;
		  newRect.size.height += 1;
		}
	      else
		{
		}

	      NSFrameRectWithWidth(newRect, 2.0);
	      //	      NSRectFill(newRect);

	    }
	  [_window flushWindow];
	  
	  [self unlockFocus];
	  
	  oldDropRow = currentDropRow;
	  oldDropOperation = currentDropOperation;
	}
    }


  return dragOperation;
}

- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
{
  if ([_dataSource respondsToSelector: @selector(tableView:acceptDrop:row:dropOperation:)])
    {
      return [_dataSource tableView: self
			  acceptDrop: sender
			  row: currentDropRow
			  dropOperation: currentDropOperation];
    }
  else
    return NO;
}
- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
{
  [self setNeedsDisplayInRect: oldDraggingRect];
  [self displayIfNeeded];

  return YES;
}

- (void) concludeDragOperation:(id <NSDraggingInfo>)sender
{
}

/* 
 * sorting 
 */
- (void) setSortDescriptors: (NSArray *)array
{
  // FIXME
}

- (NSArray *)sortDescriptors
{
  // FIXME
  return nil;
}

/*
 * User interface validation
 */
- (BOOL) validateUserInterfaceItem: (id <NSValidatedUserInterfaceItem>)anItem
{
  // FIXME
  return YES;
}

/*
 * (NotificationRequestMethods)
 */
- (void) _postSelectionIsChangingNotification
{
  [nc postNotificationName: 
	NSTableViewSelectionIsChangingNotification
      object: self];
}
- (void) _postSelectionDidChangeNotification
{
  [nc postNotificationName: 
	NSTableViewSelectionDidChangeNotification
      object: self];
}
- (void) _postColumnDidMoveNotificationWithOldIndex: (int) oldIndex
					   newIndex: (int) newIndex
{
  [nc postNotificationName: 
	NSTableViewColumnDidMoveNotification
      object: self
      userInfo: [NSDictionary 
		  dictionaryWithObjectsAndKeys:
		  [NSNumber numberWithInt: newIndex],
		  @"NSNewColumn",
		    [NSNumber numberWithInt: oldIndex],
		  @"NSOldColumn",
		  nil]];
}

- (void) _postColumnDidResizeNotificationWithOldWidth: (float) oldWidth
{
  [nc postNotificationName: 
	NSTableViewColumnDidResizeNotification
      object: self
      userInfo: [NSDictionary 
		  dictionaryWithObjectsAndKeys:
		    [NSNumber numberWithFloat: oldWidth],
		  @"NSOldWidth", 
		  nil]];
}

- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn
{
  if ([_delegate respondsToSelector: 
		   @selector (tableView:shouldSelectTableColumn:)] == YES) 
    {
      if ([_delegate tableView: self  shouldSelectTableColumn: tableColumn] == NO)
	{
	  return NO;
	}
    }

  return YES;
}

- (BOOL) _shouldSelectRow: (int)rowIndex
{
  if ([_delegate respondsToSelector: 
		   @selector (tableView:shouldSelectRow:)] == YES) 
    {
      if ([_delegate tableView: self  shouldSelectRow: rowIndex] == NO)
	{
	  return NO;
	}
    }
  
  return YES;
}

- (BOOL) _shouldSelectionChange
{
  if ([_delegate respondsToSelector: 
	  @selector (selectionShouldChangeInTableView:)] == YES) 
    {
      if ([_delegate selectionShouldChangeInTableView: self] == NO)
	{
	  return NO;
	}
    }
  
  return YES;
}

- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
			    row: (int) rowIndex
{
  if ([_delegate respondsToSelector: 
			@selector(tableView:shouldEditTableColumn:row:)])
    {
      if ([_delegate tableView: self shouldEditTableColumn: tableColumn
		     row: rowIndex] == NO)
	{
	  return NO;
	}
    }

  return YES;
}

- (id) _objectValueForTableColumn: (NSTableColumn *)tb
			      row: (int) index
{
  id result = nil;

  if([_dataSource respondsToSelector:
		    @selector(tableView:objectValueForTableColumn:row:)])
    {
      result = [_dataSource tableView: self
			    objectValueForTableColumn: tb
			    row: index];
    }

  return result;
}

- (void) _setObjectValue: (id)value
	  forTableColumn: (NSTableColumn *)tb
		     row: (int) index
{
  if([_dataSource respondsToSelector:
		    @selector(tableView:objectValueForTableColumn:row:)])
    {
      [_dataSource tableView: self
		   setObjectValue: value
		   forTableColumn: tb
		   row: index];
    }
}

- (BOOL) _isDraggingSource
{
  return [_dataSource respondsToSelector:
			@selector(tableView:writeRows:toPasteboard:)];
}

- (BOOL) _writeRows: (NSArray *) rows
       toPasteboard: (NSPasteboard *)pboard
{
  if ([_dataSource respondsToSelector:
		     @selector(tableView:writeRows:toPasteboard:)] == YES)
    {
      return [_dataSource tableView: self
			  writeRows: rows
			  toPasteboard: pboard];
    }
  return NO;
}

@end /* implementation of NSTableView */

@implementation NSTableView (SelectionHelper)

- (NSArray *) _selectedRowArray
{
  NSMutableArray *selected = [NSMutableArray array];
  unsigned int row = [_selectedRows firstIndex];
      
  while (row != NSNotFound)
    {
      NSNumber *num  = [NSNumber numberWithInt: row];

      [selected addObject: num]; 
      row = [_selectedRows indexGreaterThanIndex: row];
    }  
	    
  return selected;
}

- (BOOL) _selectRow: (int)rowIndex
{
  if (![self _shouldSelectRow: rowIndex])
    {
      return NO;
    }
  
  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
  [_selectedRows addIndex: rowIndex];
  _selectedRow = rowIndex;
  return YES;
}

- (BOOL) _selectUnselectedRow: (int)rowIndex
{
  if ([_selectedRows containsIndex: rowIndex])
    {
      return NO;
    }
  
  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
  [_selectedRows addIndex: rowIndex];
  _selectedRow = rowIndex;
  return YES;
}

- (BOOL) _unselectRow: (int)rowIndex
{
  if (![_selectedRows containsIndex: rowIndex])
    {
      return NO;
    }

  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
  [_selectedRows removeIndex: rowIndex];

  if (_selectedRow == rowIndex)
    {
      _selectedRow = -1;
    }

  return YES;
}

- (void) _unselectAllRows
{
  /* Compute rect to redraw to clear the old row selection */
  unsigned int row = [_selectedRows firstIndex];
      
  while (row != NSNotFound)
    {
      [self setNeedsDisplayInRect: [self rectOfRow: row]];
      row = [_selectedRows indexGreaterThanIndex: row];
    }
  [_selectedRows removeAllIndexes];
  _selectedRow = -1;
}

- (NSArray *) _selectedColumArray
{
  NSMutableArray *selected = [NSMutableArray array];
  unsigned int column = [_selectedColumns firstIndex];
      
  while (column != NSNotFound)
    {
      NSNumber *num  = [NSNumber numberWithInt: column];

      [selected addObject: num]; 
      column = [_selectedColumns indexGreaterThanIndex: column];
    }  
	    
  return selected;
}

- (void) _unselectAllColumns
{
  /* Compute rect to redraw to clear the old column selection */
  unsigned int column = [_selectedColumns firstIndex];
      
  while (column != NSNotFound)
    {
      [self setNeedsDisplayInRect: [self rectOfColumn: column]];
      column = [_selectedColumns indexGreaterThanIndex: column];
    }	  
  [_selectedColumns removeAllIndexes];
  _selectedColumn = -1;  
}

@end
