1/** <title>NSTableView</title>
2
3   Copyright (C) 2000 Free Software Foundation, Inc.
4
5   Author: Nicola Pero <n.pero@mi.flashnet.it>
6   Date: March 2000, June 2000, August 2000, September 2000
7
8   Author: Pierre-Yves Rivaille <pyrivail@ens-lyon.fr>
9   Date: August 2001, January 2002
10
11   Author: Fred Kiefer <fredkiefer@gmx.de>
12   Date: March 2004
13
14   This file is part of the GNUstep GUI Library.
15
16   This library is free software; you can redistribute it and/or
17   modify it under the terms of the GNU Lesser General Public
18   License as published by the Free Software Foundation; either
19   version 2 of the License, or (at your option) any later version.
20
21   This library is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
24   Lesser General Public License for more details.
25
26   You should have received a copy of the GNU Lesser General Public
27   License along with this library; see the file COPYING.LIB.
28   If not, see <http://www.gnu.org/licenses/> or write to the
29   Free Software Foundation, 51 Franklin Street, Fifth Floor,
30   Boston, MA 02110-1301, USA.
31*/
32
33#import <Foundation/NSAutoreleasePool.h>
34#import <Foundation/NSDebug.h>
35#import <Foundation/NSDictionary.h>
36#import <Foundation/NSEnumerator.h>
37#import <Foundation/NSException.h>
38#import <Foundation/NSFormatter.h>
39#import <Foundation/NSIndexSet.h>
40#import <Foundation/NSKeyValueCoding.h>
41#import <Foundation/NSNotification.h>
42#import <Foundation/NSSet.h>
43#import <Foundation/NSSortDescriptor.h>
44#import <Foundation/NSUserDefaults.h>
45#import <Foundation/NSValue.h>
46#import <Foundation/NSKeyedArchiver.h>
47
48#import "AppKit/NSTableView.h"
49#import "AppKit/NSApplication.h"
50#import "AppKit/NSCell.h"
51#import "AppKit/NSClipView.h"
52#import "AppKit/NSColor.h"
53#import "AppKit/NSEvent.h"
54#import "AppKit/NSImage.h"
55#import "AppKit/NSGraphics.h"
56#import "AppKit/NSKeyValueBinding.h"
57#import "AppKit/NSScroller.h"
58#import "AppKit/NSScrollView.h"
59#import "AppKit/NSTableColumn.h"
60#import "AppKit/NSTableHeaderView.h"
61#import "AppKit/NSText.h"
62#import "AppKit/NSTextFieldCell.h"
63#import "AppKit/NSWindow.h"
64#import "AppKit/PSOperators.h"
65#import "AppKit/NSCachedImageRep.h"
66#import "AppKit/NSPasteboard.h"
67#import "AppKit/NSDragging.h"
68#import "AppKit/NSCustomImageRep.h"
69#import "GNUstepGUI/GSTheme.h"
70#import "GSBindingHelpers.h"
71
72#include <math.h>
73static NSNotificationCenter *nc = nil;
74
75static const int currentVersion = 5;
76
77static NSRect oldDraggingRect;
78static NSInteger oldDropRow;
79static NSTableViewDropOperation oldDropOperation;
80static NSTableViewDropOperation currentDropOperation;
81static NSInteger currentDropRow;
82static NSInteger lastQuarterPosition;
83static NSDragOperation currentDragOperation;
84
85/*
86 * Nib compatibility struct.  This structure is used to
87 * pull the attributes out of the nib that we need to fill
88 * in the flags.
89 */
90typedef struct _tableViewFlags
91{
92#if GS_WORDS_BIGENDIAN == 1
93  unsigned int columnOrdering:1;
94  unsigned int columnResizing:1;
95  unsigned int drawsGrid:1;
96  unsigned int emptySelection:1;
97  unsigned int multipleSelection:1;
98  unsigned int columnSelection:1;
99  unsigned int _unused:26;
100#else
101  unsigned int _unused:26;
102  unsigned int columnSelection:1;
103  unsigned int multipleSelection:1;
104  unsigned int emptySelection:1;
105  unsigned int drawsGrid:1;
106  unsigned int columnResizing:1;
107  unsigned int columnOrdering:1;
108#endif
109} GSTableViewFlags;
110
111#define ALLOWS_MULTIPLE (1)
112#define ALLOWS_EMPTY (1 << 1)
113#define SHIFT_DOWN (1 << 2)
114#define CONTROL_DOWN (1 << 3)
115#define ADDING_ROW (1 << 4)
116
117@interface NSTableView (NotificationRequestMethods)
118- (void) _postSelectionIsChangingNotification;
119- (void) _postSelectionDidChangeNotification;
120- (void) _postColumnDidMoveNotificationWithOldIndex: (NSInteger) oldIndex
121					   newIndex: (NSInteger) newIndex;
122- (void) _postColumnDidResizeNotification;
123- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn;
124- (BOOL) _shouldSelectRow: (NSInteger)rowIndex;
125
126- (BOOL) _shouldSelectionChange;
127- (void) _didChangeSortDescriptors: (NSArray *)oldSortDescriptors;
128- (void) _didClickTableColumn: (NSTableColumn *)tc;
129- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
130			    row: (NSInteger) rowIndex;
131- (void) _willDisplayCell: (NSCell*)cell
132	   forTableColumn: (NSTableColumn *)tb
133		      row: (NSInteger)index;
134
135- (BOOL) _writeRows: (NSIndexSet *)rows
136       toPasteboard: (NSPasteboard *)pboard;
137- (BOOL) _isDraggingSource;
138- (id)_objectValueForTableColumn: (NSTableColumn *)tb
139			     row: (NSInteger)index;
140- (void)_setObjectValue: (id)value
141	 forTableColumn: (NSTableColumn *)tb
142		    row: (NSInteger)index;
143
144- (BOOL) _isEditableColumn: (NSInteger)columnIndex
145                       row: (NSInteger)rowIndex;
146- (BOOL) _isCellSelectableColumn: (NSInteger)columnIndex
147                             row: (NSInteger)rowIndex;
148- (BOOL) _isCellEditableColumn: (NSInteger)columnIndex
149			   row: (NSInteger)rowIndex;
150- (NSInteger) _numRows;
151@end
152
153@interface NSTableView (SelectionHelper)
154- (void) _setSelectingColumns: (BOOL)flag;
155- (NSArray *) _indexSetToArray: (NSIndexSet*)indexSet;
156- (NSArray *) _selectedRowArray;
157- (BOOL) _selectRow: (NSInteger)rowIndex;
158- (BOOL) _selectUnselectedRow: (NSInteger)rowIndex;
159- (BOOL) _unselectRow: (NSInteger)rowIndex;
160- (void) _unselectAllRows;
161- (NSArray *) _selectedColumArray;
162- (void) _unselectAllColumns;
163@end
164
165@interface NSTableView (EventLoopHelper)
166- (void) _trackCellAtColumn:(NSInteger)column row:(NSInteger)row withEvent:(NSEvent *)ev;
167- (BOOL) _startDragOperationWithEvent:(NSEvent *)theEvent;
168@end
169
170/*
171 *  A specific struct and its associated quick sort function
172 *  This is used by the -sizeToFit method
173 */
174typedef struct {
175  CGFloat width;
176  BOOL isMax;
177} columnSorting;
178
179
180static
181void quick_sort_internal(columnSorting *data, int p, int r)
182{
183  if (p < r)
184    {
185      int q;
186      {
187	CGFloat x = data[p].width;
188	BOOL y = data[p].isMax;
189	int i = p - 1;
190	int j = r + 1;
191	columnSorting exchange;
192	while (1)
193	  {
194	    j--;
195	    for (;
196		(data[j].width > x)
197		  || ((data[j].width == x)
198		      && (data[j].isMax == YES)
199		      && (y == NO));
200		j--)
201	      ;
202
203	    i++;
204	    for (;
205		(data[i].width < x)
206		  || ((data[i].width == x)
207		      && (data[i].isMax == NO)
208		      && (y == YES));
209		i++)
210	      ;
211	    if (i < j)
212	      {
213		exchange = data[j];
214		data[j] = data[i];
215		data[i] = exchange;
216	      }
217	    else
218	      {
219		q = j;
220		break;
221	      }
222	  }
223      }
224      quick_sort_internal(data, p, q);
225      quick_sort_internal(data, q + 1, r);
226    }
227}
228
229/*
230 * Now some auxiliary functions used to manage real-time user selections.
231 *
232 */
233
234static void computeNewSelection
235(NSTableView *tv,
236 NSIndexSet *_oldSelectedRows,
237 NSMutableIndexSet *_selectedRows,
238 NSInteger _originalRow,
239 NSInteger _oldRow,
240 NSInteger _currentRow,
241 NSInteger *_selectedRow,
242 unsigned selectionMode)
243{
244  if (!(selectionMode & ALLOWS_MULTIPLE))
245    {
246      if ((selectionMode & SHIFT_DOWN) &&
247	  (selectionMode & ALLOWS_EMPTY) &&
248	  !(selectionMode & ADDING_ROW))
249	  // we will unselect the selected row
250	  // ic, sc : ok
251        {
252	  NSUInteger count = [_selectedRows count];
253
254	  if ((count == 0) && (_oldRow == -1))
255	    {
256	      NSLog(@"how did you get there ?");
257	      NSLog(@"you're supposed to have clicked on a selected row,");
258	      NSLog(@"but there's no selected row!");
259	      return;
260	    }
261	  else if (count > 1)
262	    {
263	      [tv _unselectAllRows];
264	      [tv _postSelectionIsChangingNotification];
265	    }
266	  else if (_currentRow != _originalRow)
267	    {
268	      if (*_selectedRow == _originalRow)
269	        {
270		  // we are already selected, don't do anything
271		}
272	      else
273	        {
274		  //begin checking code
275		  if (count > 0)
276		    {
277		      NSLog(@"There should not be any row selected");
278		    }
279		  //end checking code
280
281		  if ([tv _selectRow: _originalRow])
282		    {
283		      [tv _postSelectionIsChangingNotification];
284		    }
285		}
286	    }
287	  else if (_currentRow == _originalRow)
288	    {
289	      if (count == 0)
290	        {
291		  // the row is already deselected
292		  // nothing to do !
293		}
294	      else
295	        {
296		  [tv _unselectRow: _originalRow];
297		  [tv _postSelectionIsChangingNotification];
298		}
299	    }
300	}
301      else  //(!(selectionMode & ALLOWS_MULTIPLE) &&
302	    //(!(selectionMode & SHIFT_DOWN) ||
303	    //!(selectionMode & ALLOWS_EMPTY) ||
304	    //(selectionMode & ADDING_ROW)))
305	  // we'll be selecting exactly one row
306	  // ic, sc : ok
307        {
308	  NSUInteger count = [_selectedRows count];
309
310	  if ([tv _shouldSelectRow: _currentRow] == NO)
311	    {
312	      return;
313	    }
314
315	  if ((count != 1) || (_oldRow == -1))
316	    {
317	      // this is the first call that goes thru shouldSelectRow
318	      // Therefore we don't know anything about the selection
319	      BOOL notified = ![_selectedRows containsIndex: _currentRow];
320	      [tv _unselectAllRows];
321	      [_selectedRows addIndex: _currentRow];
322	      *_selectedRow = _currentRow;
323
324	      if (notified == YES)
325	        {
326		  [tv setNeedsDisplayInRect: [tv rectOfRow: _currentRow]];
327		}
328	      else
329	        {
330		  if (count > 1)
331		    {
332		      notified = YES;
333		    }
334		}
335	      if (notified == YES)
336	        {
337		  [tv _postSelectionIsChangingNotification];
338		}
339	    }
340	  else
341	    {
342	      // we know there is only one column selected
343	      // this column is *_selectedRow
344
345	      //begin checking code
346	      if (![_selectedRows containsIndex: *_selectedRow])
347	        {
348		  NSLog(@"*_selectedRow is not the only selected row!");
349		}
350	      //end checking code
351
352	      if (*_selectedRow == _currentRow)
353	        {
354		  // currentRow is already selecteed
355		  return;
356		}
357	      else
358	        {
359		  [tv _unselectRow: *_selectedRow];
360		  // CHANGE: This does a check more
361		  [tv _selectRow: _currentRow];
362		  [tv _postSelectionIsChangingNotification];
363		}
364	    }
365	}
366    }
367  else if ((selectionMode & ALLOWS_MULTIPLE)
368	   && (selectionMode & SHIFT_DOWN)
369	   && (selectionMode & ADDING_ROW))
370    // we add new row to the current selection
371    {
372      if (_oldRow == -1)
373	// this is the first pass
374	{
375	  BOOL notified = NO;
376	  NSInteger i;
377	  NSInteger diff = _currentRow - _originalRow;
378
379	  if (diff >= 0)
380	    {
381	      for (i = _originalRow; i <= _currentRow; i++)
382		{
383		  if ([_selectedRows containsIndex: i] ||
384		      [tv _selectRow: i])
385		    {
386		      *_selectedRow = i;
387		      notified = YES;
388		    }
389		}
390	    }
391	  else
392	    {
393	      // this case does happen, (sometimes)
394	      for (i = _originalRow; i >= _currentRow; i--)
395		{
396		  if ([_selectedRows containsIndex: i] ||
397		      [tv _selectRow: i])
398		    {
399		      *_selectedRow = i;
400		      notified = YES;
401		    }
402		}
403	    }
404
405	  if (notified == YES)
406	    {
407	      [tv _postSelectionIsChangingNotification];
408	    }
409	}
410      else // new multiple selection, after first pass
411	{
412	  NSInteger oldDiff, newDiff, i;
413	  oldDiff = _oldRow - _originalRow;
414	  newDiff = _currentRow - _originalRow;
415	  if (oldDiff >= 0 && newDiff >= 0)
416	    {
417	      if (newDiff >= oldDiff)
418		// we're extending the selection
419		{
420		  BOOL notified = NO;
421
422		  for (i = _oldRow + 1; i <= _currentRow; i++)
423		    {
424		      if ([_selectedRows containsIndex: i] ||
425			  [tv _selectRow: i])
426			{
427			  *_selectedRow = i;
428			  notified = YES;
429			}
430		    }
431		  if (notified == YES)
432		    {
433		      [tv _postSelectionIsChangingNotification];
434		    }
435		}
436	      else
437		// we're reducing the selection
438		{
439		  BOOL notified = NO;
440
441		  for (i = _oldRow; i > _currentRow; i--)
442		    {
443		      if ([_oldSelectedRows containsIndex: i])
444			{
445			  // this row was in the old selection
446			  // leave it selected
447			  continue;
448			}
449
450		      if ([tv _unselectRow: i])
451		        {
452			  notified = YES;
453			}
454		    }
455		  if (*_selectedRow == -1)
456		    {
457		      NSUInteger last = [_selectedRows lastIndex];
458
459		      if (last == NSNotFound)
460			{
461			  *_selectedRow = -1;
462			}
463		      else
464			{
465			  *_selectedRow = last;
466			}
467		    }
468		  if (notified)
469		    {
470		      [tv _postSelectionIsChangingNotification];
471		    }
472		}
473	    }
474	  else if (oldDiff <= 0 && newDiff <= 0)
475	    {
476	      if (newDiff <= oldDiff)
477		// we're extending the selection
478		{
479		  BOOL notified = NO;
480		  for (i = _oldRow - 1; i >= _currentRow; i--)
481		    {
482		      if ([_selectedRows containsIndex: i] ||
483			  [tv _selectRow: i])
484			{
485			  *_selectedRow = i;
486			  notified = YES;
487			}
488		    }
489		  if (notified == YES)
490		    {
491		      [tv _postSelectionIsChangingNotification];
492		    }
493		}
494	      else
495		// we're reducing the selection
496		{
497		  BOOL notified = NO;
498
499		  for (i = _oldRow; i < _currentRow; i++)
500		    {
501		      if ([_oldSelectedRows containsIndex: i])
502			{
503			  // this row was in the old selection
504			  // leave it selected
505			  continue;
506			}
507
508		      if ([tv _unselectRow: i])
509			{
510			  notified = YES;
511			}
512		    }
513		  if (*_selectedRow == -1)
514		    {
515		      NSUInteger first = [_selectedRows firstIndex];
516
517		      if (first == NSNotFound)
518			{
519			  *_selectedRow = -1;
520			}
521		      else
522			{
523			  *_selectedRow = first;
524			}
525		    }
526		  if (notified == YES)
527		    {
528		      [tv _postSelectionIsChangingNotification];
529		    }
530		}
531	    }
532	  else if (oldDiff <= 0 && newDiff >= 0)
533	    {
534	      BOOL notified = NO;
535
536	      // we're reducing the selection
537	      {
538		for (i = _oldRow; i < _originalRow; i++)
539		  {
540		    if ([_oldSelectedRows containsIndex: i])
541		      {
542			// this row was in the old selection
543			// leave it selected
544			continue;
545		      }
546
547		    if ([tv _unselectRow: i])
548		      {
549			notified = YES;
550		      }
551		  }
552	      }
553	      // then we're extending it
554	      for (i = _originalRow + 1; i <= _currentRow; i++)
555	        {
556		  if ([_selectedRows containsIndex: i] ||
557		      [tv _selectRow: i])
558		    {
559		      *_selectedRow = i;
560		      notified = YES;
561		    }
562		}
563
564	      if (*_selectedRow == -1)
565		{
566		  NSUInteger first = [_selectedRows firstIndex];
567
568		  if (first == NSNotFound)
569		    {
570		      *_selectedRow = -1;
571		    }
572		  else
573		    {
574		      *_selectedRow = first;
575		    }
576		}
577	      if (notified == YES)
578		{
579		  [tv _postSelectionIsChangingNotification];
580		}
581	    }
582	  else if (oldDiff >= 0 && newDiff <= 0)
583	    {
584	      BOOL notified = NO;
585
586	      // we're reducing the selection
587	      for (i = _oldRow; i > _originalRow; i--)
588	        {
589		  if ([_oldSelectedRows containsIndex: i])
590		    {
591		      // this row was in the old selection
592		      // leave it selected
593		      continue;
594		    }
595
596		  if ([tv _unselectRow: i])
597		    {
598		      notified = YES;
599		    }
600		}
601	      // then we're extending it
602	      for (i = _originalRow - 1; i >= _currentRow; i--)
603	        {
604		  if ([_selectedRows containsIndex: i] ||
605		      [tv _selectRow: i])
606		    {
607		      *_selectedRow = i;
608		      notified = YES;
609		    }
610		}
611
612	      if (*_selectedRow == -1)
613		{
614		  NSUInteger last = [_selectedRows lastIndex];
615
616		  if (last == NSNotFound)
617		    {
618		      *_selectedRow = -1;
619		    }
620		  else
621		    {
622		      *_selectedRow = last;
623		    }
624		}
625	      if (notified == YES)
626		{
627		  [tv _postSelectionIsChangingNotification];
628		}
629	    }
630
631	}
632    }
633  else if ((selectionMode & ALLOWS_MULTIPLE)
634	   && ((selectionMode & SHIFT_DOWN) == 0)
635	   && (selectionMode & ALLOWS_EMPTY)
636	)
637    // ic, sr : ok
638    // new multiple selection (empty possible)
639    {
640      if (_oldRow == -1)
641	// this is the first pass
642	// we'll clear the selection first
643	{
644	  NSInteger diff, i;
645	  NSUInteger count = [_selectedRows count];
646	  BOOL notified = NO;
647      	  diff = _currentRow - _originalRow;
648
649	  if (count > 0)
650	    {
651	      notified = YES;
652	    }
653
654	  [tv _unselectAllRows];
655
656	  if (diff >= 0)
657	    {
658	      for (i = _originalRow; i <= _currentRow; i++)
659		{
660		  if ([tv _selectRow: i])
661		    {
662		      notified = YES;
663		    }
664		}
665	    }
666	  else
667	    {
668	      // this case does happen (sometimes)
669	      for (i = _originalRow; i >= _currentRow; i--)
670		{
671		  if ([tv _selectRow: i])
672		    {
673		      notified = YES;
674		    }
675		}
676	    }
677	  if (notified == YES)
678	    {
679	      [tv _postSelectionIsChangingNotification];
680	    }
681	}
682      else // new multiple selection, after first pass
683	{
684	  NSInteger oldDiff, newDiff, i;
685	  oldDiff = _oldRow - _originalRow;
686	  newDiff = _currentRow - _originalRow;
687	  if (oldDiff >= 0 && newDiff >= 0)
688	    {
689	      if (newDiff >= oldDiff)
690		{
691		  BOOL notified = NO;
692		  for (i = _oldRow + 1; i <= _currentRow; i++)
693		    {
694		      if ([tv _selectRow: i])
695			{
696			  notified = YES;
697			}
698		    }
699		  if (notified == YES)
700		    {
701		      [tv _postSelectionIsChangingNotification];
702		    }
703		}
704	      else
705		{
706		  BOOL notified = NO;
707
708		  for (i = _oldRow; i > _currentRow; i--)
709		    {
710		      if ([tv _unselectRow: i])
711			{
712			  notified = YES;
713			}
714		    }
715		  if (*_selectedRow == -1)
716		    {
717		      NSUInteger last = [_selectedRows lastIndex];
718
719		      if (last == NSNotFound)
720			{
721			  *_selectedRow = -1;
722			}
723		      else
724			{
725			  *_selectedRow = last;
726			}
727		    }
728		  if (notified == YES)
729		    {
730		      [tv _postSelectionIsChangingNotification];
731		    }
732		}
733	    }
734	  else if (oldDiff <= 0 && newDiff <= 0)
735	    {
736	      if (newDiff <= oldDiff)
737		// we're extending the selection
738		{
739		  BOOL notified = NO;
740
741		  for (i = _oldRow - 1; i >= _currentRow; i--)
742		    {
743		      if ([tv _selectRow: i])
744			{
745			  notified = YES;
746			}
747		    }
748		  if (notified == YES)
749		    {
750		      [tv _postSelectionIsChangingNotification];
751		    }
752		}
753	      else
754		// we're reducing the selection
755		{
756		  BOOL notified = NO;
757
758		  for (i = _oldRow; i < _currentRow; i++)
759		    {
760		      if ([tv _unselectRow: i])
761			{
762			  notified = YES;
763			}
764		    }
765		  if (*_selectedRow == -1)
766		    {
767		      NSUInteger first = [_selectedRows firstIndex];
768
769		      if (first == NSNotFound)
770			{
771			  *_selectedRow = -1;
772			}
773		      else
774			{
775			  *_selectedRow = first;
776			}
777		    }
778		  if (notified == YES)
779		    {
780		      [tv _postSelectionIsChangingNotification];
781		    }
782		}
783	    }
784	  else if (oldDiff <= 0 && newDiff >= 0)
785	    {
786	      BOOL notified = NO;
787
788	      // we're reducing the selection
789	      for (i = _oldRow; i < _originalRow; i++)
790	        {
791		  if ([tv _unselectRow: i])
792		    {
793		      notified = YES;
794		    }
795		}
796	      // then we're extending it
797	      for (i = _originalRow + 1; i <= _currentRow; i++)
798	        {
799		  if ([tv _selectRow: i])
800		    {
801		      notified = YES;
802		    }
803		}
804
805	      if (*_selectedRow == -1)
806		{
807		  NSUInteger first = [_selectedRows firstIndex];
808
809		  if (first == NSNotFound)
810		    {
811		      *_selectedRow = -1;
812		    }
813		  else
814		    {
815		      *_selectedRow = first;
816		    }
817		}
818	      if (notified == YES)
819		{
820		  [tv _postSelectionIsChangingNotification];
821		}
822	    }
823	  else if (oldDiff >= 0 && newDiff <= 0)
824	    {
825	      BOOL notified = NO;
826
827	      // we're reducing the selection
828	      for (i = _oldRow; i > _originalRow; i--)
829	        {
830		  if ([tv _unselectRow: i])
831		    {
832		      notified = YES;
833		    }
834		}
835	      // then we're extending it
836	      for (i = _originalRow - 1; i >= _currentRow; i--)
837	        {
838		  if ([tv _selectRow: i])
839		    {
840		      notified = YES;
841		    }
842		}
843
844	      if (*_selectedRow == -1)
845		{
846		  NSUInteger last = [_selectedRows lastIndex];
847
848		  if (last == NSNotFound)
849		    {
850		      *_selectedRow = -1;
851		    }
852		  else
853		    {
854		      *_selectedRow = last;
855		    }
856		}
857	      if (notified == YES)
858		{
859		  [tv _postSelectionIsChangingNotification];
860		}
861	    }
862	}
863    }
864  else if (((selectionMode & ALLOWS_MULTIPLE)
865	    && ((selectionMode & SHIFT_DOWN) == 0)
866	    && ((selectionMode & ALLOWS_EMPTY) == 0)
867	    && (selectionMode & ADDING_ROW))
868	   // the following case can be assimilated to the
869	   // one before, although it will lead to an
870	   // extra redraw
871	   // TODO: solve this issue
872	   ||
873	   ((selectionMode & ALLOWS_MULTIPLE)
874	    && ((selectionMode & SHIFT_DOWN) == 0)
875	    && ((selectionMode & ALLOWS_EMPTY) == 0)
876	    && ((selectionMode & ADDING_ROW) == 0))
877	)
878    {
879      if (_oldRow == -1)
880	{
881	  // if we can select the _originalRow, we'll clear the old selection
882	  // else we'll add to the old selection
883	  if ([tv _shouldSelectRow: _currentRow] == YES)
884	    {
885	      // let's clear the old selection
886	      // this code is copied from another case
887	      // (AM = 1, SD=0, AE=1, AR=*, first pass)
888	      NSInteger diff, i;
889	      NSUInteger count = [_selectedRows count];
890	      BOOL notified = NO;
891	      diff = _currentRow - _originalRow;
892
893	      if (count > 0)
894		{
895		  notified = YES;
896		}
897
898	      [tv _unselectAllRows];
899
900	      if (diff >= 0)
901		{
902		  for (i = _originalRow; i <= _currentRow; i++)
903		    {
904		      if ([tv _selectRow: i])
905		        {
906			  notified = YES;
907			}
908		    }
909		}
910	      else
911		{
912		  for (i = _originalRow; i >= _currentRow; i--)
913		    {
914		      if ([tv _selectRow: i])
915		        {
916			  notified = YES;
917			}
918		    }
919		}
920              if (notified == YES)
921                {
922                  [tv _postSelectionIsChangingNotification];
923                }
924	    }
925	  else
926	    {
927	      // let's add to the old selection
928	      // this code is copied from another case
929	      // (AM=1, SD=1, AE=*, AR=1)
930	      NSInteger diff, i;
931	      BOOL notified = NO;
932	      diff = _currentRow - _originalRow;
933
934	      if (diff >= 0)
935		{
936		  for (i = _originalRow; i <= _currentRow; i++)
937		    {
938		      if ([_selectedRows containsIndex: i] ||
939			  [tv _selectRow: i])
940			{
941			  *_selectedRow = i;
942			  notified = YES;
943			}
944		    }
945		}
946	      else
947		{
948		  // this case does happen (sometimes)
949		  for (i = _originalRow; i >= _currentRow; i--)
950		    {
951		      if ([_selectedRows containsIndex: i] ||
952			  [tv _selectRow: i])
953			{
954			  *_selectedRow = i;
955			  notified = YES;
956			}
957		    }
958		}
959	      if (notified == YES)
960		{
961		  [tv _postSelectionIsChangingNotification];
962		}
963	    }
964	}
965      else if ([_selectedRows containsIndex: _originalRow])
966	// as the originalRow is selected,
967	// we are in a new selection
968	{
969	  // this code is copied from another case
970	  // (AM=1, SD=0, AE=1, AR=*, after first pass)
971	  NSInteger oldDiff, newDiff, i;
972	  oldDiff = _oldRow - _originalRow;
973	  newDiff = _currentRow - _originalRow;
974	  if (oldDiff >= 0 && newDiff >= 0)
975	    {
976	      if (newDiff >= oldDiff)
977		{
978		  BOOL notified = NO;
979		  for (i = _oldRow + 1; i <= _currentRow; i++)
980		    {
981		      if ([tv _selectRow: i])
982		        {
983			  notified = YES;
984			}
985		    }
986		  if (notified == YES)
987		    {
988		      [tv _postSelectionIsChangingNotification];
989		    }
990		}
991	      else
992		{
993		  BOOL notified = NO;
994
995		  for (i = _oldRow; i > _currentRow; i--)
996		    {
997		      if ([tv _unselectRow: i])
998		        {
999			  notified = YES;
1000			}
1001		    }
1002		  if (*_selectedRow == -1)
1003		    {
1004		      NSUInteger last = [_selectedRows lastIndex];
1005
1006		      if (last == NSNotFound)
1007		        {
1008			  *_selectedRow = -1;
1009			}
1010		      else
1011		        {
1012			  *_selectedRow = last;
1013			}
1014		    }
1015		  if (notified == YES)
1016		    {
1017		      [tv _postSelectionIsChangingNotification];
1018		    }
1019		}
1020	    }
1021	  else if (oldDiff <= 0 && newDiff <= 0)
1022	    {
1023	      if (newDiff <= oldDiff)
1024		// we're extending the selection
1025		{
1026		  BOOL notified = NO;
1027
1028		  for (i = _oldRow - 1; i >= _currentRow; i--)
1029		    {
1030		      if ([tv _selectRow: i])
1031		        {
1032			  notified = YES;
1033			}
1034		    }
1035		  if (notified == YES)
1036		    {
1037		      [tv _postSelectionIsChangingNotification];
1038		    }
1039		}
1040	      else
1041		// we're reducing the selection
1042		{
1043		  BOOL notified = NO;
1044
1045		  for (i = _oldRow; i < _currentRow; i++)
1046		    {
1047		      if ([tv _unselectRow: i])
1048		        {
1049			  notified = YES;
1050			}
1051		    }
1052
1053		  if (*_selectedRow == -1)
1054		    {
1055		      NSUInteger first = [_selectedRows firstIndex];
1056
1057		      if (first == NSNotFound)
1058			{
1059			  *_selectedRow = -1;
1060			}
1061		      else
1062			{
1063			  *_selectedRow = first;
1064			}
1065		    }
1066		  if (notified == YES)
1067		    {
1068		      [tv _postSelectionIsChangingNotification];
1069		    }
1070		}
1071	    }
1072	  else if (oldDiff <= 0 && newDiff >= 0)
1073	    {
1074	      BOOL notified = NO;
1075
1076	      // we're reducing the selection
1077	      for (i = _oldRow; i < _originalRow; i++)
1078	        {
1079		  if ([tv _unselectRow: i])
1080		    {
1081		      notified = YES;
1082		    }
1083		}
1084	      // then we're extending it
1085	      for (i = _originalRow + 1; i <= _currentRow; i++)
1086	        {
1087		  if ([tv _selectRow: i])
1088		    {
1089		      notified = YES;
1090		    }
1091		}
1092
1093	      if (*_selectedRow == -1)
1094		{
1095		  NSUInteger first = [_selectedRows firstIndex];
1096
1097		  if (first == NSNotFound)
1098		    {
1099		      *_selectedRow = -1;
1100		    }
1101		  else
1102		    {
1103		      *_selectedRow = first;
1104		    }
1105		}
1106	      if (notified == YES)
1107		{
1108		  [tv _postSelectionIsChangingNotification];
1109		}
1110	    }
1111	  else if (oldDiff >= 0 && newDiff <= 0)
1112	    {
1113	      BOOL notified = NO;
1114
1115	      // we're reducing the selection
1116	      for (i = _oldRow; i > _originalRow; i--)
1117	        {
1118		  if ([tv _unselectRow: i])
1119		    {
1120		      notified = YES;
1121		    }
1122		}
1123	      // then we're extending it
1124	      for (i = _originalRow - 1; i >= _currentRow; i--)
1125	        {
1126		  if ([tv _selectRow: i])
1127		    {
1128		      notified = YES;
1129		    }
1130		}
1131
1132	      if (*_selectedRow == -1)
1133		{
1134		  NSUInteger last = [_selectedRows lastIndex];
1135
1136		  if (last == NSNotFound)
1137		    {
1138		      *_selectedRow = -1;
1139		    }
1140		  else
1141		    {
1142		      *_selectedRow = last;
1143		    }
1144		}
1145	      if (notified == YES)
1146		{
1147		  [tv _postSelectionIsChangingNotification];
1148		}
1149	    }
1150	}
1151      else
1152	// as the originalRow is not selection,
1153	// we are adding to the old selection
1154	{
1155	  // this code is copied from another case
1156	  // (AM=1, SD=1, AE=*, AR=1, after first pass)
1157	  NSInteger oldDiff, newDiff, i;
1158	  oldDiff = _oldRow - _originalRow;
1159	  newDiff = _currentRow - _originalRow;
1160
1161	  if (oldDiff >= 0 && newDiff >= 0)
1162	    {
1163	      if (newDiff >= oldDiff)
1164		// we're extending the selection
1165		{
1166		  BOOL notified = NO;
1167		  for (i = _oldRow + 1; i <= _currentRow; i++)
1168		    {
1169		      if ([_selectedRows containsIndex: i] ||
1170			  [tv _selectRow: i])
1171			{
1172			  *_selectedRow = i;
1173			  notified = YES;
1174			}
1175		    }
1176		  if (notified == YES)
1177		    {
1178		      [tv _postSelectionIsChangingNotification];
1179		    }
1180		}
1181	      else
1182		// we're reducing the selection
1183		{
1184		  BOOL notified = NO;
1185
1186		  for (i = _oldRow; i > _currentRow; i--)
1187		    {
1188		      if ([_oldSelectedRows containsIndex: i])
1189			{
1190			  // this row was in the old selection
1191			  // leave it selected
1192			  continue;
1193			}
1194
1195		      if ([tv _unselectRow: i])
1196		        {
1197			  notified = YES;
1198			}
1199		    }
1200
1201		  if (*_selectedRow == -1)
1202		    {
1203		      NSUInteger last = [_selectedRows lastIndex];
1204
1205		      if (last == NSNotFound)
1206		        {
1207			  *_selectedRow = -1;
1208			}
1209		      else
1210		        {
1211			  *_selectedRow = last;
1212			}
1213		    }
1214		  if (notified == YES)
1215		    {
1216		      [tv _postSelectionIsChangingNotification];
1217		    }
1218		}
1219	    }
1220	  else if (oldDiff <= 0 && newDiff <= 0)
1221	    {
1222	      if (newDiff <= oldDiff)
1223		// we're extending the selection
1224		{
1225		  BOOL notified = NO;
1226		  for (i = _oldRow - 1; i >= _currentRow; i--)
1227		    {
1228		      if ([_selectedRows containsIndex: i] ||
1229			  [tv _selectRow: i])
1230			{
1231			  *_selectedRow = i;
1232			  notified = YES;
1233			}
1234		    }
1235
1236		  if (notified == YES)
1237		    {
1238		      [tv _postSelectionIsChangingNotification];
1239		    }
1240		}
1241	      else
1242		// we're reducing the selection
1243		{
1244		  BOOL notified = NO;
1245
1246		  for (i = _oldRow; i < _currentRow; i++)
1247		    {
1248		      if ([_oldSelectedRows containsIndex: i])
1249			{
1250			  // this row was in the old selection
1251			  // leave it selected
1252			  continue;
1253			}
1254		      if ([tv _unselectRow: i])
1255		        {
1256			  notified = YES;
1257			}
1258		    }
1259		  if (*_selectedRow == -1)
1260		    {
1261		      NSUInteger first = [_selectedRows firstIndex];
1262
1263		      if (first == NSNotFound)
1264			{
1265			  *_selectedRow = -1;
1266			}
1267		      else
1268			{
1269			  *_selectedRow = first;
1270			}
1271		    }
1272		  if (notified == YES)
1273		    {
1274		      [tv _postSelectionIsChangingNotification];
1275		    }
1276		}
1277	    }
1278	  else if (oldDiff <= 0 && newDiff >= 0)
1279	    {
1280	      BOOL notified = NO;
1281
1282	      // we're reducing the selection
1283	      for (i = _oldRow; i < _originalRow; i++)
1284	        {
1285		  if ([_oldSelectedRows containsIndex: i])
1286		    {
1287		      // this row was in the old selection
1288		      // leave it selected
1289		      continue;
1290		    }
1291		  if ([tv _unselectRow: i])
1292		    {
1293		      notified = YES;
1294		    }
1295		}
1296	      // then we're extending it
1297	      for (i = _originalRow + 1; i <= _currentRow; i++)
1298	        {
1299		  if ([_selectedRows containsIndex: i] ||
1300		      [tv _selectRow: i])
1301		    {
1302		      *_selectedRow = i;
1303		      notified = YES;
1304		    }
1305		}
1306
1307	      if (*_selectedRow == -1)
1308		{
1309		  NSUInteger first = [_selectedRows firstIndex];
1310
1311		  if (first == NSNotFound)
1312		    {
1313		      *_selectedRow = -1;
1314		    }
1315		  else
1316		    {
1317		      *_selectedRow = first;
1318		    }
1319		}
1320	      if (notified == YES)
1321		{
1322		  [tv _postSelectionIsChangingNotification];
1323		}
1324	    }
1325	  else if (oldDiff >= 0 && newDiff <= 0)
1326	    {
1327	      BOOL notified = NO;
1328
1329	      // we're reducing the selection
1330	      for (i = _oldRow; i > _originalRow; i--)
1331	        {
1332		  if ([_oldSelectedRows containsIndex: i])
1333		    {
1334		      // this row was in the old selection
1335		      // leave it selected
1336		      continue;
1337		    }
1338
1339		  if ([tv _unselectRow: i])
1340		    {
1341		      notified = YES;
1342		    }
1343		}
1344
1345	      // then we're extending it
1346	      for (i = _originalRow - 1; i >= _currentRow; i--)
1347	        {
1348		  if ([_selectedRows containsIndex: i] ||
1349		      [tv _selectRow: i])
1350		    {
1351		      *_selectedRow = i;
1352		      notified = YES;
1353		    }
1354		}
1355
1356	      if (*_selectedRow == -1)
1357		{
1358		  NSUInteger last = [_selectedRows lastIndex];
1359
1360		  if (last == NSNotFound)
1361		    {
1362		      *_selectedRow = -1;
1363		    }
1364		  else
1365		    {
1366		      *_selectedRow = last;
1367		    }
1368		}
1369	      if (notified == YES)
1370		{
1371		  [tv _postSelectionIsChangingNotification];
1372		}
1373	    }
1374	}
1375    }
1376  else if ((selectionMode & ALLOWS_MULTIPLE)
1377	   && (selectionMode & SHIFT_DOWN)
1378	   && (selectionMode & ALLOWS_EMPTY)
1379	   && ((selectionMode & ADDING_ROW) == 0))
1380    {
1381      if (_oldRow == -1)
1382	// this is the first pass
1383	{
1384	  NSInteger diff, i;
1385	  BOOL notified = NO;
1386
1387      	  diff = _currentRow - _originalRow;
1388
1389	  if (diff >= 0)
1390	    {
1391	      for (i = _originalRow; i <= _currentRow; i++)
1392		{
1393		  if ([tv _unselectRow: i])
1394		    {
1395		      notified = YES;
1396		    }
1397		}
1398	      if (*_selectedRow == -1)
1399		{
1400		  NSUInteger first = [_selectedRows firstIndex];
1401
1402		  if (first == NSNotFound)
1403		    {
1404		      *_selectedRow = -1;
1405		    }
1406		  else
1407		    {
1408		      *_selectedRow = first;
1409		    }
1410		}
1411	    }
1412	  else
1413	    {
1414	      // this case does happen (sometimes)
1415	      for (i = _originalRow; i >= _currentRow; i--)
1416		{
1417		  if ([tv _unselectRow: i])
1418		    {
1419		      notified = YES;
1420		    }
1421		}
1422	      if (*_selectedRow == -1)
1423		{
1424		  NSUInteger first = [_selectedRows firstIndex];
1425
1426		  if (first == NSNotFound)
1427		    {
1428		      *_selectedRow = -1;
1429		    }
1430		  else
1431		    {
1432		      *_selectedRow = first;
1433		    }
1434		}
1435	    }
1436	  if (notified == YES)
1437	    {
1438	      [tv _postSelectionIsChangingNotification];
1439	    }
1440	}
1441      else // new multiple antiselection, after first pass
1442	{
1443	  NSInteger oldDiff, newDiff, i;
1444
1445	  oldDiff = _oldRow - _originalRow;
1446	  newDiff = _currentRow - _originalRow;
1447	  if (oldDiff >= 0 && newDiff >= 0)
1448	    {
1449	      if (newDiff >= oldDiff)
1450		// we're extending the antiselection
1451		{
1452		  BOOL notified = NO;
1453		  for (i = _oldRow + 1; i <= _currentRow; i++)
1454		    {
1455		      if ([tv _unselectRow: i])
1456			{
1457			  notified = YES;
1458			}
1459		    }
1460
1461		  if (*_selectedRow == -1)
1462		    {
1463			NSUInteger first = [_selectedRows firstIndex];
1464
1465			if (first == NSNotFound)
1466			  {
1467			    *_selectedRow = -1;
1468			  }
1469			else
1470			  {
1471			    *_selectedRow = first;
1472			  }
1473		    }
1474
1475		  if (notified == YES)
1476		    {
1477		      [tv _postSelectionIsChangingNotification];
1478		    }
1479		}
1480	      else
1481		// we're reducing the selection
1482		{
1483		  BOOL notified = NO;
1484
1485		  for (i = _oldRow; i > _currentRow; i--)
1486		    {
1487		      if ([_oldSelectedRows containsIndex: i])
1488			{
1489			  // this row was in the old selection
1490			  // select it
1491			  [tv setNeedsDisplayInRect: [tv rectOfRow: i]];
1492			  [_selectedRows addIndex: i];
1493			  *_selectedRow = i;
1494			  notified = YES;
1495			}
1496		    }
1497		  if (notified == YES)
1498		    {
1499		      [tv _postSelectionIsChangingNotification];
1500		    }
1501		}
1502	    }
1503	  else if (oldDiff <= 0 && newDiff <= 0)
1504	    {
1505	      if (newDiff <= oldDiff)
1506		// we're extending the selection
1507		{
1508		  BOOL notified = NO;
1509		  for (i = _oldRow - 1; i >= _currentRow; i--)
1510		    {
1511		      if ([tv _unselectRow: i])
1512			{
1513			  notified = YES;
1514			}
1515		    }
1516
1517		  if (*_selectedRow == -1)
1518		    {
1519		      NSUInteger first = [_selectedRows firstIndex];
1520
1521		      if (first == NSNotFound)
1522		        {
1523			  *_selectedRow = -1;
1524			}
1525		      else
1526		        {
1527			  *_selectedRow = first;
1528			}
1529		    }
1530
1531		  if (notified == YES)
1532		    {
1533		      [tv _postSelectionIsChangingNotification];
1534		    }
1535		}
1536	      else
1537		// we're reducing the selection
1538		{
1539		  BOOL notified = NO;
1540
1541		  for (i = _oldRow; i < _currentRow; i++)
1542		    {
1543		      if ([_oldSelectedRows containsIndex: i])
1544			{
1545			  // this row was in the old selection
1546			  // select it
1547			  [tv setNeedsDisplayInRect:
1548				[tv rectOfRow: i]];
1549			  [_selectedRows addIndex: i];
1550			  *_selectedRow = i;
1551			  notified = YES;
1552			}
1553		    }
1554
1555		  if (notified == YES)
1556		    {
1557		      [tv _postSelectionIsChangingNotification];
1558		    }
1559		}
1560	    }
1561	  else if (oldDiff <= 0 && newDiff >= 0)
1562	    {
1563	      BOOL notified = NO;
1564
1565	      // we're reducing the selection
1566	      {
1567		for (i = _oldRow; i < _originalRow; i++)
1568		  {
1569		    if ([_oldSelectedRows containsIndex: i])
1570		      {
1571			// this row was in the old selection
1572			// select it
1573			[tv setNeedsDisplayInRect:
1574			      [tv rectOfRow: i]];
1575			[_selectedRows addIndex: i];
1576			*_selectedRow = i;
1577			notified = YES;
1578		      }
1579		  }
1580	      }
1581	      // then we're extending it
1582	      {
1583		for (i = _originalRow + 1; i <= _currentRow; i++)
1584		  {
1585		    if ([tv _unselectRow: i])
1586		      {
1587			notified = YES;
1588		      }
1589		  }
1590	      }
1591
1592	      if (*_selectedRow == -1)
1593		{
1594		  NSUInteger first = [_selectedRows firstIndex];
1595
1596		  if (first == NSNotFound)
1597		    {
1598		      *_selectedRow = -1;
1599		    }
1600		  else
1601		    {
1602		      *_selectedRow = first;
1603		    }
1604		}
1605
1606	      if (notified == YES)
1607		{
1608		  [tv _postSelectionIsChangingNotification];
1609		}
1610	    }
1611	  else if (oldDiff >= 0 && newDiff <= 0)
1612	    {
1613	      BOOL notified = NO;
1614
1615	      // we're reducing the selection
1616	      {
1617		for (i = _oldRow; i > _originalRow; i--)
1618		  {
1619		    if ([_oldSelectedRows containsIndex: i])
1620		      {
1621			// this row was in the old selection
1622			// select it
1623			[tv setNeedsDisplayInRect:
1624			      [tv rectOfRow: i]];
1625			[_selectedRows addIndex: i];
1626			*_selectedRow = i;
1627			notified = YES;
1628		      }
1629		  }
1630	      }
1631	      // then we're extending it
1632	      {
1633		for (i = _originalRow - 1; i >= _currentRow; i--)
1634		  {
1635		    if ([tv _unselectRow: i])
1636		      {
1637			notified = YES;
1638		      }
1639		  }
1640	      }
1641
1642	      if (*_selectedRow == -1)
1643		{
1644		  NSUInteger first = [_selectedRows firstIndex];
1645
1646		  if (first == NSNotFound)
1647		    {
1648		      *_selectedRow = -1;
1649		    }
1650		  else
1651		    {
1652		      *_selectedRow = first;
1653		    }
1654		}
1655
1656	      if (notified == YES)
1657		{
1658		  [tv _postSelectionIsChangingNotification];
1659		}
1660	    }
1661	}
1662    }
1663  else if ((selectionMode & ALLOWS_MULTIPLE)
1664	   && (selectionMode & SHIFT_DOWN)
1665	   && ((selectionMode & ALLOWS_EMPTY) == 0)
1666	   && ((selectionMode & ADDING_ROW) == 0))
1667    {
1668      if (_oldRow == -1)
1669	// this is the first pass
1670	{
1671	  NSInteger diff, i;
1672	  NSUInteger count = [_selectedRows count];
1673	  BOOL notified = NO;
1674      	  diff = _currentRow - _originalRow;
1675
1676	  if (diff >= 0)
1677	    {
1678	      for (i = _originalRow; i <= _currentRow; i++)
1679		{
1680		  if ((count > 1) && [tv  _unselectRow: i])
1681		    {
1682		      notified = YES;
1683		      count--;
1684		    }
1685		}
1686	      if (*_selectedRow == -1)
1687		{
1688		  NSUInteger first = [_selectedRows firstIndex];
1689
1690		  if (first == NSNotFound)
1691		    {
1692		      NSLog(@"error!");
1693		      *_selectedRow = -1;
1694		    }
1695		  else
1696		    {
1697		      *_selectedRow = first;
1698		    }
1699		}
1700	    }
1701	  else
1702	    {
1703	      // this case does happen (sometimes)
1704	      for (i = _originalRow; i >= _currentRow; i--)
1705		{
1706		  if ((count > 1) && [tv  _unselectRow: i])
1707		    {
1708		      notified = YES;
1709		      count--;
1710		    }
1711		}
1712	      if (*_selectedRow == -1)
1713		{
1714		  NSUInteger first = [_selectedRows firstIndex];
1715
1716		  if (first == NSNotFound)
1717		    {
1718		      NSLog(@"error!");
1719		      *_selectedRow = -1;
1720		    }
1721		  else
1722		    {
1723		      *_selectedRow = first;
1724		    }
1725		}
1726	    }
1727	  if (notified == YES)
1728	    {
1729	      [tv _postSelectionIsChangingNotification];
1730	    }
1731	}
1732      else // new multiple antiselection, after first pass
1733	{
1734	  NSInteger oldDiff, newDiff, i;
1735	  NSUInteger count = [_selectedRows count];
1736	  oldDiff = _oldRow - _originalRow;
1737	  newDiff = _currentRow - _originalRow;
1738	  if (oldDiff >= 0 && newDiff >= 0)
1739	    {
1740	      if (newDiff >= oldDiff)
1741		// we're extending the antiselection
1742		{
1743		  BOOL notified = NO;
1744		  for (i = _oldRow + 1; i <= _currentRow; i++)
1745		    {
1746		      if ((count > 1) && [tv  _unselectRow: i])
1747		        {
1748			  notified = YES;
1749			  count--;
1750			}
1751		    }
1752
1753		  if (*_selectedRow == -1)
1754		    {
1755		      NSUInteger first = [_selectedRows firstIndex];
1756
1757		      if (first == NSNotFound)
1758		        {
1759			  NSLog(@"error!");
1760			  *_selectedRow = -1;
1761			}
1762		      else
1763		        {
1764			  *_selectedRow = first;
1765			}
1766		    }
1767
1768		  if (notified == YES)
1769		    {
1770		      [tv _postSelectionIsChangingNotification];
1771		    }
1772		}
1773	      else
1774		// we're reducing the selection
1775		{
1776		  BOOL notified = NO;
1777
1778		  for (i = _oldRow; i > _currentRow; i--)
1779		    {
1780		      if (([_oldSelectedRows containsIndex: i]))
1781			{
1782			  // this row was in the old selection
1783			  // select it
1784			  if ([tv _selectUnselectedRow: i])
1785			    {
1786			      notified = YES;
1787			    }
1788			}
1789		    }
1790		  if (notified == YES)
1791		    {
1792		      [tv _postSelectionIsChangingNotification];
1793		    }
1794		}
1795	    }
1796	  else if (oldDiff <= 0 && newDiff <= 0)
1797	    {
1798	      if (newDiff <= oldDiff)
1799		// we're extending the selection
1800		{
1801		  BOOL notified = NO;
1802		  for (i = _oldRow - 1; i >= _currentRow; i--)
1803		    {
1804		      if ((count > 1) && [tv  _unselectRow: i])
1805		        {
1806			  notified = YES;
1807			  count--;
1808			}
1809		    }
1810
1811		  if (*_selectedRow == -1)
1812		    {
1813		      NSUInteger first = [_selectedRows firstIndex];
1814
1815		      if (first == NSNotFound)
1816		        {
1817			  NSLog(@"error!");
1818			  *_selectedRow = -1;
1819			}
1820		      else
1821		        {
1822			  *_selectedRow = first;
1823			}
1824		    }
1825
1826		  if (notified == YES)
1827		    {
1828		      [tv _postSelectionIsChangingNotification];
1829		    }
1830		}
1831	      else
1832		// we're reducing the selection
1833		{
1834		  BOOL notified = NO;
1835
1836		  for (i = _oldRow; i < _currentRow; i++)
1837		    {
1838		      if ([_oldSelectedRows containsIndex: i])
1839			{
1840			  // this row was in the old selection
1841			  // select it
1842			  if ([tv _selectUnselectedRow: i])
1843			    {
1844			      notified = YES;
1845			    }
1846			  count++;
1847			}
1848		    }
1849
1850		  if (notified == YES)
1851		    {
1852		      [tv _postSelectionIsChangingNotification];
1853		    }
1854		}
1855	    }
1856	  else if (oldDiff <= 0 && newDiff >= 0)
1857	    {
1858	      BOOL notified = NO;
1859
1860	      // we're reducing the selection
1861	      {
1862		for (i = _oldRow; i < _originalRow; i++)
1863		  {
1864		    if ([_oldSelectedRows containsIndex: i])
1865		      {
1866			// this row was in the old selection
1867			// select it
1868			if ([tv _selectUnselectedRow: i])
1869			  {
1870			    notified = YES;
1871			  }
1872		      }
1873		  }
1874	      }
1875	      // then we're extending it
1876	      {
1877		for (i = _originalRow + 1; i <= _currentRow; i++)
1878		  {
1879		    if ((count > 1) && [tv  _unselectRow: i])
1880		      {
1881			notified = YES;
1882			count--;
1883		      }
1884		  }
1885	      }
1886
1887	      if (*_selectedRow == -1)
1888		{
1889		  NSUInteger first = [_selectedRows firstIndex];
1890
1891		  if (first == NSNotFound)
1892		    {
1893		      NSLog(@"error!");
1894		      *_selectedRow = -1;
1895		    }
1896		  else
1897		    {
1898		      *_selectedRow = first;
1899		    }
1900		}
1901
1902	      if (notified == YES)
1903		{
1904		  [tv _postSelectionIsChangingNotification];
1905		}
1906	    }
1907	  else if (oldDiff >= 0 && newDiff <= 0)
1908	    {
1909	      BOOL notified = NO;
1910
1911	      // we're reducing the selection
1912	      {
1913		for (i = _oldRow; i > _originalRow; i--)
1914		  {
1915		    if ([_oldSelectedRows containsIndex: i])
1916		      {
1917			// this row was in the old selection
1918			// select it
1919			if ([tv _selectUnselectedRow: i])
1920			  {
1921			    notified = YES;
1922			  }
1923		      }
1924		  }
1925	      }
1926	      // then we're extending it
1927	      {
1928		for (i = _originalRow - 1; i >= _currentRow; i--)
1929		  {
1930		    if ((count > 1) && [tv  _unselectRow: i])
1931		      {
1932			notified = YES;
1933			count--;
1934		      }
1935		  }
1936	      }
1937
1938	      if (*_selectedRow == -1)
1939		{
1940		  NSUInteger first = [_selectedRows firstIndex];
1941
1942		  if (first == NSNotFound)
1943		    {
1944		      NSLog(@"error!");
1945		      *_selectedRow = -1;
1946		    }
1947		  else
1948		    {
1949		      *_selectedRow = first;
1950		    }
1951		}
1952
1953	      if (notified == YES)
1954		{
1955		  [tv _postSelectionIsChangingNotification];
1956		}
1957	    }
1958	}
1959    }
1960}
1961
1962@interface GSTableCornerView : NSView
1963{}
1964@end
1965
1966@implementation GSTableCornerView
1967
1968- (BOOL) isFlipped
1969{
1970  return YES;
1971}
1972
1973- (void) drawRect: (NSRect)aRect
1974{
1975  [[GSTheme theme] drawTableCornerView: self withClip: aRect];
1976}
1977
1978@end
1979
1980@interface NSTableView (TableViewInternalPrivate)
1981- (void) _setSelectingColumns: (BOOL)flag;
1982- (BOOL) _editNextEditableCellAfterRow: (NSInteger)row
1983				column: (NSInteger)column;
1984- (BOOL) _editPreviousEditableCellBeforeRow: (NSInteger)row
1985				     column: (NSInteger)column;
1986- (void) _editNextCellAfterRow:(NSInteger)row inColumn:(NSInteger)column;
1987- (void) _autosaveTableColumns;
1988- (void) _autoloadTableColumns;
1989@end
1990
1991
1992@implementation NSTableView
1993
1994+ (void) initialize
1995{
1996  if (self == [NSTableView class])
1997    {
1998      [self setVersion: currentVersion];
1999      nc = [NSNotificationCenter defaultCenter];
2000      // FIXME
2001      [self exposeBinding: NSContentBinding];
2002      [self exposeBinding: NSSelectionIndexesBinding];
2003      [self exposeBinding: NSSortDescriptorsBinding];
2004    }
2005}
2006
2007/*
2008 * Initializing/Releasing
2009 */
2010
2011- (void) _initDefaults
2012{
2013  _isValidating     = NO;
2014  _drawsGrid        = YES;
2015  _rowHeight        = 16.0;
2016  _intercellSpacing = NSMakeSize (5.0, 2.0);
2017  ASSIGN(_selectedColumns, [NSMutableIndexSet indexSet]);
2018  ASSIGN(_selectedRows, [NSMutableIndexSet indexSet]);
2019  _allowsEmptySelection = YES;
2020  _allowsMultipleSelection = NO;
2021  _allowsColumnSelection = YES;
2022  _allowsColumnResizing = YES;
2023  _allowsColumnReordering = YES;
2024  _autoresizesAllColumnsToFit = NO;
2025  _selectingColumns = NO;
2026  _verticalMotionDrag = NO;
2027  _editedColumn = -1;
2028  _editedRow = -1;
2029  _clickedRow = -1;
2030  _clickedColumn = -1;
2031  _selectedColumn = -1;
2032  _selectedRow = -1;
2033  _highlightedTableColumn = nil;
2034  _draggingSourceOperationMaskForLocal = NSDragOperationCopy
2035      | NSDragOperationLink | NSDragOperationGeneric | NSDragOperationPrivate;
2036  _draggingSourceOperationMaskForRemote = NSDragOperationNone;
2037  ASSIGN(_sortDescriptors, [NSArray array]);
2038}
2039
2040- (id) initWithFrame: (NSRect)frameRect
2041{
2042  self = [super initWithFrame: frameRect];
2043  if (!self)
2044    return self;
2045
2046  [self _initDefaults];
2047  ASSIGN(_gridColor, [NSColor gridColor]);
2048  ASSIGN(_backgroundColor, [NSColor controlBackgroundColor]);
2049  ASSIGN(_tableColumns, [NSMutableArray array]);
2050
2051  _headerView = [NSTableHeaderView new];
2052  [_headerView setFrameSize: NSMakeSize (frameRect.size.width, 22.0)];
2053  [_headerView setTableView: self];
2054  _cornerView = [GSTableCornerView new];
2055  [self tile];
2056  return self;
2057}
2058
2059- (void) dealloc
2060{
2061  [self abortEditing];
2062
2063  RELEASE (_gridColor);
2064  RELEASE (_backgroundColor);
2065  RELEASE (_tableColumns);
2066  RELEASE (_selectedColumns);
2067  RELEASE (_selectedRows);
2068  RELEASE (_sortDescriptors);
2069  TEST_RELEASE (_headerView);
2070  TEST_RELEASE (_cornerView);
2071  if (_autosaveTableColumns == YES)
2072    {
2073      [nc removeObserver: self
2074	  name: NSTableViewColumnDidResizeNotification
2075	  object: self];
2076    }
2077  TEST_RELEASE (_autosaveName);
2078  if (_numberOfColumns > 0)
2079    {
2080      NSZoneFree (NSDefaultMallocZone (), _columnOrigins);
2081    }
2082  if (_delegate != nil)
2083    {
2084      [nc removeObserver: _delegate  name: nil  object: self];
2085      _delegate = nil;
2086    }
2087  [super dealloc];
2088}
2089
2090- (BOOL) isFlipped
2091{
2092  return YES;
2093}
2094
2095/*
2096 * Table Dimensions
2097 */
2098
2099- (NSInteger) numberOfColumns
2100{
2101  return _numberOfColumns;
2102}
2103
2104- (NSInteger) numberOfRows
2105{
2106  return [self _numRows];
2107}
2108
2109/*
2110 * Columns
2111 */
2112
2113- (void) addTableColumn: (NSTableColumn *)aColumn
2114{
2115  [aColumn setTableView: self];
2116  [_tableColumns addObject: aColumn];
2117  _numberOfColumns++;
2118  if (_numberOfColumns > 1)
2119    {
2120      _columnOrigins = NSZoneRealloc (NSDefaultMallocZone (), _columnOrigins,
2121				      (sizeof (CGFloat)) * _numberOfColumns);
2122    }
2123  else
2124    {
2125      _columnOrigins = NSZoneMalloc (NSDefaultMallocZone (), sizeof (CGFloat));
2126    }
2127  [self tile];
2128}
2129
2130- (void) removeTableColumn: (NSTableColumn *)aColumn
2131{
2132  NSInteger columnIndex = [self columnWithIdentifier: [aColumn identifier]];
2133
2134  if (columnIndex == -1)
2135    {
2136      NSLog (@"Warning: Tried to remove not-existent column from table");
2137      return;
2138    }
2139
2140  /* Remove selection on this column */
2141  [self deselectColumn: columnIndex];
2142  /* Shift column indexes on the right by one */
2143  if (_selectedColumn > columnIndex)
2144    {
2145      _selectedColumn--;
2146    }
2147
2148  [_selectedColumns removeIndex: columnIndex];
2149
2150  /* Now really remove the column */
2151
2152  /* NB: Set table view to nil before removing the column from the
2153     array, because removing it from the array could deallocate it !  */
2154  [aColumn setTableView: nil];
2155  [_tableColumns removeObject: aColumn];
2156  _numberOfColumns--;
2157  if (_numberOfColumns > 0)
2158    {
2159      _columnOrigins = NSZoneRealloc (NSDefaultMallocZone (), _columnOrigins,
2160				      (sizeof (CGFloat)) * _numberOfColumns);
2161    }
2162  else
2163    {
2164      NSZoneFree (NSDefaultMallocZone (), _columnOrigins);
2165    }
2166  [self tile];
2167}
2168
2169- (void) moveColumn: (NSInteger)columnIndex toColumn: (NSInteger)newIndex
2170{
2171  /* The range of columns which need to be shifted,
2172     extremes included */
2173  NSInteger minRange, maxRange;
2174  /* Amount of shift for these columns */
2175  NSInteger shift;
2176  BOOL selected = NO;
2177
2178  if ((columnIndex < 0) || (columnIndex > (_numberOfColumns - 1)))
2179    {
2180      NSLog (@"Attempt to move column outside table");
2181      return;
2182    }
2183  if ((newIndex < 0) || (newIndex > (_numberOfColumns - 1)))
2184    {
2185      NSLog (@"Attempt to move column to outside table");
2186      return;
2187    }
2188
2189  if (columnIndex == newIndex)
2190    return;
2191
2192  if (columnIndex > newIndex)
2193    {
2194      minRange = newIndex;
2195      maxRange = columnIndex - 1;
2196      shift = +1;
2197    }
2198  else // columnIndex < newIndex
2199    {
2200      minRange = columnIndex + 1;
2201      maxRange = newIndex;
2202      shift = -1;
2203    }
2204
2205  /* Rearrange selection */
2206  if (_selectedColumn == columnIndex)
2207    {
2208      _selectedColumn = newIndex;
2209    }
2210  else if ((_selectedColumn >= minRange) && (_selectedColumn <= maxRange))
2211    {
2212      _selectedColumn += shift;
2213    }
2214
2215  if ([_selectedColumns containsIndex: columnIndex])
2216    {
2217      selected = YES;
2218    }
2219  [_selectedColumns shiftIndexesStartingAtIndex: columnIndex + 1 by: -1];
2220  [_selectedColumns shiftIndexesStartingAtIndex: newIndex by: 1];
2221  if (selected)
2222    {
2223      [_selectedColumns addIndex: newIndex];
2224    }
2225
2226  /* Update edited cell */
2227  if (_editedColumn == columnIndex)
2228    {
2229      _editedColumn = newIndex;
2230    }
2231  else if ((_editedColumn >= minRange) && (_editedColumn <= maxRange))
2232    {
2233      _editedColumn += shift;
2234    }
2235
2236  /* Now really move the column */
2237  if (columnIndex < newIndex)
2238    {
2239      [_tableColumns insertObject: [_tableColumns objectAtIndex: columnIndex]
2240		     atIndex: newIndex + 1];
2241      [_tableColumns removeObjectAtIndex: columnIndex];
2242    }
2243  else
2244    {
2245      [_tableColumns insertObject: [_tableColumns objectAtIndex: columnIndex]
2246		     atIndex: newIndex];
2247      [_tableColumns removeObjectAtIndex: columnIndex + 1];
2248    }
2249  /* Tile */
2250  [self tile];
2251
2252  /* Post notification */
2253
2254  [self _postColumnDidMoveNotificationWithOldIndex: columnIndex
2255                                          newIndex: newIndex];
2256
2257  [self _autosaveTableColumns];
2258}
2259
2260- (NSArray *) tableColumns
2261{
2262  return AUTORELEASE ([_tableColumns mutableCopyWithZone:
2263				       NSDefaultMallocZone ()]);
2264}
2265
2266- (NSInteger) columnWithIdentifier: (id)identifier
2267{
2268  NSEnumerator	*enumerator = [_tableColumns objectEnumerator];
2269  NSTableColumn	*tb;
2270  NSInteger      return_value = 0;
2271
2272  while ((tb = [enumerator nextObject]) != nil)
2273    {
2274      // Also handle a nil identifier.
2275      if ((identifier == [tb identifier]) ||
2276          [[tb identifier] isEqual: identifier])
2277        return return_value;
2278      else
2279        return_value++;
2280    }
2281  return -1;
2282}
2283
2284- (NSTableColumn *) tableColumnWithIdentifier:(id)anObject
2285{
2286  NSInteger indexOfColumn = [self columnWithIdentifier: anObject];
2287
2288  if (indexOfColumn == -1)
2289    return nil;
2290  else
2291    return [_tableColumns objectAtIndex: indexOfColumn];
2292}
2293
2294/*
2295 * Data Source
2296 */
2297
2298- (id) dataSource
2299{
2300  return _dataSource;
2301}
2302
2303- (void) setDataSource: (id)anObject
2304{
2305  /* Used only for readability */
2306  const SEL sel_a = @selector (numberOfRowsInTableView:);
2307  const SEL sel_b = @selector (tableView:objectValueForTableColumn:row:);
2308  const SEL sel_c = @selector(tableView:setObjectValue:forTableColumn:row:);
2309  GSKeyValueBinding *theBinding;
2310
2311  // If we have content binding the data source is used only
2312  // like a delegate
2313  theBinding = [GSKeyValueBinding getBinding: NSContentBinding
2314                                  forObject: self];
2315  if (theBinding == nil)
2316    {
2317      if (anObject && [anObject respondsToSelector: sel_a] == NO)
2318        {
2319          [NSException
2320            raise: NSInternalInconsistencyException
2321            format: @"Data Source doesn't respond to numberOfRowsInTableView:"];
2322        }
2323
2324      if (anObject && [anObject respondsToSelector: sel_b] == NO)
2325        {
2326          /* This method isn't required.
2327             [NSException raise: NSInternalInconsistencyException
2328             format: @"Data Source doesn't respond to "
2329             @"tableView:objectValueForTableColumn:row:"];
2330          */
2331        }
2332    }
2333
2334  _dataSource_editable = [anObject respondsToSelector: sel_c];
2335
2336  /* We do *not* retain the dataSource, it's like a delegate */
2337  _dataSource = anObject;
2338
2339  [self tile];
2340  [self reloadData];
2341}
2342
2343/*
2344 * Loading data
2345 */
2346
2347- (void) reloadData
2348{
2349  [self noteNumberOfRowsChanged];
2350  [self setNeedsDisplay: YES];
2351}
2352
2353/*
2354 * Target-action
2355 */
2356
2357- (void) setAction: (SEL)aSelector
2358{
2359  _action = aSelector;
2360}
2361
2362- (SEL) action
2363{
2364  return _action;
2365}
2366
2367- (void) setDoubleAction: (SEL)aSelector
2368{
2369  _doubleAction = aSelector;
2370}
2371
2372- (SEL) doubleAction
2373{
2374  return _doubleAction;
2375}
2376
2377- (void) setTarget:(id)anObject
2378{
2379  _target = anObject;
2380}
2381
2382- (id) target
2383{
2384  return _target;
2385}
2386
2387- (NSInteger) clickedColumn
2388{
2389  return _clickedColumn;
2390}
2391
2392- (NSInteger) clickedRow
2393{
2394  return _clickedRow;
2395}
2396
2397/*
2398 * The NSTableHeaderView calls this method when it receives a double click.
2399 */
2400
2401- (void) _sendDoubleActionForColumn: (NSInteger)columnIndex
2402{
2403  _clickedColumn = columnIndex;
2404  _clickedRow = -1;
2405  [self sendAction: _doubleAction  to: _target];
2406}
2407
2408/*
2409 * And this when it gets a simple click which turns out to be for
2410 * selecting/deselecting a column.
2411 * We don't support subtracting a column from the selection (Cocoa doesn't
2412 * either).
2413 * However we support adding a distinct column with the control key (unlike
2414 * Cocoa where the user can only make column range selection).
2415 */
2416- (void) _selectColumn: (NSInteger)columnIndex
2417	     modifiers: (unsigned int)modifiers
2418{
2419  NSIndexSet *oldIndexes = [self selectedColumnIndexes];
2420  BOOL addRange = ((modifiers & NSShiftKeyMask)
2421    && _allowsMultipleSelection && [oldIndexes count] > 0);
2422  BOOL addSingle = ((modifiers & NSControlKeyMask)
2423    && _allowsMultipleSelection);
2424  BOOL shouldSelect = ([self _shouldSelectionChange]
2425    && [self _shouldSelectTableColumn: [_tableColumns objectAtIndex: columnIndex]]);
2426  NSIndexSet *newIndexes = [NSIndexSet indexSetWithIndex: columnIndex];
2427
2428  if (_allowsColumnSelection == NO || shouldSelect == NO)
2429    {
2430      return;
2431    }
2432
2433  if (_selectingColumns == NO)
2434    {
2435      [self _setSelectingColumns: YES];
2436    }
2437
2438  /* Single select has priority over range select when both modifiers are pressed */
2439  if (addSingle)
2440    {
2441      [self selectColumnIndexes: newIndexes byExtendingSelection: YES];
2442    }
2443  else if (addRange)
2444    {
2445      NSUInteger firstIndex = [oldIndexes firstIndex];
2446      NSUInteger lastIndex = [oldIndexes lastIndex];
2447      NSRange range;
2448
2449      /* We extend the selection to the left or the right of the last selected
2450         column. */
2451      if (columnIndex > [self selectedColumn])
2452        {
2453          lastIndex = columnIndex;
2454        }
2455      else
2456        {
2457          firstIndex = columnIndex;
2458        }
2459
2460      range = NSMakeRange(firstIndex, lastIndex - firstIndex + 1);
2461      newIndexes = [NSIndexSet indexSetWithIndexesInRange: range];
2462      [self selectColumnIndexes: newIndexes byExtendingSelection: YES];
2463    }
2464  else
2465    {
2466      [self selectColumnIndexes: newIndexes byExtendingSelection: NO];
2467    }
2468}
2469
2470
2471/*
2472 *Configuration
2473 */
2474
2475- (void) setAllowsColumnReordering: (BOOL)flag
2476{
2477  _allowsColumnReordering = flag;
2478}
2479
2480- (BOOL) allowsColumnReordering
2481{
2482  return _allowsColumnReordering;
2483}
2484
2485- (void) setAllowsColumnResizing: (BOOL)flag
2486{
2487  _allowsColumnResizing = flag;
2488}
2489
2490- (BOOL) allowsColumnResizing
2491{
2492  return _allowsColumnResizing;
2493}
2494
2495- (void) setAllowsMultipleSelection: (BOOL)flag
2496{
2497  _allowsMultipleSelection = flag;
2498}
2499
2500- (BOOL) allowsMultipleSelection
2501{
2502  return _allowsMultipleSelection;
2503}
2504
2505- (void) setAllowsEmptySelection: (BOOL)flag
2506{
2507  _allowsEmptySelection = flag;
2508}
2509
2510- (BOOL) allowsEmptySelection
2511{
2512  return _allowsEmptySelection;
2513}
2514
2515- (void) setAllowsColumnSelection: (BOOL)flag
2516{
2517  _allowsColumnSelection = flag;
2518}
2519
2520- (BOOL) allowsColumnSelection
2521{
2522  return _allowsColumnSelection;
2523}
2524
2525/*
2526 * Drawing Attributes
2527 */
2528
2529- (void) setIntercellSpacing: (NSSize)aSize
2530{
2531  _intercellSpacing = aSize;
2532  [self setNeedsDisplay: YES];
2533}
2534
2535- (NSSize) intercellSpacing
2536{
2537  return _intercellSpacing;
2538}
2539
2540- (void) setRowHeight: (CGFloat)rowHeight
2541{
2542  _rowHeight = rowHeight;
2543  [self tile];
2544}
2545
2546- (CGFloat) rowHeight
2547{
2548  return _rowHeight;
2549}
2550
2551- (void) setBackgroundColor: (NSColor *)aColor
2552{
2553  ASSIGN (_backgroundColor, aColor);
2554}
2555
2556- (NSColor *) backgroundColor
2557{
2558  return _backgroundColor;
2559}
2560
2561- (void) setUsesAlternatingRowBackgroundColors: (BOOL)useAlternatingRowColors
2562{
2563  // FIXME
2564}
2565
2566- (BOOL) usesAlternatingRowBackgroundColors
2567{
2568  // FIXME
2569  return NO;
2570}
2571
2572- (void)setSelectionHighlightStyle: (NSTableViewSelectionHighlightStyle)s
2573{
2574  // FIXME implement me really
2575  _selectionHighlightStyle = s;
2576  if (_selectionHighlightStyle == NSTableViewSelectionHighlightStyleSourceList)
2577    {
2578      // should also set draggingDestinationFeedbackStyle to NSTableViewDraggingDestinationFeedbackStyleSourceList
2579      // but we don't have it yet anyway
2580    }
2581}
2582
2583- (NSTableViewSelectionHighlightStyle) selectionHighlightStyle
2584{
2585  return _selectionHighlightStyle;
2586}
2587
2588/*
2589 * Selecting Columns and Rows
2590 */
2591- (void) selectColumn: (NSInteger)columnIndex
2592 byExtendingSelection: (BOOL)flag
2593{
2594  if (columnIndex < 0 || columnIndex > _numberOfColumns)
2595    {
2596      NSDebugLLog(@"NSTableView", @"Column index %d out of table in selectColumn", (int)columnIndex);
2597      return;
2598    }
2599
2600  _selectingColumns = YES;
2601
2602  if (flag == NO)
2603    {
2604      /* If the current selection is the one we want, just ends editing
2605       * This is not just a speed up, it prevents us from sending
2606       * a NSTableViewSelectionDidChangeNotification.
2607       * This behaviour is required by the specifications */
2608      if ([_selectedColumns count] == 1
2609	  && [_selectedColumns containsIndex: columnIndex] == YES)
2610	{
2611	  /* Stop editing if any */
2612	  if (_textObject != nil)
2613	    {
2614	      [self validateEditing];
2615	      [self abortEditing];
2616	    }
2617	  return;
2618	}
2619
2620      /* If _numberOfColumns == 1, we can skip trying to deselect the
2621	 only column - because we have been called to select it. */
2622      if (_numberOfColumns > 1)
2623	{
2624	  [self _unselectAllColumns];
2625	}
2626    }
2627  else // flag == YES
2628    {
2629      if (_allowsMultipleSelection == NO)
2630	{
2631	  [NSException raise: NSInternalInconsistencyException
2632		       format: @"Can not extend selection in table view when multiple selection is disabled"];
2633	}
2634    }
2635
2636  /* Stop editing if any */
2637  if (_textObject != nil)
2638    {
2639      [self validateEditing];
2640      [self abortEditing];
2641    }
2642
2643  /* Now select the column and post notification only if needed */
2644  if ([_selectedColumns containsIndex: columnIndex] == NO)
2645    {
2646      [_selectedColumns addIndex: columnIndex];
2647      _selectedColumn = columnIndex;
2648
2649      [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
2650      if (_headerView)
2651	{
2652	  [_headerView setNeedsDisplayInRect:
2653			 [_headerView headerRectOfColumn: columnIndex]];
2654	}
2655      [self _postSelectionDidChangeNotification];
2656    }
2657  else /* Otherwise simply change the last selected column */
2658    {
2659      _selectedColumn = columnIndex;
2660    }
2661}
2662
2663- (void) selectRow: (NSInteger)rowIndex
2664byExtendingSelection: (BOOL)flag
2665{
2666  if (rowIndex < 0 || rowIndex >= _numberOfRows)
2667    {
2668      NSDebugLLog(@"NSTableView", @"Row index %d out of table in selectRow", (int)rowIndex);
2669      return;
2670    }
2671
2672  if (_selectingColumns)
2673    {
2674      _selectingColumns = NO;
2675      if (_headerView)
2676	{
2677	  [_headerView setNeedsDisplay: YES];
2678	}
2679    }
2680
2681  if (flag == NO)
2682    {
2683      /* If the current selection is the one we want, just ends editing
2684       * This is not just a speed up, it prevents us from sending
2685       * a NSTableViewSelectionDidChangeNotification.
2686       * This behaviour is required by the specifications */
2687      if ([_selectedRows count] == 1
2688	  && [_selectedRows containsIndex: rowIndex] == YES)
2689	{
2690	  /* Stop editing if any */
2691	  if (_textObject != nil)
2692	    {
2693	      [self validateEditing];
2694	      [self abortEditing];
2695	    }
2696
2697	   /* reset the _clickedRow for keyboard navigation  */
2698	  _clickedRow = rowIndex;
2699	  return;
2700	}
2701
2702      /* If _numberOfRows == 1, we can skip trying to deselect the
2703	 only row - because we have been called to select it. */
2704      if (_numberOfRows > 1)
2705	{
2706	  [self _unselectAllRows];
2707	}
2708    }
2709  else // flag == YES
2710    {
2711      if (_allowsMultipleSelection == NO)
2712	{
2713	  [NSException raise: NSInternalInconsistencyException
2714		       format: @"Can not extend selection in table view when multiple selection is disabled"];
2715	}
2716    }
2717
2718  /* Stop editing if any */
2719  if (_textObject != nil)
2720    {
2721      [self validateEditing];
2722      [self abortEditing];
2723    }
2724
2725  /*
2726   * Now select the row and post notification only if needed
2727   * also update the _clickedRow for keyboard navigation.
2728   */
2729  if ([self _selectUnselectedRow: rowIndex])
2730    {
2731      _clickedRow = rowIndex;
2732      [self _postSelectionDidChangeNotification];
2733    }
2734  else /* Otherwise simply change the last selected row */
2735    {
2736      _selectedRow = rowIndex;
2737      _clickedRow = rowIndex;
2738    }
2739}
2740
2741- (void) selectColumnIndexes: (NSIndexSet *)indexes byExtendingSelection: (BOOL)extend
2742{
2743  BOOL empty = ([indexes firstIndex] == NSNotFound);
2744  BOOL changed = NO;
2745  NSUInteger col;
2746
2747  if (!_selectingColumns)
2748    {
2749      _selectingColumns = YES;
2750      if (_headerView)
2751	{
2752	  [_headerView setNeedsDisplay: YES];
2753	}
2754    }
2755
2756  /* Stop editing if any */
2757  if (_textObject != nil)
2758    {
2759      [self validateEditing];
2760      [self abortEditing];
2761    }
2762
2763  if (extend == NO)
2764    {
2765      /* If the current selection is the one we want, just ends editing
2766       * This is not just a speed up, it prevents us from sending
2767       * a NSTableViewSelectionDidChangeNotification.
2768       * This behaviour is required by the specifications */
2769      if ([_selectedColumns isEqual: indexes])
2770        {
2771	  if (!empty)
2772	    {
2773	      _selectedColumn = [indexes lastIndex];
2774	    }
2775	  return;
2776	}
2777
2778      [self _unselectAllColumns];
2779      changed = YES;
2780    }
2781
2782  if (!empty)
2783    {
2784      if ([indexes lastIndex] >= _numberOfColumns)
2785        {
2786	  [NSException raise: NSInvalidArgumentException
2787                      format: @"Column index out of table in selectColumn"];
2788	}
2789
2790      /* This check is not fully correct, as both sets may contain just
2791	 the same entry, but works according to the old specification. */
2792      if (_allowsMultipleSelection == NO &&
2793	  [_selectedColumns count] + [indexes count] > 1)
2794        {
2795	  [NSException raise: NSInternalInconsistencyException
2796                      format: @"Can not set multiple selection in table view when multiple selection is disabled"];
2797	}
2798
2799      col = [indexes firstIndex];
2800      while (col != NSNotFound)
2801        {
2802	  if (![_selectedColumns containsIndex: col])
2803	    {
2804	      [self setNeedsDisplayInRect: [self rectOfColumn: col]];
2805	      if (_headerView)
2806	        {
2807		  [_headerView setNeedsDisplayInRect:
2808				   [_headerView headerRectOfColumn: col]];
2809		}
2810	      changed = YES;
2811	    }
2812	  col = [indexes indexGreaterThanIndex: col];
2813	}
2814      [_selectedColumns addIndexes: indexes];
2815      _selectedColumn = [indexes lastIndex];
2816    }
2817
2818  if (changed)
2819    {
2820      [self _postSelectionDidChangeNotification];
2821    }
2822}
2823
2824- (void) selectRowIndexes: (NSIndexSet *)indexes byExtendingSelection: (BOOL)extend
2825{
2826  BOOL empty = ([indexes firstIndex] == NSNotFound);
2827  BOOL changed = NO;
2828  NSUInteger row;
2829
2830  if (_selectingColumns)
2831    {
2832      _selectingColumns = NO;
2833      if (_headerView)
2834	{
2835	  [_headerView setNeedsDisplay: YES];
2836	}
2837    }
2838
2839  /* Stop editing if any */
2840  if (_textObject != nil)
2841    {
2842      [self validateEditing];
2843      [self abortEditing];
2844    }
2845
2846  if (extend == NO)
2847    {
2848      /* If the current selection is the one we want, just ends editing
2849       * This is not just a speed up, it prevents us from sending
2850       * a NSTableViewSelectionDidChangeNotification.
2851       * This behaviour is required by the specifications */
2852      if ([_selectedRows isEqual: indexes])
2853        {
2854	  if (!empty)
2855	    {
2856	      _selectedRow = [indexes lastIndex];
2857	    }
2858	  return;
2859	}
2860
2861      [self _unselectAllRows];
2862      changed = YES;
2863    }
2864
2865  if (!empty)
2866    {
2867      if ([indexes lastIndex] >= _numberOfRows)
2868        {
2869	  [NSException raise: NSInvalidArgumentException
2870                      format: @"Row index out of table in selectRow"];
2871	}
2872
2873      /* This check is not fully correct, as both sets may contain just
2874	 the same entry, but works according to the old specification. */
2875      if (_allowsMultipleSelection == NO &&
2876	  [_selectedRows count] + [indexes count] > 1)
2877        {
2878	  [NSException raise: NSInternalInconsistencyException
2879		       format: @"Can not set multiple selection in table view when multiple selection is disabled"];
2880	}
2881
2882      row = [indexes firstIndex];
2883      while (row != NSNotFound)
2884        {
2885	  if (![_selectedRows containsIndex: row])
2886	    {
2887	      [self setNeedsDisplayInRect: [self rectOfRow: row]];
2888	    }
2889	  row = [indexes indexGreaterThanIndex: row];
2890	}
2891      [_selectedRows addIndexes: indexes];
2892      _selectedRow = [indexes lastIndex];
2893      changed = YES;
2894    }
2895
2896  if (changed)
2897    {
2898      [self _postSelectionDidChangeNotification];
2899    }
2900}
2901
2902- (NSIndexSet *) selectedColumnIndexes
2903{
2904  return [[_selectedColumns copy] autorelease];
2905}
2906
2907- (NSIndexSet *) selectedRowIndexes
2908{
2909  return [[_selectedRows copy] autorelease];
2910}
2911
2912- (void) deselectColumn: (NSInteger)columnIndex
2913{
2914  if ([_selectedColumns containsIndex: columnIndex] == NO)
2915    {
2916      return;
2917    }
2918
2919  /* Now by internal consistency we assume columnIndex is in fact a
2920     valid column index, since it was the index of a selected column */
2921
2922  if (_textObject != nil)
2923    {
2924      [self validateEditing];
2925      [self abortEditing];
2926    }
2927
2928  _selectingColumns = YES;
2929
2930  [_selectedColumns removeIndex: columnIndex];
2931
2932  if (_selectedColumn == columnIndex)
2933    {
2934      NSUInteger less = [_selectedColumns indexLessThanIndex: columnIndex];
2935      NSUInteger greater = [_selectedColumns indexGreaterThanIndex: columnIndex];
2936
2937      if (less == NSNotFound)
2938        {
2939	  if (greater == NSNotFound)
2940	    {
2941	      _selectedColumn = -1;
2942	    }
2943	  else
2944	    {
2945	      _selectedColumn = greater;
2946	    }
2947	}
2948      else if (greater == NSNotFound)
2949        {
2950	  _selectedColumn = less;
2951	}
2952      else if (columnIndex - less > greater - columnIndex)
2953        {
2954	  _selectedColumn = greater;
2955	}
2956      else
2957        {
2958	  _selectedColumn = less;
2959	}
2960    }
2961
2962  [self setNeedsDisplayInRect: [self rectOfColumn: columnIndex]];
2963  if (_headerView)
2964    {
2965      [_headerView setNeedsDisplayInRect:
2966		     [_headerView headerRectOfColumn: columnIndex]];
2967    }
2968
2969  [self _postSelectionDidChangeNotification];
2970}
2971
2972- (void) deselectRow: (NSInteger)rowIndex
2973{
2974  if ([_selectedRows containsIndex: rowIndex] == NO)
2975    {
2976      return;
2977    }
2978
2979  if (_textObject != nil)
2980    {
2981      [self validateEditing];
2982      [self abortEditing];
2983    }
2984
2985  _selectingColumns = NO;
2986
2987  [_selectedRows removeIndex: rowIndex];
2988
2989  if (_selectedRow == rowIndex)
2990    {
2991      NSUInteger less = [_selectedRows indexLessThanIndex: rowIndex];
2992      NSUInteger greater = [_selectedRows indexGreaterThanIndex: rowIndex];
2993
2994      if (less == NSNotFound)
2995        {
2996	  if (greater == NSNotFound)
2997	    {
2998	      _selectedRow = -1;
2999	    }
3000	  else
3001	    {
3002	      _selectedRow = greater;
3003	    }
3004	}
3005      else if (greater == NSNotFound)
3006        {
3007	  _selectedRow = less;
3008	}
3009      else if (rowIndex - less > greater - rowIndex)
3010        {
3011	  _selectedRow = greater;
3012	}
3013      else
3014        {
3015	  _selectedRow = less;
3016	}
3017    }
3018
3019  [self _postSelectionDidChangeNotification];
3020}
3021
3022- (NSInteger) numberOfSelectedColumns
3023{
3024  return [_selectedColumns count];
3025}
3026
3027- (NSInteger) numberOfSelectedRows
3028{
3029  return [_selectedRows count];
3030}
3031
3032- (NSInteger) selectedColumn
3033{
3034  return _selectedColumn;
3035}
3036
3037- (NSInteger) selectedRow
3038{
3039  return _selectedRow;
3040}
3041
3042- (BOOL) isColumnSelected: (NSInteger)columnIndex
3043{
3044  return [_selectedColumns containsIndex: columnIndex];
3045}
3046
3047- (BOOL) isRowSelected: (NSInteger)rowIndex
3048{
3049  return [_selectedRows containsIndex: rowIndex];
3050}
3051
3052- (NSEnumerator *) selectedColumnEnumerator
3053{
3054  return [[self _selectedColumArray] objectEnumerator];
3055}
3056
3057- (NSEnumerator *) selectedRowEnumerator
3058{
3059  return [[self _selectedRowArray] objectEnumerator];
3060}
3061
3062- (void) selectAll: (id) sender
3063{
3064  if (_allowsMultipleSelection == NO)
3065    return;
3066
3067  /* Ask the delegate if we can select all columns or rows */
3068  if (_selectingColumns == YES)
3069    {
3070      if ([_selectedColumns count] == (NSUInteger)_numberOfColumns)
3071	{
3072	  // Nothing to do !
3073	  return;
3074	}
3075
3076      {
3077	NSEnumerator *enumerator = [_tableColumns objectEnumerator];
3078	NSTableColumn *tb;
3079	while ((tb = [enumerator nextObject]) != nil)
3080	  {
3081	    if ([self _shouldSelectTableColumn: tb] == NO)
3082	      {
3083		return;
3084	      }
3085	  }
3086      }
3087    }
3088  else // selecting rows
3089    {
3090      if ([_selectedRows count] == (NSUInteger)_numberOfRows)
3091	{
3092	  // Nothing to do !
3093	  return;
3094	}
3095
3096      {
3097	NSInteger row;
3098
3099	for (row = 0; row < _numberOfRows; row++)
3100	  {
3101	    if ([self _shouldSelectRow: row] == NO)
3102	      return;
3103	  }
3104      }
3105    }
3106
3107  /* Stop editing if any */
3108  if (_textObject != nil)
3109    {
3110      [self validateEditing];
3111      [self abortEditing];
3112    }
3113
3114  /* Do the real selection */
3115  if (_selectingColumns == YES)
3116    {
3117      [_selectedColumns removeAllIndexes];
3118      [_selectedColumns addIndexesInRange: NSMakeRange(0, _numberOfColumns)];
3119    }
3120  else // selecting rows
3121    {
3122      [_selectedRows removeAllIndexes];
3123      [_selectedRows addIndexesInRange: NSMakeRange(0, _numberOfRows)];
3124    }
3125
3126  [self setNeedsDisplay: YES];
3127  [self _postSelectionDidChangeNotification];
3128}
3129
3130- (void) deselectAll: (id) sender
3131{
3132  if (_allowsEmptySelection == NO)
3133    return;
3134
3135  if ([self _shouldSelectionChange] == NO)
3136    {
3137      return;
3138    }
3139
3140  if (_textObject != nil)
3141    {
3142      [self validateEditing];
3143      [self abortEditing];
3144    }
3145
3146  if (([_selectedColumns count] > 0) || ([_selectedRows count] > 0))
3147    {
3148      [_selectedColumns removeAllIndexes];
3149      [_selectedRows removeAllIndexes];
3150      _selectedColumn = -1;
3151      _selectedRow = -1;
3152      _selectingColumns = NO;
3153      [self setNeedsDisplay: YES];
3154      [self _postSelectionDidChangeNotification];
3155    }
3156  else
3157    {
3158      _selectedColumn = -1;
3159      _selectedRow = -1;
3160      _selectingColumns = NO;
3161    }
3162}
3163
3164/*
3165 * Grid Drawing attributes
3166 */
3167
3168- (void) setDrawsGrid: (BOOL)flag
3169{
3170  _drawsGrid = flag;
3171}
3172
3173- (BOOL) drawsGrid
3174{
3175  return _drawsGrid;
3176}
3177
3178- (void) setGridColor: (NSColor *)aColor
3179{
3180  ASSIGN (_gridColor, aColor);
3181}
3182
3183- (NSColor *) gridColor
3184{
3185  return _gridColor;
3186}
3187
3188- (void) setGridStyleMask: (NSTableViewGridLineStyle)gridType
3189{
3190  // FIXME
3191}
3192
3193- (NSTableViewGridLineStyle) gridStyleMask
3194{
3195  // FIXME
3196  return 0;
3197}
3198
3199/*
3200 * Providing Cells
3201 */
3202
3203- (NSCell *) preparedCellAtColumn: (NSInteger)columnIndex row: (NSInteger)rowIndex
3204{
3205  NSCell *cell = nil;
3206  NSTableColumn *tb = [_tableColumns objectAtIndex: columnIndex];
3207
3208  if ([_delegate respondsToSelector:
3209        @selector(tableView:dataCellForTableColumn:row:)])
3210    {
3211      cell = [_delegate tableView: self dataCellForTableColumn: tb
3212                                                           row: rowIndex];
3213    }
3214  if (cell == nil)
3215    {
3216      cell = [tb dataCellForRow: rowIndex];
3217    }
3218  return cell;
3219}
3220
3221/*
3222 * Editing Cells
3223 */
3224
3225- (BOOL) abortEditing
3226{
3227  if (_textObject)
3228    {
3229      [_editedCell endEditing: _textObject];
3230      DESTROY(_editedCell);
3231      [self setNeedsDisplayInRect:
3232	      [self frameOfCellAtColumn: _editedColumn row: _editedRow]];
3233      _editedRow = -1;
3234      _editedColumn = -1;
3235      _textObject = nil;
3236      return YES;
3237    }
3238  else
3239    return NO;
3240}
3241
3242- (NSText *) currentEditor
3243{
3244  if (_textObject && ([_window firstResponder] == _textObject))
3245    return _textObject;
3246  else
3247    return nil;
3248}
3249
3250- (void) validateEditing
3251{
3252  if (_textObject && (_isValidating == NO))
3253    {
3254      NSFormatter *formatter;
3255      NSString *string;
3256      id newObjectValue = nil;
3257      BOOL validatedOK = YES;
3258
3259      // Avoid potential recursive sequences...
3260      _isValidating = YES;
3261
3262      formatter = [_editedCell formatter];
3263      string = AUTORELEASE([[_textObject text] copy]);
3264
3265      if (formatter != nil)
3266        {
3267          NSString *error;
3268
3269          if ([formatter getObjectValue: &newObjectValue
3270                         forString: string
3271                         errorDescription: &error] == YES)
3272            {
3273              [_editedCell setObjectValue: newObjectValue];
3274
3275              if (_dataSource_editable)
3276                {
3277                  NSTableColumn *tb;
3278
3279                  tb = [_tableColumns objectAtIndex: _editedColumn];
3280
3281                  [self _setObjectValue: newObjectValue
3282                        forTableColumn: tb
3283                        row: _editedRow];
3284                }
3285              return;
3286            }
3287          else
3288            {
3289              SEL sel = @selector(control:didFailToFormatString:errorDescription:);
3290
3291              if ([_delegate respondsToSelector: sel])
3292                {
3293                  validatedOK = [_delegate control: self
3294                                           didFailToFormatString: string
3295                                           errorDescription: error];
3296                }
3297              // Allow an empty string to fall through
3298              else if (![string isEqualToString: @""])
3299                {
3300                  validatedOK = NO;
3301                }
3302            }
3303        }
3304
3305      if (validatedOK)
3306        {
3307          [_editedCell setStringValue: string];
3308
3309          if (_dataSource_editable)
3310            {
3311              NSTableColumn *tb;
3312
3313              tb = [_tableColumns objectAtIndex: _editedColumn];
3314
3315              [self _setObjectValue: string // newObjectValue
3316                    forTableColumn: tb
3317                    row: _editedRow];
3318            }
3319        }
3320
3321      // Avoid potential recursive sequences...
3322      _isValidating = NO;
3323    }
3324}
3325
3326- (void) editColumn: (NSInteger) columnIndex
3327                row: (NSInteger) rowIndex
3328          withEvent: (NSEvent *) theEvent
3329             select: (BOOL) flag
3330{
3331  NSText *t;
3332  NSTableColumn *tb;
3333  NSRect drawingRect;
3334  NSUInteger length = 0;
3335
3336  if (rowIndex != _selectedRow)
3337    {
3338      [NSException raise:NSInvalidArgumentException
3339	      format:@"Attempted to edit unselected row"];
3340    }
3341
3342  if (rowIndex < 0 || rowIndex >= _numberOfRows
3343      || columnIndex < 0 || columnIndex >= _numberOfColumns)
3344    {
3345      [NSException raise: NSInvalidArgumentException
3346		   format: @"Row/column out of index in edit"];
3347    }
3348
3349  [self scrollRowToVisible: rowIndex];
3350  [self scrollColumnToVisible: columnIndex];
3351
3352  if (_textObject != nil)
3353    {
3354      [self validateEditing];
3355      [self abortEditing];
3356    }
3357
3358  // Now (_textObject == nil)
3359
3360  t = [_window fieldEditor: YES  forObject: self];
3361
3362  if ([t superview] != nil)
3363    {
3364      if ([t resignFirstResponder] == NO)
3365	{
3366	  return;
3367	}
3368    }
3369
3370  _editedRow = rowIndex;
3371  _editedColumn = columnIndex;
3372
3373  // Prepare the cell
3374  // NB: need to be released when no longer used
3375  _editedCell = [[self preparedCellAtColumn: columnIndex row: rowIndex] copy];
3376
3377  tb = [_tableColumns objectAtIndex: columnIndex];
3378  [_editedCell setObjectValue: [self _objectValueForTableColumn: tb
3379				     row: rowIndex]];
3380
3381  // But of course the delegate can mess it up if it wants
3382  [self _willDisplayCell: _editedCell
3383	forTableColumn: tb
3384	row: rowIndex];
3385
3386  /* Please note the important point - calling stringValue normally
3387     causes the _editedCell to call the validateEditing method of its
3388     control view ... which happens to be this NSTableView object :-)
3389     but we don't want any spurious validateEditing to be performed
3390     before the actual editing is started (otherwise you easily end up
3391     with the table view picking up the string stored in the field
3392     editor, which is likely to be the string resulting from the last
3393     edit somewhere else ... getting into the bug that when you TAB
3394     from one cell to another one, the string is copied!), so we must
3395     call stringValue when _textObject is still nil.  */
3396  if (flag)
3397    {
3398      length = [[_editedCell stringValue] length];
3399    }
3400
3401  _textObject = [_editedCell setUpFieldEditorAttributes: t];
3402  // FIXME: Which background color do we want here?
3403  [_textObject setBackgroundColor: [NSColor selectedControlColor]];
3404  [_textObject setDrawsBackground: YES];
3405
3406  drawingRect = [self frameOfCellAtColumn: columnIndex  row: rowIndex];
3407  if (flag)
3408    {
3409      [_editedCell selectWithFrame: drawingRect
3410		   inView: self
3411		   editor: _textObject
3412		   delegate: self
3413		   start: 0
3414		   length: length];
3415    }
3416  else
3417    {
3418      [_editedCell editWithFrame: drawingRect
3419		   inView: self
3420		   editor: _textObject
3421		   delegate: self
3422		   event: theEvent];
3423    }
3424  return;
3425}
3426
3427- (NSInteger) editedRow
3428{
3429  return _editedRow;
3430}
3431
3432- (NSInteger) editedColumn
3433{
3434  return _editedColumn;
3435}
3436
3437
3438static inline NSTimeInterval computePeriod(NSPoint mouseLocationWin,
3439                                           CGFloat minYVisible,
3440                                           CGFloat maxYVisible)
3441{
3442    /* We have three zones of speed.
3443       0   -  50 pixels: period 0.2  <zone 1>
3444       50  - 100 pixels: period 0.1  <zone 2>
3445       100 - 150 pixels: period 0.01 <zone 3> */
3446    CGFloat distance = 0;
3447
3448    if (mouseLocationWin.y < minYVisible)
3449      {
3450	distance = minYVisible - mouseLocationWin.y;
3451      }
3452    else if (mouseLocationWin.y > maxYVisible)
3453      {
3454	distance = mouseLocationWin.y - maxYVisible;
3455      }
3456
3457    if (distance < 50)
3458      return 0.2;
3459    else if (distance < 100)
3460      return 0.1;
3461    else
3462      return 0.01;
3463}
3464
3465- (void) _trackCellAtColumn: (NSInteger) columnIndex
3466		row: (NSInteger) rowIndex
3467		withEvent: (NSEvent *) theEvent
3468{
3469  if (rowIndex == -1 || columnIndex == -1)
3470    {
3471      return;
3472    }
3473
3474  /* we should copy the cell here, as we do on editing.
3475     otherwise validation on a cell being edited could
3476     cause the cell we are selecting to get it's objectValue */
3477  NSCell *cell = [[self preparedCellAtColumn: columnIndex row: rowIndex] copy];
3478  NSTableColumn *tb = [_tableColumns objectAtIndex: columnIndex];
3479  id originalValue = RETAIN([self _objectValueForTableColumn: tb
3480                                                         row: rowIndex]);
3481  [cell setObjectValue: originalValue];
3482  NSRect cellFrame = [self frameOfCellAtColumn: columnIndex
3483                                           row: rowIndex];
3484  [cell setHighlighted: YES];
3485  [self setNeedsDisplayInRect: cellFrame];
3486  /* give delegate a chance to i.e set target */
3487  [self _willDisplayCell: cell
3488		forTableColumn: tb
3489	 	row: rowIndex];
3490
3491  if ([cell trackMouse: theEvent
3492		   inRect: cellFrame
3493		   ofView: self
3494	     untilMouseUp: [[cell class]
3495			     prefersTrackingUntilMouseUp]])
3496    {
3497      id newValue = [cell objectValue];
3498
3499      /* don't check editability that only pertains to editColumn:... */
3500      if (originalValue != newValue
3501	  && ![originalValue isEqual: newValue])
3502        {
3503	  [self _setObjectValue: newValue
3504			forTableColumn: tb
3505			row: rowIndex];
3506	}
3507    }
3508  RELEASE(originalValue);
3509  [cell setHighlighted: NO];
3510  [self setNeedsDisplayInRect: cellFrame];
3511  RELEASE(cell);
3512}
3513
3514- (BOOL) _startDragOperationWithEvent: (NSEvent *) theEvent
3515{
3516  NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
3517  NSPoint startPoint = [self convertPoint: [theEvent locationInWindow]
3518                                 fromView: nil];
3519
3520  if ([self canDragRowsWithIndexes: _selectedRows atPoint: startPoint]
3521    && [self _writeRows: _selectedRows toPasteboard: pboard])
3522    {
3523      NSPoint	p = NSZeroPoint;
3524      NSImage	*dragImage;
3525      NSSize	s;
3526      // FIXME
3527      NSArray *cols = nil;
3528
3529      dragImage = [self dragImageForRowsWithIndexes: _selectedRows
3530                        tableColumns: cols
3531                        event: theEvent
3532                        offset: &p];
3533
3534      /*
3535       * Store image offset in s ... the returned
3536       * value is the position of the center of
3537       * the image, so we adjust to the bottom left
3538       * corner.
3539       */
3540       s = [dragImage size];
3541       s.width = p.x - s.width/2;
3542       s.height = p.y + s.height/2; // View is flipped
3543
3544       /*
3545	* Reuse the current mouse location and adjust
3546	* it to determine the location of the bottom
3547	* left corner of the image in this view's
3548	* coordinate system.
3549	*/
3550       p = startPoint;
3551       p.x += s.width;
3552       p.y += s.height;
3553
3554
3555       [self dragImage: dragImage
3556		    at: p
3557		offset: NSMakeSize(0, 0)
3558		 event: theEvent
3559	    pasteboard: pboard
3560	        source: self
3561	     slideBack: YES];
3562      return YES;
3563    }
3564  return NO;
3565}
3566
3567- (void) mouseDown: (NSEvent *)theEvent
3568{
3569  NSPoint initialLocation = [theEvent locationInWindow];
3570  NSPoint location;
3571  NSInteger clickCount = [theEvent clickCount];
3572
3573  // Pathological case -- ignore mouse down
3574  if ((_numberOfRows == 0) || (_numberOfColumns == 0))
3575    {
3576      return;
3577    }
3578
3579  /* Stop editing if any */
3580  if (_textObject != nil)
3581    {
3582      if (_editedCell != nil
3583          && [_editedCell isEntryAcceptable:[_textObject text]] == NO)
3584        {
3585          NSBeep();
3586          return;
3587        }
3588      [self validateEditing];
3589      [self abortEditing];
3590    }
3591
3592  // Determine row and column which were clicked
3593  location = [self convertPoint: initialLocation fromView: nil];
3594  _clickedRow  = [self rowAtPoint: location];
3595  _clickedColumn = [self columnAtPoint: location];
3596
3597  if ([theEvent type] == NSLeftMouseDown
3598       && clickCount > 1)
3599    {
3600      // Double-click event
3601
3602      if (![self isRowSelected: _clickedRow])
3603        {
3604	  return;
3605	}
3606
3607      if (![self _isCellSelectableColumn: _clickedColumn row: _clickedRow])
3608        {
3609	  // Send double-action but don't edit
3610	  [self _trackCellAtColumn: _clickedColumn
3611                               row: _clickedRow
3612                         withEvent: theEvent];
3613	  if (_clickedRow != -1)
3614	    [self sendAction: _doubleAction to: _target];
3615	}
3616      else if (clickCount == 2) // if < 2, dont want to abort editing
3617        {
3618	  // It is OK to edit column.  Go on, do it.
3619          [self editColumn: _clickedColumn
3620		   row: _clickedRow
3621		   withEvent: theEvent
3622		   select: YES];
3623	}
3624    }
3625  else
3626    {
3627#define COMPUTE_NEW_SELECTION do { \
3628if (originalRow == -1) \
3629  { \
3630    originalRow = currentRow; \
3631  } \
3632if (currentRow >= 0 && currentRow < _numberOfRows) \
3633  { \
3634    computeNewSelection(self, \
3635    oldSelectedRows, \
3636    _selectedRows, \
3637    originalRow, \
3638    oldRow, \
3639    currentRow, \
3640    &_selectedRow, \
3641    selectionMode); \
3642    [self displayIfNeeded]; \
3643  } \
3644} while (0);
3645
3646      // Selection
3647      NSUInteger modifiers = [theEvent modifierFlags];
3648      NSUInteger eventMask = (NSLeftMouseUpMask
3649				| NSLeftMouseDownMask
3650				| NSLeftMouseDraggedMask
3651				| NSPeriodicMask);
3652      unsigned selectionMode = 0;
3653      NSPoint mouseLocationWin;
3654      NSPoint mouseLocationView;
3655      NSDate *distantFuture = [NSDate distantFuture];
3656      NSEvent *lastEvent;
3657      NSIndexSet *oldSelectedRows;
3658      BOOL startedPeriodicEvents = NO;
3659      BOOL mouseBelowView = NO;
3660      BOOL done = NO;
3661      BOOL mouseMoved = NO;
3662      BOOL didTrackCell = NO;
3663      BOOL dragOperationPossible = [self _isDraggingSource];
3664      NSRect visibleRect = [self convertRect: [self visibleRect]
3665				 toView: nil];
3666      CGFloat minYVisible = NSMinY (visibleRect);
3667      CGFloat maxYVisible = NSMaxY (visibleRect);
3668      NSTimeInterval oldPeriod = 0;
3669      NSInteger originalRow = _clickedRow;
3670      NSInteger oldRow = -1;
3671      NSInteger currentRow = -1;
3672      BOOL getNextEvent = YES;
3673      BOOL sendAction = NO;
3674
3675      if (_allowsMultipleSelection == YES)
3676	{
3677	  selectionMode |= ALLOWS_MULTIPLE;
3678	}
3679
3680      if (_allowsEmptySelection == YES)
3681	{
3682	  selectionMode |= ALLOWS_EMPTY;
3683	}
3684
3685      if (modifiers & NSShiftKeyMask)
3686	{
3687	  selectionMode |= SHIFT_DOWN;
3688	}
3689
3690      if (![_selectedRows containsIndex: _clickedRow])
3691	{
3692	  selectionMode |= ADDING_ROW;
3693	}
3694
3695      if (modifiers & NSControlKeyMask)
3696	{
3697	  selectionMode |= CONTROL_DOWN;
3698	  if (_allowsMultipleSelection == YES && _selectedRow != -1)
3699	    {
3700	      originalRow = _selectedRow;
3701	      selectionMode |= SHIFT_DOWN;
3702	      selectionMode |= ADDING_ROW;
3703	    }
3704	}
3705
3706      // is the delegate ok for a new selection ?
3707      if ([self _shouldSelectionChange] == NO)
3708	{
3709	  return;
3710	}
3711
3712      // if we are in column selection mode, stop it
3713      [self _setSelectingColumns: NO];
3714
3715      // let's sort the _selectedRows
3716      oldSelectedRows = [_selectedRows copy];
3717      lastEvent = theEvent;
3718
3719      while (done != YES)
3720	{
3721	  /*
3722	    Wrap each iteration in an autorelease pool. Otherwise, we end
3723	    up allocating huge amounts of objects if the button is held
3724	    down for a long time.
3725	  */
3726	  CREATE_AUTORELEASE_POOL(arp);
3727	  NSEventType eventType = [lastEvent type];
3728
3729	  mouseLocationWin = [lastEvent locationInWindow];
3730	  mouseLocationView = [self convertPoint: mouseLocationWin
3731					    fromView: nil];
3732
3733	  switch (eventType)
3734	    {
3735	    case NSLeftMouseUp:
3736	      if ((mouseLocationWin.y > minYVisible)
3737		  && (mouseLocationWin.y < maxYVisible))
3738		{
3739		  // mouse up within table
3740		  if (startedPeriodicEvents == YES)
3741		    {
3742		      [NSEvent stopPeriodicEvents];
3743		      startedPeriodicEvents = NO;
3744		    }
3745		  mouseLocationView.x = _bounds.origin.x;
3746		  oldRow = currentRow;
3747		  currentRow = [self rowAtPoint: mouseLocationView];
3748
3749		  if (oldRow != currentRow)
3750		    {
3751		      COMPUTE_NEW_SELECTION;
3752		    }
3753
3754		  if (!didTrackCell && currentRow == _clickedRow)
3755		    {
3756		      /*
3757		       * a dragging operation is still possible so
3758		       * selections were never dragged,
3759		       * and a drag operation was never attempted.
3760		       * the cell was clicked,
3761		       * track the cell with the old mouseDown event
3762		       * then it will get the current event mouseUp.
3763		       */
3764		      [self _trackCellAtColumn: _clickedColumn
3765			    row: _clickedRow
3766			    withEvent: theEvent];
3767		    }
3768		}
3769	      else
3770		{
3771		  // Mouse dragged out of the table
3772		  // we don't care
3773		}
3774	      done = YES;
3775	      break;
3776
3777	    case NSLeftMouseDown:
3778	    case NSLeftMouseDragged:
3779	      if (fabs(mouseLocationWin.x - initialLocation.x) > 1
3780	          || fabs(mouseLocationWin.y - initialLocation.y) > 1)
3781		{
3782		  mouseMoved = YES;
3783		}
3784
3785	      if (dragOperationPossible == YES)
3786		{
3787		  if ([_selectedRows containsIndex:_clickedRow] == NO
3788 		      || (_verticalMotionDrag == NO
3789 			  && fabs(mouseLocationWin.y - initialLocation.y) > 2))
3790		    {
3791		      dragOperationPossible = NO;
3792		    }
3793 		  else if ((fabs(mouseLocationWin.x - initialLocation.x) >= 4)
3794 			   || (_verticalMotionDrag
3795 			       && fabs(mouseLocationWin.y - initialLocation.y) >= 4))
3796		    {
3797		      if ([self _startDragOperationWithEvent: theEvent])
3798			{
3799                          RELEASE(oldSelectedRows);
3800                          IF_NO_GC(DESTROY(arp));
3801			  return;
3802			}
3803		      else
3804			{
3805			  dragOperationPossible = NO;
3806			}
3807		    }
3808		}
3809	      else if ((mouseLocationWin.y > minYVisible)
3810		       && (mouseLocationWin.y < maxYVisible))
3811		{
3812		  // mouse dragged within table
3813		  if (startedPeriodicEvents == YES)
3814		    {
3815		      [NSEvent stopPeriodicEvents];
3816		      startedPeriodicEvents = NO;
3817		    }
3818
3819		  mouseLocationView.x = _bounds.origin.x;
3820		  oldRow = currentRow;
3821		  currentRow = [self rowAtPoint: mouseLocationView];
3822		  if (oldRow != currentRow)
3823		    {
3824		      COMPUTE_NEW_SELECTION;
3825		    }
3826
3827		  if (eventType == NSLeftMouseDown)
3828		    {
3829		      /*
3830		       * Can never get here from a dragging source
3831		       * so they need to track in mouse up.
3832		       */
3833		      NSCell *cell = [self preparedCellAtColumn: _clickedColumn
3834                                                            row: _clickedRow];
3835
3836		      [self _trackCellAtColumn: _clickedColumn
3837				row: _clickedRow
3838				withEvent: theEvent];
3839		      didTrackCell = YES;
3840
3841		      if ([[cell class] prefersTrackingUntilMouseUp])
3842		        {
3843		          /* the mouse could have gone up outside of the cell
3844		           * avoid selecting the row under mouse cursor */
3845			  sendAction = YES;
3846			  done = YES;
3847			}
3848		    }
3849		  /*
3850		   * Since we may have tracked a cell which may have caused
3851		   * a change to the currentEvent we may need to loop over
3852		   * the current event
3853		   */
3854		  getNextEvent = (lastEvent == [NSApp currentEvent]);
3855		}
3856	      else
3857		{
3858		  // Mouse dragged out of the table
3859		  NSTimeInterval period = computePeriod(mouseLocationWin,
3860					       minYVisible,
3861					       maxYVisible);
3862
3863		  if (startedPeriodicEvents == YES)
3864		    {
3865		      /* Check - if the mouse did not change zone,
3866			 we do nothing */
3867		      if (period == oldPeriod)
3868			break;
3869
3870		      [NSEvent stopPeriodicEvents];
3871		    }
3872		  /* Start periodic events */
3873		  oldPeriod = period;
3874		  [NSEvent startPeriodicEventsAfterDelay: 0
3875			   withPeriod: oldPeriod];
3876		  startedPeriodicEvents = YES;
3877		  if (mouseLocationWin.y <= minYVisible)
3878		    mouseBelowView = YES;
3879		  else
3880		    mouseBelowView = NO;
3881		}
3882	      break;
3883	    case NSPeriodic:
3884	      if (mouseBelowView == YES)
3885		{
3886		  if (currentRow == -1 && oldRow != -1)
3887		    currentRow = oldRow + 1;
3888
3889		  if (currentRow != -1 && currentRow < _numberOfRows - 1)
3890		    {
3891		      oldRow = currentRow;
3892		      currentRow++;
3893		      [self scrollRowToVisible: currentRow];
3894		      if (dragOperationPossible == NO)
3895			COMPUTE_NEW_SELECTION;
3896		    }
3897		}
3898	      else
3899		{
3900		  if (currentRow == -1 && oldRow != -1)
3901		    currentRow = oldRow - 1;
3902
3903		  if (currentRow > 0)
3904		    {
3905		      oldRow = currentRow;
3906		      currentRow--;
3907		      [self scrollRowToVisible: currentRow];
3908		      if (dragOperationPossible == NO)
3909			COMPUTE_NEW_SELECTION;
3910		    }
3911		}
3912	      break;
3913	    default:
3914	      break;
3915	    }
3916
3917	  if (done == NO)
3918	    {
3919	      /* in certain cases we are working with events that have already
3920	       * occurred and been dequeued by NSCell classes, in these cases
3921	       * getNextEvent is set to NO, use the current event.
3922	       */
3923	      if (getNextEvent == YES)
3924	        {
3925		  lastEvent = [NSApp nextEventMatchingMask: eventMask
3926				 untilDate: distantFuture
3927				 inMode: NSEventTrackingRunLoopMode
3928				 dequeue: YES];
3929		}
3930	      else
3931		{
3932		  lastEvent = [NSApp currentEvent];
3933		  getNextEvent = YES;
3934		}
3935	    }
3936	  IF_NO_GC(DESTROY(arp));
3937	}
3938
3939      if (startedPeriodicEvents == YES)
3940	[NSEvent stopPeriodicEvents];
3941
3942      if (![_selectedRows isEqual: oldSelectedRows])
3943	{
3944	  [self _postSelectionDidChangeNotification];
3945	}
3946
3947      RELEASE(oldSelectedRows);
3948
3949      if (!mouseMoved)
3950        sendAction = YES;
3951
3952      /* If this was a simple click (ie. no dragging), we send our action. */
3953      if (sendAction)
3954	{
3955	  /*
3956	  _clickedRow and _clickedColumn are already set at the start of
3957	  this function.
3958
3959	  TODO: should we ask the data source/column for the cell for this
3960	  row/column and check whether it has its own action/target?
3961	  */
3962	  if (_clickedRow != -1)
3963	    [self sendAction: _action  to: _target];
3964	}
3965    }
3966
3967  _clickedRow = _selectedRow;
3968}
3969
3970
3971/* helpers for keyboard selection */
3972#define CHECK_CHANGING(x) { \
3973if (!x) \
3974  { \
3975    [self _postSelectionIsChangingNotification]; \
3976    x = YES; \
3977  } \
3978}
3979static BOOL selectContiguousRegion(NSTableView *self,
3980  				   NSIndexSet *_selectedRows,
3981				   NSInteger originalRow,
3982				   NSInteger oldRow,
3983				   NSInteger currentRow)
3984{
3985  NSInteger first = (oldRow < currentRow) ? oldRow : currentRow;
3986  NSInteger last = (oldRow < currentRow) ? currentRow : oldRow;
3987  NSInteger row;
3988  BOOL notified = NO;
3989
3990  if (![_selectedRows containsIndex: currentRow])
3991    {
3992      CHECK_CHANGING(notified);
3993      [self _selectRow: currentRow];
3994    }
3995
3996  /*
3997   * check if the old row is not between the current row and the original row
3998   * and not the original or current rows
3999   */
4000  if (((!((oldRow < currentRow
4001	   && currentRow > originalRow
4002	   && oldRow > originalRow)
4003	  || (oldRow > currentRow
4004	      && currentRow < originalRow
4005	      && oldRow < originalRow)))
4006       && (!(oldRow == currentRow
4007	      || oldRow == originalRow))))
4008    {
4009      CHECK_CHANGING(notified);
4010      [self _unselectRow: oldRow];
4011    }
4012
4013  /*
4014   * there is an off by one here it could be on either end of the loop
4015   * but its either oldRow or currentRow so above we select the currentRow
4016   * and possibly unselect the oldRow, one of the two will then
4017   * be selected or deselected again in in this loop
4018   */
4019  for (row = first; row < last; row++)
4020    {
4021
4022      /* check if the old row is between the current row and the original row */
4023      if ((row < currentRow
4024	   && row > originalRow
4025	   && currentRow > oldRow)
4026	  || (row > currentRow
4027	      && row < originalRow
4028	      && currentRow < oldRow))
4029	{
4030	  if (![_selectedRows containsIndex: row])
4031	    {
4032	      CHECK_CHANGING(notified);
4033	      [self _selectRow: row];
4034	    }
4035	}
4036      else if (row == currentRow || row == originalRow)
4037	{
4038	  if (![_selectedRows containsIndex: row])
4039	    {
4040	      CHECK_CHANGING(notified);
4041	      [self _selectRow: row];
4042	    }
4043	}
4044      else
4045	{
4046	  if ([_selectedRows containsIndex: row])
4047	    {
4048              CHECK_CHANGING(notified);
4049	      [self _unselectRow: row];
4050	    }
4051	}
4052    }
4053  return notified;
4054}
4055
4056- (void) keyDown: (NSEvent *)theEvent
4057{
4058   NSInteger oldRow = -1;
4059   NSInteger currentRow = _selectedRow;
4060   NSInteger originalRow = -1;
4061   NSString *characters = [theEvent characters];
4062   NSUInteger len = [characters length];
4063   NSUInteger modifiers = [theEvent modifierFlags];
4064   CGFloat rowHeight = [self rowHeight];
4065   NSRect visRect = [self visibleRect];
4066   BOOL modifySelection = YES;
4067   NSPoint noModPoint = NSZeroPoint;
4068   NSInteger visRows;
4069   NSUInteger i;
4070   BOOL gotMovementKey = NO;
4071
4072   // will not contain partial rows.
4073   visRows = visRect.size.height / [self rowHeight];
4074
4075   // _clickedRow is stored between calls as the first selected row
4076   // when doing multiple selection, so the selection may grow and shrink.
4077
4078   /*
4079    * do a contiguous selection on shift
4080    */
4081   if (modifiers & NSShiftKeyMask)
4082     {
4083       originalRow = _clickedRow;
4084       if (_allowsMultipleSelection == YES)
4085	 {
4086	   oldRow = _selectedRow;
4087	 }
4088     }
4089
4090   /* just scroll don't modify any selection */
4091   if (modifiers & NSControlKeyMask)
4092     {
4093       modifySelection = NO;
4094     }
4095
4096   for (i = 0; i < len; i++)
4097     {
4098       unichar c = [characters characterAtIndex: i];
4099
4100       switch (c)
4101         {
4102	   case NSUpArrowFunctionKey:
4103	     gotMovementKey = YES;
4104   	     if (modifySelection == NO)
4105	       {
4106   		 noModPoint.x = visRect.origin.x;
4107		 noModPoint.y = NSMinY(visRect) - rowHeight;
4108	       }
4109	     else
4110	       {
4111		 currentRow--;
4112	       }
4113	     break;
4114	   case NSDownArrowFunctionKey:
4115	     gotMovementKey = YES;
4116   	     if (modifySelection == NO)
4117	       {
4118   		 noModPoint.x = visRect.origin.x;
4119		 noModPoint.y = NSMinY(visRect) + rowHeight;
4120	       }
4121	     else
4122	       {
4123 	         currentRow++;
4124	       }
4125	     break;
4126	   case NSPageDownFunctionKey:
4127	     gotMovementKey = YES;
4128   	     if (modifySelection == NO)
4129	       {
4130   		 noModPoint.x = visRect.origin.x;
4131		 noModPoint.y = NSMinY(visRect) + (rowHeight * visRows) - rowHeight;
4132	       }
4133	     else
4134	       {
4135		 currentRow += visRows;
4136	       }
4137	     break;
4138	   case NSPageUpFunctionKey:
4139	     gotMovementKey = YES;
4140	     if (modifySelection == NO)
4141	       {
4142   		 noModPoint.x = visRect.origin.x;
4143		 noModPoint.y = NSMinY(visRect) - (rowHeight * visRows) + rowHeight;
4144	       }
4145	     else
4146	       {
4147	         currentRow -= visRows;
4148	       }
4149	     break;
4150	   case NSHomeFunctionKey:
4151	     gotMovementKey = YES;
4152	     if (modifySelection == NO)
4153	       {
4154		 noModPoint.x = visRect.origin.x;
4155		 noModPoint.y = NSMinY(_bounds);
4156	       }
4157	     else
4158	       {
4159	         currentRow = 0;
4160	       }
4161	     break;
4162	   case NSEndFunctionKey:
4163	     gotMovementKey = YES;
4164	     if (modifySelection == NO)
4165	       {
4166		 noModPoint.x = visRect.origin.x;
4167		 noModPoint.y = NSMaxY(_bounds);
4168	       }
4169	     else
4170	       {
4171		 currentRow = _numberOfRows - 1;
4172	       }
4173	     break;
4174	   default:
4175	     break;
4176        }
4177     }
4178
4179  /*
4180   * if scrolled off the bottom or top the selection.
4181   * the modifiers might have changed so recompute the selection.
4182   */
4183  if (gotMovementKey == NO)
4184    {
4185      /* no handled keys. */
4186      [super keyDown: theEvent];
4187      return;
4188    }
4189  else if (currentRow < 0)
4190    {
4191      currentRow = 0;
4192    }
4193  else if (currentRow >= _numberOfRows)
4194    {
4195      currentRow = _numberOfRows - 1;
4196    }
4197
4198  if (_numberOfRows)
4199    {
4200      if (modifySelection)
4201        {
4202	  BOOL notified = NO;
4203
4204          [self _setSelectingColumns: NO];
4205
4206          if (originalRow == -1)
4207            {
4208	      /* we're not extending any selection */
4209              originalRow = currentRow;
4210              _clickedRow = currentRow;
4211	    }
4212
4213          if (_clickedRow == -1)
4214            {
4215	      /* user must have hit a key with no selected rows */
4216              _clickedRow = currentRow;
4217	    }
4218
4219	  if ((!(modifiers & NSShiftKeyMask && _allowsMultipleSelection)))
4220	    {
4221	      NSUInteger first = [_selectedRows firstIndex];
4222	      NSUInteger last = [_selectedRows lastIndex];
4223
4224	      if ((first == last && first == currentRow) == 0)
4225	        {
4226		  CHECK_CHANGING(notified)
4227		  [self _unselectAllRows];
4228		  [self _selectRow: currentRow];
4229	          _selectedRow = currentRow;
4230		}
4231	    }
4232	  else
4233	    {
4234	      notified = selectContiguousRegion(self, _selectedRows,
4235			         originalRow, oldRow, currentRow);
4236	      _selectedRow = currentRow;
4237	    }
4238
4239	  if (notified)
4240            {
4241              [self _postSelectionDidChangeNotification];
4242	    }
4243
4244	  [self scrollRowToVisible: currentRow];
4245	  [self displayIfNeeded];
4246        }
4247      else
4248        {
4249	  noModPoint = [self convertPoint: noModPoint
4250		  		   toView: _super_view];
4251	  noModPoint =
4252	     [(NSClipView *)_super_view constrainScrollPoint: noModPoint];
4253	  [(NSClipView *)_super_view scrollToPoint: noModPoint];
4254	}
4255    }
4256}
4257
4258/*
4259 * Auxiliary Components
4260 */
4261
4262- (void) setHeaderView: (NSTableHeaderView*)aHeaderView
4263{
4264
4265  if ([_headerView respondsToSelector:@selector(setTableView:)])
4266    [_headerView setTableView: nil];
4267
4268  ASSIGN (_headerView, aHeaderView);
4269
4270  if ([_headerView respondsToSelector:@selector(setTableView:)])
4271    [_headerView setTableView: self];
4272
4273  [self tile]; // resizes corner and header views, then displays
4274
4275  if (_super_view != nil)
4276    {
4277      id ssv = [_super_view superview];
4278      if ([ssv isKindOfClass: [NSScrollView class]])
4279        [ssv tile]; // draws any border type over corner and header views
4280    }
4281}
4282
4283- (NSTableHeaderView*) headerView
4284{
4285  return _headerView;
4286}
4287
4288- (void) setCornerView: (NSView*)aView
4289{
4290  ASSIGN (_cornerView, aView);
4291  [self tile]; // resizes corner and header views, then displays
4292  if (_super_view)
4293    {
4294      id ssv = [_super_view superview];
4295      if ([ssv isKindOfClass: [NSScrollView class]])
4296        [ssv tile]; // draws any border type over corner and header views
4297    }
4298}
4299
4300- (NSView*) cornerView
4301{
4302  return _cornerView;
4303}
4304
4305/*
4306 * Layout
4307 */
4308
4309- (NSRect) rectOfColumn: (NSInteger)columnIndex
4310{
4311  NSRect rect;
4312
4313  if (columnIndex < 0 || columnIndex > _numberOfColumns)
4314    {
4315      NSDebugLLog(@"NSTableView", @"Column index %d out of table in rectOfColumn", (int)columnIndex);
4316      return NSZeroRect;
4317    }
4318
4319  rect.origin.x = _columnOrigins[columnIndex];
4320  rect.origin.y = _bounds.origin.y;
4321  rect.size.width = [[_tableColumns objectAtIndex: columnIndex] width];
4322  rect.size.height = _numberOfRows * _rowHeight;
4323  return rect;
4324}
4325
4326- (NSRect) rectOfRow: (NSInteger)rowIndex
4327{
4328  NSRect rect;
4329
4330  if (rowIndex < 0 || rowIndex >= _numberOfRows)
4331    {
4332      NSDebugLLog(@"NSTableView", @"Row index %d out of table in rectOfRow", (int)rowIndex);
4333      return NSZeroRect;
4334    }
4335
4336  rect.origin.x = _bounds.origin.x;
4337  rect.origin.y = _bounds.origin.y + (_rowHeight * rowIndex);
4338  rect.size.width = _bounds.size.width;
4339  rect.size.height = _rowHeight;
4340  return rect;
4341}
4342
4343/** Returns the indexes of the table columns which intersects the given rect.
4344
4345The rect is expressed in  the receiver coordinate space.
4346
4347Hidden table columns are never tested. */
4348- (NSIndexSet *) columnIndexesInRect: (NSRect)aRect
4349{
4350  NSRange range = [self columnsInRect: aRect];
4351  NSMutableIndexSet *indexes = [NSMutableIndexSet indexSetWithIndexesInRange: range];
4352  NSUInteger i;
4353
4354  for (i = range.location; i < range.length; i++)
4355    {
4356      NSTableColumn *tableColumn = [_tableColumns objectAtIndex: i];
4357
4358      if ([tableColumn isHidden])
4359        [indexes removeIndex: i];
4360    }
4361
4362  return indexes;
4363}
4364
4365/** Returns the index range of the table columns which intersects the given rect.
4366
4367The rect is expressed in  the receiver coordinate space.
4368
4369The returned range can include hidden table column indexes.
4370
4371This method is deprecated, use -columnIndexesInRect:. */
4372- (NSRange) columnsInRect: (NSRect)aRect
4373{
4374  NSRange range;
4375
4376  range.location = [self columnAtPoint: aRect.origin];
4377  range.length = [self columnAtPoint:
4378			 NSMakePoint (NSMaxX (aRect), _bounds.origin.y)];
4379  range.length -= range.location;
4380  range.length += 1;
4381  return range;
4382}
4383
4384- (NSRange) rowsInRect: (NSRect)aRect
4385{
4386  NSRange range;
4387  NSInteger lastRowInRect;
4388
4389  range.location = [self rowAtPoint: aRect.origin];
4390  lastRowInRect = [self rowAtPoint:
4391			 NSMakePoint (_bounds.origin.x, NSMaxY (aRect))];
4392
4393  if (lastRowInRect == -1)
4394    {
4395      lastRowInRect = _numberOfRows - 1;
4396    }
4397
4398  range.length = lastRowInRect;
4399  range.length -= range.location;
4400  range.length += 1;
4401  return range;
4402}
4403
4404- (NSInteger) columnAtPoint: (NSPoint)aPoint
4405{
4406  if ((NSMouseInRect (aPoint, _bounds, YES)) == NO)
4407    {
4408      return -1;
4409    }
4410  else
4411    {
4412      NSInteger i = 0;
4413
4414      while ((i < _numberOfColumns) && (aPoint.x >= _columnOrigins[i]))
4415	{
4416	  i++;
4417	}
4418      return i - 1;
4419    }
4420}
4421
4422- (NSInteger) rowAtPoint: (NSPoint)aPoint
4423{
4424  /* NB: Y coordinate system is flipped in NSTableView */
4425  if ((NSMouseInRect (aPoint, _bounds, YES)) == NO)
4426    {
4427      return -1;
4428    }
4429  else if (_rowHeight == 0.0)
4430    {
4431      return -1;
4432    }
4433  else
4434    {
4435      NSInteger return_value;
4436
4437      aPoint.y -= _bounds.origin.y;
4438      return_value = (NSInteger) (aPoint.y / _rowHeight);
4439      /* This could happen if point lies on the grid line or below the last row */
4440      if (return_value >= _numberOfRows)
4441	{
4442	  return_value = -1;
4443	}
4444      return return_value;
4445    }
4446}
4447
4448- (NSRect) frameOfCellAtColumn: (NSInteger)columnIndex
4449			   row: (NSInteger)rowIndex
4450{
4451  NSRect frameRect;
4452
4453  if ((columnIndex < 0)
4454      || (rowIndex < 0)
4455      || (columnIndex > (_numberOfColumns - 1))
4456      || (rowIndex > (_numberOfRows - 1)))
4457    return NSZeroRect;
4458
4459  frameRect.origin.y  = _bounds.origin.y + (rowIndex * _rowHeight);
4460  frameRect.origin.y += _intercellSpacing.height / 2;
4461  frameRect.size.height = _rowHeight - _intercellSpacing.height;
4462
4463  frameRect.origin.x = _columnOrigins[columnIndex];
4464  frameRect.origin.x  += _intercellSpacing.width / 2;
4465  frameRect.size.width = [[_tableColumns objectAtIndex: columnIndex] width];
4466  frameRect.size.width -= _intercellSpacing.width;
4467
4468  // We add some space to separate the cell from the grid
4469  if (_drawsGrid)
4470    {
4471      frameRect.size.width -= 4;
4472      frameRect.origin.x += 2;
4473    }
4474
4475  // Safety check
4476  if (frameRect.size.width < 0)
4477    frameRect.size.width = 0;
4478
4479  return frameRect;
4480}
4481
4482- (void) setAutoresizesAllColumnsToFit: (BOOL)flag
4483{
4484  _autoresizesAllColumnsToFit = flag;
4485}
4486
4487- (BOOL) autoresizesAllColumnsToFit
4488{
4489  return _autoresizesAllColumnsToFit;
4490}
4491
4492- (NSTableViewColumnAutoresizingStyle) columnAutoresizingStyle
4493{
4494  // FIXME
4495  return NSTableViewNoColumnAutoresizing;
4496}
4497
4498- (void) setColumnAutoresizingStyle: (NSTableViewColumnAutoresizingStyle)style
4499{
4500  // FIXME
4501}
4502
4503- (void) sizeLastColumnToFit
4504{
4505  if ((_super_view != nil) && (_numberOfColumns > 0))
4506    {
4507      CGFloat excess_width;
4508      CGFloat last_column_width;
4509      NSTableColumn *lastColumn;
4510
4511      lastColumn = [_tableColumns objectAtIndex: (_numberOfColumns - 1)];
4512      if ([lastColumn isResizable] == NO)
4513        {
4514          return;
4515        }
4516      excess_width = NSMaxX([self convertRect: [_super_view bounds]
4517				     fromView: _super_view]) - NSMaxX(_bounds);
4518      last_column_width = [lastColumn width] + excess_width;
4519      // This will automatically retile the table
4520      [lastColumn setWidth: last_column_width];
4521    }
4522}
4523
4524- (void) setFrame: (NSRect)frameRect
4525{
4526  NSRect tmpRect = frameRect;
4527
4528  if ([_super_view respondsToSelector: @selector(documentVisibleRect)])
4529    {
4530      CGFloat rowsHeight = ((_numberOfRows * _rowHeight) + 1);
4531      NSRect docRect = [(NSClipView *)_super_view documentVisibleRect];
4532
4533      if (rowsHeight < docRect.size.height)
4534	{
4535	  tmpRect.size.height = docRect.size.height;
4536	}
4537      else
4538        {
4539	  tmpRect.size.height = rowsHeight;
4540        }
4541      // TODO width?
4542    }
4543  [super setFrame: tmpRect];
4544}
4545
4546- (void) setFrameSize: (NSSize)frameSize
4547{
4548  NSSize tmpSize = frameSize;
4549
4550  if ([_super_view respondsToSelector: @selector(documentVisibleRect)])
4551    {
4552      CGFloat rowsHeight = ((_numberOfRows * _rowHeight) + 1);
4553      NSRect docRect = [(NSClipView *)_super_view documentVisibleRect];
4554
4555      if (rowsHeight < docRect.size.height)
4556	{
4557	  tmpSize.height = docRect.size.height;
4558	}
4559      else
4560        {
4561          tmpSize.height = rowsHeight;
4562        }
4563      // TODO width?
4564    }
4565  [super setFrameSize: tmpSize];
4566}
4567
4568- (void) viewWillMoveToSuperview:(NSView *)newSuper
4569{
4570  [super viewWillMoveToSuperview: newSuper];
4571  /* need to potentially enlarge to fill the documentRect of the clip view */
4572  [self setFrame: _frame];
4573}
4574
4575- (void) sizeToFit
4576{
4577  NSTableColumn *tb;
4578  NSInteger i, j;
4579  CGFloat remainingWidth;
4580  columnSorting *columnInfo;
4581  CGFloat *currentWidth;
4582  CGFloat *maxWidth;
4583  CGFloat *minWidth;
4584  BOOL *isResizable;
4585  NSInteger numberOfCurrentColumns = 0;
4586  CGFloat previousPoint;
4587  CGFloat nextPoint;
4588  CGFloat toAddToCurrentColumns;
4589
4590  if ((_super_view == nil) || (_numberOfColumns == 0))
4591    return;
4592
4593  columnInfo = NSZoneMalloc(NSDefaultMallocZone(),
4594			    sizeof(columnSorting) * 2
4595			    * _numberOfColumns);
4596  currentWidth = NSZoneMalloc(NSDefaultMallocZone(),
4597			      sizeof(CGFloat) * _numberOfColumns);
4598  maxWidth = NSZoneMalloc(NSDefaultMallocZone(),
4599			  sizeof(CGFloat) * _numberOfColumns);
4600  minWidth = NSZoneMalloc(NSDefaultMallocZone(),
4601			  sizeof(CGFloat) * _numberOfColumns);
4602  isResizable = NSZoneMalloc(NSDefaultMallocZone(),
4603			     sizeof(BOOL) * _numberOfColumns);
4604
4605  remainingWidth = NSMaxX([self convertRect: [_super_view bounds]
4606				fromView: _super_view]);
4607
4608  /*
4609   *  We store the minWidth and the maxWidth of every column
4610   *  because we'll use those values *a lot*
4611   *  At the same time we set every column to its mininum width
4612   */
4613  for (i = 0; i < _numberOfColumns; i++)
4614    {
4615      tb = [_tableColumns objectAtIndex: i];
4616      isResizable[i] = [tb isResizable];
4617      if (isResizable[i] == YES)
4618	{
4619	  minWidth[i] = [tb minWidth];
4620	  maxWidth[i] = [tb maxWidth];
4621
4622	  if (minWidth[i] < 0)
4623	    minWidth[i] = 0;
4624	  if (minWidth[i] > maxWidth[i])
4625	    {
4626	      minWidth[i] = [tb width];
4627	      maxWidth[i] = minWidth[i];
4628	    }
4629	  columnInfo[i * 2].width = minWidth[i];
4630	  columnInfo[i * 2].isMax = 0;
4631	  currentWidth[i] = minWidth[i];
4632	  remainingWidth -= minWidth[i];
4633
4634	  columnInfo[i * 2 + 1].width = maxWidth[i];
4635	  columnInfo[i * 2 + 1].isMax = 1;
4636	}
4637      else
4638	{
4639	  minWidth[i] = [tb width];
4640	  columnInfo[i * 2].width = minWidth[i];
4641	  columnInfo[i * 2].isMax = 0;
4642	  currentWidth[i] = minWidth[i];
4643	  remainingWidth -= minWidth[i];
4644
4645	  maxWidth[i] = minWidth[i];
4646	  columnInfo[i * 2 + 1].width = maxWidth[i];
4647	  columnInfo[i * 2 + 1].isMax = 1;
4648	}
4649    }
4650
4651  // sort the info we have
4652  quick_sort_internal(columnInfo, 0, 2 * _numberOfColumns - 1);
4653
4654  previousPoint = columnInfo[0].width;
4655  numberOfCurrentColumns = 1;
4656
4657  if (remainingWidth >= 0.)
4658    {
4659      for (i = 1; i < 2 * _numberOfColumns; i++)
4660	{
4661	  nextPoint = columnInfo[i].width;
4662
4663	  if (numberOfCurrentColumns > 0 &&
4664	      (nextPoint - previousPoint) > 0.)
4665	    {
4666	      NSInteger verification = 0;
4667
4668	      if ((nextPoint - previousPoint) * numberOfCurrentColumns
4669		  <= remainingWidth)
4670		{
4671		  toAddToCurrentColumns = nextPoint - previousPoint;
4672		  remainingWidth -=
4673		    (nextPoint - previousPoint) * numberOfCurrentColumns;
4674
4675		  for (j = 0; j < _numberOfColumns; j++)
4676		    {
4677		      if (minWidth[j] <= previousPoint
4678			  && maxWidth[j] >= nextPoint)
4679			{
4680			  verification++;
4681			  currentWidth[j] += toAddToCurrentColumns;
4682			}
4683		    }
4684		  if (verification != numberOfCurrentColumns)
4685		    {
4686		      NSLog(@"[NSTableView sizeToFit]: unexpected error");
4687		    }
4688		}
4689	      else
4690		{
4691		  int remainingInt = floor(remainingWidth);
4692		  int quotient = remainingInt / numberOfCurrentColumns;
4693		  int remainder = remainingInt - quotient * numberOfCurrentColumns;
4694		  int oldRemainder = remainder;
4695
4696		  for (j = _numberOfColumns - 1; j >= 0; j--)
4697		    {
4698		      if (minWidth[j] <= previousPoint
4699			  && maxWidth[j] >= nextPoint)
4700			{
4701			  currentWidth[j] += quotient;
4702			  if (remainder > 0
4703			      && maxWidth[j] >= currentWidth[j] + 1)
4704			    {
4705			      remainder--;
4706			      currentWidth[j]++;
4707			    }
4708			}
4709		    }
4710		  while (oldRemainder > remainder && remainder > 0)
4711		    {
4712		      oldRemainder = remainder;
4713		      for (j = 0; j < _numberOfColumns; j++)
4714			{
4715			  if (minWidth[j] <= previousPoint
4716			      && maxWidth[j] >= nextPoint)
4717			    {
4718			      if (remainder > 0
4719				  && maxWidth[j] >= currentWidth[j] + 1)
4720				{
4721				  remainder--;
4722				  currentWidth[j]++;
4723				}
4724			    }
4725
4726			}
4727		    }
4728		  if (remainder > 0)
4729		    NSLog(@"There is still free space to fill.\
4730 However it seems better to use integer width for the columns");
4731		  else
4732		    remainingWidth = 0.;
4733		}
4734
4735
4736	    }
4737	  else if (numberOfCurrentColumns < 0)
4738	    {
4739	      NSLog(@"[NSTableView sizeToFit]: unexpected error");
4740	    }
4741
4742	  if (columnInfo[i].isMax)
4743	    numberOfCurrentColumns--;
4744	  else
4745	    numberOfCurrentColumns++;
4746	  previousPoint = nextPoint;
4747
4748	  if (remainingWidth == 0.)
4749	    {
4750	      break;
4751	    }
4752	}
4753    }
4754
4755  _tilingDisabled = YES;
4756
4757  remainingWidth = 0.;
4758  for (i = 0; i < _numberOfColumns; i++)
4759    {
4760      if (isResizable[i] == YES)
4761	{
4762	  tb = [_tableColumns objectAtIndex: i];
4763	  remainingWidth += currentWidth[i];
4764	  [tb setWidth: currentWidth[i]];
4765	}
4766      else
4767	{
4768	  remainingWidth += minWidth[i];
4769	}
4770    }
4771
4772  _tilingDisabled = NO;
4773  NSZoneFree(NSDefaultMallocZone(), columnInfo);
4774  NSZoneFree(NSDefaultMallocZone(), currentWidth);
4775  NSZoneFree(NSDefaultMallocZone(), maxWidth);
4776  NSZoneFree(NSDefaultMallocZone(), minWidth);
4777  NSZoneFree(NSDefaultMallocZone(), isResizable);
4778
4779  [self tile];
4780}
4781/*
4782- (void) sizeToFit
4783{
4784  NSCell *cell;
4785  NSEnumerator	*enumerator;
4786  NSTableColumn	*tb;
4787  float table_width;
4788  float width;
4789  float candidate_width;
4790  int row;
4791
4792  _tilingDisabled = YES;
4793
4794  // First Step
4795  // Resize Each Column to its Minimum Width
4796  table_width = _bounds.origin.x;
4797  enumerator = [_tableColumns objectEnumerator];
4798  while ((tb = [enumerator nextObject]) != nil)
4799    {
4800      // Compute min width of column
4801      width = [[tb headerCell] cellSize].width;
4802      for (row = 0; row < _numberOfRows; row++)
4803	{
4804	  cell = [self preparedCellAtColumn: [_tableColumns indexOfObject: tb]
4805                                        row: row];
4806	  [cell setObjectValue: [_dataSource tableView: self
4807					     objectValueForTableColumn: tb
4808					     row: row]];
4809	  [self _willDisplayCell: cell
4810	        forTableColumn: tb
4811	        row: row];
4812	  candidate_width = [cell cellSize].width;
4813
4814	  if (_drawsGrid)
4815	    candidate_width += 4;
4816
4817	  if (candidate_width > width)
4818	    {
4819	      width = candidate_width;
4820	    }
4821	}
4822      width += _intercellSpacing.width;
4823      [tb setWidth: width];
4824      // It is necessary to ask the column for the width, since it might have
4825      // been changed by the column to constrain it to a min or max width
4826      table_width += [tb width];
4827    }
4828
4829  // Second Step
4830  // If superview (clipview) is bigger than that, divide remaining space
4831  // between all columns
4832  if ((_super_view != nil) && (_numberOfColumns > 0))
4833    {
4834      float excess_width;
4835
4836      excess_width = NSMaxX ([self convertRect: [_super_view bounds]
4837				      fromView: _super_view]);
4838      excess_width -= table_width;
4839      // Since we resized each column at its minimum width,
4840      // it's useless to try shrinking more: we can't
4841      if (excess_width <= 0)
4842	{
4843	  _tilingDisabled = NO;
4844	  [self tile];
4845	  NSLog(@"exiting sizeToFit");
4846	  return;
4847	}
4848      excess_width = excess_width / _numberOfColumns;
4849
4850      enumerator = [_tableColumns objectEnumerator];
4851      while ((tb = [enumerator nextObject]) != nil)
4852	{
4853	  [tb setWidth: ([tb width] + excess_width)];
4854	}
4855    }
4856
4857  _tilingDisabled = NO;
4858  [self tile];
4859  NSLog(@"exiting sizeToFit");
4860}
4861*/
4862
4863- (void) noteNumberOfRowsChanged
4864{
4865  NSRect newFrame;
4866
4867  _numberOfRows = [self _numRows];
4868
4869  /* If we are selecting rows, we have to check that we have no
4870     selected rows below the new end of the table */
4871  if (!_selectingColumns)
4872    {
4873      NSUInteger row = [_selectedRows lastIndex];
4874
4875      if (row == NSNotFound)
4876        {
4877          if (!_allowsEmptySelection)
4878            {
4879              /* We shouldn't allow empty selection - try
4880                 selecting the last row */
4881              NSInteger lastRow = _numberOfRows - 1;
4882
4883              if (lastRow > -1)
4884                {
4885                  [self _postSelectionIsChangingNotification];
4886                  [_selectedRows addIndex: lastRow];
4887                  _selectedRow = lastRow;
4888                  [self _postSelectionDidChangeNotification];
4889                }
4890              else
4891                {
4892                  /* problem - there are no rows at all */
4893                  _selectedRow = -1;
4894                }
4895            }
4896        }
4897      /* Check that all selected rows are in the new range of rows */
4898      else if (row >= _numberOfRows)
4899        {
4900          [_selectedRows removeIndexesInRange:
4901             NSMakeRange(_numberOfRows,  row + 1 - _numberOfRows)];
4902          if (_selectedRow >= _numberOfRows)
4903            {
4904              row = [_selectedRows lastIndex];
4905              [self _postSelectionIsChangingNotification];
4906
4907              if (row != NSNotFound)
4908                {
4909                  _selectedRow = row;
4910                }
4911              else
4912                {
4913                  /* Argh - all selected rows were outside the table */
4914                  if (_allowsEmptySelection)
4915                    {
4916                      _selectedRow = -1;
4917                    }
4918                  else
4919                    {
4920                      /* We shouldn't allow empty selection - try
4921                         selecting the last row */
4922                      NSInteger lastRow = _numberOfRows - 1;
4923
4924                      if (lastRow > -1)
4925                        {
4926                          [_selectedRows addIndex: lastRow];
4927                          _selectedRow = lastRow;
4928                        }
4929                      else
4930                        {
4931                          /* problem - there are no rows at all */
4932                          _selectedRow = -1;
4933                        }
4934                    }
4935                }
4936              [self _postSelectionDidChangeNotification];
4937            }
4938        }
4939    }
4940
4941  newFrame = _frame;
4942  newFrame.size.height = (_numberOfRows * _rowHeight) + 1;
4943  if (NO == NSEqualRects(newFrame, NSUnionRect(newFrame, _frame)))
4944    {
4945      [_super_view setNeedsDisplayInRect: _frame];
4946    }
4947  [self setFrame: newFrame];
4948
4949  /* If we are shorter in height than the enclosing clipview, we
4950     should redraw us now. */
4951  if (_super_view != nil)
4952    {
4953      NSRect superviewBounds; // Get this *after* [self setFrame:]
4954      superviewBounds = [_super_view bounds];
4955      if ((superviewBounds.origin.x <= _frame.origin.x)
4956        && (NSMaxY(superviewBounds) >= NSMaxY(_frame)))
4957        {
4958          [self setNeedsDisplay: YES];
4959        }
4960    }
4961}
4962
4963- (void) tile
4964{
4965  CGFloat table_width = 0;
4966  CGFloat table_height;
4967
4968  if (_tilingDisabled == YES)
4969    return;
4970
4971  if (_numberOfColumns > 0)
4972    {
4973      NSInteger i;
4974      CGFloat width;
4975
4976      _columnOrigins[0] = _bounds.origin.x;
4977      width = [[_tableColumns objectAtIndex: 0] width];
4978      table_width += width;
4979      for (i = 1; i < _numberOfColumns; i++)
4980	{
4981	  _columnOrigins[i] = _columnOrigins[i - 1] + width;
4982	  width = [[_tableColumns objectAtIndex: i] width];
4983	  table_width += width;
4984	}
4985    }
4986  /* + 1 for the last grid line */
4987  table_height = (_numberOfRows * _rowHeight) + 1;
4988  [self setFrameSize: NSMakeSize (table_width, table_height)];
4989  [self setNeedsDisplay: YES];
4990
4991  if (_headerView != nil)
4992    {
4993      CGFloat innerBorderWidth = [[NSUserDefaults standardUserDefaults]
4994				   boolForKey: @"GSScrollViewNoInnerBorder"] ? 0.0 : 1.0;
4995
4996      [_headerView setFrameSize:
4997		     NSMakeSize (_frame.size.width,
4998				 [_headerView frame].size.height)];
4999      [_cornerView setFrameSize:
5000		     NSMakeSize ([NSScroller scrollerWidth] + innerBorderWidth,
5001				 [_headerView frame].size.height)];
5002      [_headerView setNeedsDisplay: YES];
5003      [_cornerView setNeedsDisplay: YES];
5004    }
5005}
5006
5007/*
5008 * Drawing
5009 */
5010
5011- (void) drawRow: (NSInteger)rowIndex clipRect: (NSRect)clipRect
5012{
5013  [[GSTheme theme] drawTableViewRow: rowIndex
5014		   clipRect: clipRect
5015		   inView: self];
5016}
5017
5018- (void) noteHeightOfRowsWithIndexesChanged: (NSIndexSet*)indexes
5019{
5020  // FIXME
5021}
5022
5023- (void) drawGridInClipRect: (NSRect)aRect
5024{
5025  [[GSTheme theme] drawTableViewGridInClipRect: aRect
5026		   inView: self];
5027}
5028
5029- (void) highlightSelectionInClipRect: (NSRect)clipRect
5030{
5031  [[GSTheme theme] highlightTableViewSelectionInClipRect: clipRect
5032		   inView: self
5033		   selectingColumns: _selectingColumns];
5034}
5035
5036- (void) drawBackgroundInClipRect: (NSRect)clipRect
5037{
5038  [[GSTheme theme] drawTableViewBackgroundInClipRect: clipRect
5039		   inView: self
5040		   withBackgroundColor: _backgroundColor];
5041}
5042
5043- (void) drawRect: (NSRect)aRect
5044{
5045  [[GSTheme theme] drawTableViewRect: aRect
5046		   inView: self];
5047}
5048
5049- (BOOL) isOpaque
5050{
5051  return YES;
5052}
5053
5054/*
5055 * Scrolling
5056 */
5057
5058- (void) scrollRowToVisible: (NSInteger)rowIndex
5059{
5060  if (_super_view != nil)
5061    {
5062      NSRect rowRect = [self rectOfRow: rowIndex];
5063      NSRect visibleRect = [self visibleRect];
5064
5065      // If the row is over the top, or it is partially visible
5066      // on top,
5067      if ((rowRect.origin.y < visibleRect.origin.y))
5068	{
5069	  // Then make it visible on top
5070	  NSPoint newOrigin;
5071
5072	  newOrigin.x = visibleRect.origin.x;
5073	  newOrigin.y = rowRect.origin.y;
5074	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
5075	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
5076	  return;
5077	}
5078      // If the row is under the bottom, or it is partially visible on
5079      // the bottom,
5080      if (NSMaxY (rowRect) > NSMaxY (visibleRect))
5081	{
5082	  // Then make it visible on bottom
5083	  NSPoint newOrigin;
5084
5085	  newOrigin.x = visibleRect.origin.x;
5086	  newOrigin.y = visibleRect.origin.y;
5087	  newOrigin.y += NSMaxY (rowRect) - NSMaxY (visibleRect);
5088	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
5089	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
5090	  return;
5091	}
5092    }
5093}
5094
5095- (void) scrollColumnToVisible: (NSInteger)columnIndex
5096{
5097  if (_super_view != nil)
5098    {
5099      NSRect columnRect = [self rectOfColumn: columnIndex];
5100      NSRect visibleRect = [self visibleRect];
5101      CGFloat diff;
5102
5103      // If the row is out on the left, or it is partially visible
5104      // on the left
5105      if ((columnRect.origin.x < visibleRect.origin.x))
5106	{
5107	  // Then make it visible on the left
5108	  NSPoint newOrigin;
5109
5110	  newOrigin.x = columnRect.origin.x;
5111	  newOrigin.y = visibleRect.origin.y;
5112	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
5113	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
5114	  return;
5115	}
5116      diff = NSMaxX (columnRect) - NSMaxX (visibleRect);
5117      // If the row is out on the right, or it is partially visible on
5118      // the right,
5119      if (diff > 0)
5120	{
5121	  // Then make it visible on the right
5122	  NSPoint newOrigin;
5123
5124	  newOrigin.x = visibleRect.origin.x;
5125	  newOrigin.y = visibleRect.origin.y;
5126	  newOrigin.x += diff;
5127	  newOrigin = [self convertPoint: newOrigin  toView: _super_view];
5128	  [(NSClipView *)_super_view scrollToPoint: newOrigin];
5129	  return;
5130	}
5131    }
5132}
5133
5134
5135/*
5136 * Text delegate methods
5137 */
5138
5139- (void) textDidBeginEditing: (NSNotification *)aNotification
5140{
5141  [super textDidBeginEditing: aNotification];
5142}
5143
5144- (void) textDidChange: (NSNotification *)aNotification
5145{
5146  // MacOS-X asks us to inform the cell if possible.
5147  if ((_editedCell != nil) && [_editedCell respondsToSelector:
5148						 @selector(textDidChange:)])
5149    [_editedCell textDidChange: aNotification];
5150
5151  [super textDidChange: aNotification];
5152}
5153
5154- (void) textDidEndEditing: (NSNotification *)aNotification
5155{
5156  id textMovement;
5157  NSInteger row, column;
5158
5159  /* Save values */
5160  row = _editedRow;
5161  column = _editedColumn;
5162
5163  [super textDidEndEditing: aNotification];
5164
5165  textMovement = [[aNotification userInfo] objectForKey: @"NSTextMovement"];
5166  if (textMovement)
5167    {
5168      switch ([(NSNumber *)textMovement intValue])
5169	{
5170	case NSReturnTextMovement:
5171	  [self _editNextCellAfterRow: row inColumn: column];
5172	  // Send action ?
5173	  break;
5174	case NSTabTextMovement:
5175	  if ([self _editNextEditableCellAfterRow: row  column: column] == YES)
5176	    {
5177	      break;
5178	    }
5179	  [_window selectKeyViewFollowingView: self];
5180	  break;
5181	case NSBacktabTextMovement:
5182	  if ([self _editPreviousEditableCellBeforeRow: row  column: column] == YES)
5183	    {
5184	      break;
5185	    }
5186	  [_window selectKeyViewPrecedingView: self];
5187	  break;
5188	}
5189    }
5190}
5191
5192- (BOOL) textShouldBeginEditing: (NSText *)textObject
5193{
5194  if (_delegate && [_delegate respondsToSelector:
5195				@selector(control:textShouldBeginEditing:)])
5196    return [_delegate control: self
5197		      textShouldBeginEditing: textObject];
5198  else
5199    return YES;
5200}
5201
5202- (BOOL) textShouldEndEditing: (NSText*)textObject
5203{
5204  if ([_delegate respondsToSelector:
5205		   @selector(control:textShouldEndEditing:)])
5206    {
5207      if ([_delegate control: self
5208		     textShouldEndEditing: textObject] == NO)
5209	{
5210	  NSBeep ();
5211	  return NO;
5212	}
5213
5214      return YES;
5215    }
5216
5217  if ([_delegate respondsToSelector:
5218		   @selector(control:isValidObject:)] == YES)
5219    {
5220      NSFormatter *formatter;
5221      id newObjectValue;
5222
5223      formatter = [_editedCell formatter];
5224
5225      if ([formatter getObjectValue: &newObjectValue
5226		     forString: [_textObject text]
5227		     errorDescription: NULL] == YES)
5228	{
5229	  if ([_delegate control: self
5230			 isValidObject: newObjectValue] == NO)
5231	    return NO;
5232	}
5233    }
5234
5235  return [_editedCell isEntryAcceptable: [textObject text]];
5236}
5237
5238/*
5239 * Persistence
5240 */
5241
5242- (NSString *) autosaveName
5243{
5244  return _autosaveName;
5245}
5246
5247- (BOOL) autosaveTableColumns
5248{
5249  return _autosaveTableColumns;
5250}
5251
5252- (void) setAutosaveName: (NSString *)name
5253{
5254  ASSIGN (_autosaveName, name);
5255  [self _autoloadTableColumns];
5256}
5257
5258- (void) setAutosaveTableColumns: (BOOL)flag
5259{
5260  if (flag == _autosaveTableColumns)
5261    {
5262      return;
5263    }
5264
5265  _autosaveTableColumns = flag;
5266  if (flag)
5267    {
5268      [self _autoloadTableColumns];
5269      [nc addObserver: self
5270          selector: @selector(_autosaveTableColumns)
5271	  name: NSTableViewColumnDidResizeNotification
5272	  object: self];
5273    }
5274  else
5275    {
5276      [nc removeObserver: self
5277	  name: NSTableViewColumnDidResizeNotification
5278	  object: self];
5279    }
5280}
5281
5282/*
5283 * Delegate
5284 */
5285
5286- (void) setDelegate: (id)anObject
5287{
5288  const SEL sel = @selector(tableView:willDisplayCell:forTableColumn:row:);
5289
5290  if (_delegate)
5291    [nc removeObserver: _delegate name: nil object: self];
5292  _delegate = anObject;
5293
5294#define SET_DELEGATE_NOTIFICATION(notif_name) \
5295  if ([_delegate respondsToSelector: @selector(tableView##notif_name:)]) \
5296    [nc addObserver: _delegate \
5297      selector: @selector(tableView##notif_name:) \
5298      name: NSTableView##notif_name##Notification object: self]
5299
5300  SET_DELEGATE_NOTIFICATION(ColumnDidMove);
5301  SET_DELEGATE_NOTIFICATION(ColumnDidResize);
5302  SET_DELEGATE_NOTIFICATION(SelectionDidChange);
5303  SET_DELEGATE_NOTIFICATION(SelectionIsChanging);
5304
5305  /* Cache */
5306  _del_responds = [_delegate respondsToSelector: sel];
5307}
5308
5309- (id) delegate
5310{
5311  return _delegate;
5312}
5313
5314
5315/* indicator image */
5316- (NSImage *) indicatorImageInTableColumn: (NSTableColumn *)aTableColumn
5317{
5318  // TODO
5319  NSLog(@"Method %s is not implemented for class %s",
5320	"indicatorImageInTableColumn:", "NSTableView");
5321  return nil;
5322}
5323
5324- (void) setIndicatorImage: (NSImage *)anImage
5325	     inTableColumn: (NSTableColumn *)aTableColumn
5326{
5327  // TODO
5328  NSLog(@"Method %s is not implemented for class %s",
5329	"setIndicatorImage:inTableColumn:", "NSTableView");
5330}
5331
5332/* highlighting columns */
5333- (NSTableColumn *) highlightedTableColumn
5334{
5335  return _highlightedTableColumn;
5336}
5337
5338- (void) setHighlightedTableColumn: (NSTableColumn *)aTableColumn
5339{
5340  NSUInteger tableColumnIndex;
5341
5342  tableColumnIndex = [_tableColumns indexOfObject: aTableColumn];
5343
5344  if (tableColumnIndex == NSNotFound)
5345    {
5346      NSLog(@"setHighlightedTableColumn received an invalid\
5347 NSTableColumn object");
5348      return;
5349    }
5350
5351  // we do not need to retain aTableColumn as it is already in
5352  // _tableColumns array
5353  _highlightedTableColumn = aTableColumn;
5354
5355  [_headerView setNeedsDisplay: YES];
5356}
5357
5358/* dragging rows */
5359- (NSImage*) dragImageForRows: (NSArray*)dragRows
5360                        event: (NSEvent*)dragEvent
5361              dragImageOffset: (NSPoint*)dragImageOffset
5362{
5363  // FIXME
5364  NSImage *dragImage = [[NSImage alloc]
5365			 initWithSize: NSMakeSize(8, 8)];
5366
5367  return AUTORELEASE(dragImage);
5368}
5369
5370- (NSImage *) dragImageForRowsWithIndexes: (NSIndexSet*)rows
5371                             tableColumns: (NSArray*)cols
5372                                    event: (NSEvent*)event
5373                                   offset: (NSPoint*)offset
5374{
5375  // FIXME
5376  NSArray *rowArray;
5377
5378  rowArray = [self _indexSetToArray: rows];
5379  return [self dragImageForRows: rowArray
5380               event: event
5381               dragImageOffset: offset];
5382}
5383
5384- (void) setDropRow: (NSInteger)row
5385      dropOperation: (NSTableViewDropOperation)operation
5386{
5387  if (row < -1 || row > _numberOfRows
5388    || (operation == NSTableViewDropOn && row == _numberOfRows))
5389    {
5390      currentDropRow = -1;
5391      currentDropOperation = NSTableViewDropOn;
5392    }
5393  else
5394    {
5395      currentDropRow = row;
5396      currentDropOperation = operation;
5397    }
5398}
5399
5400- (void) setVerticalMotionCanBeginDrag: (BOOL)flag
5401{
5402  _verticalMotionDrag = flag;
5403}
5404
5405- (BOOL) verticalMotionCanBeginDrag
5406{
5407  return _verticalMotionDrag;
5408}
5409
5410- (NSArray*) namesOfPromisedFilesDroppedAtDestination: (NSURL *)dropDestination
5411{
5412  if ([_dataSource respondsToSelector:
5413                    @selector(tableView:namesOfPromisedFilesDroppedAtDestination:forDraggedRowsWithIndexes:)])
5414    {
5415      return [_dataSource tableView: self
5416                          namesOfPromisedFilesDroppedAtDestination: dropDestination
5417                          forDraggedRowsWithIndexes: _selectedRows];
5418    }
5419  else
5420    {
5421      return nil;
5422    }
5423}
5424
5425/*
5426 * Encoding/Decoding
5427 */
5428
5429- (void) encodeWithCoder: (NSCoder*)aCoder
5430{
5431  if ([aCoder allowsKeyedCoding])
5432    {
5433      unsigned int vFlags = 0;
5434      NSSize intercellSpacing = [self intercellSpacing];
5435      GSTableViewFlags tableViewFlags;
5436
5437      // make sure the corner view is properly encoded...
5438      [super encodeWithCoder: aCoder];
5439
5440      if ([self dataSource])
5441	{
5442	  [aCoder encodeObject: [self dataSource] forKey: @"NSDataSource"];
5443	}
5444      if ([self delegate])
5445	{
5446	  [aCoder encodeObject: [self delegate] forKey: @"NSDelegate"];
5447	}
5448      if ([self target])
5449	{
5450	  [aCoder encodeObject: [self target] forKey: @"NSTarget"];
5451	}
5452      if ([self action])
5453	{
5454	  [aCoder encodeObject: NSStringFromSelector([self action]) forKey: @"NSAction"];
5455	}
5456      if ([self doubleAction] != NULL)
5457	{
5458	  [aCoder encodeObject: NSStringFromSelector([self doubleAction]) forKey: @"NSDoubleAction"];
5459	}
5460
5461      [aCoder encodeObject: [self backgroundColor] forKey: @"NSBackgroundColor"];
5462      [aCoder encodeObject: [self gridColor] forKey: @"NSGridColor"];
5463      [aCoder encodeFloat: intercellSpacing.height forKey: @"NSIntercellSpacingHeight"];
5464      [aCoder encodeFloat: intercellSpacing.width forKey: @"NSIntercellSpacingWidth"];
5465      [aCoder encodeFloat: [self rowHeight] forKey: @"NSRowHeight"];
5466      [aCoder encodeObject: [self tableColumns] forKey: @"NSTableColumns"];
5467
5468      if (_headerView)
5469        {
5470          [aCoder encodeObject: _headerView forKey: @"NSHeaderView"];
5471        }
5472      if (_cornerView)
5473        {
5474          [aCoder encodeObject: _cornerView forKey: @"NSCornerView"];
5475        }
5476
5477      if ([[self sortDescriptors] count] > 0)
5478        {
5479          [aCoder encodeObject: _sortDescriptors forKey: @"NSSortDescriptors"];
5480        }
5481
5482      tableViewFlags.columnSelection = [self allowsColumnSelection];
5483      tableViewFlags.multipleSelection = [self allowsMultipleSelection];
5484      tableViewFlags.emptySelection = [self allowsEmptySelection];
5485      tableViewFlags.drawsGrid = [self drawsGrid];
5486      tableViewFlags.columnResizing = [self allowsColumnResizing];
5487      tableViewFlags.columnOrdering = [self allowsColumnReordering];
5488
5489      memcpy((void *)&vFlags,(void *)&tableViewFlags,sizeof(unsigned int));
5490
5491      // encode..
5492      [aCoder encodeInt: vFlags forKey: @"NSTvFlags"];
5493    }
5494  else
5495    {
5496      float rowHeight;
5497      int number;
5498
5499      [super encodeWithCoder: aCoder];
5500      [aCoder encodeConditionalObject: _dataSource];
5501      [aCoder encodeObject: _tableColumns];
5502      [aCoder encodeObject: _gridColor];
5503      [aCoder encodeObject: _backgroundColor];
5504      [aCoder encodeObject: _headerView];
5505      [aCoder encodeObject: _cornerView];
5506      [aCoder encodeConditionalObject: _delegate];
5507      [aCoder encodeConditionalObject: _target];
5508
5509      number = _numberOfRows;
5510      [aCoder encodeValueOfObjCType: @encode(int) at: &number];
5511      number = _numberOfColumns;
5512      [aCoder encodeValueOfObjCType: @encode(int) at: &number];
5513
5514      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_drawsGrid];
5515      rowHeight = _rowHeight;
5516      [aCoder encodeValueOfObjCType: @encode(float) at: &rowHeight];
5517      [aCoder encodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
5518      [aCoder encodeSize: _intercellSpacing];
5519
5520      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
5521      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
5522      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnSelection];
5523      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnResizing];
5524      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnReordering];
5525      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autoresizesAllColumnsToFit];
5526      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_verticalMotionDrag];
5527      [aCoder encodeObject: _sortDescriptors];
5528    }
5529}
5530
5531- (id) initWithCoder: (NSCoder*)aDecoder
5532{
5533  self = [super initWithCoder: aDecoder];
5534  if (!self)
5535    return self;
5536
5537  if ([aDecoder allowsKeyedCoding])
5538    {
5539      NSSize intercellSpacing;
5540      NSArray *columns;
5541      NSEnumerator *e;
5542      NSTableColumn *col;
5543
5544      // assign defaults, so that there's color in case none is specified
5545      [self _initDefaults];
5546      ASSIGN(_gridColor, [NSColor gridColor]);
5547      ASSIGN(_backgroundColor, [NSColor controlBackgroundColor]);
5548      ASSIGN(_tableColumns, [NSMutableArray array]);
5549      ASSIGN(_sortDescriptors, [NSArray array]);
5550
5551      //
5552      // Check for nil on some of these, since they are usually set
5553      // in NSIBOutletConnector objects we don't want to override
5554      // that setting unless they're directly encoded with the
5555      // object.
5556      //
5557      // I'm not sure why IB encodes nil values for these, but
5558      // the behaviour here should match that on Mac OS X.
5559      //
5560      if ([aDecoder containsValueForKey: @"NSDataSource"])
5561        {
5562	  id obj = [aDecoder decodeObjectForKey: @"NSDataSource"];
5563	  if(obj != nil)
5564	    {
5565	      [self setDataSource: obj];
5566	    }
5567	}
5568      if ([aDecoder containsValueForKey: @"NSDelegate"])
5569        {
5570	  id obj = [aDecoder decodeObjectForKey: @"NSDelegate"];
5571	  if(obj != nil)
5572	    {
5573	      [self setDelegate: obj];
5574	    }
5575	}
5576      if ([aDecoder containsValueForKey: @"NSTarget"])
5577        {
5578	  id obj = [aDecoder decodeObjectForKey: @"NSTarget"];
5579	  if(obj != nil)
5580	    {
5581	      [self setTarget: obj];
5582	    }
5583	}
5584      if ([aDecoder containsValueForKey: @"NSAction"])
5585        {
5586          NSString *action = [aDecoder decodeObjectForKey: @"NSAction"];
5587	  if(action != nil)
5588	    {
5589	      [self setAction: NSSelectorFromString(action)];
5590	    }
5591	}
5592      if ([aDecoder containsValueForKey: @"NSDoubleAction"])
5593        {
5594          NSString *action = [aDecoder decodeObjectForKey: @"NSDoubleAction"];
5595	  if(action != nil)
5596	    {
5597	      [self setDoubleAction: NSSelectorFromString(action)];
5598	    }
5599	}
5600
5601      if ([aDecoder containsValueForKey: @"NSBackgroundColor"])
5602        {
5603          [self setBackgroundColor: [aDecoder decodeObjectForKey: @"NSBackgroundColor"]];
5604        }
5605      if ([aDecoder containsValueForKey: @"NSGridColor"])
5606        {
5607          [self setGridColor: [aDecoder decodeObjectForKey: @"NSGridColor"]];
5608        }
5609
5610      intercellSpacing = [self intercellSpacing];
5611      if ([aDecoder containsValueForKey: @"NSIntercellSpacingHeight"])
5612        {
5613          intercellSpacing.height = [aDecoder decodeFloatForKey: @"NSIntercellSpacingHeight"];
5614        }
5615      if ([aDecoder containsValueForKey: @"NSIntercellSpacingWidth"])
5616        {
5617          intercellSpacing.width = [aDecoder decodeFloatForKey: @"NSIntercellSpacingWidth"];
5618        }
5619      [self setIntercellSpacing: intercellSpacing];
5620
5621      if ([aDecoder containsValueForKey: @"NSDraggingSourceMaskForLocal"])
5622        {
5623          [self setDraggingSourceOperationMask:
5624                    [aDecoder decodeIntForKey: @"NSDraggingSourceMaskForLocal"]
5625                forLocal: YES];
5626        }
5627      if ([aDecoder containsValueForKey: @"NSDraggingSourceMaskForNonLocal"])
5628        {
5629          [self setDraggingSourceOperationMask:
5630                    [aDecoder decodeIntForKey: @"NSDraggingSourceMaskForNonLocal"]
5631                forLocal: NO];
5632        }
5633
5634      if ([aDecoder containsValueForKey: @"NSRowHeight"])
5635        {
5636          [self setRowHeight: [aDecoder decodeFloatForKey: @"NSRowHeight"]];
5637        }
5638
5639      if ([aDecoder containsValueForKey: @"NSCornerView"])
5640        {
5641	  NSView *aView = [aDecoder decodeObjectForKey: @"NSCornerView"];
5642          [self setCornerView: aView];
5643	  [aView setHidden: NO];
5644        }
5645      else
5646        {
5647          _cornerView = [GSTableCornerView new];
5648        }
5649
5650      if ([aDecoder containsValueForKey: @"NSHeaderView"])
5651        {
5652          [self setHeaderView: [aDecoder decodeObjectForKey: @"NSHeaderView"]];
5653        }
5654
5655      if ([aDecoder containsValueForKey: @"NSSortDescriptors"])
5656        {
5657          ASSIGN(_sortDescriptors, [aDecoder decodeObjectForKey: @"NSSortDescriptors"]);
5658        }
5659
5660      if ([aDecoder containsValueForKey: @"NSTvFlags"])
5661        {
5662          unsigned int flags = [aDecoder decodeIntForKey: @"NSTvFlags"];
5663          GSTableViewFlags tableViewFlags;
5664          memcpy((void *)&tableViewFlags,(void *)&flags,sizeof(struct _tableViewFlags));
5665
5666          [self setAllowsColumnSelection: tableViewFlags.columnSelection];
5667          [self setAllowsMultipleSelection: tableViewFlags.multipleSelection];
5668          [self setAllowsEmptySelection: tableViewFlags.emptySelection];
5669          [self setDrawsGrid: tableViewFlags.drawsGrid];
5670          [self setAllowsColumnResizing: tableViewFlags.columnResizing];
5671          [self setAllowsColumnReordering: tableViewFlags.columnOrdering];
5672        }
5673
5674      // get the table columns...
5675      columns = [aDecoder decodeObjectForKey: @"NSTableColumns"];
5676      e = [columns objectEnumerator];
5677      while ((col = [e nextObject]) != nil)
5678        {
5679          /* Will initialize -[NSTableColumn tableView], _numberOfColumns and
5680             allocate _columnsOrigins */
5681          [self addTableColumn: col];
5682        }
5683
5684      [self tile]; /* Initialize _columnOrigins */
5685    }
5686  else
5687    {
5688      int version = [aDecoder versionForClassName:
5689                                  @"NSTableView"];
5690      id aDelegate;
5691      float rowHeight;
5692      int number;
5693
5694      [self _initDefaults];
5695      _dataSource      = [aDecoder decodeObject];
5696      _tableColumns    = RETAIN([aDecoder decodeObject]);
5697      _gridColor       = RETAIN([aDecoder decodeObject]);
5698      _backgroundColor = RETAIN([aDecoder decodeObject]);
5699      _headerView      = RETAIN([aDecoder decodeObject]);
5700      _cornerView      = RETAIN([aDecoder decodeObject]);
5701      aDelegate        = [aDecoder decodeObject];
5702      _target          = [aDecoder decodeObject];
5703
5704      [self setDelegate: aDelegate];
5705      [_headerView setTableView: self];
5706      [_tableColumns makeObjectsPerformSelector: @selector(setTableView:)
5707                     withObject: self];
5708
5709      [aDecoder decodeValueOfObjCType: @encode(int) at: &number];
5710      _numberOfRows = number;
5711      [aDecoder decodeValueOfObjCType: @encode(int) at: &number];
5712      _numberOfColumns = number;
5713      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_drawsGrid];
5714      [aDecoder decodeValueOfObjCType: @encode(float) at: &rowHeight];
5715      _rowHeight = rowHeight;
5716      [aDecoder decodeValueOfObjCType: @encode(SEL) at: &_doubleAction];
5717      _intercellSpacing = [aDecoder decodeSize];
5718
5719      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsMultipleSelection];
5720      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsEmptySelection];
5721      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnSelection];
5722      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnResizing];
5723      if (version >= 3)
5724        {
5725          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_allowsColumnReordering];
5726        }
5727      if (version >= 2)
5728        {
5729          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autoresizesAllColumnsToFit];
5730        }
5731
5732      if (version >= 4)
5733        {
5734          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_verticalMotionDrag];
5735        }
5736      if (version >= 5)
5737	{
5738	  ASSIGN(_sortDescriptors, [aDecoder decodeObject]);
5739	}
5740
5741      if (_numberOfColumns > 0)
5742        {
5743          _columnOrigins = NSZoneMalloc (NSDefaultMallocZone (),
5744                                         sizeof(CGFloat) * _numberOfColumns);
5745        }
5746      [self tile]; /* Initialize _columnOrigins */
5747    }
5748
5749  return self;
5750}
5751
5752- (void) updateCell: (NSCell*)aCell
5753{
5754  NSInteger i, j;
5755
5756  if (aCell == nil)
5757    return;
5758
5759  return;
5760
5761  for (i = 0; i < _numberOfColumns; i++)
5762    {
5763      if ([self preparedCellAtColumn: i row: -1] == aCell)
5764	{
5765	  [self setNeedsDisplayInRect: [self rectOfColumn: i]];
5766	}
5767      else
5768	{
5769	  NSRect columnRect = [self rectOfColumn: i];
5770	  NSRect rowRect;
5771	  NSRect visibleRect = [self convertRect: [_super_view bounds]
5772				     toView: self];
5773	  NSPoint top = NSMakePoint(NSMinX(visibleRect),
5774				    NSMinY(visibleRect));
5775	  NSPoint bottom = NSMakePoint(NSMinX(visibleRect),
5776				       NSMaxY(visibleRect));
5777	  NSInteger firstVisibleRow = [self rowAtPoint: top];
5778	  NSInteger lastVisibleRow = [self rowAtPoint: bottom];
5779
5780	  if (firstVisibleRow == -1)
5781	    firstVisibleRow = 0;
5782
5783	  if (lastVisibleRow == -1)
5784	    lastVisibleRow = _numberOfColumns - 1;
5785
5786	  for (j = firstVisibleRow; j < lastVisibleRow; j++)
5787	    {
5788	      if ([self preparedCellAtColumn: i row: j] == aCell)
5789		{
5790		  rowRect = [self rectOfRow: j];
5791		  [self setNeedsDisplayInRect:
5792			  NSIntersectionRect(columnRect, rowRect)];
5793		}
5794	    }
5795	}
5796    }
5797}
5798
5799- (void) _userResizedTableColumn: (NSInteger)index
5800			   width: (CGFloat)width
5801{
5802  [[_tableColumns objectAtIndex: index] setWidth: width];
5803}
5804
5805- (CGFloat *) _columnOrigins
5806{
5807  return _columnOrigins;
5808}
5809
5810- (void) _mouseDownInHeaderOfTableColumn: (NSTableColumn *)tc
5811{
5812  if ([_delegate
5813	respondsToSelector:
5814	  @selector(tableView:mouseDownInHeaderOfTableColumn:)])
5815    {
5816      [_delegate tableView: self
5817		 mouseDownInHeaderOfTableColumn: tc];
5818    }
5819}
5820
5821- (void) _clickTableColumn: (NSTableColumn *)tc
5822{
5823  NSSortDescriptor *oldMainSortDescriptor = nil;
5824  NSSortDescriptor *newMainSortDescriptor = [tc sortDescriptorPrototype];
5825  NSMutableArray *newSortDescriptors =
5826    [NSMutableArray arrayWithArray: [self sortDescriptors]];
5827  NSEnumerator *e = [newSortDescriptors objectEnumerator];
5828  NSSortDescriptor *descriptor = nil;
5829  NSMutableArray *outdatedDescriptors = [NSMutableArray array];
5830
5831  if ([[self sortDescriptors] count] > 0)
5832    {
5833      oldMainSortDescriptor = [[self sortDescriptors] objectAtIndex: 0];
5834    }
5835
5836  /* Remove every main descriptor equivalents (normally only one) */
5837  while ((descriptor = [e nextObject]) != nil)
5838    {
5839      if ([[descriptor key] isEqual: [newMainSortDescriptor key]])
5840        [outdatedDescriptors addObject: descriptor];
5841    }
5842
5843  /* Invert the sort direction when the same column header is clicked twice */
5844  if ([[newMainSortDescriptor key] isEqual: [oldMainSortDescriptor key]])
5845    {
5846      newMainSortDescriptor = [oldMainSortDescriptor reversedSortDescriptor];
5847    }
5848
5849  [newSortDescriptors removeObjectsInArray: outdatedDescriptors];
5850  if (newMainSortDescriptor != nil)
5851	[newSortDescriptors insertObject: newMainSortDescriptor atIndex: 0];
5852
5853  [self setSortDescriptors: newSortDescriptors];
5854
5855  [self _didClickTableColumn: tc];
5856}
5857
5858- (void) _editNextCellAfterRow: (NSInteger) row
5859		inColumn: (NSInteger) column
5860{
5861  if (++row >= _numberOfRows)
5862    row = 0;
5863
5864  if ([self _shouldSelectRow: row])
5865    {
5866      [self selectRowIndexes: [NSIndexSet indexSetWithIndex: row]
5867	      byExtendingSelection: NO];
5868
5869      if ([self _isCellEditableColumn: column row:row])
5870        {
5871          [self editColumn: column
5872                row: row
5873                withEvent: nil
5874                select: YES];
5875        }
5876    }
5877}
5878
5879-(BOOL) _editNextEditableCellAfterRow: (NSInteger)row
5880                               column: (NSInteger)column
5881{
5882  NSInteger i, j;
5883
5884  if (row > -1)
5885    {
5886      // First look for cells in the same row
5887      for (j = column + 1; j < _numberOfColumns; j++)
5888        {
5889          if ([self _isCellEditableColumn: j row: row])
5890            {
5891              [self editColumn: j row: row withEvent: nil select: YES];
5892              return YES;
5893            }
5894        }
5895    }
5896
5897  // Otherwise, make the big cycle.
5898  for (i = row + 1; i < _numberOfRows; i++)
5899    {
5900      for (j = 0; j < _numberOfColumns; j++)
5901        {
5902          if ([self _isCellEditableColumn: j row: i])
5903            {
5904              // Need to select row to be able to edit it.
5905              [self selectRow: i byExtendingSelection: NO];
5906              [self editColumn: j row: i withEvent: nil select: YES];
5907              return YES;
5908            }
5909        }
5910    }
5911
5912// Should we loop around or not?
5913#if 0
5914  // Nothing found? Search in the rows before the current
5915  for (i = 0; i < row; i++)
5916    {
5917      for (j = 0; j < _numberOfColumns; j++)
5918        {
5919          if ([self _isCellEditableColumn: j row: i])
5920            {
5921              // Need to select row to be able to edit it.
5922              [self selectRow: i byExtendingSelection: NO];
5923              [self editColumn: j row: i withEvent: nil select: YES];
5924              return YES;
5925            }
5926        }
5927    }
5928
5929  // Still nothing? Look at the beginning of the current row
5930  if (row > -1)
5931    {
5932      // First look for cells in the same row
5933      for (j = 0; j < column; j++)
5934        {
5935          if ([self _isCellEditableColumn: j row: row])
5936            {
5937              [self editColumn: j row: row withEvent: nil select: YES];
5938              return YES;
5939            }
5940        }
5941    }
5942#endif
5943
5944  return NO;
5945}
5946
5947-(BOOL) _editPreviousEditableCellBeforeRow: (NSInteger)row
5948				    column: (NSInteger)column
5949{
5950  NSInteger i, j;
5951
5952  if (row > -1)
5953    {
5954      // First look for cells in the same row
5955      for (j = column - 1; j > -1; j--)
5956        {
5957          if ([self _isCellEditableColumn: j row: row])
5958            {
5959              [self editColumn: j row: row withEvent: nil select: YES];
5960              return YES;
5961            }
5962        }
5963    }
5964
5965  // Otherwise, make the big cycle.
5966  for (i = row - 1; i > -1; i--)
5967    {
5968      for (j = _numberOfColumns - 1; j > -1; j--)
5969        {
5970          if ([self _isCellEditableColumn: j row: i])
5971            {
5972              // Need to select row to be able to edit it.
5973              [self selectRow: i byExtendingSelection: NO];
5974              [self editColumn: j row: i withEvent: nil select: YES];
5975              return YES;
5976            }
5977        }
5978    }
5979
5980// Should we loop around or not?
5981#if 0
5982  // Nothing found? Search in the rows after the current
5983  for (i = _numberOfRows - 1; i > row; i--)
5984    {
5985      for (j = _numberOfColumns - 1; j > -1; j--)
5986        {
5987          if ([self _isCellEditableColumn: j row: i])
5988            {
5989              // Need to select row to be able to edit it.
5990              [self selectRow: i byExtendingSelection: NO];
5991              [self editColumn: j row: i withEvent: nil select: YES];
5992              return YES;
5993            }
5994        }
5995    }
5996
5997  // Still nothing? Look at the end of the current row
5998  if (row > -1)
5999    {
6000      // First look for cells in the same row
6001      for (j = _numberOfColumns - 1; j > column; j++)
6002        {
6003          if ([self _isCellEditableColumn: j row: row])
6004            {
6005              [self editColumn: j row: row withEvent: nil select: YES];
6006              return YES;
6007            }
6008        }
6009    }
6010#endif
6011
6012  return NO;
6013}
6014
6015- (void) _autosaveTableColumns
6016{
6017  if (_autosaveTableColumns && _autosaveName != nil)
6018    {
6019      NSUserDefaults      *defaults;
6020      NSString            *tableKey;
6021      NSMutableDictionary *config;
6022      NSTableColumn       *column;
6023      id                  en;
6024
6025      defaults  = [NSUserDefaults standardUserDefaults];
6026      tableKey = [NSString stringWithFormat: @"NSTableView Columns %@",
6027			   _autosaveName];
6028      config = [NSMutableDictionary new];
6029
6030      en = [[self tableColumns] objectEnumerator];
6031      while ((column = [en nextObject]) != nil)
6032	{
6033	  NSArray *array;
6034	  NSNumber *width, *identNum;
6035	  id ident;
6036
6037	  width = [NSNumber numberWithInt: [column width]];
6038	  ident = [column identifier];
6039	  identNum = [NSNumber numberWithInt: [self columnWithIdentifier:
6040						      ident]];
6041	  array = [NSArray arrayWithObjects: width, identNum, nil];
6042	  [config setObject: array  forKey: ident];
6043	}
6044      [defaults setObject: config  forKey: tableKey];
6045      [defaults synchronize];
6046      RELEASE (config);
6047    }
6048}
6049
6050- (void) _autoloadTableColumns
6051{
6052  if (_autosaveTableColumns && _autosaveName != nil)
6053    {
6054      NSUserDefaults     *defaults;
6055      NSDictionary       *config;
6056      NSString           *tableKey;
6057
6058      defaults  = [NSUserDefaults standardUserDefaults];
6059      tableKey = [NSString stringWithFormat: @"NSTableView Columns %@",
6060			   _autosaveName];
6061      config = [defaults objectForKey: tableKey];
6062      if (config != nil)
6063	{
6064	  NSEnumerator *en = [[config allKeys] objectEnumerator];
6065	  NSString *colKey;
6066	  NSArray *colDesc;
6067	  NSTableColumn *col;
6068
6069	  while ((colKey = [en nextObject]) != nil)
6070	    {
6071	      col = [self tableColumnWithIdentifier: colKey];
6072
6073	      if (col != nil)
6074		{
6075		  colDesc = [config objectForKey: colKey];
6076		  [col setWidth: [[colDesc objectAtIndex: 0] intValue]];
6077		  [self moveColumn: [self columnWithIdentifier: colKey]
6078			toColumn: [[colDesc objectAtIndex: 1] intValue]];
6079		}
6080	    }
6081	}
6082    }
6083}
6084
6085- (void) superviewFrameChanged: (NSNotification*)aNotification
6086{
6087  if (_autoresizesAllColumnsToFit == YES)
6088    {
6089      CGFloat visible_width = [self convertRect: [_super_view bounds]
6090				  fromView: _super_view].size.width;
6091      CGFloat table_width = 0;
6092
6093      if (_numberOfColumns > 0)
6094        {
6095          table_width =
6096            _columnOrigins[_numberOfColumns - 1] +
6097            [[_tableColumns objectAtIndex: _numberOfColumns - 1] width];
6098        }
6099
6100      /*
6101	NSLog(@"columnOrigins[0] %f", _columnOrigins[0]);
6102	NSLog(@"superview.bounds %@",
6103	      NSStringFromRect([_super_view bounds]));
6104	NSLog(@"superview.frame %@",
6105	      NSStringFromRect([_super_view frame]));
6106	NSLog(@"table_width %f", table_width);
6107	NSLog(@"width %f", visible_width);
6108	NSLog(@"_superview_width %f", _superview_width);
6109      */
6110
6111      if (table_width - _superview_width <= 0.001
6112	   && table_width - _superview_width >= -0.001)
6113	{
6114	  // the last column had been sized to fit
6115	  [self sizeToFit];
6116	}
6117      else if (table_width <= _superview_width
6118		&& table_width >= visible_width)
6119	{
6120	  // the tableView was too small and is now too large
6121	  [self sizeToFit];
6122	}
6123      else if (table_width >= _superview_width
6124	       && table_width <= visible_width)
6125	{
6126	  // the tableView was too large and is now too small
6127	  if (_numberOfColumns > 0)
6128	    [self scrollColumnToVisible: 0];
6129	  [self sizeToFit];
6130	}
6131      _superview_width = visible_width;
6132    }
6133  else
6134    {
6135      CGFloat visible_width = [self convertRect: [_super_view bounds]
6136				  fromView: _super_view].size.width;
6137      CGFloat table_width = 0;
6138
6139      if (_numberOfColumns > 0)
6140        {
6141          table_width =
6142            _columnOrigins[_numberOfColumns - 1] +
6143            [[_tableColumns objectAtIndex: _numberOfColumns - 1] width];
6144        }
6145
6146      /*
6147	NSLog(@"columnOrigins[0] %f", _columnOrigins[0]);
6148	NSLog(@"superview.bounds %@",
6149	      NSStringFromRect([_super_view bounds]));
6150	NSLog(@"superview.frame %@",
6151	      NSStringFromRect([_super_view frame]));
6152	NSLog(@"table_width %f", table_width);
6153	NSLog(@"width %f", visible_width);
6154	NSLog(@"_superview_width %f", _superview_width);
6155      */
6156
6157      if (table_width - _superview_width <= 0.001
6158	   && table_width - _superview_width >= -0.001)
6159	{
6160	  // the last column had been sized to fit
6161	  [self sizeLastColumnToFit];
6162	}
6163      else if (table_width <= _superview_width
6164		&& table_width >= visible_width)
6165	{
6166	  // the tableView was too small and is now too large
6167	  [self sizeLastColumnToFit];
6168	}
6169      else if (table_width >= _superview_width
6170	       && table_width <= visible_width)
6171	{
6172	  // the tableView was too large and is now too small
6173	  if (_numberOfColumns > 0)
6174	    [self scrollColumnToVisible: 0];
6175	  [self sizeLastColumnToFit];
6176	}
6177      _superview_width = visible_width;
6178    }
6179  [self setFrame:_frame];
6180}
6181
6182
6183- (NSDragOperation) draggingSourceOperationMaskForLocal: (BOOL)isLocal
6184{
6185  if (isLocal)
6186    {
6187      return _draggingSourceOperationMaskForLocal;
6188    }
6189  else
6190    {
6191      return _draggingSourceOperationMaskForRemote;
6192    }
6193}
6194
6195- (void) setDraggingSourceOperationMask: (NSDragOperation)mask
6196                               forLocal: (BOOL)isLocal
6197{
6198  if (isLocal)
6199    {
6200      _draggingSourceOperationMaskForLocal = mask;
6201    }
6202  else
6203    {
6204      _draggingSourceOperationMaskForRemote = mask;
6205    }
6206}
6207
6208- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
6209{
6210  currentDropRow = -1;
6211  currentDropOperation = -1;
6212  oldDropRow = -1;
6213  lastQuarterPosition = -1;
6214  oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
6215  currentDragOperation = NSDragOperationEvery;
6216  return currentDragOperation;
6217}
6218
6219- (void) draggingExited: (id <NSDraggingInfo>) sender
6220{
6221  [self setNeedsDisplayInRect: oldDraggingRect];
6222  [self displayIfNeeded];
6223}
6224
6225- (void) _drawDropIndicator
6226{
6227  NSRect newRect = NSZeroRect;
6228
6229  [self lockFocus];
6230  [self setNeedsDisplayInRect: oldDraggingRect];
6231  [self displayIfNeeded];
6232
6233  [[NSColor darkGrayColor] set];
6234
6235  if (currentDropRow == -1)
6236    {
6237	   newRect = [self bounds];
6238	   NSFrameRectWithWidth(newRect, 2.0);
6239	   oldDraggingRect = newRect;
6240	}
6241  else if (currentDropOperation == NSTableViewDropAbove)
6242	{
6243	  if (currentDropRow == 0)
6244		{
6245		  newRect = NSMakeRect([self visibleRect].origin.x,
6246					currentDropRow * _rowHeight,
6247					[self visibleRect].size.width,
6248					3);
6249		}
6250	  else if (currentDropRow == _numberOfRows)
6251		{
6252		  newRect = NSMakeRect([self visibleRect].origin.x,
6253					currentDropRow * _rowHeight - 2,
6254					[self visibleRect].size.width,
6255					3);
6256		}
6257	  else
6258	    {
6259          newRect = NSMakeRect([self visibleRect].origin.x,
6260				    currentDropRow * _rowHeight - 1,
6261				    [self visibleRect].size.width,
6262				    3);
6263	    }
6264	  NSRectFill(newRect);
6265	  oldDraggingRect = newRect;
6266	}
6267  else
6268	{
6269	  newRect = [self frameOfCellAtColumn: 0
6270	                                  row: currentDropRow];
6271	  newRect.origin.x = _bounds.origin.x;
6272	  newRect.size.width = _bounds.size.width + 2;
6273	  newRect.origin.x -= _intercellSpacing.height / 2;
6274	  newRect.size.height += _intercellSpacing.height;
6275
6276	  newRect.size.height -= 1;
6277	  newRect.origin.x += 3;
6278	  newRect.size.width -= 3;
6279
6280	  if (_drawsGrid)
6281		{
6282			//newRect.origin.y += 1;
6283			//newRect.origin.x += 1;
6284			//newRect.size.width -= 2;
6285			newRect.size.height += 1;
6286		}
6287	  NSFrameRectWithWidth(newRect, 2.0);
6288
6289	  oldDraggingRect = newRect;
6290	  oldDraggingRect.origin.y -= 1;
6291	  oldDraggingRect.size.height += 2;
6292	}
6293
6294	[_window flushWindow];
6295	[self unlockFocus];
6296}
6297
6298/* This is a crude method of scrolling the view while dragging so the user can
6299drag to any cell even if it's not visible. Unfortunately we don't receive
6300events when the drag is outside the view, so the pointer must still be in the
6301view to drag. */
6302- (void) _scrollRowAtPointToVisible: (NSPoint)p
6303{
6304  NSInteger currentRow;
6305
6306  if (p.y < NSMinY([self visibleRect]) + 3)
6307    {
6308      currentRow = [self rowAtPoint: p] - 1;
6309      if (currentRow > 0)
6310        [self scrollRowToVisible: currentRow];
6311    }
6312  else if (p.y > NSMaxY([self visibleRect]) - 3)
6313    {
6314      currentRow = [self rowAtPoint: p] + 1;
6315      if (currentRow < _numberOfRows)
6316        [self scrollRowToVisible: currentRow];
6317    }
6318}
6319
6320- (NSInteger) _computedRowAtPoint: (NSPoint)p
6321{
6322  return (NSInteger)(p.y - _bounds.origin.y) / (NSInteger)_rowHeight;
6323}
6324
6325- (void) _setDropOperationAndRow: (NSInteger)row
6326              usingPositionInRow: (NSInteger)positionInRow
6327                         atPoint: (NSPoint)p
6328{
6329  NSParameterAssert(row > -1);
6330  BOOL isPositionInsideMiddleQuartersOfRow =
6331    (positionInRow > _rowHeight / 4 && positionInRow <= (3 * _rowHeight) / 4);
6332  BOOL isDropOn = (row > _numberOfRows || isPositionInsideMiddleQuartersOfRow);
6333
6334  [self setDropRow: (isDropOn ? [self _computedRowAtPoint: p] : row)
6335     dropOperation: (isDropOn ? NSTableViewDropOn : NSTableViewDropAbove)];
6336}
6337
6338- (NSInteger) _dropRowFromQuarterPosition: (NSInteger)quarterPosition
6339{
6340  if ((quarterPosition - oldDropRow * 4 <= 2) &&
6341      (quarterPosition - oldDropRow * 4 >= -3))
6342    {
6343      return oldDropRow;
6344    }
6345  else
6346    {
6347      return (quarterPosition + 2) / 4;
6348    }
6349}
6350
6351- (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender
6352{
6353  NSPoint p = [self convertPoint: [sender draggingLocation] fromView: nil];
6354  NSInteger positionInRow = (NSInteger)(p.y - _bounds.origin.y) % (NSInteger)_rowHeight;
6355  NSInteger quarterPosition = (NSInteger)([self _computedRowAtPoint: p] * 4.);
6356  NSInteger row = [self _dropRowFromQuarterPosition: quarterPosition];
6357  NSDragOperation dragOperation = [sender draggingSourceOperationMask];
6358  BOOL isSameDropTargetThanBefore = (lastQuarterPosition == quarterPosition
6359    && currentDragOperation == dragOperation);
6360
6361  [self _scrollRowAtPointToVisible: p];
6362
6363  if (isSameDropTargetThanBefore)
6364    return currentDragOperation;
6365
6366  /* Remember current drop target */
6367  currentDragOperation = dragOperation;
6368  lastQuarterPosition = quarterPosition;
6369
6370  /* The user can retarget this default drop using -setDropRow:dropOperation:
6371     in -tableView:validateDrop:proposedRow:proposedDropOperation:. */
6372  [self _setDropOperationAndRow: row
6373             usingPositionInRow: positionInRow
6374                        atPoint: p];
6375
6376  if ([_dataSource respondsToSelector:
6377      @selector(tableView:validateDrop:proposedRow:proposedDropOperation:)])
6378    {
6379      currentDragOperation = [_dataSource tableView: self
6380                                       validateDrop: sender
6381                                        proposedRow: currentDropRow
6382                              proposedDropOperation: currentDropOperation];
6383    }
6384
6385  /* -setDropRow:dropOperation: can changes both currentDropRow and
6386     currentDropOperation. Whether we have to redraw the drop indicator depends
6387     on this change. */
6388  if (currentDropRow != oldDropRow || currentDropOperation != oldDropOperation)
6389    {
6390      [self _drawDropIndicator];
6391      oldDropRow = (currentDropRow > -1 ? currentDropRow : _numberOfRows);
6392      oldDropOperation = currentDropOperation;
6393    }
6394
6395  return currentDragOperation;
6396}
6397
6398- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
6399{
6400  if ([_dataSource respondsToSelector: @selector(tableView:acceptDrop:row:dropOperation:)])
6401    {
6402      return [_dataSource tableView: self
6403			  acceptDrop: sender
6404			  row: currentDropRow
6405			  dropOperation: currentDropOperation];
6406    }
6407  else
6408    return NO;
6409}
6410
6411- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
6412{
6413  [self setNeedsDisplayInRect: oldDraggingRect];
6414  [self displayIfNeeded];
6415
6416  return YES;
6417}
6418
6419- (void) concludeDragOperation:(id <NSDraggingInfo>)sender
6420{
6421}
6422
6423- (BOOL) canDragRowsWithIndexes: (NSIndexSet *)indexes
6424                        atPoint: (NSPoint)point
6425{
6426  return YES;
6427}
6428
6429/*
6430 * sorting
6431 */
6432
6433/** Sets the sort descriptors used to sort the rows and delegates the sorting
6434to -tableView:didChangeSortDescriptors or -outlineView:didChangeSortDescriptors:
6435in NSOutlineView.
6436
6437The delegate methods can retrieve the new sort descriptors with
6438-sortDescriptors and override them with -setSortDescriptors:.<br />
6439The first object in the new sort descriptor array is the sort descriptor
6440prototype returned by the table column whose header was the last clicked.
6441See -[NSTableColumn sortDescriptorPrototype].
6442
6443This method is called automatically when you click on a table column header,
6444so you shouldn't need to call it usually.
6445
6446Take note the sort descriptors are encoded by the keyed archiving (rarely used
6447since neither IB or Gorm support to set these directly). */
6448- (void) setSortDescriptors: (NSArray *)sortDescriptors
6449{
6450  NSArray *oldSortDescriptors = [self sortDescriptors];
6451  NSArray *newSortDescriptors = nil;
6452
6453  /* To replicate precisely the Cocoa behavior */
6454  if (sortDescriptors == nil)
6455    {
6456      newSortDescriptors = [NSArray array];
6457    }
6458  else
6459    {
6460      /* _sortDescriptors must remain immutable since -sortDescriptors doesn't
6461         return a defensive copy */
6462      newSortDescriptors = [NSArray arrayWithArray: sortDescriptors];
6463    }
6464
6465  if ([newSortDescriptors isEqual: oldSortDescriptors])
6466    return;
6467
6468  RETAIN(oldSortDescriptors);
6469
6470  ASSIGN(_sortDescriptors, newSortDescriptors);
6471  [self _didChangeSortDescriptors: oldSortDescriptors];
6472
6473  RELEASE(oldSortDescriptors);
6474}
6475
6476/** Returns the current sort descriptors, usually updated every time a click
6477happens on a table column header.
6478
6479By default, returns an empty array.
6480
6481For a more detailed explanation, -setSortDescriptors:. */
6482- (NSArray *)sortDescriptors
6483{
6484  return _sortDescriptors;
6485}
6486
6487/*
6488 * User interface validation
6489 */
6490- (BOOL) validateUserInterfaceItem: (id <NSValidatedUserInterfaceItem>)anItem
6491{
6492  // FIXME
6493  return YES;
6494}
6495
6496/*
6497 * (NotificationRequestMethods)
6498 */
6499- (void) _postSelectionIsChangingNotification
6500{
6501  [nc postNotificationName: NSTableViewSelectionIsChangingNotification
6502      object: self];
6503}
6504
6505- (void) _postSelectionDidChangeNotification
6506{
6507  [nc postNotificationName: NSTableViewSelectionDidChangeNotification
6508      object: self];
6509}
6510
6511- (void) _postColumnDidMoveNotificationWithOldIndex: (NSInteger) oldIndex
6512					   newIndex: (NSInteger) newIndex
6513{
6514  [nc postNotificationName: NSTableViewColumnDidMoveNotification
6515      object: self
6516      userInfo: [NSDictionary
6517		  dictionaryWithObjectsAndKeys:
6518		  [NSNumber numberWithInteger: newIndex],
6519		  @"NSNewColumn",
6520		    [NSNumber numberWithInteger: oldIndex],
6521		  @"NSOldColumn",
6522		  nil]];
6523}
6524
6525- (void) _postColumnDidResizeNotificationWithOldWidth: (float) oldWidth
6526{
6527  [nc postNotificationName:
6528	NSTableViewColumnDidResizeNotification
6529      object: self
6530      userInfo: [NSDictionary
6531		  dictionaryWithObjectsAndKeys:
6532		    [NSNumber numberWithFloat: oldWidth],
6533		  @"NSOldWidth",
6534		  nil]];
6535}
6536
6537- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn
6538{
6539  if ([_delegate respondsToSelector:
6540		   @selector (tableView:shouldSelectTableColumn:)] == YES)
6541    {
6542      if ([_delegate tableView: self  shouldSelectTableColumn: tableColumn] == NO)
6543	{
6544	  return NO;
6545	}
6546    }
6547
6548  return YES;
6549}
6550
6551- (BOOL) _shouldSelectRow: (NSInteger)rowIndex
6552{
6553  if ([_delegate respondsToSelector:
6554		   @selector (tableView:shouldSelectRow:)] == YES)
6555    {
6556      if ([_delegate tableView: self shouldSelectRow: rowIndex] == NO)
6557	{
6558	  return NO;
6559	}
6560    }
6561
6562  return YES;
6563}
6564
6565- (BOOL) _shouldSelectionChange
6566{
6567  if ([_delegate respondsToSelector:
6568	  @selector (selectionShouldChangeInTableView:)] == YES)
6569    {
6570      if ([_delegate selectionShouldChangeInTableView: self] == NO)
6571	{
6572	  return NO;
6573	}
6574    }
6575
6576  return YES;
6577}
6578
6579- (void) _didChangeSortDescriptors: (NSArray *)oldSortDescriptors
6580{
6581  if ([_dataSource
6582	respondsToSelector: @selector(tableView:sortDescriptorsDidChange:)])
6583    {
6584      [_dataSource tableView: self sortDescriptorsDidChange: oldSortDescriptors];
6585    }
6586}
6587
6588- (void) _didClickTableColumn: (NSTableColumn *)tc
6589{
6590  if ([_delegate
6591	respondsToSelector:
6592	  @selector(tableView:didClickTableColumn:)])
6593    {
6594      [_delegate tableView: self
6595		 didClickTableColumn: tc];
6596    }
6597}
6598
6599- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
6600			    row: (NSInteger) rowIndex
6601{
6602  if ([_delegate respondsToSelector:
6603		     @selector(tableView:shouldEditTableColumn:row:)])
6604    {
6605      return [_delegate tableView: self shouldEditTableColumn: tableColumn
6606			row: rowIndex];
6607    }
6608
6609  return YES;
6610}
6611
6612- (BOOL) _isEditableColumn: (NSInteger) columnIndex
6613                       row: (NSInteger) rowIndex
6614{
6615  NSTableColumn *tableColumn = [_tableColumns objectAtIndex: columnIndex];
6616
6617  return [tableColumn isEditable] && [self _shouldEditTableColumn: tableColumn
6618                                                              row: rowIndex];
6619}
6620
6621- (BOOL) _isCellSelectableColumn: (NSInteger) columnIndex
6622                             row: (NSInteger) rowIndex
6623{
6624  if (![self _isEditableColumn: columnIndex row: rowIndex])
6625    {
6626      return NO;
6627    }
6628  else
6629    {
6630      NSCell *cell = [self preparedCellAtColumn: columnIndex row: rowIndex];
6631
6632      return [cell isSelectable];
6633    }
6634}
6635
6636- (BOOL) _isCellEditableColumn: (NSInteger) columnIndex
6637			   row: (NSInteger) rowIndex
6638{
6639  if (![self _isEditableColumn: columnIndex row: rowIndex])
6640    {
6641      return NO;
6642    }
6643  else
6644    {
6645      NSCell *cell = [self preparedCellAtColumn: columnIndex row: rowIndex];
6646
6647      return [cell isEditable];
6648    }
6649}
6650
6651- (void) _willDisplayCell: (NSCell*)cell
6652	   forTableColumn: (NSTableColumn *)tb
6653		      row: (NSInteger)index
6654{
6655  if (_del_responds)
6656    {
6657      [_delegate tableView: self
6658		 willDisplayCell: cell
6659		 forTableColumn: tb
6660		 row: index];
6661    }
6662}
6663
6664- (id) _objectValueForTableColumn: (NSTableColumn *)tb
6665			      row: (NSInteger) index
6666{
6667  id result = nil;
6668  GSKeyValueBinding *theBinding;
6669
6670  theBinding = [GSKeyValueBinding getBinding: NSValueBinding
6671                                   forObject: tb];
6672  if (theBinding != nil)
6673    {
6674      return [(NSArray *)[theBinding destinationValue]
6675                 objectAtIndex: index];
6676    }
6677  else if ([_dataSource respondsToSelector:
6678		    @selector(tableView:objectValueForTableColumn:row:)])
6679    {
6680      result = [_dataSource tableView: self
6681			    objectValueForTableColumn: tb
6682			    row: index];
6683    }
6684
6685  return result;
6686}
6687
6688- (void) _setObjectValue: (id)value
6689	  forTableColumn: (NSTableColumn *)tb
6690		     row: (NSInteger) index
6691{
6692  if ([_dataSource respondsToSelector:
6693		    @selector(tableView:setObjectValue:forTableColumn:row:)])
6694    {
6695      [_dataSource tableView: self
6696		   setObjectValue: value
6697		   forTableColumn: tb
6698		   row: index];
6699    }
6700}
6701
6702/* Quasi private method called on self from -noteNumberOfRowsChanged
6703 * implemented in NSTableView and subclasses
6704 * by default returns the DataSource's -numberOfRowsInTableView:
6705 */
6706- (NSInteger) _numRows
6707{
6708  GSKeyValueBinding *theBinding;
6709
6710  // If we have content binding the data source is used only
6711  // like a delegate
6712  theBinding = [GSKeyValueBinding getBinding: NSContentBinding
6713                                   forObject: self];
6714  if (theBinding != nil)
6715    {
6716      return [(NSArray *)[theBinding destinationValue] count];
6717    }
6718  else if ([_dataSource respondsToSelector:
6719		    @selector(numberOfRowsInTableView:)])
6720    {
6721      return [_dataSource numberOfRowsInTableView:self];
6722    }
6723  else
6724    {
6725      // FIXME
6726      return 0;
6727    }
6728}
6729
6730- (BOOL) _isDraggingSource
6731{
6732  return [_dataSource respondsToSelector:
6733			@selector(tableView:writeRows:toPasteboard:)]
6734      || [_dataSource respondsToSelector:
6735           @selector(tableView:writeRowsWithIndexes:toPasteboard:)];
6736}
6737
6738- (BOOL) _writeRows: (NSIndexSet *)rows
6739       toPasteboard: (NSPasteboard *)pboard
6740{
6741  if ([_dataSource respondsToSelector:
6742		     @selector(tableView:writeRowsWithIndexes:toPasteboard:)] == YES)
6743    {
6744      return [_dataSource tableView: self
6745                          writeRowsWithIndexes: rows
6746                          toPasteboard: pboard];
6747    }
6748  else if ([_dataSource respondsToSelector:
6749             @selector(tableView:writeRows:toPasteboard:)] == YES)
6750    {
6751      NSArray *rowArray;
6752
6753      rowArray = [self _indexSetToArray: rows];
6754      return [_dataSource tableView: self
6755                          writeRows: rowArray
6756                          toPasteboard: pboard];
6757    }
6758  return NO;
6759}
6760
6761- (void) reloadDataForRowIndexes: (NSIndexSet*)rowIndexes
6762                   columnIndexes: (NSIndexSet*)columnIndexes
6763{
6764  [self reloadData];
6765}
6766
6767- (void) beginUpdates
6768{
6769  _beginEndUpdates++;
6770}
6771
6772- (void) endUpdates
6773{
6774  if (_beginEndUpdates > 0)
6775    {
6776      if (--_beginEndUpdates == 0)
6777        {
6778          // Process batched inserts/removes....
6779          // Just reload table for now until we get inserts/removes working...
6780          [self reloadData];
6781        }
6782    }
6783}
6784
6785- (NSInteger) columnForView: (NSView*)view
6786{
6787  return NSNotFound;
6788}
6789
6790- (void) insertRowsAtIndexes: (NSIndexSet*)indexes
6791               withAnimation: (NSTableViewAnimationOptions)animationOptions
6792{
6793}
6794
6795- (void) removeRowsAtIndexes: (NSIndexSet*)indexes
6796               withAnimation: (NSTableViewAnimationOptions)animationOptions
6797{
6798}
6799
6800- (NSInteger) rowForView: (NSView*)view
6801{
6802  return NSNotFound;
6803}
6804
6805@end /* implementation of NSTableView */
6806
6807@implementation NSTableView (SelectionHelper)
6808
6809- (void) _setSelectingColumns: (BOOL)flag
6810{
6811  if (flag == _selectingColumns)
6812    return;
6813
6814  if (flag == NO)
6815    {
6816      [self _unselectAllColumns];
6817      _selectingColumns = NO;
6818    }
6819  else
6820    {
6821      [self _unselectAllRows];
6822      _selectingColumns = YES;
6823    }
6824}
6825
6826- (NSArray *) _indexSetToArray: (NSIndexSet*)indexSet
6827{
6828  NSMutableArray *array = [NSMutableArray array];
6829  NSUInteger index = [indexSet firstIndex];
6830
6831  while (index != NSNotFound)
6832    {
6833      NSNumber *num  = [NSNumber numberWithUnsignedInteger: index];
6834
6835      [array addObject: num];
6836      index = [indexSet indexGreaterThanIndex: index];
6837    }
6838
6839  return array;
6840}
6841
6842- (NSArray *) _selectedRowArray
6843{
6844  return [self _indexSetToArray: _selectedRows];
6845}
6846
6847- (BOOL) _selectRow: (NSInteger)rowIndex
6848{
6849  if (![self _shouldSelectRow: rowIndex])
6850    {
6851      return NO;
6852    }
6853
6854  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
6855  [_selectedRows addIndex: rowIndex];
6856  _selectedRow = rowIndex;
6857  return YES;
6858}
6859
6860- (BOOL) _selectUnselectedRow: (NSInteger)rowIndex
6861{
6862  if ([_selectedRows containsIndex: rowIndex])
6863    {
6864      return NO;
6865    }
6866
6867  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
6868  [_selectedRows addIndex: rowIndex];
6869  _selectedRow = rowIndex;
6870  return YES;
6871}
6872
6873- (BOOL) _unselectRow: (NSInteger)rowIndex
6874{
6875  if (![_selectedRows containsIndex: rowIndex])
6876    {
6877      return NO;
6878    }
6879
6880  [self setNeedsDisplayInRect: [self rectOfRow: rowIndex]];
6881  [_selectedRows removeIndex: rowIndex];
6882
6883  if (_selectedRow == rowIndex)
6884    {
6885      _selectedRow = -1;
6886    }
6887
6888  return YES;
6889}
6890
6891- (void) _unselectAllRows
6892{
6893  /* Compute rect to redraw to clear the old row selection */
6894  NSUInteger row = [_selectedRows firstIndex];
6895
6896  while (row != NSNotFound)
6897    {
6898      [self setNeedsDisplayInRect: [self rectOfRow: row]];
6899      row = [_selectedRows indexGreaterThanIndex: row];
6900    }
6901  [_selectedRows removeAllIndexes];
6902  _selectedRow = -1;
6903}
6904
6905- (NSArray *) _selectedColumArray
6906{
6907  return [self _indexSetToArray: _selectedColumns];
6908}
6909
6910- (void) _unselectAllColumns
6911{
6912  /* Compute rect to redraw to clear the old column selection */
6913  NSUInteger column = [_selectedColumns firstIndex];
6914
6915  while (column != NSNotFound)
6916    {
6917      [self setNeedsDisplayInRect: [self rectOfColumn: column]];
6918      if (_headerView)
6919        {
6920	  [_headerView setNeedsDisplayInRect:
6921			   [_headerView headerRectOfColumn: column]];
6922	}
6923      column = [_selectedColumns indexGreaterThanIndex: column];
6924    }
6925  [_selectedColumns removeAllIndexes];
6926  _selectedColumn = -1;
6927}
6928
6929- (void) setValue: (id)anObject forKey: (NSString*)aKey
6930{
6931  if ([aKey isEqual: NSContentBinding])
6932    {
6933      // Reload data
6934      [self reloadData];
6935      NSDebugLLog(@"NSBinding", @"Setting table view content to %@", anObject);
6936    }
6937  else if ([aKey isEqual: NSSelectionIndexesBinding])
6938    {
6939      if (_selectingColumns)
6940        {
6941          if (nil == anObject)
6942            {
6943              [self _unselectAllColumns];
6944            }
6945          else
6946            {
6947              return [self selectColumnIndexes: anObject
6948                          byExtendingSelection: NO];
6949            }
6950        }
6951      else
6952        {
6953          if (nil == anObject)
6954            {
6955              [self _unselectAllRows];
6956            }
6957          else
6958            {
6959              return [self selectRowIndexes: anObject
6960                       byExtendingSelection: NO];
6961            }
6962        }
6963    }
6964  else
6965    {
6966      [super setValue: anObject forKey: aKey];
6967    }
6968}
6969
6970- (id) valueForKey: (NSString*)aKey
6971{
6972  if ([aKey isEqual: NSContentBinding])
6973    {
6974      return nil;
6975    }
6976  else if ([aKey isEqual: NSSelectionIndexesBinding])
6977    {
6978      if (_selectingColumns)
6979        {
6980          return [self selectedColumnIndexes];
6981        }
6982      else
6983        {
6984          return [self selectedRowIndexes];
6985        }
6986    }
6987  else
6988    {
6989      return [super valueForKey: aKey];
6990    }
6991}
6992
6993@end
6994