libs-gui/Source/NSTableView.m

6750 lines
153 KiB
Mathematica
Raw Normal View History

/** <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
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.
*/
#import <AppKit/NSTableView.h>
#import <AppKit/NSApplication.h>
#import <AppKit/NSCell.h>
#import <AppKit/NSClipView.h>
#import <AppKit/NSColor.h>
#import <AppKit/NSEvent.h>
#import <AppKit/NSGraphics.h>
#import <AppKit/NSScroller.h>
#import <AppKit/NSTableColumn.h>
#import <AppKit/NSTableHeaderView.h>
#import <AppKit/NSText.h>
#import <AppKit/NSTextFieldCell.h>
#import <AppKit/NSWindow.h>
#import <AppKit/PSOperators.h>
#import <AppKit/NSCachedImageRep.h>
#include <math.h>
static NSNotificationCenter *nc = nil;
static const int currentVersion = 2;
#define ALLOWS_MULTIPLE (1)
#define ALLOWS_EMPTY (1 << 1)
#define SHIFT_DOWN (1 << 2)
#define CONTROL_DOWN (1 << 3)
#define ADDING_ROW (1 << 4)
/*
* 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);
}
}
/* The selection arrays are stored so that the selected columns/rows
are always in ascending order. The following function is used
whenever a new column/row is added to the array, to keep the
columns/rows in the correct order. */
static
void _insertNumberInSelectionArray (NSMutableArray *array,
NSNumber *num)
{
int i, count;
count = [array count];
for (i = 0; i < count; i++)
{
NSNumber *number;
number = [array objectAtIndex: i];
if ([number compare: num] == NSOrderedDescending)
break;
}
[array insertObject: num atIndex: i];
}
/*
* Now some auxiliary functions used to manage real-time user selections.
*
*/
static void computeNewSelection
(NSTableView *tv,
id delegate,
NSSet *_oldSelectedRows,
NSMutableArray *_selectedRows,
int _originalRow,
int _oldRow,
int _currentRow,
int *_selectedRow,
unsigned selectionMode)
{
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
{
int diff, i;
BOOL notified = NO;
diff = _currentRow - _originalRow;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
else
{
// this case does happen, (sometimes)
for (i = _originalRow; i >= _currentRow; i--)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
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
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i > _currentRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
NSNumber *lastObject = [_selectedRows lastObject];
if (lastObject == nil)
{
*_selectedRow = -1;
}
else
{
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i < _currentRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i < _originalRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i > _originalRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *lastObject =
[_selectedRows lastObject];
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
}
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;
}
for (i = 0; i < count; i++)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow:
[[_selectedRows objectAtIndex: i] intValue]]];
}
[_selectedRows removeAllObjects];
*_selectedRow = -1;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
else
{
// this case does happen (sometimes)
for (i = _originalRow; i >= _currentRow; i--)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
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 ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i > _currentRow; i--)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
NSNumber *lastObject = [_selectedRows lastObject];
if (lastObject == nil)
{
*_selectedRow = -1;
}
else
{
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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 ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i < _currentRow; i++)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i < _originalRow; i++)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i > _originalRow; i--)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *lastObject =
[_selectedRows lastObject];
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
}
else if ((((selectionMode & ALLOWS_MULTIPLE) == 0)
&& ((selectionMode & SHIFT_DOWN) == 0))
||
(((selectionMode & ALLOWS_MULTIPLE) == 0)
&& ((selectionMode & ALLOWS_EMPTY) == 0))
||
(((selectionMode & ALLOWS_MULTIPLE) == 0)
&& (selectionMode & SHIFT_DOWN)
&& (selectionMode & ALLOWS_EMPTY)
&& (selectionMode & ADDING_ROW)))
// we'll be selecting exactly one row
// ic, sc : ok
{
int i;
int j;
int count = [_selectedRows count];
BOOL notified = NO;
if (delegate != nil)
{
if ([delegate tableView: 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 = YES;
for (i = 0; i < count; i++)
{
j = [[_selectedRows objectAtIndex: i] intValue];
if (j == _currentRow)
{
notified = NO;
continue;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: j]];
}
[_selectedRows removeAllObjects];
[_selectedRows addObject:
[NSNumber numberWithInt: _currentRow]];
*_selectedRow = _currentRow;
if (notified == YES)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: _currentRow]];
}
else
{
if (count > 1)
{
notified = YES;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
{
// we know there is only one column selected
// this column is *_selectedRow
//begin checking code
if ([_selectedRows containsObject:
[NSNumber numberWithInt: *_selectedRow]]
== NO)
{
NSLog(@"*_selectedRow is not the only selected row!");
}
//end checking code
if (*_selectedRow == _currentRow)
{
// currentRow is already selecteed
return;
}
else
{
[_selectedRows replaceObjectAtIndex:0
withObject:
[NSNumber numberWithInt: _currentRow]];
[tv setNeedsDisplayInRect:
[tv rectOfRow: *_selectedRow]];
[tv setNeedsDisplayInRect:
[tv rectOfRow: _currentRow]];
*_selectedRow = _currentRow;
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (((selectionMode & ALLOWS_MULTIPLE) == 0)
&& ((selectionMode & SHIFT_DOWN) == SHIFT_DOWN)
&& ((selectionMode & ALLOWS_EMPTY) == ALLOWS_EMPTY)
&& ((selectionMode & ADDING_ROW) == 0))
// we will unselect the selected row
// ic, sc : ok
{
int i;
int count = [_selectedRows count];
/*
BOOL wasOriginalRowSelected =
[_oldSelectedRows containsObject:
[NSNumber numberWithInt: _originalRow]];
*/
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)
{
for (i = 0; i < count; i++)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow:
[[_selectedRows objectAtIndex: i] intValue]]];
}
[_selectedRows removeAllObjects];
*_selectedRow = -1;
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
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 (delegate != nil)
{
if ([delegate tableView: tv shouldSelectRow: _originalRow] == NO)
{
return;
}
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: _originalRow]];
[_selectedRows addObject:
[NSNumber numberWithInt: _originalRow]];
*_selectedRow = _originalRow;
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (_currentRow == _originalRow)
{
if (count == 0)
{
// the row is already deselected
// nothing to do !
}
else
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: _originalRow]];
[_selectedRows removeObjectAtIndex: 0];
*_selectedRow = -1;
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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 ((delegate == nil) ||
[delegate tableView: 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;
}
for (i = 0; i < count; i++)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow:
[[_selectedRows objectAtIndex: i] intValue]]];
}
[_selectedRows removeAllObjects];
*_selectedRow = -1;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
else
{
for (i = _originalRow; i >= _currentRow; i--)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
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;
// int count = [_selectedRows count];
BOOL notified = NO;
diff = _currentRow - _originalRow;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
else
{
// this case does happen (sometimes)
for (i = _originalRow; i >= _currentRow; i--)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if ([_selectedRows containsObject:
[NSNumber numberWithInt: _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 ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i > _currentRow; i--)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
NSNumber *lastObject = [_selectedRows lastObject];
if (lastObject == nil)
{
*_selectedRow = -1;
}
else
{
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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 ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i < _currentRow; i++)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i < _originalRow; i++)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i > _originalRow; i--)
{
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *lastObject =
[_selectedRows lastObject];
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i > _currentRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
NSNumber *lastObject = [_selectedRows lastObject];
if (lastObject == nil)
{
*_selectedRow = -1;
}
else
{
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
int pos;
for (i = _oldRow; i < _currentRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i < _originalRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *firstObject =
[_selectedRows objectAtIndex: 0];
*_selectedRow = [firstObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
int pos;
for (i = _oldRow; i > _originalRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// leave it selected
continue;
}
pos = [_selectedRows indexOfObject:
[NSNumber numberWithInt: i]];
if (pos != NSNotFound)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
[_selectedRows removeObject:
[NSNumber numberWithInt: i]];
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == YES)
{
*_selectedRow = i;
notified = YES;
}
else
{
if ((delegate == nil) ||
[delegate tableView: tv shouldSelectRow: i] == YES)
{
if (notified == NO)
{
notified = YES;
}
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
}
}
}
}
if (notified == YES)
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
NSNumber *lastObject =
[_selectedRows lastObject];
*_selectedRow = [lastObject intValue];
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
}
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;
unsigned pos;
// int count = [_selectedRows count];
BOOL notified = NO;
diff = _currentRow - _originalRow;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
if ( notified
&& (*_selectedRow >= _originalRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
}
else
{
// this case does happen (sometimes)
for (i = _originalRow; i >= _currentRow; i--)
{
if ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
if ( notified
&& (*_selectedRow >= _originalRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else // new multiple antiselection, after first pass
{
int oldDiff, newDiff, i;
unsigned pos;
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 ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
for (i = _oldRow; i > _currentRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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 ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
if ( notified
&& (*_selectedRow < _oldRow)
&& (*_selectedRow >= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
for (i = _oldRow; i < _currentRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
for (i = _oldRow; i < _originalRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
for (i = _oldRow; i > _originalRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if ((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
}
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
}
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;
unsigned pos;
int count = [_selectedRows count];
BOOL notified = NO;
diff = _currentRow - _originalRow;
if (diff >= 0)
{
for (i = _originalRow; i <= _currentRow; i++)
{
if (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 0))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
if ( notified
&& (*_selectedRow >= _originalRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
}
else
{
// this case does happen (sometimes)
for (i = _originalRow; i >= _currentRow; i--)
{
if (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 0))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
if ( notified
&& (*_selectedRow >= _originalRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else // new multiple antiselection, after first pass
{
int oldDiff, newDiff, i;
unsigned pos;
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 (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 1))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
for (i = _oldRow; i > _currentRow; i--)
{
if (([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]]))
{
// this row was in the old selection
// select it
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == NO)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
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 (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 1))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
if ( notified
&& (*_selectedRow < _oldRow)
&& (*_selectedRow >= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else
// we're reducing the selection
{
BOOL notified = NO;
for (i = _oldRow; i < _currentRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == NO)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
count++;
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
else if (oldDiff <= 0 && newDiff >= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
for (i = _oldRow; i < _originalRow; i++)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == NO)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
}
}
// then we're extending it
{
for (i = _originalRow + 1; i <= _currentRow; i++)
{
if (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 1))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
else if (oldDiff >= 0 && newDiff <= 0)
{
BOOL notified = NO;
// we're reducing the selection
{
for (i = _oldRow; i > _originalRow; i--)
{
if ([_oldSelectedRows
containsObject:
[NSNumber numberWithInt: i]])
{
// this row was in the old selection
// select it
if ([_selectedRows
containsObject:
[NSNumber numberWithInt: i]] == NO)
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
_insertNumberInSelectionArray
(_selectedRows,
[NSNumber numberWithInt: i]);
*_selectedRow = i;
notified = YES;
}
}
}
}
// then we're extending it
{
for (i = _originalRow - 1; i >= _currentRow; i--)
{
if (((pos = [_selectedRows
indexOfObject:
[NSNumber numberWithInt: i]])
!= NSNotFound) && (count > 1))
{
[tv setNeedsDisplayInRect:
[tv rectOfRow: i]];
notified = YES;
[_selectedRows removeObjectAtIndex: pos];
count--;
}
}
}
if ( notified
&& (*_selectedRow > _oldRow)
&& (*_selectedRow <= _currentRow))
{
if ([_selectedRows count] == 0)
{
NSLog(@"error!");
*_selectedRow = -1;
}
else
{
*_selectedRow =
[[_selectedRows objectAtIndex: 0]
intValue];
}
}
if (notified == YES)
{
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: tv];
}
}
}
}
}
/*
static void
_selectRowsInRange (NSTableView *tv, id delegate,
int startRow, int endRow, int clickedRow)
{
int i;
int tmp;
int shift = 1;
// Switch rows if in the wrong order
if (startRow > endRow)
{
tmp = startRow;
startRow = endRow;
endRow = tmp;
shift = 0;
}
for (i = startRow + shift; i < endRow + shift; i++)
{
if (i != clickedRow)
{
if (delegate != nil)
{
if ([delegate tableView: tv shouldSelectRow: i] == NO)
continue;
}
[tv selectRow: i byExtendingSelection: YES];
[tv setNeedsDisplayInRect: [tv rectOfRow: i]];
}
}
}
static void
_deselectRowsInRange (NSTableView *tv, int startRow, int endRow,
int clickedRow)
{
int i;
int tmp;
int shift = 1;
if (startRow > endRow)
{
tmp = startRow;
startRow = endRow;
endRow = tmp;
shift = 0;
}
for (i = startRow + shift; i < endRow + shift; i++)
{
if (i != clickedRow)
{
[tv deselectRow: i];
[tv setNeedsDisplayInRect: [tv rectOfRow: i]];
}
}
}
*/
/*
* The following function can work on columns or rows;
* passing the correct select/deselect function switches between one
* and the other. Currently used only for rows, anyway.
*/
static void
_selectionChange (NSTableView *tv, id delegate, int numberOfRows,
int clickedRow,
int oldSelectedRow, int newSelectedRow,
void (*deselectFunction)(NSTableView *, int, int, int),
void (*selectFunction)(NSTableView *, id, int, int, int))
{
int newDistance;
int oldDistance;
if (oldSelectedRow == newSelectedRow)
return;
/* Change coordinates - distance from the center of selection */
oldDistance = oldSelectedRow - clickedRow;
newDistance = newSelectedRow - clickedRow;
/* If they have different signs, then we decompose it into
two problems with the same sign */
if ((oldDistance * newDistance) < 0)
{
_selectionChange (tv, delegate, numberOfRows, clickedRow,
oldSelectedRow, clickedRow, deselectFunction,
selectFunction);
_selectionChange (tv, delegate, numberOfRows, clickedRow, clickedRow,
newSelectedRow, deselectFunction, selectFunction);
return;
}
/* Otherwise, take the modulus of the distances */
if (oldDistance < 0)
oldDistance = -oldDistance;
if (newDistance < 0)
newDistance = -newDistance;
/* If the new selection is more wide, we need to select */
if (newDistance > oldDistance)
{
selectFunction (tv, delegate, oldSelectedRow, newSelectedRow,
clickedRow);
}
else /* Otherwise to deselect */
{
deselectFunction (tv, newSelectedRow, oldSelectedRow, clickedRow);
}
}
static inline BOOL
_isCellEditable (id delegate, NSArray *tableColumns,
NSTableView *tableView, int row, int column)
{
SEL selector = @selector(tableView:shouldEditTableColumn:row:);
if ([delegate respondsToSelector: selector] == YES)
{
NSTableColumn *tb;
tb = [tableColumns objectAtIndex: column];
if ([delegate tableView: 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, [NSMutableArray array]);
ASSIGN (_selectedRows, [NSMutableArray array]);
_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
{
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);
}
[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]];
int column, i, count;
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--;
}
count = [_selectedColumns count];
for (i = 0; i < count; i++)
{
column = [[_selectedColumns objectAtIndex: i] intValue];
if (column > columnIndex)
{
column--;
[_selectedColumns replaceObjectAtIndex: i
withObject: [NSNumber numberWithInt: column]];
}
}
/* 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;
/* Dummy variables for the loop */
int i, count, column;
NSDictionary *dict;
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;
}
count = [_selectedColumns count];
for (i = 0; i < count; i++)
{
column = [[_selectedColumns objectAtIndex: i] intValue];
if (column == columnIndex)
{
[_selectedColumns replaceObjectAtIndex: i
withObject: [NSNumber numberWithInt: newIndex]];
continue;
}
if ((column >= minRange) && (column <= maxRange))
{
column += shift;
[_selectedColumns replaceObjectAtIndex: i
withObject: [NSNumber numberWithInt: column]];
continue;
}
if ((column > columnIndex) && (column > newIndex))
{
break;
}
}
/* 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 */
dict =[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: columnIndex], @"NSOldColumn",
[NSNumber numberWithInt: newIndex], @"NSNewColumn", nil];
[nc postNotificationName: NSTableViewColumnDidMoveNotification
object: self
userInfo: dict];
[self _autosaveTableColumns];
}
- (NSArray *) tableColumns
{
return AUTORELEASE ([_tableColumns mutableCopyWithZone:
NSDefaultMallocZone ()]);
}
- (int) columnWithIdentifier: (id)anObject
{
NSEnumerator *enumerator = [_tableColumns objectEnumerator];
NSTableColumn *tb;
int return_value = 0;
while ((tb = [enumerator nextObject]) != nil)
{
if ([[tb identifier] isEqual: anObject])
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 respondsToSelector: sel_a] == NO)
{
[NSException
raise: NSInternalInconsistencyException
format: @"Data Source doesn't respond to numberOfRowsInTableView:"];
}
if ([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) 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
{
SEL selector;
if (_allowsColumnSelection == NO)
{
return;
}
if ([self isColumnSelected: columnIndex] == YES)
{
if (([_selectedColumns count] == 1) && (_allowsEmptySelection == NO))
{
return;
}
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == 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;
}
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == NO)
{
return;
}
}
selector = @selector (tableView:shouldSelectTableColumn:);
if ([_delegate respondsToSelector: selector] == YES)
{
NSTableColumn *tc = [_tableColumns objectAtIndex: columnIndex];
if ([_delegate tableView: 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;
}
/*
* Selecting Columns and Rows
*/
- (void) selectColumn: (int)columnIndex
byExtendingSelection: (BOOL)flag
{
NSNumber *num = [NSNumber numberWithInt: columnIndex];
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 containsObject: num] == 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 removeAllObjects];
[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 containsObject: num] == NO)
{
_insertNumberInSelectionArray (_selectedColumns, num);
_selectedColumn = columnIndex;
[self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
if (_headerView)
{
[_headerView setNeedsDisplayInRect:
[_headerView headerRectOfColumn: columnIndex]];
}
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
else /* Otherwise simply change the last selected column */
{
_selectedColumn = columnIndex;
}
}
#if 0
- (void) internal_selectRow: (int)rowIndex
byExtendingSelection: (BOOL)flag
{
NSNumber *num = [NSNumber numberWithInt: rowIndex];
if (rowIndex < 0 || rowIndex > _numberOfRows)
{
[NSException raise: NSInvalidArgumentException
format: @"Row index out of table in selectRow"];
}
_selectingColumns = NO;
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 containsObject: num] == 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 removeAllObjects];
_selectedRow = -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 row and post notification only if needed */
if ([_selectedRows containsObject: num] == NO)
{
_insertNumberInSelectionArray (_selectedRows, num);
_selectedRow = rowIndex;
[nc postNotificationName: NSTableViewSelectionIsChangingNotification
object: self];
}
else /* Otherwise simply change the last selected row */
{
_selectedRow = rowIndex;
}
}
#endif
- (void) selectRow: (int)rowIndex
byExtendingSelection: (BOOL)flag
{
NSNumber *num = [NSNumber numberWithInt: rowIndex];
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 containsObject: num] == 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)
{
[self setNeedsDisplay: YES];
[_selectedRows removeAllObjects];
_selectedRow = -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 row and post notification only if needed */
if ([_selectedRows containsObject: num] == NO)
{
_insertNumberInSelectionArray (_selectedRows, num);
_selectedRow = rowIndex;
[self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
else /* Otherwise simply change the last selected row */
{
_selectedRow = rowIndex;
}
}
- (void) deselectColumn: (int)columnIndex
{
NSNumber *num = [NSNumber numberWithInt: columnIndex];
if ([_selectedColumns containsObject: num] == 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 removeObject: num];
if (_selectedColumn == columnIndex)
{
int i, count = [_selectedColumns count];
int nearestColumn = -1;
int nearestColumnDistance = _numberOfColumns;
int column, distance;
for (i = 0; i < count; i++)
{
column = [[_selectedColumns objectAtIndex: i] intValue];
distance = column - columnIndex;
if (distance < 0)
{
distance = -distance;
}
if (distance < nearestColumnDistance)
{
nearestColumn = column;
}
}
_selectedColumn = nearestColumn;
}
[self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
if (_headerView)
{
[_headerView setNeedsDisplayInRect:
[_headerView headerRectOfColumn: columnIndex]];
}
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
- (void) deselectRow: (int)rowIndex
{
NSNumber *num = [NSNumber numberWithInt: rowIndex];
if ([_selectedRows containsObject: num] == NO)
{
return;
}
if (_textObject != nil)
{
[self validateEditing];
[self abortEditing];
}
_selectingColumns = NO;
[_selectedRows removeObject: num];
if (_selectedRow == rowIndex)
{
int i, count = [_selectedRows count];
int nearestRow = -1;
int nearestRowDistance = _numberOfRows;
int row, distance;
for (i = 0; i < count; i++)
{
row = [[_selectedRows objectAtIndex: i] intValue];
distance = row - rowIndex;
if (distance < 0)
{
distance = -distance;
}
if (distance < nearestRowDistance)
{
nearestRow = row;
}
}
_selectedRow = nearestRow;
}
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
- (int) numberOfSelectedColumns
{
return [_selectedColumns count];
}
- (int) numberOfSelectedRows
{
return [_selectedRows count];
}
- (int) selectedColumn
{
return _selectedColumn;
}
- (int) selectedRow
{
return _selectedRow;
}
- (BOOL) isColumnSelected: (int)columnIndex
{
NSNumber *num = [NSNumber numberWithInt: columnIndex];
return [_selectedColumns containsObject: num];
}
- (BOOL) isRowSelected: (int)rowIndex
{
NSNumber *num = [NSNumber numberWithInt: rowIndex];
return [_selectedRows containsObject: num];
}
- (NSEnumerator *) selectedColumnEnumerator
{
return [_selectedColumns objectEnumerator];
}
- (NSEnumerator *) selectedRowEnumerator
{
return [_selectedRows objectEnumerator];
}
- (void) selectAll: (id) sender
{
SEL selector;
if (_allowsMultipleSelection == NO)
return;
/* Ask the delegate if we can select all columns or rows */
if (_selectingColumns == YES)
{
if ([_selectedColumns count] == _numberOfColumns)
{
// Nothing to do !
return;
}
selector = @selector (tableView:shouldSelectTableColumn:);
if ([_delegate respondsToSelector: selector] == YES)
{
NSEnumerator *enumerator = [_tableColumns objectEnumerator];
NSTableColumn *tb;
while ((tb = [enumerator nextObject]) != nil)
{
if ([_delegate tableView: self
shouldSelectTableColumn: tb] == NO)
{
return;
}
}
}
}
else // selecting rows
{
if ([_selectedRows count] == _numberOfRows)
{
// Nothing to do !
return;
}
selector = @selector (tableView:shouldSelectRow:);
if ([_delegate respondsToSelector: selector] == YES)
{
int row;
for (row = 0; row < _numberOfRows; row++)
{
if ([_delegate tableView: self shouldSelectRow: row] == NO)
return;
}
}
}
/* Stop editing if any */
if (_textObject != nil)
{
[self validateEditing];
[self abortEditing];
}
/* Do the real selection */
if (_selectingColumns == YES)
{
int column;
[_selectedColumns removeAllObjects];
for (column = 0; column < _numberOfColumns; column++)
{
NSNumber *num = [NSNumber numberWithInt: column];
[_selectedColumns addObject: num];
}
}
else // selecting rows
{
int row;
[_selectedRows removeAllObjects];
for (row = 0; row < _numberOfRows; row++)
{
NSNumber *num = [NSNumber numberWithInt: row];
[_selectedRows addObject: num];
}
}
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
- (void) deselectAll: (id) sender
{
SEL selector;
if (_allowsEmptySelection == NO)
return;
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == NO)
{
return;
}
}
if (_textObject != nil)
{
[self validateEditing];
[self abortEditing];
}
if (([_selectedColumns count] > 0) || ([_selectedRows count] > 0))
{
[_selectedColumns removeAllObjects];
[_selectedRows removeAllObjects];
_selectedColumn = -1;
_selectedRow = -1;
_selectingColumns = NO;
[nc postNotificationName: NSTableViewSelectionDidChangeNotification
object: self];
}
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;
}
/*
* 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];
[_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;
// 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: [_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];
}
_textObject = [_editedCell setUpFieldEditorAttributes: t];
// Now edit it
drawingRect = [self frameOfCellAtColumn: columnIndex
row: rowIndex];
if (flag)
{
[_editedCell selectWithFrame: drawingRect
inView: self
editor: _textObject
delegate: self
start: 0
length: [[_editedCell stringValue] 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)
{
/* Compute rect to redraw to clear the old column selection */
int column, i, count = [_selectedColumns count];
for (i = 0; i < count; i++)
{
column = [[_selectedColumns objectAtIndex: i] intValue];
[self setNeedsDisplayInRect: [self rectOfColumn: column]];
}
[_selectedColumns removeAllObjects];
_selectedColumn = -1;
_selectingColumns = NO;
}
else
{
/* Compute rect to redraw to clear the old row selection */
int row, i, count = [_selectedRows count];
for (i = 0; i < count; i++)
{
row = [[_selectedRows objectAtIndex: i] intValue];
[self setNeedsDisplayInRect: [self rectOfRow: row]];
}
[_selectedRows removeAllObjects];
_selectedRow = -1;
_selectingColumns = YES;
}
}
- (void) mouseDown: (NSEvent *)theEvent
{
NSPoint location = [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)
{
SEL selector;
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;
NSSet *_oldSelectedRows;
id delegateIfItTakesPart;
BOOL mouseUp = NO;
BOOL done = NO;
NSRect visibleRect = [self convertRect: [self visibleRect]
toView: nil];
float minYVisible = NSMinY (visibleRect);
float maxYVisible = NSMaxY (visibleRect);
/* 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 ()
{
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 containsObject:
[NSNumber numberWithInt: _clickedRow]] == NO)
{
selectionMode |= ADDING_ROW;
}
if (modifiers & NSControlKeyMask)
{
selectionMode |= CONTROL_DOWN;
if (_allowsMultipleSelection == YES)
{
_originalRow = _selectedRow;
selectionMode |= SHIFT_DOWN;
selectionMode |= ADDING_ROW;
}
}
// does the delegate take part in the selection
selector = @selector (tableView:shouldSelectRow:);
if ([_delegate respondsToSelector: selector] == YES)
{
delegateIfItTakesPart = _delegate;
}
else
{
delegateIfItTakesPart = nil;
}
// is the delegate ok for a new selection ?
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == 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 = [[NSSet alloc]
initWithArray: _selectedRows];
lastEvent = theEvent;
while (done != YES)
{
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)
{
// the row hasn't changed, nothing to do
}
else
{
// we should now compute the new selection
computeNewSelection(self,
delegateIfItTakesPart,
_oldSelectedRows,
_selectedRows,
_originalRow,
_oldRow,
_currentRow,
&_selectedRow,
selectionMode);
[self displayIfNeeded];
}
}
else
// Mouse dragged out of the table
{
// we don't care
}
done = YES;
break;
case NSLeftMouseDown:
case NSLeftMouseDragged:
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)
{
// the row hasn't changed, nothing to do
}
else
{
// we should now compute the new selection
computeNewSelection(self,
delegateIfItTakesPart,
_oldSelectedRows,
_selectedRows,
_originalRow,
_oldRow,
_currentRow,
&_selectedRow,
selectionMode);
[self displayIfNeeded];
// [_window flushWindow];
}
}
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)
{
// last row already selected, nothing to do
}
else
{
_oldRow = _currentRow;
_currentRow++;
[self scrollRowToVisible: _currentRow];
// we should now compute the new selection
computeNewSelection(self,
delegateIfItTakesPart,
_oldSelectedRows,
_selectedRows,
_originalRow,
_oldRow,
_currentRow,
&_selectedRow,
selectionMode);
[self displayIfNeeded];
}
}
else /* mouse above the table */
{
if (_currentRow <= 0)
{
// first row already selected, nothing to do
}
else
{
_oldRow = _currentRow;
_currentRow--;
[self scrollRowToVisible: _currentRow];
// we should now compute the new selection
computeNewSelection(self,
delegateIfItTakesPart,
_oldSelectedRows,
_selectedRows,
_originalRow,
_oldRow,
_currentRow,
&_selectedRow,
selectionMode);
[self displayIfNeeded];
}
}
break;
default:
break;
}
if (done == NO)
{
lastEvent = [NSApp nextEventMatchingMask: eventMask
untilDate: distantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
}
}
if (startedPeriodicEvents == YES)
[NSEvent stopPeriodicEvents];
[_selectedRows sortUsingSelector: @selector(compare:)];
if ([_selectedRows
isEqualToArray:
[[_oldSelectedRows allObjects]
sortedArrayUsingSelector: @selector(compare:)]] == NO)
{
[nc postNotificationName:
NSTableViewSelectionDidChangeNotification
object: self];
}
return;
}
// Double-click events
if ([self isRowSelected: _clickedRow] == NO)
return;
tb = [_tableColumns objectAtIndex: _clickedColumn];
shouldEdit = YES;
if ([tb isEditable] == NO)
{
shouldEdit = NO;
}
else if ([_delegate respondsToSelector:
@selector(tableView:shouldEditTableColumn:row:)])
{
if ([_delegate tableView: 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];
}
/*
- (void) mouseDown2: (NSEvent *)theEvent
{
NSPoint location = [theEvent locationInWindow];
NSTableColumn *tb;
int clickCount;
BOOL shouldEdit;
//
// 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];
// Selection
if (clickCount == 1)
{
SEL selector;
unsigned int modifiers;
modifiers = [theEvent modifierFlags];
// Unselect a selected row if the shift key is pressed
if ([self isRowSelected: _clickedRow] == YES
&& (modifiers & NSShiftKeyMask))
{
if (([_selectedRows count] == 1) && (_allowsEmptySelection == NO))
return;
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == NO)
{
return;
}
}
if (_selectingColumns == YES)
{
[self _setSelectingColumns: NO];
}
[self deselectRow: _clickedRow];
[self setNeedsDisplayInRect: [self rectOfRow: _clickedRow]];
return;
}
else // row is not selected
{
BOOL newSelection;
if ((modifiers & (NSShiftKeyMask | NSAlternateKeyMask))
&& _allowsMultipleSelection)
newSelection = NO;
else
newSelection = YES;
selector = @selector (selectionShouldChangeInTableView:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate selectionShouldChangeInTableView: self] == NO)
{
return;
}
}
selector = @selector (tableView:shouldSelectRow:);
if ([_delegate respondsToSelector: selector] == YES)
{
if ([_delegate tableView: self
shouldSelectRow: _clickedRow] == NO)
{
return;
}
}
if (_selectingColumns == YES)
{
[self _setSelectingColumns: NO];
}
if (newSelection == YES)
{
// No shift or alternate key pressed: clear the old selection
// Compute rect to redraw to clear the old selection
int row, i, count = [_selectedRows count];
for (i = 0; i < count; i++)
{
row = [[_selectedRows objectAtIndex: i] intValue];
[self setNeedsDisplayInRect: [self rectOfRow: row]];
}
// Draw the new selection
[self selectRow: _clickedRow byExtendingSelection: NO];
[self setNeedsDisplayInRect: [self rectOfRow: _clickedRow]];
}
else // Simply add to the old selection
{
[self selectRow: _clickedRow byExtendingSelection: YES];
[self setNeedsDisplayInRect: [self rectOfRow: _clickedRow]];
}
if (_allowsMultipleSelection == NO)
{
return;
}
// else, we track the mouse to allow extending selection to
// areas by dragging the mouse
// Draw immediately because we are going to enter an event
// loop to track the mouse
[_window flushWindow];
// We track the cursor and highlight according to the cursor
// position. When the cursor gets out (up or down) of the
// table, we start periodic events and scroll periodically
// the table enlarging or reducing selection.
{
BOOL startedPeriodicEvents = NO;
unsigned int eventMask = (NSLeftMouseUpMask
| NSLeftMouseDraggedMask
| NSPeriodicMask);
NSEvent *lastEvent;
BOOL done = NO;
NSPoint mouseLocationWin;
NSDate *distantFuture = [NSDate distantFuture];
int lastSelectedRow = _clickedRow;
NSRect visibleRect = [self convertRect: [self visibleRect]
toView: nil];
float minYVisible = NSMinY (visibleRect);
float maxYVisible = NSMaxY (visibleRect);
BOOL delegateTakesPart;
BOOL mouseUp = 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 ()
{
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;
}
selector = @selector (tableView:shouldSelectRow:);
delegateTakesPart = [_delegate respondsToSelector: selector];
while (done != YES)
{
lastEvent = [NSApp nextEventMatchingMask: eventMask
untilDate: distantFuture
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
switch ([lastEvent type])
{
case NSLeftMouseUp:
done = YES;
break;
case NSLeftMouseDragged:
mouseLocationWin = [lastEvent locationInWindow];
if ((mouseLocationWin.y > minYVisible)
&& (mouseLocationWin.y < maxYVisible))
{
NSPoint mouseLocationView;
int rowAtPoint;
if (startedPeriodicEvents == YES)
{
[NSEvent stopPeriodicEvents];
startedPeriodicEvents = NO;
}
mouseLocationView = [self convertPoint:
mouseLocationWin
fromView: nil];
mouseLocationView.x = _bounds.origin.x;
rowAtPoint = [self rowAtPoint: mouseLocationView];
if (delegateTakesPart == YES)
{
_selectionChange (self, _delegate,
_numberOfRows, _clickedRow,
lastSelectedRow, rowAtPoint,
_deselectRowsInRange,
_selectRowsInRange);
}
else
{
_selectionChange (self, nil,
_numberOfRows, _clickedRow,
lastSelectedRow, rowAtPoint,
_deselectRowsInRange,
_selectRowsInRange);
}
lastSelectedRow = rowAtPoint;
[_window flushWindow];
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: self];
}
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 (lastSelectedRow < _clickedRow)
{
[self deselectRow: lastSelectedRow];
[self setNeedsDisplayInRect:
[self rectOfRow: lastSelectedRow]];
[self scrollRowToVisible: lastSelectedRow];
[_window flushWindow];
lastSelectedRow++;
}
else
{
if ((lastSelectedRow + 1) < _numberOfRows)
{
lastSelectedRow++;
if ((delegateTakesPart == YES) &&
([_delegate
tableView: self
shouldSelectRow: lastSelectedRow]
== NO))
{
break;
}
[self selectRow: lastSelectedRow
byExtendingSelection: YES];
[self setNeedsDisplayInRect:
[self rectOfRow: lastSelectedRow]];
[self scrollRowToVisible: lastSelectedRow];
[_window flushWindow];
}
}
}
else // mouse above the table
{
if (lastSelectedRow <= _clickedRow)
{
if ((lastSelectedRow - 1) >= 0)
{
lastSelectedRow--;
if ((delegateTakesPart == YES) &&
([_delegate
tableView: self
shouldSelectRow: lastSelectedRow]
== NO))
{
break;
}
[self selectRow: lastSelectedRow
byExtendingSelection: YES];
[self setNeedsDisplayInRect:
[self rectOfRow: lastSelectedRow]];
[self scrollRowToVisible: lastSelectedRow];
[_window flushWindow];
}
}
else
{
[self deselectRow: lastSelectedRow];
[self setNeedsDisplayInRect:
[self rectOfRow: lastSelectedRow]];
[self scrollRowToVisible: lastSelectedRow];
[_window flushWindow];
lastSelectedRow--;
}
}
[nc postNotificationName:
NSTableViewSelectionIsChangingNotification
object: self];
break;
default:
break;
}
}
if (startedPeriodicEvents == YES)
[NSEvent stopPeriodicEvents];
[nc postNotificationName:
NSTableViewSelectionDidChangeNotification
object: self];
return;
}
}
}
// Double-click events
if ([self isRowSelected: _clickedRow] == NO)
return;
tb = [_tableColumns objectAtIndex: _clickedColumn];
shouldEdit = YES;
if ([tb isEditable] == NO)
{
shouldEdit = NO;
}
else if ([_delegate respondsToSelector:
@selector(tableView:shouldEditTableColumn:row:)])
{
if ([_delegate tableView: 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 ((aPoint.x >= _columnOrigins[i]) && (i < _numberOfColumns))
{
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) aRect
{
[super setFrame: aRect];
}
- (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);
[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 i, count = [_selectedRows count];
int row = -1;
/* Check that all selected rows are in the new range of rows */
for (i = 0; i < count; i++)
{
row = [[_selectedRows objectAtIndex: i] intValue];
if (row >= _numberOfRows)
{
break;
}
}
if (i < count && row > -1)
{
/* Some of them are outside the table ! - Remove them */
for (; i < count; i++)
{
[_selectedRows removeLastObject];
}
/* Now if the _selectedRow is outside the table, reset it to be
the last selected row (if any) */
if (_selectedRow >= _numberOfRows)
{
if ([_selectedRows count] > 0)
{
_selectedRow = [[_selectedRows lastObject] intValue];
}
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 addObject:
[NSNumber numberWithInt: 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)aRect
{
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 (aRect);
i = 0;
while ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns))
{
i++;
}
startingColumn = (i - 1);
if (startingColumn == -1)
startingColumn = 0;
/* Determine ending column as fast as possible */
x_pos = NSMaxX (aRect);
// Nota Bene: we do *not* reset i
while ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns))
{
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 ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns))
{
i++;
}
startingColumn = (i - 1);
x_pos = maxX;
// Nota Bene: we do *not* reset i
while ((x_pos > _columnOrigins[i]) && (i < _numberOfColumns))
{
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;
for (row = 0; row < selectedRowsCount; row++)
{
int rowNumber;
rowNumber = [[_selectedRows objectAtIndex: row] intValue];
if (rowNumber > endingRow)
break;
if (rowNumber >= startingRow)
{
// NSHighlightRect(NSIntersectionRect([self rectOfRow: rowNumber],
// clipRect));
[[NSColor whiteColor] set];
NSRectFill(NSIntersectionRect([self rectOfRow: rowNumber], clipRect));
}
}
}
else // Selecting columns
{
int selectedColumnsCount;
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;
for (column = 0; column < selectedColumnsCount; column++)
{
int columnNumber;
columnNumber = [[_selectedColumns objectAtIndex: column] intValue];
if (columnNumber > endingColumn)
break;
if (columnNumber >= startingColumn)
{
NSHighlightRect (NSIntersectionRect(
[self rectOfColumn: columnNumber],
clipRect));
}
}
}
}
- (void) drawRect: (NSRect)aRect
{
int startingRow;
int endingRow;
int i;
/* Draw background */
/*
[_backgroundColor set];
NSRectFill (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 convertRect: [_super_view bounds]
toView: self];
// 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 convertRect: [_super_view bounds]
toView: self];
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 alloc] initWithDictionary:
[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 alloc] initWithDictionary:
[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];
d = [[NSMutableDictionary alloc] initWithDictionary:
[aNotification userInfo]];
[d setObject: [aNotification object] forKey: @"NSFieldEditor"];
[nc postNotificationName: NSControlTextDidEndEditingNotification
object: self
userInfo: d];
[_editedCell endEditing: [aNotification object]];
[self setNeedsDisplayInRect:
[self frameOfCellAtColumn: _editedColumn row: _editedRow]];
_textObject = nil;
_editedCell = nil;
RELEASE (_editedCell);
/* Save values */
row = _editedRow;
column = _editedColumn;
/* Only then Reset them */
_editedColumn = -1;
_editedRow = -1;
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 *)aTextObject
{
if ([_delegate respondsToSelector:
@selector(control:textShouldEndEditing:)])
{
if ([_delegate control: self
textShouldEndEditing: aTextObject] == 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: [aTextObject 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;
// TODO
NSLog(@"Method %s is not implemented for class %s",
"highlightedTableColumn", "NSTableView");
return nil;
}
- (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
{
// TODO
NSLog(@"Method %s is not implemented for class %s",
"dragImageForRows:event:dragImageOffset:", "NSTableView");
return nil;
}
- (void) setDropRow: (int)row
dropOperation: (NSTableViewDropOperation)operation
{
// TODO
NSLog(@"Method %s is not implemented for class %s",
"setDropRow:dropOperation:", "NSTableView");
}
- (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: &_autoresizesAllColumnsToFit];
}
- (id) initWithCoder: (NSCoder*)aDecoder
{
int version = [aDecoder versionForClassName:
NSStringFromClass([self class])];
id aDelegate;
if (version == currentVersion)
{
self = [super initWithCoder: aDecoder];
_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];
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autoresizesAllColumnsToFit];
ASSIGN (_selectedColumns, [NSMutableArray array]);
ASSIGN (_selectedRows, [NSMutableArray array]);
if (_numberOfColumns)
_columnOrigins = NSZoneMalloc (NSDefaultMallocZone (),
sizeof(float) * _numberOfColumns);
_clickedRow = -1;
_clickedColumn = -1;
_selectingColumns = NO;
_selectedColumn = -1;
_selectedRow = -1;
_editedColumn = -1;
_editedRow = -1;
}
else if (version == 1)
{
self = [super initWithCoder: aDecoder];
_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];
ASSIGN (_selectedColumns, [NSMutableArray array]);
ASSIGN (_selectedRows, [NSMutableArray array]);
if (_numberOfColumns)
_columnOrigins = NSZoneMalloc (NSDefaultMallocZone (),
sizeof(float) * _numberOfColumns);
_clickedRow = -1;
_clickedColumn = -1;
_selectingColumns = NO;
_selectedColumn = -1;
_selectedRow = -1;
_editedColumn = -1;
_editedRow = -1;
}
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, row, i) == 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)
{
[self sizeToFit];
}
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;
}
}
@end /* implementation of NSTableView */