1/** <title>NSMatrix</title>
2
3   <abstract>Matrix class for grouping controls</abstract>
4
5   Copyright (C) 1996-2015 Free Software Foundation, Inc.
6
7   Author: Ovidiu Predescu <ovidiu@net-community.com>
8   Date: March 1997
9   A completely rewritten version of the original source by Pascal Forget and
10   Scott Christley.
11   Modified: Felipe A. Rodriguez <far@ix.netcom.com>
12   Date: August 1998
13   Cell handling rewritten: Richard Frith-Macdonald <richard@brainstorm.co.uk>
14   Date: November 1999
15   Implementation of Editing: Nicola Pero <n.pero@mi.flashnet.it>
16   Date: November 1999
17   Modified: Mirko Viviani <mirko.viviani@rccr.cremona.it>
18   Date: March 2001
19
20   This file is part of the GNUstep GUI Library.
21
22   This library is free software; you can redistribute it and/or
23   modify it under the terms of the GNU Lesser General Public
24   License as published by the Free Software Foundation; either
25   version 2 of the License, or (at your option) any later version.
26
27   This library is distributed in the hope that it will be useful,
28   but WITHOUT ANY WARRANTY; without even the implied warranty of
29   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
30   Lesser General Public License for more details.
31
32   You should have received a copy of the GNU Lesser General Public
33   License along with this library; see the file COPYING.LIB.
34   If not, see <http://www.gnu.org/licenses/> or write to the
35   Free Software Foundation, 51 Franklin Street, Fifth Floor,
36   Boston, MA 02110-1301, USA.
37*/
38
39/* Mouse Tracking Notes:
40
41   The behaviour of mouse tracking is a bit different on OS42 and MaxOSX. The
42   implementation here reflects OS42 more closely (as the original code
43   in NSMatrix). Examples of differences:
44   - highlighting of NSButtonCells is different;
45   - OS42 makes each cell under the cursor track the mouse, MacOSX makes only
46     the clicked cell track it, untilMouseUp;
47   - if mouse goes up outside of a cell, OS42 sends the action, MacOSX does not
48   - keys used for selection in list mode are not the same (shift and alternate
49     on OS42, command and shift on MacOSX).
50*/
51
52#include "config.h"
53#include <stdlib.h>
54
55#import <Foundation/NSValue.h>
56#import <Foundation/NSArray.h>
57#import <Foundation/NSAutoreleasePool.h>
58#import <Foundation/NSCharacterSet.h>
59#import <Foundation/NSException.h>
60#import <Foundation/NSDictionary.h>
61#import <Foundation/NSKeyedArchiver.h>
62#import <Foundation/NSKeyValueCoding.h>
63#import <Foundation/NSKeyValueObserving.h>
64#import <Foundation/NSNotification.h>
65#import <Foundation/NSFormatter.h>
66#import <Foundation/NSDebug.h>
67#import <Foundation/NSString.h>
68#import <Foundation/NSZone.h>
69
70#import "AppKit/NSApplication.h"
71#import "AppKit/NSButtonCell.h"
72#import "AppKit/NSColor.h"
73#import "AppKit/NSCursor.h"
74#import "AppKit/NSEvent.h"
75#import "AppKit/NSGraphics.h"
76#import "AppKit/NSKeyValueBinding.h"
77#import "AppKit/NSMatrix.h"
78#import "AppKit/NSWindow.h"
79#import "GSCodingFlags.h"
80
81#include <math.h>
82
83static NSNotificationCenter *nc;
84
85#define	NSMATRIX_STRICT_CHECKING	0
86
87#ifdef MIN
88# undef MIN
89#endif
90#define MIN(A,B)  ({ typeof(A) __a = (A); \
91		  typeof(B) __b = (B); \
92		  __a < __b ? __a : __b; })
93
94#ifdef MAX
95# undef MAX
96#endif
97#define MAX(A,B)  ({ typeof(A) __a = (A); \
98		  typeof(B) __b = (B); \
99		  __a < __b ? __b : __a; })
100
101#ifdef ABS
102# undef ABS
103#endif
104#define ABS(A)	({ typeof(A) __a = (A); __a < 0 ? -__a : __a; })
105
106
107#define SIGN(x) \
108    ({typeof(x) _SIGN_x = (x); \
109      _SIGN_x > 0 ? 1 : (_SIGN_x == 0 ? 0 : -1); })
110
111#define POINT_FROM_INDEX(index) \
112    ({MPoint point = { (index) % _numCols, (index) / _numCols }; point; })
113
114#define INDEX_FROM_COORDS(x,y) \
115    ((y) * _numCols + (x))
116#define INDEX_FROM_POINT(point) \
117    ((point).y * _numCols + (point).x)
118
119
120/* Some stuff needed to compute the selection in the list mode. */
121typedef struct {
122  NSInteger x;
123  NSInteger y;
124} MPoint;
125
126typedef struct {
127  NSInteger x;
128  NSInteger y;
129  NSInteger width;
130  NSInteger height;
131} MRect;
132
133static inline MPoint MakePoint (NSInteger x, NSInteger y)
134{
135  MPoint point = { x, y };
136  return point;
137}
138
139@interface NSMatrix (PrivateMethods)
140- (void) _renewRows: (NSInteger)row
141	    columns: (NSInteger)col
142	   rowSpace: (NSInteger)rowSpace
143	   colSpace: (NSInteger)colSpace;
144- (void) _setState: (NSInteger)state
145	 highlight: (BOOL)highlight
146	startIndex: (NSInteger)start
147	  endIndex: (NSInteger)end;
148- (BOOL) _selectNextSelectableCellAfterRow: (NSInteger)row
149				    column: (NSInteger)column;
150- (BOOL) _selectPreviousSelectableCellBeforeRow: (NSInteger)row
151					 column: (NSInteger)column;
152- (void) _setKeyRow: (NSInteger)row
153             column: (NSInteger)column;
154@end
155
156enum {
157  DEFAULT_CELL_HEIGHT = 17,
158  DEFAULT_CELL_WIDTH = 100
159};
160
161/** <p>TODO documentation</p>
162 */
163
164@implementation NSMatrix
165
166/* Class variables */
167static Class defaultCellClass = nil;
168static NSUInteger mouseDownFlags = 0;
169static SEL copySel;
170static SEL initSel;
171static SEL allocSel;
172static SEL getSel;
173
174+ (void) initialize
175{
176  if (self == [NSMatrix class])
177    {
178      /* Set the initial version */
179      [self setVersion: 1];
180
181      copySel = @selector(copyWithZone:);
182      initSel = @selector(init);
183      allocSel = @selector(allocWithZone:);
184      getSel = @selector(objectAtIndex:);
185
186      /*
187       * MacOS-X docs say default cell class is NSActionCell
188       */
189      defaultCellClass = [NSActionCell class];
190      //
191      nc = [NSNotificationCenter defaultCenter];
192
193      [self exposeBinding: NSSelectedTagBinding];
194    }
195}
196
197/**<p>Returns the cell class used to create cells. By default it is a
198   NSActionCell class</p><p>See Also: +setCellClass:</p>
199 */
200+ (Class) cellClass
201{
202  return defaultCellClass;
203}
204
205/**<p>Sets the cell class used to create cells to <var>classId</var>.
206   By default it is a NSActionCell class</p><p>See Also: +setCellClass:</p>
207 */
208+ (void) setCellClass: (Class)classId
209{
210  defaultCellClass = classId;
211  if (defaultCellClass == nil)
212    defaultCellClass = [NSActionCell class];
213}
214
215- (id) init
216{
217  return [self initWithFrame: NSZeroRect
218		        mode: NSRadioModeMatrix
219		   cellClass: [object_getClass(self) cellClass]
220		numberOfRows: 0
221	     numberOfColumns: 0];
222}
223
224/** <p>Initializes and returns a NSMatrix in frame frameRect.
225    By default the matrix has no row and no column, the NSMatrix's mode is
226    NSRadioModeMatrix and the cell class is a NSActionCell class.</p><p>See
227    Also: -initWithFrame:mode:cellClass:numberOfRows:numberOfColumns:</p>
228 */
229- (id) initWithFrame: (NSRect)frameRect
230{
231  return [self initWithFrame: frameRect
232		        mode: NSRadioModeMatrix
233		   cellClass: [object_getClass(self) cellClass]
234		numberOfRows: 0
235	     numberOfColumns: 0];
236}
237
238- (void) _privateFrame: (NSRect)frameRect
239                  mode: (NSMatrixMode)aMode
240          numberOfRows: (NSInteger)rows
241       numberOfColumns: (NSInteger)cols
242{
243  _myZone = [self zone];
244  [self _renewRows: rows columns: cols rowSpace: 0 colSpace: 0];
245  _mode = aMode;
246  if ((_numCols > 0) && (_numRows > 0))
247    {
248      /*
249	 We must not round the _cellSize to integers here!
250
251	 Any approximation is a loss of information.  We should give
252	 to the backend as much information as possible, and trust
253	 that it will use that information to provide the best
254	 possible rendering on that device.  Depending on the backend,
255	 that might go up to using antialias or advanced graphics
256	 tricks to make an advanced rendering of things not lying on
257	 pixel boundaries.  Approximating here just gives less
258	 information to the backend, making the rendering worse.
259
260	 Even if the backend is just approximating to pixels, it would
261	 still be wrong to round _cellSize here, because rounding
262	 sizes of rectangles without considering the origin of the
263	 rectangles has been definitely found to be wrong and to cause
264	 incorrect rendering.  The origin of the whole matrix is very
265	 likely a non-integer - if not originally, as a consequence of
266	 the fact that the user resized the window - so making the
267	 cell size integer does not cause drawing to be done on pixel
268	 boundaries anyway, and will actually make more difficult for
269	 the backend to render the rectangles properly since it will
270	 be drawing approximately rectangles which are already only an
271	 approximate description - and this first approximation having
272	 been done incorrectly too! - of what we really want to draw.
273      */
274
275      _cellSize = NSMakeSize (frameRect.size.width/_numCols,
276			      frameRect.size.height/_numRows);
277    }
278  else
279    {
280      _cellSize = NSMakeSize (DEFAULT_CELL_WIDTH, DEFAULT_CELL_HEIGHT);
281    }
282
283  _intercell = NSMakeSize(1, 1);
284  [self setAutosizesCells: YES];
285  [self setFrame: frameRect];
286
287  _tabKeyTraversesCells = YES;
288  [self setBackgroundColor: [NSColor controlColor]];
289  [self setDrawsBackground: NO];
290  [self setCellBackgroundColor: [NSColor controlColor]];
291  [self setDrawsCellBackground: NO];
292  [self setSelectionByRect: YES];
293  _dottedRow = _dottedColumn = -1;
294  if (_mode == NSRadioModeMatrix && _numRows > 0 && _numCols > 0)
295    {
296      [self selectCellAtRow: 0 column: 0];
297    }
298  else
299    {
300      _selectedCell = nil;
301      _selectedRow = _selectedColumn = -1;
302    }
303}
304
305/**<p>Initializes and returns a new NSMatrix in the specified frame frameRect.
306   The <ref type="type" id="NSMatrixMode">NSMatrixMode</ref> is specified
307   by mode, the cell class used specified by classId and the number of rows
308   and columns specified by rowsHigh and colsWide respectively</p><p>See Also:
309   -initWithFrame:mode:prototype:numberOfRows:numberOfColumns:</p>
310 */
311- (id) initWithFrame: (NSRect)frameRect
312	        mode: (NSMatrixMode)aMode
313	   cellClass: (Class)classId
314        numberOfRows: (NSInteger)rowsHigh
315     numberOfColumns: (NSInteger)colsWide
316{
317  if (!( self = [super initWithFrame: frameRect]))
318    {
319      return nil;
320    }
321
322  [self setCellClass: classId];
323  [self _privateFrame: frameRect
324                 mode: aMode
325         numberOfRows: rowsHigh
326      numberOfColumns: colsWide];
327  return self;
328}
329
330/**<p>Initializes and returns a new NSMatrix in the specified frame frameRect.
331   The <ref type="type" id="NSMatrixMode">NSMatrixMode</ref> is specified
332   by mode, the cell used specified by aCell and the number of rows
333   and columns specified by rowsHigh and colsWide respectively</p><p>See Also:
334   -initWithFrame:mode:prototype:numberOfRows:numberOfColumns:</p>
335 */
336- (id) initWithFrame: (NSRect)frameRect
337	        mode: (NSMatrixMode)aMode
338	   prototype: (NSCell*)aCell
339        numberOfRows: (NSInteger)rowsHigh
340     numberOfColumns: (NSInteger)colsWide
341{
342  if (!(self = [super initWithFrame: frameRect]))
343    {
344      return nil;
345    }
346
347  [self setPrototype: aCell];
348  [self _privateFrame: frameRect
349                 mode: aMode
350         numberOfRows: rowsHigh
351      numberOfColumns: colsWide];
352  return self;
353}
354
355- (void) dealloc
356{
357  int i;
358
359  if (_textObject != nil)
360    {
361      [_selectedCell endEditing: _textObject];
362      _textObject = nil;
363    }
364
365  for (i = 0; i < _maxRows; i++)
366    {
367      int j;
368
369      for (j = 0; j < _maxCols; j++)
370	{
371	  [_cells[i][j] release];
372	}
373      NSZoneFree(_myZone, _cells[i]);
374      NSZoneFree(_myZone, _selectedCells[i]);
375    }
376  NSZoneFree(_myZone, _cells);
377  NSZoneFree(_myZone, _selectedCells);
378
379  [_cellPrototype release];
380  [_backgroundColor release];
381  [_cellBackgroundColor release];
382
383  if (_delegate != nil)
384    {
385      [nc removeObserver: _delegate  name: nil  object: self];
386      _delegate = nil;
387    }
388
389  [super dealloc];
390}
391
392/**<p>Inserts a new column after the current last column.</p>
393   <p>See Also: -insertColumn:withCells: </p>
394 */
395- (void) addColumn
396{
397  [self insertColumn: _numCols withCells: nil];
398}
399
400/**<p>Inserts a new column of cells specified by cellArray after the current
401   last column.</p><p>See Also: -insertColumn:withCells: </p>
402 */
403- (void) addColumnWithCells: (NSArray*)cellArray
404{
405  [self insertColumn: _numCols withCells: cellArray];
406}
407
408/**<p>Inserts a new row after the current last row.</p>
409   <p>See Also: -insertRow:withCells: </p>
410 */
411- (void) addRow
412{
413  [self insertRow: _numRows withCells: nil];
414}
415
416/**<p>Inserts a new row of cells specified by cellArray
417   after the current last row.</p><p>See Also: -insertRow:withCells: </p>
418 */
419- (void) addRowWithCells: (NSArray*)cellArray
420{
421  [self insertRow: _numRows withCells: cellArray];
422}
423
424/**<p>Inserts a new column at the specified column <var>column</var>.</p>
425   <p>See Also: -insertColumn:withCells:</p>
426 */
427- (void) insertColumn: (NSInteger)column
428{
429  [self insertColumn: column withCells: nil];
430}
431
432/**<p>Inserts a new column of cells ( specified by <var>cellArray</var>)
433   at the specified column <var>column</var>. This method can grows
434   the matrix as necessay if needed</p>
435   <p>See Also: -insertColumn:</p>
436 */
437- (void) insertColumn: (NSInteger)column withCells: (NSArray*)cellArray
438{
439  NSInteger count = [cellArray count];
440  NSInteger i = _numCols + 1;
441
442  if (column < 0)
443    {
444      column = 0;
445#if	NSMATRIX_STRICT_CHECKING == 0
446      NSLog(@"insert negative column (%d) in matrix", (int)column);
447#else
448      [NSException raise: NSRangeException
449		  format: @"insert negative column (%d) in matrix", (int)column];
450#endif
451    }
452
453  if ((cellArray != nil) && (count != _numRows))
454    {
455#if	NSMATRIX_STRICT_CHECKING == 0
456      NSLog(@"Wrong number of cells (%d) in column insert in matrix", (int)count);
457#else
458      [NSException raise: NSRangeException
459		  format: @"Wrong number of cells (%d) in column insert in matrix", (int)count];
460#endif
461    }
462
463  if (column >= i)
464    {
465      i = column + 1;
466    }
467
468  /*
469   * Use _renewRows:columns:rowSpace:colSpace: to grow the matrix as necessary.
470   * MacOS-X docs say that if the matrix is empty, we make it have one column
471   * and enough rows for all the elements.
472   */
473  if (count > 0 && (_numRows == 0 || _numCols == 0))
474    {
475      [self _renewRows: count columns: 1 rowSpace: 0 colSpace: count];
476    }
477  else
478    {
479      [self _renewRows: _numRows ? _numRows : 1
480	       columns: i
481	      rowSpace: 0
482	      colSpace: count];
483    }
484
485  /*
486   * Rotate the new column to the insertion point if necessary.
487   */
488  if (_numCols != column)
489    {
490      for (i = 0; i < _numRows; i++)
491	{
492	  int	j = _numCols;
493	  id	old = _cells[i][j-1];
494
495	  while (--j > column)
496	    {
497	      _cells[i][j] = _cells[i][j-1];
498	      _selectedCells[i][j] = _selectedCells[i][j-1];
499	    }
500	  _cells[i][column] = old;
501	  _selectedCells[i][column] = NO;
502	}
503      if (_selectedCell && (_selectedColumn >= column))
504	{
505	  _selectedColumn++;
506	}
507      if (_dottedColumn >= column)
508	{
509	  _dottedColumn++;
510	}
511    }
512
513  /*
514   * Now put the new cells from the array into the matrix.
515   */
516  if (count > 0)
517    {
518      IMP getImp = [cellArray methodForSelector: getSel];
519
520      for (i = 0; i < _numRows && i < count; i++)
521	{
522	  ASSIGN(_cells[i][column], (*getImp)(cellArray, getSel, i));
523	}
524    }
525
526  if (_mode == NSRadioModeMatrix && _allowsEmptySelection == NO
527    && _selectedCell == nil)
528    {
529      [self selectCellAtRow: 0 column: 0];
530    }
531
532  [self setNeedsDisplay: YES];
533}
534
535/**<p>Inserts a new row at index <var>row</var>.</p>
536   <p>See Also: -insertRow:withCells: </p>
537 */
538- (void) insertRow: (NSInteger)row
539{
540  [self insertRow: row withCells: nil];
541}
542
543/**<p>Inserts a new row of cells ( specified by <var>cellArray</var>)
544   at the specified row <var>row</var>. This method can grows
545   the matrix as necessay if needed</p>
546   <p>See Also: -insertColumn:</p>
547 */
548- (void) insertRow: (NSInteger)row withCells: (NSArray*)cellArray
549{
550  NSInteger count = [cellArray count];
551  NSInteger i = _numRows + 1;
552
553  if (row < 0)
554    {
555      row = 0;
556#if	NSMATRIX_STRICT_CHECKING == 0
557      NSLog(@"insert negative row (%d) in matrix", (int)row);
558#else
559      [NSException raise: NSRangeException
560		  format: @"insert negative row (%d) in matrix", (int)row];
561#endif
562    }
563
564  if ((cellArray != nil) && (count != _numCols))
565    {
566#if	NSMATRIX_STRICT_CHECKING == 0
567      NSLog(@"Wrong number of cells (%d) in row insert in matrix", (int)count);
568#else
569      [NSException raise: NSRangeException
570		  format: @"Wrong number of cells (%d) in row insert in matrix", (int)count];
571#endif
572    }
573
574  if (row >= i)
575    {
576      i = row + 1;
577    }
578
579  /*
580   * Grow the matrix to have the new row.
581   * MacOS-X docs say that if the matrix is empty, we make it have one
582   * row and enough columns for all the elements.
583   */
584  if (count > 0 && (_numRows == 0 || _numCols == 0))
585    {
586      [self _renewRows: 1 columns: count rowSpace: count colSpace: 0];
587    }
588  else
589    {
590      [self _renewRows: i
591	       columns: _numCols ? _numCols : 1
592	      rowSpace: count
593	      colSpace: 0];
594    }
595
596  /*
597   * Rotate the newly created row to the insertion point if necessary.
598   */
599  if (_numRows != row)
600    {
601      id *oldr = _cells[_numRows - 1];
602      BOOL *olds = _selectedCells[_numRows - 1];
603
604      for (i = _numRows - 1; i > row; i--)
605	{
606	  _cells[i] = _cells[i-1];
607	  _selectedCells[i] = _selectedCells[i-1];
608	}
609      _cells[row] = oldr;
610      _selectedCells[row] = olds;
611      if (_selectedCell && (_selectedRow >= row))
612	_selectedRow++;
613
614      if (_dottedRow != -1 && _dottedRow >= row)
615	_dottedRow++;
616    }
617
618  /*
619   * Put cells from the array into the matrix.
620   */
621  if (count > 0)
622    {
623      IMP getImp = [cellArray methodForSelector: getSel];
624
625      for (i = 0; i < _numCols && i < count; i++)
626	{
627	  ASSIGN(_cells[row][i], (*getImp)(cellArray, getSel, i));
628	}
629    }
630
631  if (_mode == NSRadioModeMatrix && !_allowsEmptySelection
632    && _selectedCell == nil)
633    {
634      [self selectCellAtRow: 0 column: 0];
635    }
636
637  [self setNeedsDisplay: YES];
638}
639
640/**<p>Makes and returns new cell at row <var>row</var> and
641   column <var>column</var>.</p>
642 */
643- (NSCell*) makeCellAtRow: (NSInteger)row
644		   column: (NSInteger)column
645{
646  NSCell *aCell;
647
648  if (_cellPrototype != nil)
649    {
650      aCell = (*_cellNew)(_cellPrototype, copySel, _myZone);
651    }
652  else
653    {
654      aCell = (*_cellNew)(_cellClass, allocSel, _myZone);
655      if (aCell != nil)
656	{
657	  aCell = (*_cellInit)(aCell, initSel);
658	}
659    }
660  /*
661   * This is only ever called when we are creating a new cell - so we know
662   * we can simply assign a value into the matrix without releasing an old
663   * value.  If someone uses this method directly (which the documentation
664   * specifically says they shouldn't) they may produce a memory leak.
665   */
666  _cells[row][column] = aCell;
667  return aCell;
668}
669
670/** <p>Returns the rectangle of the cell at row <var>row</var> and column
671    <var>column</var></p>
672 */
673- (NSRect) cellFrameAtRow: (NSInteger)row
674		   column: (NSInteger)column
675{
676  NSRect rect;
677
678  rect.origin.x = column * (_cellSize.width + _intercell.width);
679  rect.origin.y = row * (_cellSize.height + _intercell.height);
680  rect.size = _cellSize;
681  return rect;
682}
683
684/**<p>Gets the number of rows and columns of the NSMatrix</p>
685   <p>See Also: -numberOfColumns -numberOfRows</p>
686 */
687- (void) getNumberOfRows: (NSInteger*)rowCount
688		 columns: (NSInteger*)columnCount
689{
690  *rowCount = _numRows;
691  *columnCount = _numCols;
692}
693
694/**<p>Replaces the NSMatrix's cell at row <var>row</var> and column <var>
695   column</var> by <var>newCell</var> and mark for display the new cell.
696   Raises a NSRangeException if the <var>row</var> or <var>column</var>
697   are out of range.</p>
698 */
699- (void) putCell: (NSCell*)newCell
700	   atRow: (NSInteger)row
701	  column: (NSInteger)column
702{
703  if (row < 0 || row >= _numRows || column < 0 || column >= _numCols)
704    {
705      [NSException raise: NSRangeException
706		  format: @"attempt to put cell outside matrix bounds"];
707    }
708
709  if ((row == _selectedRow) && (column == _selectedColumn)
710    && (_selectedCell != nil))
711    {
712      _selectedCell = newCell;
713    }
714
715  ASSIGN(_cells[row][column], newCell);
716
717  [self setNeedsDisplayInRect: [self cellFrameAtRow: row column: column]];
718}
719
720/**<p>Removes the NSMatrix's column at index <var>column</var></p>
721   <p>See Also: -removeRow:</p>
722 */
723- (void) removeColumn: (NSInteger)column
724{
725  if (column >= 0 && column < _numCols)
726    {
727      NSInteger i;
728
729      for (i = 0; i < _maxRows; i++)
730	{
731	  NSInteger j;
732
733	  AUTORELEASE(_cells[i][column]);
734	  for (j = column + 1; j < _maxCols; j++)
735	    {
736	      _cells[i][j-1] = _cells[i][j];
737	      _selectedCells[i][j-1] = _selectedCells[i][j];
738	    }
739	}
740      _numCols--;
741      _maxCols--;
742
743      if (_maxCols == 0)
744	{
745	  _numRows = _maxRows = 0;
746	}
747
748      if (column == _selectedColumn)
749	{
750	  _selectedCell = nil;
751	  [self selectCellAtRow: _selectedRow column: 0];
752	}
753      if (column == _dottedColumn)
754	{
755	  if (_numCols && [_cells[_dottedRow][0] acceptsFirstResponder])
756	    _dottedColumn = 0;
757	  else
758	    _dottedRow = _dottedColumn = -1;
759	}
760    }
761  else
762    {
763#if	NSMATRIX_STRICT_CHECKING == 0
764      NSLog(@"remove non-existent column (%d) from matrix", (int) column);
765#else
766      [NSException raise: NSRangeException
767	format: @"remove non-existent column (%d) from matrix",  (int)column];
768#endif
769    }
770}
771
772
773/**<p>Removes the NSMatrix's row at index <var>row</var></p>
774   <p>See Also: -removeColumn:</p>
775 */
776- (void) removeRow: (NSInteger)row
777{
778  if (row >= 0 && row < _numRows)
779    {
780      NSInteger i;
781
782      for (i = 0; i < _maxCols; i++)
783	{
784	  AUTORELEASE(_cells[row][i]);
785	}
786      NSZoneFree(_myZone, _selectedCells[row]);
787      NSZoneFree(_myZone, _cells[row]);
788      for (i = row + 1; i < _maxRows; i++)
789	{
790	  _cells[i-1] = _cells[i];
791	  _selectedCells[i-1] = _selectedCells[i];
792	}
793      _maxRows--;
794      _numRows--;
795
796      if (_maxRows == 0)
797	{
798	  _numCols = _maxCols = 0;
799	}
800
801      if (row == _selectedRow)
802	{
803	  _selectedCell = nil;
804	  [self selectCellAtRow: 0 column: _selectedColumn];
805	}
806      if (row == _dottedRow)
807	{
808	  if (_numRows && [_cells[0][_dottedColumn] acceptsFirstResponder])
809	    _dottedRow = 0;
810	  else
811	    _dottedRow = _dottedColumn = -1;
812	}
813    }
814  else
815    {
816#if	NSMATRIX_STRICT_CHECKING == 0
817      NSLog(@"remove non-existent row (%d) from matrix", (int)row);
818#else
819      [NSException raise: NSRangeException
820		  format: @"remove non-existent row (%d) from matrix", (int)row];
821#endif
822    }
823}
824
825- (void) renewRows: (NSInteger)newRows
826	   columns: (NSInteger)newColumns
827{
828  [self _renewRows: newRows columns: newColumns rowSpace: 0 colSpace: 0];
829}
830
831- (void) setCellSize: (NSSize)aSize
832{
833  _cellSize = aSize;
834  [self sizeToCells];
835}
836
837/** <p>Sets the space size between cells to aSize and resizes the matrix to
838    fits the new cells spacing.</p>
839    <p>See Also: -intercellSpacing -sizeToCells</p>
840 */
841- (void) setIntercellSpacing: (NSSize)aSize
842{
843  _intercell = aSize;
844  [self sizeToCells];
845}
846
847- (void) sortUsingFunction: (NSComparisonResult (*)(id element1, id element2,
848				   void *userData))comparator
849		   context: (void*)context
850{
851  NSMutableArray *sorted;
852  IMP add;
853  IMP get;
854  NSInteger i, j, index = 0;
855
856  sorted = [NSMutableArray arrayWithCapacity: _numRows * _numCols];
857  add = [sorted methodForSelector: @selector(addObject:)];
858  get = [sorted methodForSelector: @selector(objectAtIndex:)];
859
860  for (i = 0; i < _numRows; i++)
861    {
862      for (j = 0; j < _numCols; j++)
863	{
864	  (*add)(sorted, @selector(addObject:), _cells[i][j]);
865	}
866    }
867
868  [sorted sortUsingFunction: comparator context: context];
869
870  for (i = 0; i < _numRows; i++)
871    {
872      for (j = 0; j < _numCols; j++)
873	{
874	  _cells[i][j] = (*get)(sorted, @selector(objectAtIndex:), index++);
875	}
876    }
877}
878
879- (void) sortUsingSelector: (SEL)comparator
880{
881  NSMutableArray *sorted;
882  IMP add;
883  IMP get;
884  NSInteger i, j, index = 0;
885
886  sorted = [NSMutableArray arrayWithCapacity: _numRows * _numCols];
887  add = [sorted methodForSelector: @selector(addObject:)];
888  get = [sorted methodForSelector: @selector(objectAtIndex:)];
889
890  for (i = 0; i < _numRows; i++)
891    {
892      for (j = 0; j < _numCols; j++)
893	{
894	  (*add)(sorted, @selector(addObject:), _cells[i][j]);
895	}
896    }
897
898  [sorted sortUsingSelector: comparator];
899
900  for (i = 0; i < _numRows; i++)
901    {
902      for (j = 0; j < _numCols; j++)
903	{
904	  _cells[i][j] = (*get)(sorted, @selector(objectAtIndex:), index++);
905	}
906    }
907}
908
909/** <p>Gets the row and the column of the NSMatrix correponding to the
910    specified NSPoint aPoint. Returns YES if aPoint is within the NSMatrix,
911    NO otherwise</p>
912 */
913- (BOOL) getRow: (NSInteger*)row
914	 column: (NSInteger*)column
915       forPoint: (NSPoint)aPoint
916{
917  BOOL	betweenRows;
918  BOOL	betweenCols;
919  BOOL	beyondRows;
920  BOOL	beyondCols;
921  int	approxRow = aPoint.y / (_cellSize.height + _intercell.height);
922  float	approxRowsHeight = approxRow * (_cellSize.height + _intercell.height);
923  int	approxCol = aPoint.x / (_cellSize.width + _intercell.width);
924  float	approxColsWidth = approxCol * (_cellSize.width + _intercell.width);
925
926  /* First check the limit cases - is the point outside the matrix */
927  beyondCols = (aPoint.x > _bounds.size.width  || aPoint.x < 0);
928  beyondRows = (aPoint.y > _bounds.size.height || aPoint.y < 0);
929
930  /* Determine if the point is inside a cell - note: if the point lies
931     on the cell boundaries, we consider it inside the cell.  to be
932     outside the cell (that is, in the intercell spacing) it must be
933     completely in the intercell spacing - not on the border */
934  /* The following is non zero if the point lies between rows (not inside
935     a cell) */
936  betweenRows = (aPoint.y < approxRowsHeight
937    || aPoint.y > approxRowsHeight + _cellSize.height);
938  betweenCols = (aPoint.x < approxColsWidth
939    || aPoint.x > approxColsWidth + _cellSize.width);
940
941  if (beyondRows || betweenRows || beyondCols || betweenCols
942    || (_numCols == 0) || (_numRows == 0))
943    {
944      if (row)
945	{
946	  *row = -1;
947	}
948
949      if (column)
950	{
951	  *column = -1;
952	}
953
954      return NO;
955    }
956
957  if (row)
958    {
959      if (approxRow < 0)
960	{
961	  approxRow = 0;
962	}
963      else if (approxRow >= _numRows)
964	{
965	  approxRow = _numRows - 1;
966	}
967      *row = approxRow;
968    }
969
970  if (column)
971    {
972      if (approxCol < 0)
973	{
974	  approxCol = 0;
975	}
976      else if (approxCol >= _numCols)
977	{
978	  approxCol = _numCols - 1;
979	}
980      *column = approxCol;
981    }
982
983  return YES;
984}
985
986/** <p>Gets the row and the column of the NSMatrix correponding to the
987    specified NSCell aCell. Returns YES if aCell is in the NSMatrix,
988    NO otherwise</p>
989 */
990- (BOOL) getRow: (NSInteger*)row
991	 column: (NSInteger*)column
992	 ofCell: (NSCell*)aCell
993{
994  NSInteger i;
995
996  for (i = 0; i < _numRows; i++)
997    {
998      NSInteger j;
999
1000      for (j = 0; j < _numCols; j++)
1001	{
1002	  if (_cells[i][j] == aCell)
1003	    {
1004	      if (row)
1005		*row = i;
1006	      if (column)
1007		*column = j;
1008	      return YES;
1009	    }
1010	}
1011    }
1012
1013  if (row)
1014    *row = -1;
1015  if (column)
1016    *column = -1;
1017
1018  return NO;
1019}
1020
1021/** <p>Sets the state of the cell at row <var>row</var> and <var>column</var>
1022    to value. If the NSMatrix's mode is NSRadioModeMatrix it deselects
1023    the cell currently selected if needed.</p>
1024 */
1025- (void) setState: (NSInteger)value
1026	    atRow: (NSInteger)row
1027	   column: (NSInteger)column
1028{
1029  NSCell *aCell = [self cellAtRow: row column: column];
1030
1031  if (!aCell)
1032    {
1033      return;
1034    }
1035
1036  if (_mode == NSRadioModeMatrix)
1037    {
1038      if (value)
1039	{
1040	  if (_selectedRow > -1 && _selectedColumn > -1)
1041	    {
1042	      _selectedCells[_selectedRow][_selectedColumn] = NO;
1043	      [_selectedCell setState: NSOffState];
1044	      [self setNeedsDisplayInRect:
1045		     [self cellFrameAtRow: _selectedRow
1046				   column: _selectedColumn]];
1047	    }
1048
1049	  _selectedCell = aCell;
1050	  _selectedRow = row;
1051	  _selectedColumn = column;
1052
1053	  [_selectedCell setState: value];
1054	  _selectedCells[row][column] = YES;
1055
1056          [self _setKeyRow: row column: column];
1057	}
1058      else if (_allowsEmptySelection)
1059	{
1060	  [self deselectSelectedCell];
1061	}
1062    }
1063  else
1064    {
1065      [aCell setState: value];
1066    }
1067  [self setNeedsDisplayInRect: [self cellFrameAtRow: row column: column]];
1068}
1069
1070/**<p>Deselects all NSMatrix's cells. Does nothing if the NSMatrix's mode
1071   is NSRadioModeMatrix and if it does not allows empty selection.
1072   Except for the case, when there are no cells left at all. Then the
1073   selection is always cleared.</p>
1074   <p>See Also: -mode -allowsEmptySelection -setNeedsDisplayInRect:</p>
1075 */
1076- (void) deselectAllCells
1077{
1078  NSInteger i;
1079
1080  if (_numRows > 0 && _numCols > 0 &&
1081      !_allowsEmptySelection && _mode == NSRadioModeMatrix)
1082    {
1083      return;
1084    }
1085
1086  for (i = 0; i < _numRows; i++)
1087    {
1088      NSInteger j;
1089
1090      for (j = 0; j < _numCols; j++)
1091	{
1092	  if (_selectedCells[i][j])
1093	    {
1094	      NSCell *aCell = _cells[i][j];
1095	      BOOL isHighlighted = [aCell isHighlighted];
1096
1097	      _selectedCells[i][j] = NO;
1098
1099	      if ([aCell state] || isHighlighted)
1100		{
1101		  [aCell setState: NSOffState];
1102
1103		  if (isHighlighted)
1104		    {
1105		      [aCell setHighlighted: NO];
1106		    }
1107		  [self setNeedsDisplayInRect: [self cellFrameAtRow: i
1108						     column: j]];
1109		}
1110	    }
1111	}
1112    }
1113  _selectedCell = nil;
1114  _selectedRow = -1;
1115  _selectedColumn = -1;
1116}
1117
1118/**<p>Deselects the selected cell.Does nothing if the NSMatrix's mode
1119   is NSRadioModeMatrix and if it does not allows empty selection</p>
1120 */
1121- (void) deselectSelectedCell
1122{
1123  NSInteger i,j;
1124
1125  if (!_selectedCell
1126    || (!_allowsEmptySelection && (_mode == NSRadioModeMatrix)))
1127    return;
1128
1129  /*
1130   * For safety (as in macosx)
1131   */
1132  for (i = 0; i < _numRows; i++)
1133    {
1134      for (j = 0; j < _numCols; j++)
1135	{
1136	  if (_selectedCells[i][j])
1137	    {
1138	      [_cells[i][j] setState: NSOffState];
1139	      _selectedCells[i][j] = NO;
1140	    }
1141	}
1142    }
1143
1144  _selectedCell = nil;
1145  _selectedRow = -1;
1146  _selectedColumn = -1;
1147}
1148
1149/**<p>Selects all the cells and marks self for display. Does nothing if the
1150   NSMatrix's mode is NSRadioModeMatrix</p><p>See Also:
1151   -selectCellAtRow:column: -selectCell:</p>
1152 */
1153- (void) selectAll: (id)sender
1154{
1155  NSInteger i, j;
1156
1157  /* Can't select all if only one can be selected.  */
1158  if (_mode == NSRadioModeMatrix)
1159    {
1160      return;
1161    }
1162
1163  _selectedCell = nil;
1164  _selectedRow = -1;
1165  _selectedColumn = -1;
1166
1167  for (i = 0; i < _numRows; i++)
1168    {
1169      for (j = 0; j < _numCols; j++)
1170	{
1171	  if ([_cells[i][j] isEnabled] == YES
1172	    && [_cells[i][j] isEditable] == NO)
1173	    {
1174	      _selectedCell = _cells[i][j];
1175	      [_selectedCell setState: NSOnState];
1176	      _selectedCells[i][j] = YES;
1177
1178	      _selectedRow = i;
1179	      _selectedColumn = j;
1180	    }
1181	  else
1182	    {
1183	      _selectedCells[i][j] = NO;
1184	      [_cells[i][j] setShowsFirstResponder: NO];
1185	    }
1186	}
1187    }
1188
1189  [self setNeedsDisplay: YES];
1190}
1191
1192- (void) _selectCell: (NSCell *)aCell atRow: (NSInteger)row column: (NSInteger)column
1193{
1194  if (aCell)
1195    {
1196      NSRect cellFrame;
1197
1198      if (_selectedCell && _selectedCell != aCell)
1199	{
1200          if (_mode == NSRadioModeMatrix && _selectedRow > -1 && _selectedColumn > -1)
1201            {
1202              _selectedCells[_selectedRow][_selectedColumn] = NO;
1203              [_selectedCell setState: NSOffState];
1204            }
1205	  [self setNeedsDisplayInRect: [self cellFrameAtRow: _selectedRow
1206					     column: _selectedColumn]];
1207	}
1208
1209      _selectedCell = aCell;
1210      _selectedRow = row;
1211      _selectedColumn = column;
1212      _selectedCells[row][column] = YES;
1213
1214      if (_mode == NSListModeMatrix || _mode == NSRadioModeMatrix)
1215	{
1216	  [_selectedCell setState: NSOnState];
1217	}
1218      else
1219	{
1220	  [_selectedCell setNextState];
1221	}
1222
1223      if (_mode == NSListModeMatrix)
1224	[aCell setHighlighted: YES];
1225
1226      cellFrame = [self cellFrameAtRow: row column: column];
1227      if (_autoscroll)
1228	[self scrollRectToVisible: cellFrame];
1229
1230      [self setNeedsDisplayInRect: cellFrame];
1231
1232      [self _setKeyRow: row column: column];
1233    }
1234  else
1235    {
1236      _selectedCell = nil;
1237      _selectedRow = _selectedColumn = -1;
1238    }
1239}
1240
1241- (void) selectCell: (NSCell *)aCell
1242{
1243  NSInteger row, column;
1244
1245  if ([self getRow: &row column: &column ofCell: aCell] == YES)
1246    {
1247      [self _selectCell: aCell atRow: row column: column];
1248
1249      // Note: we select the cell iff it is 'selectable', not 'editable'
1250      // as macosx says.  This looks definitely more appropriate.
1251      // [This is going to start editing only if the cell is also editable,
1252      // otherwise the text gets selected and that's all.]
1253      [self selectTextAtRow: row column: column];
1254    }
1255}
1256
1257/** <p>Selects the cell and the text inside at row <var>row</var>
1258    and column <var>column</var>. If row or column is -1 it deselects all
1259    the cells.</p>
1260    <p>See Also: -deselectSelectedCell -selectTextAtRow:column:</p>
1261 */
1262- (void) selectCellAtRow: (NSInteger)row column: (NSInteger)column
1263{
1264  NSCell *aCell;
1265
1266  if ((row == -1) || (column == -1))
1267    {
1268      [self deselectAllCells];
1269      return;
1270    }
1271
1272  aCell = [self cellAtRow: row column: column];
1273
1274  if (aCell)
1275    {
1276      [self _selectCell: aCell atRow: row column: column];
1277      [self selectTextAtRow: row column: column];
1278    }
1279}
1280
1281/**<p>Selects the cell (and the text inside) with tag <var>anInt</var>.
1282   Return YES if the NSMatrix contains a cell with tag <var>anInt</var>,
1283   NO otherwise.</p><p>See Also: -deselectSelectedCell
1284   -selectTextAtRow:column:</p>
1285 */
1286- (BOOL) selectCellWithTag: (NSInteger)anInt
1287{
1288  id aCell;
1289  NSInteger i = _numRows;
1290
1291  while (i-- > 0)
1292    {
1293      NSInteger j = _numCols;
1294
1295      while (j-- > 0)
1296	{
1297	  aCell = _cells[i][j];
1298	  if ([aCell tag] == anInt)
1299	    {
1300	      [self _selectCell: aCell atRow: i column: j];
1301	      [self selectTextAtRow: i column: j];
1302	      return YES;
1303	    }
1304	}
1305    }
1306  return NO;
1307}
1308
1309/**<p>Returns an array of the selected cells</p>
1310 */
1311- (NSArray*) selectedCells
1312{
1313  NSMutableArray *array = [NSMutableArray array];
1314  NSInteger i;
1315
1316  for (i = 0; i < _numRows; i++)
1317    {
1318      NSInteger j;
1319
1320      for (j = 0; j < _numCols; j++)
1321	{
1322	  if (_selectedCells[i][j] == YES)
1323	    {
1324	      [array addObject: _cells[i][j]];
1325	    }
1326	}
1327    }
1328  return array;
1329}
1330
1331- (void) setSelectionFrom: (NSInteger)startPos
1332		       to: (NSInteger)endPos
1333		   anchor: (NSInteger)anchorPos
1334	        highlight: (BOOL)flag
1335{
1336  /* Cells are selected from the anchor (A) to the point where the mouse
1337   * went down (S) and then they are selected (if the mouse moves away from A)
1338   * or deselected (if the mouse moves closer to A) until the point
1339   * where the mouse goes up (E).
1340   * This is inverted if flag is false (not sure about this though; if this is
1341   * changed, mouse tracking in list mode should be changed too).
1342   */
1343  /* An easy way of doing this is unselecting all cells from A to S and then
1344   * selecting all cells from A to E. Let's try to do it in a more optimized
1345   * way..
1346   */
1347  /* Linear and rectangular selections are a bit different */
1348  if (![self isSelectionByRect]
1349      || [self numberOfRows] == 1 || [self numberOfColumns] == 1)
1350    {
1351      /* Linear selection
1352       * There are three possibilities (ignoring direction):
1353       *    A    S    E
1354       *    sssssssssss
1355       *
1356       *    A    E    S
1357       *    ssssssuuuuu
1358       *
1359       *    E    A    S
1360       *    ssssssuuuuu
1361       *
1362       * So, cells from A to E are selected and, if S is outside the
1363       * range from A to E, cells from S to its closest point are unselected
1364       */
1365      NSInteger selStart = MIN(anchorPos, endPos);
1366      NSInteger selEnd = MAX(anchorPos, endPos);
1367      [self _setState: flag ? NSOnState : NSOffState
1368	    highlight: flag
1369	    startIndex: selStart
1370	    endIndex: selEnd];
1371      if (startPos > selEnd)
1372        {
1373          [self _setState: flag ? NSOffState : NSOnState
1374	        highlight: !flag
1375	        startIndex: selEnd+1
1376	        endIndex: startPos];
1377        }
1378      else if (startPos < selStart)
1379        {
1380          [self _setState: flag ? NSOffState : NSOnState
1381	        highlight: !flag
1382	        startIndex: startPos
1383	        endIndex: selStart-1];
1384        }
1385    }
1386  else
1387    {
1388      /* Rectangular selection
1389       *
1390       *   A     sss
1391       *    S    sss
1392       *     E   sss
1393       *
1394       *   A     ssu
1395       *    E    ssu
1396       *     S   uuu
1397       *
1398       *   E     ss
1399       *    A    ssu
1400       *     S    uu
1401       *
1402       *   A     ssu
1403       *     S   ssu
1404       *    E    ss
1405       *
1406       * So, cells of the rect from A to E are selected and cells of the
1407       * rect from A to S that are outside the first rect are unselected
1408       */
1409      MPoint anchorPoint = POINT_FROM_INDEX(anchorPos);
1410      MPoint endPoint = POINT_FROM_INDEX(endPos);
1411      MPoint startPoint = POINT_FROM_INDEX(startPos);
1412      NSInteger minx_AE = MIN(anchorPoint.x, endPoint.x);
1413      NSInteger miny_AE = MIN(anchorPoint.y, endPoint.y);
1414      NSInteger maxx_AE = MAX(anchorPoint.x, endPoint.x);
1415      NSInteger maxy_AE = MAX(anchorPoint.y, endPoint.y);
1416      NSInteger minx_AS = MIN(anchorPoint.x, startPoint.x);
1417      NSInteger miny_AS = MIN(anchorPoint.y, startPoint.y);
1418      NSInteger maxx_AS = MAX(anchorPoint.x, startPoint.x);
1419      NSInteger maxy_AS = MAX(anchorPoint.y, startPoint.y);
1420
1421      [self _setState: flag ? NSOnState : NSOffState
1422	    highlight: flag
1423	    startIndex: INDEX_FROM_COORDS(minx_AE, miny_AE)
1424	    endIndex: INDEX_FROM_COORDS(maxx_AE, maxy_AE)];
1425      if (startPoint.x > maxx_AE)
1426        {
1427          [self _setState: flag ? NSOffState : NSOnState
1428	        highlight: !flag
1429	        startIndex: INDEX_FROM_COORDS(maxx_AE+1, miny_AS)
1430	        endIndex: INDEX_FROM_COORDS(startPoint.x, maxy_AS)];
1431        }
1432      else if (startPoint.x < minx_AE)
1433        {
1434          [self _setState: flag ? NSOffState : NSOnState
1435	        highlight: !flag
1436	        startIndex: INDEX_FROM_COORDS(startPoint.x, miny_AS)
1437	        endIndex: INDEX_FROM_COORDS(minx_AE-1, maxy_AS)];
1438        }
1439      if (startPoint.y > maxy_AE)
1440        {
1441          [self _setState: flag ? NSOffState : NSOnState
1442	        highlight: !flag
1443	        startIndex: INDEX_FROM_COORDS(minx_AS, maxy_AE+1)
1444	        endIndex: INDEX_FROM_COORDS(maxx_AS, startPoint.y)];
1445        }
1446      else if (startPoint.y < miny_AE)
1447        {
1448          [self _setState: flag ? NSOffState : NSOnState
1449	        highlight: !flag
1450	        startIndex: INDEX_FROM_COORDS(minx_AS, startPoint.y)
1451	        endIndex: INDEX_FROM_COORDS(maxx_AS, miny_AE-1)];
1452        }
1453    }
1454
1455  /*
1456  Update the _selectedCell and related ivars. This could be optimized a lot
1457  in many cases, but the full search cannot be avoided in the general case,
1458  and being correct comes first.
1459  */
1460  {
1461    NSInteger i, j;
1462    for (i = _numRows - 1; i >= 0; i--)
1463      {
1464	for (j = _numCols - 1; j >= 0; j--)
1465	  {
1466	    if (_selectedCells[i][j])
1467	      {
1468		_selectedCell = _cells[i][j];
1469		_selectedRow = i;
1470		_selectedColumn = j;
1471		return;
1472	      }
1473	  }
1474      }
1475    _selectedCell = nil;
1476    _selectedColumn = -1;
1477    _selectedRow = -1;
1478  }
1479}
1480
1481/**<p>Returns the cell at row <var>row</var> and column <var>column</var>
1482   Returns nil if the <var>row</var> or <var>column</var> are out of
1483   range</p>
1484 */
1485- (id) cellAtRow: (NSInteger)row
1486	  column: (NSInteger)column
1487{
1488  if (row < 0 || row >= _numRows || column < 0 || column >= _numCols)
1489    return nil;
1490  return _cells[row][column];
1491}
1492
1493/**<p>Returns the cell with tag <var>anInt</var>
1494   Returns nil if no cell has a tag <var>anInt</var></p>
1495 */
1496- (id) cellWithTag: (NSInteger)anInt
1497{
1498  NSInteger i = _numRows;
1499
1500  while (i-- > 0)
1501    {
1502      NSInteger j = _numCols;
1503
1504      while (j-- > 0)
1505	{
1506	  id aCell = _cells[i][j];
1507
1508	  if ([aCell tag] == anInt)
1509	    {
1510	      return aCell;
1511	    }
1512	}
1513    }
1514  return nil;
1515}
1516
1517/** <p>Returns an array of the NSMatrix's cells</p>
1518 */
1519- (NSArray*) cells
1520{
1521  NSMutableArray *c;
1522  IMP add;
1523  NSInteger i;
1524
1525  c = [NSMutableArray arrayWithCapacity: _numRows * _numCols];
1526  add = [c methodForSelector: @selector(addObject:)];
1527  for (i = 0; i < _numRows; i++)
1528    {
1529      NSInteger j;
1530
1531      for (j = 0; j < _numCols; j++)
1532	{
1533	  (*add)(c, @selector(addObject:), _cells[i][j]);
1534	}
1535    }
1536  return c;
1537}
1538
1539- (void) selectText: (id)sender
1540{
1541  // Attention, we are *not* doing what MacOS-X does.
1542  // But they are *not* doing what the OpenStep specification says.
1543  // This is a compromise -- and fully OpenStep compliant.
1544  NSSelectionDirection s =  NSDirectSelection;
1545
1546  if (_window)
1547    s = [_window keyViewSelectionDirection];
1548
1549  switch (s)
1550    {
1551      // _window selecting backwards
1552    case NSSelectingPrevious:
1553      [self _selectPreviousSelectableCellBeforeRow: _numRows
1554	    column: _numCols];
1555      break;
1556      // _Window selecting forward
1557    case NSSelectingNext:
1558      [self _selectNextSelectableCellAfterRow: -1
1559	    column: -1];
1560      break;
1561    case NSDirectSelection:
1562      // Someone else -- we have some freedom here
1563      if ([_selectedCell isSelectable])
1564	{
1565	  [self selectTextAtRow: _selectedRow
1566			 column: _selectedColumn];
1567	}
1568      else
1569	{
1570	  if (_dottedRow != -1)
1571	    {
1572              [self selectTextAtRow: _dottedRow  column: _dottedColumn];
1573	    }
1574	}
1575      break;
1576    }
1577}
1578
1579/**<p>Select the text of the cell at row <var>row</var> and column
1580   <var>column</var>. The cell is selected if and only if the cell
1581   is selectable ( MacOSX select it if the cell is editable ). This
1582   methods returns the selected cell if exists and selectable,
1583   nil otherwise</p>
1584 */
1585- (id) selectTextAtRow: (NSInteger)row column: (NSInteger)column
1586{
1587  if (row < 0 || row >= _numRows || column < 0 || column >= _numCols)
1588    return self;
1589
1590  // macosx doesn't select the cell if it isn't 'editable'; instead,
1591  // we select the cell if and only if it is 'selectable', which looks
1592  // more appropriate.  This is going to start editing if and only if
1593  // the cell is also 'editable'.
1594  if ([_cells[row][column] isSelectable] == NO)
1595    {
1596      return nil;
1597    }
1598
1599  if (_textObject)
1600    {
1601      if (_selectedCell == _cells[row][column])
1602	{
1603	  [_textObject selectAll: self];
1604	  return _selectedCell;
1605	}
1606      else
1607	{
1608	  [self validateEditing];
1609	  [self abortEditing];
1610	}
1611    }
1612
1613  // Now _textObject == nil
1614  {
1615    NSText *text = [_window fieldEditor: YES
1616			forObject: self];
1617    NSUInteger length;
1618
1619    if (([text superview] != nil) && ([text resignFirstResponder] == NO))
1620      {
1621	return nil;
1622      }
1623
1624    [self _selectCell: _cells[row][column] atRow: row column: column];
1625
1626    /* See comment in NSTextField */
1627    length = [[_selectedCell stringValue] length];
1628    _textObject = [_selectedCell setUpFieldEditorAttributes: text];
1629    [_selectedCell selectWithFrame: [self cellFrameAtRow: _selectedRow
1630					  column: _selectedColumn]
1631		   inView: self
1632		   editor: _textObject
1633		   delegate: self
1634		   start: 0
1635		   length: length];
1636    return _selectedCell;
1637  }
1638}
1639
1640- (id) keyCell
1641{
1642  if (_dottedRow == -1 || _dottedColumn == -1)
1643    {
1644      return nil;
1645    }
1646  else if (_cells != 0)
1647    {
1648      return _cells[_dottedRow][_dottedColumn];
1649    }
1650
1651  return nil;
1652}
1653
1654- (void) setKeyCell: (NSCell *)aCell
1655{
1656  BOOL isValid;
1657  NSInteger row, column;
1658
1659  isValid = [self getRow: &row  column: &column  ofCell: aCell];
1660
1661  if (isValid == YES)
1662    {
1663      [self _setKeyRow: row column: column];
1664    }
1665}
1666
1667/**<p>Returns the next key view</p>
1668   <p>See Also: -setNextText: [NSView-nextKeyView]</p>
1669 */
1670- (id) nextText
1671{
1672  return [self nextKeyView];
1673}
1674
1675/**<p>Returns the previous key view</p>
1676   <p>See Also: -setPreviousText: [NSView-previousKeyView]</p>
1677 */
1678- (id) previousText
1679{
1680  return [self previousKeyView];
1681}
1682
1683/**<p>Invokes when the text cell starts to be editing.This methods posts
1684   a NSControlTextDidBeginEditingNotification with a dictionary containing
1685   the NSFieldEditor as user info </p><p>See Also:
1686   [NSNotificationCenter-postNotificationName:object:userInfo:]</p>
1687*/
1688- (void) textDidBeginEditing: (NSNotification *)aNotification
1689{
1690  [super textDidBeginEditing: aNotification];
1691}
1692
1693/**<p>Invokes when the text cell is changed. This methods posts a
1694   NSControlTextDidChangeNotification with a dictionary containing the
1695   NSFieldEditor as user info </p><p>See Also:
1696   [NSNotificationCenter-postNotificationName:object:userInfo:]</p>
1697*/
1698- (void) textDidChange: (NSNotification *)aNotification
1699{
1700  NSFormatter *formatter;
1701
1702  // MacOS-X asks us to inform the cell if possible.
1703  if ((_selectedCell != nil) && [_selectedCell respondsToSelector:
1704						 @selector(textDidChange:)])
1705    {
1706      [_selectedCell textDidChange: aNotification];
1707    }
1708
1709  [super textDidChange: aNotification];
1710
1711  formatter = [_selectedCell formatter];
1712  if (formatter != nil)
1713    {
1714      /*
1715       * FIXME: This part needs heavy interaction with the yet to finish
1716       * text system.
1717       *
1718       */
1719      NSString *partialString;
1720      NSString *newString = nil;
1721      NSString *error = nil;
1722      BOOL wasAccepted;
1723
1724      partialString = [_textObject string];
1725      wasAccepted = [formatter isPartialStringValid: partialString
1726			       newEditingString: &newString
1727			       errorDescription: &error];
1728
1729      if (wasAccepted == NO)
1730	{
1731	  SEL sel = @selector(control:didFailToValidatePartialString:errorDescription:);
1732
1733	  if ([_delegate respondsToSelector: sel])
1734	    {
1735	      [_delegate control: self
1736			 didFailToValidatePartialString: partialString
1737			 errorDescription: error];
1738	    }
1739	}
1740
1741      if (newString != nil)
1742	{
1743	  NSLog (@"Unimplemented: should set string to %@", newString);
1744	  // FIXME ! This would reset editing !
1745	  //[_textObject setString: newString];
1746	}
1747      else
1748	{
1749	  if (wasAccepted == NO)
1750	    {
1751	      // FIXME: Need to delete last typed character (?!)
1752	      NSLog (@"Unimplemented: should delete last typed character");
1753	    }
1754	}
1755
1756    }
1757}
1758
1759/**<p>Invokes when the text cell is changed.
1760   This methods posts a NSControlTextDidEndEditingNotification
1761   a dictionary containing the NSFieldEditor as user info </p><p>See Also:
1762   [NSNotificationCenter-postNotificationName:object:userInfo:]</p>
1763*/
1764- (void) textDidEndEditing: (NSNotification *)aNotification
1765{
1766  id textMovement;
1767
1768  [super textDidEndEditing: aNotification];
1769
1770  textMovement = [[aNotification userInfo] objectForKey: @"NSTextMovement"];
1771  if (textMovement)
1772    {
1773      switch ([(NSNumber *)textMovement intValue])
1774	{
1775	case NSReturnTextMovement:
1776	  if ([self sendAction] == NO)
1777	    {
1778	      NSEvent *event = [_window currentEvent];
1779
1780	      if ([self performKeyEquivalent: event] == NO
1781		  && [_window performKeyEquivalent: event] == NO)
1782		[self selectText: self];
1783	    }
1784	  break;
1785	case NSTabTextMovement:
1786          if ([_selectedCell sendsActionOnEndEditing])
1787            [self sendAction];
1788
1789	  if (_tabKeyTraversesCells)
1790	    {
1791	      if ([self _selectNextSelectableCellAfterRow: _selectedRow
1792		       column: _selectedColumn])
1793		break;
1794	    }
1795	  [_window selectKeyViewFollowingView: self];
1796
1797	  if ([_window firstResponder] == _window)
1798	    {
1799	      if (_tabKeyTraversesCells)
1800		{
1801		  if ([self _selectNextSelectableCellAfterRow: -1
1802			   column: -1])
1803		    break;
1804		}
1805	      [self selectText: self];
1806	    }
1807	  break;
1808	case NSBacktabTextMovement:
1809          if ([_selectedCell sendsActionOnEndEditing])
1810            [self sendAction];
1811
1812	  if (_tabKeyTraversesCells)
1813	    {
1814	      if ([self _selectPreviousSelectableCellBeforeRow: _selectedRow
1815		       column: _selectedColumn])
1816		break;
1817	    }
1818	  [_window selectKeyViewPrecedingView: self];
1819
1820	  if ([_window firstResponder] == _window)
1821	    {
1822	      if (_tabKeyTraversesCells)
1823		{
1824		  if ([self _selectPreviousSelectableCellBeforeRow: _numRows
1825			   column: _numCols])
1826		    break;
1827		}
1828	      [self selectText: self];
1829	    }
1830	  break;
1831	}
1832    }
1833}
1834
1835/**<p>Asks to the delegate (if it implements -control:textShouldBeginEditing: )
1836   if the text should be edit. Returns YES if the delegate does not implement
1837   this method</p>
1838 */
1839- (BOOL) textShouldBeginEditing: (NSText*)aTextObject
1840{
1841  if (_delegate && [_delegate respondsToSelector:
1842    @selector(control:textShouldBeginEditing:)])
1843    {
1844      return [_delegate control: self
1845	 textShouldBeginEditing: aTextObject];
1846    }
1847  return YES;
1848}
1849
1850- (BOOL) textShouldEndEditing: (NSText *)aTextObject
1851{
1852  if ([_selectedCell isEntryAcceptable: [aTextObject text]] == NO)
1853    {
1854      [self sendAction: _errorAction to: _target];
1855      return NO;
1856    }
1857
1858  if ([_delegate respondsToSelector:
1859		   @selector(control:textShouldEndEditing:)])
1860    {
1861      if ([_delegate control: self
1862		     textShouldEndEditing: aTextObject] == NO)
1863	{
1864	  NSBeep ();
1865	  return NO;
1866	}
1867    }
1868
1869  if ([_delegate respondsToSelector:
1870		   @selector(control:isValidObject:)] == YES)
1871    {
1872      NSFormatter *formatter;
1873      id newObjectValue;
1874
1875      formatter = [_selectedCell formatter];
1876
1877      if ([formatter getObjectValue: &newObjectValue
1878		     forString: [_textObject text]
1879		     errorDescription: NULL] == YES)
1880	{
1881	  if ([_delegate control: self
1882			 isValidObject: newObjectValue] == NO)
1883            {
1884              return NO;
1885            }
1886	}
1887    }
1888
1889  // In all other cases
1890  return YES;
1891}
1892
1893- (BOOL) tabKeyTraversesCells
1894{
1895  return _tabKeyTraversesCells;
1896}
1897
1898- (void) setTabKeyTraversesCells: (BOOL)flag
1899{
1900  _tabKeyTraversesCells = flag;
1901}
1902
1903/**<p>Sets the next key view to <var>anObject</var></p>
1904   <p>See Also: -nextText [NSView-setNextKeyView:</p>
1905 */
1906- (void) setNextText: (id)anObject
1907{
1908  [self setNextKeyView: anObject];
1909}
1910
1911/**<p>Sets the previous key view to <var>anObject</var></p>
1912   <p>See Also: -previousText [NSView-setPreviousKeyView:</p>
1913 */
1914- (void) setPreviousText: (id)anObject
1915{
1916  [self setPreviousKeyView: anObject];
1917}
1918
1919- (void) setValidateSize: (BOOL)flag
1920{
1921  // TODO
1922}
1923
1924- (void) sizeToCells
1925{
1926  NSSize newSize;
1927  NSInteger nc = _numCols;
1928  NSInteger nr = _numRows;
1929
1930  if (!nc)
1931    nc = 1;
1932  if (!nr)
1933    nr = 1;
1934  newSize.width = nc * (_cellSize.width + _intercell.width) - _intercell.width;
1935  newSize.height = nr * (_cellSize.height + _intercell.height) - _intercell.height;
1936  [super setFrameSize: newSize];
1937}
1938
1939- (void) sizeToFit
1940{
1941  /*
1942   * A simple explanation of the logic behind this method.
1943   *
1944   * Example of when you would like to use this method:
1945   * you have a matrix containing radio buttons.  Say that you have the
1946   * following radio buttons -
1947   *
1948   * * First option
1949   * * Second option
1950   * * Third option
1951   * * No thanks, no option for me
1952   *
1953   * this method should size the matrix so that it can comfortably
1954   * show all the cells it contains.  To do it, we must consider that
1955   * all the cells should be given the same size, yet some cells need
1956   * more space than the others to show their contents, so we need to
1957   * choose the cell size as to be enough to display every cell.  We
1958   * loop on all cells, call cellSize on each (which returns the
1959   * *minimum* comfortable size to display that cell), and choose a
1960   * final cellSize which is enough big to be bigger than all these
1961   * cellSizes.  We resize the matrix to have that cellSize, and
1962   * that's it.  */
1963  NSSize newSize = NSZeroSize;
1964  NSInteger i, j;
1965
1966  for (i = 0; i < _numRows; i++)
1967    {
1968      for (j = 0; j < _numCols; j++)
1969	{
1970	  NSSize tempSize = [_cells[i][j] cellSize];
1971	  tempSize.height = ceil(tempSize.height);
1972	  tempSize.width = ceil(tempSize.width);
1973	  if (tempSize.width > newSize.width)
1974	    {
1975	      newSize.width = tempSize.width;
1976	    }
1977	  if (tempSize.height > newSize.height)
1978	    {
1979	      newSize.height = tempSize.height;
1980	    }
1981	}
1982    }
1983
1984  [self setCellSize: newSize];
1985}
1986
1987/**<p>Scrolls the NSMatrix to make the cell at row <var>row</var> and column
1988   <var>column</var> visible</p>
1989   <p>See Also: -scrollRectToVisible: -cellFrameAtRow:column:</p>
1990 */
1991- (void) scrollCellToVisibleAtRow: (NSInteger)row
1992			   column: (NSInteger)column
1993{
1994  [self scrollRectToVisible: [self cellFrameAtRow: row column: column]];
1995}
1996
1997- (void) setAutoscroll: (BOOL)flag
1998{
1999  _autoscroll = flag;
2000}
2001
2002- (void) setScrollable: (BOOL)flag
2003{
2004  NSInteger i;
2005
2006  for (i = 0; i < _numRows; i++)
2007    {
2008      NSInteger j;
2009
2010      for (j = 0; j < _numCols; j++)
2011	{
2012	  [_cells[i][j] setScrollable: flag];
2013	}
2014    }
2015  [_cellPrototype setScrollable: flag];
2016}
2017
2018- (void) drawRect: (NSRect)rect
2019{
2020  NSInteger i, j;
2021  NSInteger row1, col1;	// The cell at the upper left corner
2022  NSInteger row2, col2;	// The cell at the lower right corner
2023
2024  if (_drawsBackground)
2025    {
2026      [_backgroundColor set];
2027      NSRectFill(rect);
2028    }
2029
2030  if (!_numRows || !_numCols)
2031    return;
2032
2033  row1 = rect.origin.y / (_cellSize.height + _intercell.height);
2034  col1 = rect.origin.x / (_cellSize.width + _intercell.width);
2035  row2 = NSMaxY(rect) / (_cellSize.height + _intercell.height);
2036  col2 = NSMaxX(rect) / (_cellSize.width + _intercell.width);
2037
2038  if (row1 < 0)
2039    {
2040      row1 = 0;
2041    }
2042  else if (row1 >= _numRows)
2043    {
2044      row1 = _numRows - 1;
2045    }
2046
2047  if (col1 < 0)
2048    {
2049      col1 = 0;
2050    }
2051  else if (col1 >= _numCols)
2052    {
2053      col1 = _numCols - 1;
2054    }
2055
2056  if (row2 < 0)
2057    {
2058      row2 = 0;
2059    }
2060  else if (row2 >= _numRows)
2061    {
2062      row2 = _numRows - 1;
2063    }
2064
2065  if (col2 < 0)
2066    {
2067      col2 = 0;
2068    }
2069  else if (col2 >= _numCols)
2070    {
2071      col2 = _numCols - 1;
2072    }
2073
2074  /* Draw the cells within the drawing rectangle. */
2075  for (i = row1; i <= row2 && i < _numRows; i++)
2076    {
2077      for (j = col1; j <= col2 && j < _numCols; j++)
2078        {
2079          [self drawCellAtRow: i column: j];
2080        }
2081    }
2082}
2083
2084- (BOOL) isOpaque
2085{
2086  return _drawsBackground;
2087}
2088
2089- (void) drawCell: (NSCell *)aCell
2090{
2091  NSInteger row, column;
2092
2093  if ([self getRow: &row  column: &column  ofCell: aCell] == YES)
2094    {
2095      [self drawCellAtRow: row  column: column];
2096    }
2097}
2098
2099/**<p>Draws the cell at row <var>row</var> and column <var>column</var></p>
2100   <p>See Also: [NSCell-drawWithFrame:inView:] -setDrawsCellBackground:
2101   -drawsCellBackground</p>
2102 */
2103- (void) drawCellAtRow: (NSInteger)row column: (NSInteger)column
2104{
2105  NSCell *aCell = [self cellAtRow: row column: column];
2106
2107  if (aCell)
2108    {
2109      NSRect cellFrame = [self cellFrameAtRow: row column: column];
2110
2111     if (_drawsCellBackground)
2112        {
2113          [_cellBackgroundColor set];
2114          NSRectFill(cellFrame);
2115        }
2116
2117      if (_dottedRow == row
2118          && _dottedColumn == column
2119          && [aCell acceptsFirstResponder]
2120          && [_window isKeyWindow]
2121          && [_window firstResponder] == self)
2122        {
2123          [aCell setShowsFirstResponder: YES];
2124          [aCell drawWithFrame: cellFrame inView: self];
2125          [aCell setShowsFirstResponder: NO];
2126        }
2127      else
2128        {
2129          [aCell setShowsFirstResponder: NO];
2130          [aCell drawWithFrame: cellFrame inView: self];
2131        }
2132    }
2133}
2134
2135/** <p>(Un)Highlights the cell (if exists ) at row at row <var>row</var>
2136    and column <var>column</var>. and maks the cell rect for display.</p>
2137    <p>See Also: -setNeedsDisplayInRect: [NSCell-setHighlighted:]</p>
2138 */
2139- (void) highlightCell: (BOOL)flag atRow: (NSInteger)row column: (NSInteger)column
2140{
2141  NSCell *aCell = [self cellAtRow: row column: column];
2142
2143  if (aCell)
2144    {
2145      [aCell setHighlighted: flag];
2146      [self setNeedsDisplayInRect: [self cellFrameAtRow: row column: column]];
2147    }
2148}
2149
2150/**<p>Sends the cell action, if a NSMatrix's cell is selected
2151   and enabled, sends the NSMatrix action otherwise. Returns YES if
2152   the action is succesfully sent. NO if a cell is selected but not enabled
2153   or if an action can not be sent.</p>
2154   <p>See Also: -sendAction:to: -selectedCell</p>
2155 */
2156- (BOOL) sendAction
2157{
2158  if (_selectedCell)
2159    {
2160      if ([_selectedCell isEnabled] == NO)
2161        {
2162          return NO;
2163        }
2164
2165      return [self sendAction: [_selectedCell action]
2166		   to:         [_selectedCell target]];
2167    }
2168
2169  // _selectedCell == nil
2170  return [super sendAction: _action to: _target];
2171}
2172
2173- (BOOL) sendAction: (SEL)theAction
2174		 to: (id)theTarget
2175{
2176  if (theAction)
2177    {
2178      if (theTarget)
2179	{
2180	  return [super sendAction: theAction to: theTarget];
2181	}
2182      else
2183	{
2184	  return [super sendAction: theAction to: _target];
2185	}
2186    }
2187  else
2188    {
2189      return [super sendAction: _action to: _target];
2190    }
2191}
2192
2193- (void) sendAction: (SEL)aSelector
2194		 to: (id)anObject
2195        forAllCells: (BOOL)flag
2196{
2197  NSInteger i;
2198
2199  if (flag)
2200    {
2201      for (i = 0; i < _numRows; i++)
2202	{
2203	  NSInteger j;
2204
2205	  for (j = 0; j < _numCols; j++)
2206	    {
2207	      if (![anObject performSelector: aSelector
2208				  withObject: _cells[i][j]])
2209		{
2210		  return;
2211		}
2212	    }
2213	}
2214    }
2215  else
2216    {
2217      for (i = 0; i < _numRows; i++)
2218	{
2219	  NSInteger j;
2220
2221	  for (j = 0; j < _numCols; j++)
2222	    {
2223	      if (_selectedCells[i][j])
2224		{
2225		  if (![anObject performSelector: aSelector
2226				      withObject: _cells[i][j]])
2227		    {
2228		      return;
2229		    }
2230		}
2231	    }
2232	}
2233    }
2234}
2235
2236/**
2237 */
2238- (void) sendDoubleAction
2239{
2240  if ([_selectedCell isEnabled] == NO)
2241    return;
2242
2243  if (_doubleAction)
2244    [self sendAction: _doubleAction to: _target];
2245  else
2246    [self sendAction];
2247}
2248
2249/**<p>Returns NO if the NSMatrix's mode is <ref type="type" id="NSMatrixMode">
2250   NSListModeMatrix</ref>,  YES otherwise.</p>
2251   <p>See Also: -setMode: -mode</p>
2252 */
2253- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent
2254{
2255  if (_mode == NSListModeMatrix)
2256    return NO;
2257  else
2258    return YES;
2259}
2260
2261- (void) _mouseDownNonListMode: (NSEvent *)theEvent
2262{
2263  BOOL mouseUpInCell = NO, onCell, scrolling = NO, mouseUp = NO;
2264  NSCell *mouseCell;
2265  NSInteger mouseRow;
2266  NSInteger mouseColumn;
2267  NSPoint mouseLocation;
2268  NSRect mouseCellFrame;
2269  NSCell *originallySelectedCell = _selectedCell;
2270  NSUInteger eventMask = NSLeftMouseUpMask | NSLeftMouseDownMask
2271                     | NSMouseMovedMask  | NSLeftMouseDraggedMask;
2272
2273  while (!mouseUp)
2274    {
2275      mouseLocation = [self convertPoint: [theEvent locationInWindow]
2276                                fromView: nil];
2277
2278      onCell = [self getRow: &mouseRow
2279		     column: &mouseColumn
2280		     forPoint: mouseLocation];
2281
2282      if (onCell)
2283	{
2284	  mouseCellFrame = [self cellFrameAtRow: mouseRow column: mouseColumn];
2285	  mouseCell = [self cellAtRow: mouseRow column: mouseColumn];
2286
2287	  if (_autoscroll)
2288            {
2289	      scrolling = [self scrollRectToVisible: mouseCellFrame];
2290            }
2291
2292	  if ([mouseCell isEnabled])
2293	    {
2294              int old_state;
2295
2296              /* Select the cell before tracking. The cell can send its action
2297               * during tracking, and the target discovers which cell was
2298               * clicked calling selectedCell.
2299               * The cell calls -nextState before sending the action, so its
2300               * state should not be changed here (except in radio mode).
2301               */
2302              old_state = [mouseCell state];
2303              [self _selectCell: mouseCell atRow: mouseRow column: mouseColumn];
2304              if (_mode == NSRadioModeMatrix && !_allowsEmptySelection)
2305                {
2306                  [mouseCell setState: NSOffState];
2307                }
2308              else
2309                {
2310                  [mouseCell setState: old_state];
2311                }
2312
2313	      if (_mode != NSTrackModeMatrix)
2314		{
2315		  [self highlightCell: YES
2316			atRow: mouseRow
2317			column: mouseColumn];
2318		}
2319
2320	      mouseUpInCell = [mouseCell trackMouse: theEvent
2321                                         inRect: mouseCellFrame
2322                                         ofView: self
2323					 untilMouseUp:
2324					   [[mouseCell class]
2325					     prefersTrackingUntilMouseUp]];
2326
2327	      if (_mode != NSTrackModeMatrix)
2328		{
2329		  [self highlightCell: NO
2330			atRow: mouseRow
2331			column: mouseColumn];
2332		}
2333              else
2334                {
2335                  if ([mouseCell state] != old_state)
2336                    {
2337                      [self setNeedsDisplayInRect: mouseCellFrame];
2338                    }
2339                }
2340
2341	      mouseUp = mouseUpInCell
2342                      || ([[NSApp currentEvent] type] == NSLeftMouseUp);
2343
2344	      if (!mouseUpInCell)
2345		{
2346	          _selectedCells[_selectedRow][_selectedColumn] = NO;
2347	          _selectedCell = nil;
2348	          _selectedRow = _selectedColumn = -1;
2349		}
2350	    }
2351	}
2352
2353      // if mouse didn't go up, take next event
2354      if (!mouseUp)
2355	{
2356          NSEvent *newEvent;
2357	  newEvent = [NSApp nextEventMatchingMask: eventMask
2358			    untilDate: !scrolling
2359			    ? [NSDate distantFuture]
2360			    : [NSDate dateWithTimeIntervalSinceNow: 0.05]
2361			    inMode: NSEventTrackingRunLoopMode
2362			    dequeue: YES];
2363
2364	  if (newEvent != nil)
2365            {
2366              theEvent = newEvent;
2367              mouseUp = ([theEvent type] == NSLeftMouseUp);
2368            }
2369	}
2370    }
2371
2372  if (!mouseUpInCell)
2373    {
2374      if (_mode == NSRadioModeMatrix && !_allowsEmptySelection)
2375        {
2376          [self selectCell: originallySelectedCell];
2377        }
2378      [self sendAction]; /* like OPENSTEP, unlike MacOSX */
2379    }
2380}
2381
2382- (void) _mouseDownListMode: (NSEvent *) theEvent
2383{
2384  NSPoint locationInWindow, mouseLocation;
2385  NSInteger mouseRow, mouseColumn;
2386  NSInteger mouseIndex, previousIndex = 0, anchor = 0;
2387  id mouseCell, previousCell = nil;
2388  BOOL onCell;
2389  BOOL isSelecting = YES;
2390  NSUInteger eventMask = NSLeftMouseUpMask | NSLeftMouseDownMask
2391                     | NSMouseMovedMask | NSLeftMouseDraggedMask
2392                     | NSPeriodicMask;
2393
2394  // List mode
2395  // multiple cells can be selected, dragging the mouse
2396  // cells do not track the mouse
2397  // shift key makes expands selection noncontiguously
2398  // alternate key expands selection contiguously
2399  // implementation based on OS 4.2 behaviour, that is different from MacOS X
2400
2401  if (_autoscroll)
2402    {
2403      [NSEvent startPeriodicEventsAfterDelay: 0.05 withPeriod: 0.05];
2404    }
2405
2406  locationInWindow = [theEvent locationInWindow];
2407
2408  while ([theEvent type] != NSLeftMouseUp)
2409    {
2410      // must convert location each time or periodic events won't work well
2411      mouseLocation = [self convertPoint: locationInWindow fromView: nil];
2412      onCell = [self getRow: &mouseRow
2413		     column: &mouseColumn
2414		     forPoint: mouseLocation];
2415
2416      if (onCell)
2417	{
2418	  mouseCell = [self cellAtRow: mouseRow column: mouseColumn];
2419          mouseIndex = INDEX_FROM_COORDS(mouseColumn, mouseRow);
2420
2421	  if (_autoscroll)
2422            {
2423              NSRect mouseRect;
2424              mouseRect = [self cellFrameAtRow: mouseRow column: mouseColumn];
2425	      [self scrollRectToVisible: mouseRect];
2426            }
2427
2428
2429	  if (mouseCell != previousCell && [mouseCell isEnabled] == YES)
2430	    {
2431	      if (!previousCell)
2432		{
2433                  // When the user first clicks on a cell
2434                  // we clear the existing selection
2435                  // unless the Alternate or Shift keys have been pressed.
2436		  if (!(mouseDownFlags & NSShiftKeyMask)
2437		    && !(mouseDownFlags & NSAlternateKeyMask))
2438		    {
2439		      [self deselectAllCells];
2440		    }
2441
2442                  /* The clicked cell is the anchor of the selection, unless
2443                   * the Alternate key is pressed, when the anchor is made
2444                   * the key cell, from which the selection will be
2445                   * extended (this is probably not the best cell when
2446                   * selection is by rect)
2447                   */
2448		  if (!(mouseDownFlags & NSAlternateKeyMask))
2449                    {
2450		      anchor = INDEX_FROM_COORDS(mouseColumn, mouseRow);
2451                    }
2452                  else
2453                    {
2454                      if (_dottedColumn != -1)
2455		        anchor = INDEX_FROM_COORDS(_dottedColumn, _dottedRow);
2456                      else
2457                        anchor = INDEX_FROM_COORDS(0, 0);
2458                    }
2459
2460                  /* With the shift key pressed, clicking on a selected cell
2461                   * deselects it (and inverts the selection on mouse dragging).
2462                   */
2463		  if (mouseDownFlags & NSShiftKeyMask)
2464                    {
2465                      isSelecting = ([mouseCell state] == NSOffState);
2466                    }
2467                  else
2468                    {
2469                      isSelecting = YES;
2470                    }
2471
2472                  previousIndex = mouseIndex;
2473		}
2474
2475	      [self setSelectionFrom: previousIndex
2476	            to: mouseIndex
2477	            anchor: anchor
2478	            highlight: isSelecting];
2479              [self _setKeyRow: mouseRow column: mouseColumn];
2480
2481              previousIndex = mouseIndex;
2482	      previousCell = mouseCell;
2483	    }
2484	}
2485
2486      theEvent = [NSApp nextEventMatchingMask: eventMask
2487                                    untilDate: [NSDate distantFuture]
2488                                       inMode: NSEventTrackingRunLoopMode
2489                                      dequeue: YES];
2490
2491      NSDebugLLog(@"NSMatrix", @"matrix: got event of type: %d\n",
2492                  (int)[theEvent type]);
2493
2494      if ([theEvent type] != NSPeriodic)
2495        {
2496          locationInWindow = [theEvent locationInWindow];
2497        }
2498    }
2499
2500  if (_autoscroll)
2501    {
2502      [NSEvent stopPeriodicEvents];
2503    }
2504
2505  [self sendAction];
2506}
2507
2508- (void) mouseDown: (NSEvent*)theEvent
2509{
2510  NSInteger row, column;
2511  NSPoint lastLocation = [theEvent locationInWindow];
2512  NSInteger clickCount;
2513
2514  /*
2515   * Pathological case -- ignore mouse down
2516   */
2517  if ((_numRows == 0) || (_numCols == 0))
2518    {
2519      [super mouseDown: theEvent];
2520      return;
2521    }
2522
2523  // Manage multi-click events
2524  clickCount = [theEvent clickCount];
2525
2526  if (clickCount > 2)
2527    return;
2528
2529  if (clickCount == 2 && (_ignoresMultiClick == NO))
2530    {
2531      [self sendDoubleAction];
2532      return;
2533    }
2534
2535  // From now on, code to manage simple-click events
2536
2537  lastLocation = [self convertPoint: lastLocation
2538		       fromView: nil];
2539
2540  // If mouse down was on a selectable cell, start editing/selecting.
2541  if ([self getRow: &row
2542	    column: &column
2543	    forPoint: lastLocation])
2544    {
2545      if ([_cells[row][column] isEnabled])
2546	{
2547	  if ([_cells[row][column] isSelectable])
2548	    {
2549	      NSText *t = [_window fieldEditor: YES forObject: self];
2550
2551	      if ([t superview] != nil)
2552		{
2553		  if ([t resignFirstResponder] == NO)
2554		    {
2555		      if ([_window makeFirstResponder: _window] == NO)
2556			return;
2557		    }
2558		}
2559	      // During editing, the selected cell is the cell being edited
2560              [self _selectCell: _cells[row][column] atRow: row column: column];
2561	      _textObject = [_selectedCell setUpFieldEditorAttributes: t];
2562	      [_selectedCell editWithFrame: [self cellFrameAtRow: row
2563						  column: column]
2564			     inView: self
2565			     editor: _textObject
2566			     delegate: self
2567			     event: theEvent];
2568	      return;
2569	    }
2570	}
2571    }
2572
2573  // Paranoia check -- _textObject should already be nil, since we
2574  // accept first responder, so NSWindow should have already given
2575  // us first responder status (thus already ending editing with _textObject).
2576  if (_textObject)
2577    {
2578      NSLog (@"Hi, I am a bug.");
2579      [self validateEditing];
2580      [self abortEditing];
2581    }
2582
2583  mouseDownFlags = [theEvent modifierFlags];
2584
2585  if (_mode != NSListModeMatrix)
2586    {
2587      [self _mouseDownNonListMode: theEvent];
2588    }
2589  else
2590    {
2591      [self _mouseDownListMode: theEvent];
2592    }
2593}
2594
2595
2596- (void) updateCell: (NSCell*)aCell
2597{
2598  NSInteger row, col;
2599  NSRect rect;
2600
2601  if ([self getRow: &row column: &col ofCell: aCell] == NO)
2602    {
2603      return;	// Not a cell in this matrix - we can't update it.
2604    }
2605
2606  rect = [self cellFrameAtRow: row column: col];
2607  [self setNeedsDisplayInRect: rect];
2608}
2609
2610/**<p>Simulates a mouse click for the first cell with the corresponding
2611   key Equivalent.</p>
2612   <p>See Also: [NSCell-keyEquivalent]</p>
2613 */
2614- (BOOL) performKeyEquivalent: (NSEvent*)theEvent
2615{
2616  NSString *keyEquivalent = [theEvent charactersIgnoringModifiers];
2617  NSUInteger modifiers = [theEvent modifierFlags];
2618  int i;
2619  NSUInteger relevantModifiersMask = NSCommandKeyMask | NSAlternateKeyMask | NSControlKeyMask;
2620
2621  /* Take shift key into account only for control keys and arrow and function keys */
2622  if ((modifiers & NSFunctionKeyMask)
2623      || ([keyEquivalent length] > 0 && [[NSCharacterSet controlCharacterSet] characterIsMember:[keyEquivalent characterAtIndex:0]]))
2624    relevantModifiersMask |= NSShiftKeyMask;
2625
2626  if ([keyEquivalent length] == 0)
2627    return NO; // don't respond to zero-length string (such as the Windows key)
2628
2629  for (i = 0; i < _numRows; i++)
2630    {
2631      int j;
2632
2633      for (j = 0; j < _numCols; j++)
2634	{
2635	  NSCell *aCell = _cells[i][j];
2636          NSUInteger mask = 0;
2637
2638          if ([aCell respondsToSelector:@selector(keyEquivalentModifierMask)])
2639            mask = [(NSButtonCell *)aCell keyEquivalentModifierMask];
2640
2641	  if ([aCell isEnabled]
2642	    && [[aCell keyEquivalent] isEqualToString: keyEquivalent]
2643              && (mask & relevantModifiersMask) == (modifiers & relevantModifiersMask))
2644	    {
2645	      NSCell *oldSelectedCell = _selectedCell;
2646	      int     oldSelectedRow = _selectedRow;
2647	      int     oldSelectedColumn = _selectedColumn;
2648
2649	      _selectedCell = aCell;
2650	      [self lockFocus];
2651	      [self highlightCell: YES atRow: i column: j];
2652	      [_window flushWindow];
2653	      [aCell setNextState];
2654	      [self sendAction];
2655	      [self highlightCell: NO atRow: i column: j];
2656	      [self unlockFocus];
2657	      _selectedCell = oldSelectedCell;
2658	      _selectedRow = oldSelectedRow;
2659	      _selectedColumn = oldSelectedColumn;
2660
2661	      return YES;
2662	    }
2663	}
2664    }
2665
2666  return NO;
2667}
2668
2669- (void) resetCursorRects
2670{
2671  NSInteger i;
2672
2673  for (i = 0; i < _numRows; i++)
2674    {
2675      NSInteger j;
2676
2677      for (j = 0; j < _numCols; j++)
2678	{
2679	  NSCell *aCell = _cells[i][j];
2680
2681	  [aCell resetCursorRect: [self cellFrameAtRow: i column: j]
2682		 inView: self];
2683	}
2684    }
2685}
2686
2687- (NSString*) toolTipForCell: (NSCell*)cell
2688{
2689  // FIXME
2690  return @"";
2691}
2692
2693- (void) setToolTip: (NSString*)toolTipString forCell: (NSCell*)cell
2694{
2695  // FIXME
2696}
2697
2698- (void) encodeWithCoder: (NSCoder*)aCoder
2699{
2700  [super encodeWithCoder: aCoder];
2701  if ([aCoder allowsKeyedCoding])
2702    {
2703      GSMatrixFlags matrixFlags;
2704      unsigned int mFlags = 0;
2705
2706      [aCoder encodeObject: [self backgroundColor] forKey: @"NSBackgroundColor"];
2707      [aCoder encodeObject: [self cellBackgroundColor] forKey: @"NSCellBackgroundColor"];
2708      [aCoder encodeObject: [self prototype] forKey: @"NSProtoCell"];
2709      [aCoder encodeObject: NSStringFromClass([self cellClass]) forKey: @"NSCellClass"];
2710      [aCoder encodeSize: _cellSize forKey: @"NSCellSize"];
2711      [aCoder encodeSize: _intercell forKey: @"NSIntercellSpacing"];
2712
2713      /// set the flags...
2714      matrixFlags.isRadio = ([self mode] == NSRadioModeMatrix);
2715      matrixFlags.isList = ([self mode] == NSListModeMatrix);
2716      matrixFlags.isHighlight = ([self mode] == NSHighlightModeMatrix);
2717      matrixFlags.allowsEmptySelection = [self allowsEmptySelection];
2718      matrixFlags.selectionByRect = [self isSelectionByRect];
2719      matrixFlags.drawCellBackground = [self drawsCellBackground];
2720      matrixFlags.drawBackground = [self drawsBackground];
2721      matrixFlags.tabKeyTraversesCells = _tabKeyTraversesCells;
2722      matrixFlags.autosizesCells = _autosizesCells;
2723
2724      // clear unused...
2725      matrixFlags.autoScroll = 0;
2726      matrixFlags.drawingAncestor = 0;
2727      matrixFlags.tabKeyTraversesCellsExplicitly = 0;
2728      matrixFlags.canSearchIncrementally = 0;
2729      matrixFlags.unused = 0;
2730
2731      memcpy((void *)&mFlags,(void *)&matrixFlags,sizeof(unsigned int));
2732      [aCoder encodeInt: mFlags forKey: @"NSMatrixFlags"];
2733
2734      [aCoder encodeInt: _numCols forKey: @"NSNumCols"];
2735      [aCoder encodeInt: _numRows forKey: @"NSNumRows"];
2736      [aCoder encodeObject: [self cells] forKey: @"NSCells"];
2737      [aCoder encodeInt: _selectedColumn forKey: @"NSSelectedCol"];
2738      [aCoder encodeInt: _selectedRow forKey: @"NSSelectedRow"];
2739    }
2740  else
2741    {
2742      [aCoder encodeValueOfObjCType: @encode (int) at: &_mode];
2743      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_allowsEmptySelection];
2744      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_selectionByRect];
2745      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_autosizesCells];
2746      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_autoscroll];
2747      [aCoder encodeSize: _cellSize];
2748      [aCoder encodeSize: _intercell];
2749      [aCoder encodeObject: _backgroundColor];
2750      [aCoder encodeObject: _cellBackgroundColor];
2751      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_drawsBackground];
2752      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_drawsCellBackground];
2753      [aCoder encodeObject: NSStringFromClass (_cellClass)];
2754      [aCoder encodeObject: _cellPrototype];
2755      [aCoder encodeValueOfObjCType: @encode (int) at: &_numRows];
2756      [aCoder encodeValueOfObjCType: @encode (int) at: &_numCols];
2757
2758      /* This is slower, but does not expose NSMatrix internals and will work
2759     with subclasses */
2760      [aCoder encodeObject: [self cells]];
2761
2762      [aCoder encodeConditionalObject: _delegate];
2763      [aCoder encodeConditionalObject: _target];
2764      [aCoder encodeValueOfObjCType: @encode (SEL) at: &_action];
2765      [aCoder encodeValueOfObjCType: @encode (SEL) at: &_doubleAction];
2766      [aCoder encodeValueOfObjCType: @encode (SEL) at: &_errorAction];
2767      [aCoder encodeValueOfObjCType: @encode (BOOL) at: &_tabKeyTraversesCells];
2768      [aCoder encodeObject: [self keyCell]];
2769      /* We do not encode information on selected cells, because this is saved
2770	 with the cells themselves */
2771    }
2772}
2773
2774- (id) initWithCoder: (NSCoder*)aDecoder
2775{
2776  Class class;
2777  id cell;
2778  int rows = 0, columns = 0;
2779  NSArray *array;
2780  NSInteger i = 0, count = 0;
2781
2782  self = [super initWithCoder: aDecoder];
2783  if (!self)
2784    return nil;
2785
2786  if ([aDecoder allowsKeyedCoding])
2787    {
2788      if ([aDecoder containsValueForKey: @"NSBackgroundColor"])
2789        {
2790          [self setBackgroundColor: [aDecoder decodeObjectForKey: @"NSBackgroundColor"]];
2791        }
2792      if ([aDecoder containsValueForKey: @"NSCellBackgroundColor"])
2793        {
2794          [self setCellBackgroundColor: [aDecoder decodeObjectForKey: @"NSCellBackgroundColor"]];
2795        }
2796      if ([aDecoder containsValueForKey: @"NSProtoCell"])
2797        {
2798          [self setPrototype: [aDecoder decodeObjectForKey: @"NSProtoCell"]];
2799        }
2800      if ([aDecoder containsValueForKey: @"NSCellClass"])
2801        {
2802          class = NSClassFromString((NSString *)[aDecoder decodeObjectForKey: @"NSCellClass"]);
2803          if (class != Nil)
2804            {
2805              [self setCellClass: class];
2806            }
2807        }
2808      if ([aDecoder containsValueForKey: @"NSCellSize"])
2809        {
2810          // Don't use method here as this would change the frame
2811          _cellSize = [aDecoder decodeSizeForKey: @"NSCellSize"];
2812        }
2813      if ([aDecoder containsValueForKey: @"NSIntercellSpacing"])
2814        {
2815          // Don't use method here as this would change the frame
2816          _intercell = [aDecoder decodeSizeForKey: @"NSIntercellSpacing"];
2817        }
2818      if ([aDecoder containsValueForKey: @"NSMatrixFlags"])
2819        {
2820          int mFlags = [aDecoder decodeIntForKey: @"NSMatrixFlags"];
2821          GSMatrixFlags matrixFlags;
2822
2823          memcpy((void *)&matrixFlags,(void *)&mFlags,sizeof(struct _GSMatrixFlags));
2824
2825          if (matrixFlags.isRadio)
2826            {
2827              [self setMode: NSRadioModeMatrix];
2828            }
2829          else if (matrixFlags.isList)
2830            {
2831              [self setMode: NSListModeMatrix];
2832            }
2833          else if (matrixFlags.isHighlight)
2834            {
2835              [self setMode: NSHighlightModeMatrix];
2836            }
2837
2838          [self setAllowsEmptySelection: matrixFlags.allowsEmptySelection];
2839          [self setSelectionByRect: matrixFlags.selectionByRect];
2840          [self setDrawsCellBackground: matrixFlags.drawCellBackground];
2841          [self setDrawsBackground: matrixFlags.drawBackground];
2842          _autosizesCells = matrixFlags.autosizesCells;
2843          _tabKeyTraversesCells = matrixFlags.tabKeyTraversesCells;
2844        }
2845      if ([aDecoder containsValueForKey: @"NSNumCols"])
2846        {
2847          columns = [aDecoder decodeIntForKey: @"NSNumCols"];
2848        }
2849      if ([aDecoder containsValueForKey: @"NSNumRows"])
2850        {
2851          rows = [aDecoder decodeIntForKey: @"NSNumRows"];
2852        }
2853
2854      array = [aDecoder decodeObjectForKey: @"NSCells"];
2855      [self renewRows: rows columns: columns];
2856      count = [array count];
2857      if (count != rows * columns)
2858        {
2859          NSLog (@"Trying to decode an invalid NSMatrix: cell number does not fit matrix dimension");
2860          // Quick fix to do what we can
2861          if (count > rows * columns)
2862            {
2863              count = rows * columns;
2864            }
2865        }
2866
2867      _selectedRow = _selectedColumn = -1;
2868
2869      for (i = 0; i < count; i++)
2870        {
2871          NSInteger row, column;
2872
2873          cell = [array objectAtIndex: i];
2874          row = i / columns;
2875          column = i % columns;
2876
2877          [self putCell: cell atRow: row column: column];
2878          if ([cell state])
2879            {
2880              [self selectCellAtRow: row column: column];
2881            }
2882        }
2883
2884      // mis-use these variables for selection
2885      rows = -1;
2886      columns = -1;
2887      if ([aDecoder containsValueForKey: @"NSSelectedCol"])
2888        {
2889          columns = [aDecoder decodeIntForKey: @"NSSelectedCol"];
2890        }
2891      if ([aDecoder containsValueForKey: @"NSSelectedRow"])
2892        {
2893          rows = [aDecoder decodeIntForKey: @"NSSelectedRow"];
2894        }
2895      if ((rows != -1) && (columns != -1))
2896        [self selectCellAtRow: rows column: columns];
2897    }
2898  else
2899    {
2900      _myZone = [self zone];
2901      [aDecoder decodeValueOfObjCType: @encode (int) at: &_mode];
2902      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_allowsEmptySelection];
2903      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_selectionByRect];
2904      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_autosizesCells];
2905      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_autoscroll];
2906      _cellSize = [aDecoder decodeSize];
2907      _intercell = [aDecoder decodeSize];
2908      [aDecoder decodeValueOfObjCType: @encode (id) at: &_backgroundColor];
2909      [aDecoder decodeValueOfObjCType: @encode (id) at: &_cellBackgroundColor];
2910      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_drawsBackground];
2911      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_drawsCellBackground];
2912
2913      class = NSClassFromString ((NSString *)[aDecoder decodeObject]);
2914      if (class != Nil)
2915        {
2916          [self setCellClass: class];
2917        }
2918
2919      cell = [aDecoder decodeObject];
2920      if (cell != nil)
2921        {
2922          [self setPrototype: cell];
2923        }
2924
2925      if (_cellPrototype == nil)
2926        {
2927          [self setCellClass: [object_getClass(self) cellClass]];
2928        }
2929
2930      [aDecoder decodeValueOfObjCType: @encode (int) at: &rows];
2931      [aDecoder decodeValueOfObjCType: @encode (int) at: &columns];
2932
2933      /* NB: This works without changes for NSForm */
2934      array = [aDecoder decodeObject];
2935      [self renewRows: rows  columns: columns];
2936      count = [array count];
2937      if (count != rows * columns)
2938        {
2939          NSLog (@"Trying to decode an invalid NSMatrix: cell number does not fit matrix dimension");
2940          // Quick fix to do what we can
2941          if (count > rows * columns)
2942            {
2943              count = rows * columns;
2944            }
2945        }
2946
2947      _selectedRow = _selectedColumn = -1;
2948
2949      for (i = 0; i < count; i++)
2950        {
2951          NSInteger row, column;
2952
2953          cell = [array objectAtIndex: i];
2954          row = i / columns;
2955          column = i % columns;
2956
2957          [self putCell: cell atRow: row column: column];
2958          if ([cell state])
2959            {
2960              [self selectCellAtRow: row column: column];
2961            }
2962        }
2963
2964      [aDecoder decodeValueOfObjCType: @encode (id) at: &_delegate];
2965      [aDecoder decodeValueOfObjCType: @encode (id) at: &_target];
2966      [aDecoder decodeValueOfObjCType: @encode (SEL) at: &_action];
2967      [aDecoder decodeValueOfObjCType: @encode (SEL) at: &_doubleAction];
2968      [aDecoder decodeValueOfObjCType: @encode (SEL) at: &_errorAction];
2969      [aDecoder decodeValueOfObjCType: @encode (BOOL) at: &_tabKeyTraversesCells];
2970      [self setKeyCell: [aDecoder decodeObject]];
2971    }
2972
2973  return self;
2974}
2975
2976/** <p>Sets the NSMatrix's mode to aMode. See <ref type="type"
2977    id="NSMatrixMode">NSMatrixMode</ref> for more informations. By default
2978    the mode is <ref type="type" id="NSMatrixMode">NSRadioModeMatrix</ref>.
2979    </p><p>See Also: -setMode:</p>
2980 */
2981- (void) setMode: (NSMatrixMode)aMode
2982{
2983  _mode = aMode;
2984}
2985
2986/** <p>Returns the NSMatrix's mode. See <ref type="type" id="NSMatrixMode">
2987    NSMatrixMode</ref> for more informations. By default the mode
2988    is <ref type="type" id="NSMatrixMode">NSRadioModeMatrix</ref>.</p>
2989    <p>See Also: -setMode:</p>
2990 */
2991- (NSMatrixMode) mode
2992{
2993  return _mode;
2994}
2995
2996/** <p>Sets the cell class used by the NSMatrix when it creates new cells
2997    to classId. The default cell class is a NSActionCell class</p>
2998    <p>See Also: -cellClass -setPrototype: -prototype</p>
2999 */
3000- (void) setCellClass: (Class)classId
3001{
3002  _cellClass = classId;
3003  if (_cellClass == nil)
3004    {
3005      _cellClass = defaultCellClass;
3006    }
3007  _cellNew = [_cellClass methodForSelector: allocSel];
3008  _cellInit = [_cellClass instanceMethodForSelector: initSel];
3009  DESTROY(_cellPrototype);
3010}
3011
3012/** <p>Returns the cell class used by the NSMatrix when it creates new cells.
3013    The default cell class  is a NSActionCell class</p>
3014    <p>See Also: -setCellClass: -setPrototype: -prototype</p>
3015 */
3016- (Class) cellClass
3017{
3018  return _cellClass;
3019}
3020
3021/**<p>Sets the  prototype cell used by the NSMatrix when it creates new cells
3022   to aCell. The default cell is  NSActionCell</p>
3023    <p>See Also: -cellClass -setPrototype: -prototype</p>
3024 */
3025- (void) setPrototype: (NSCell*)aCell
3026{
3027  ASSIGN(_cellPrototype, aCell);
3028  if (_cellPrototype == nil)
3029    {
3030      [self setCellClass: defaultCellClass];
3031    }
3032  else
3033    {
3034      _cellNew = [_cellPrototype methodForSelector: copySel];
3035      _cellInit = 0;
3036      _cellClass = [aCell class];
3037    }
3038}
3039
3040/**<p>Returns the  prototype cell used by the NSMatrix when it creates new
3041   cells. The default cell is  NSActionCell</p>
3042    <p>See Also: -cellClass -setPrototype: -prototype</p>
3043 */
3044- (id) prototype
3045{
3046  return _cellPrototype;
3047}
3048
3049/**<p>Returns the size of the NSMatrix's cells</p>
3050   <p>See Also: -setCellSize:</p>
3051 */
3052- (NSSize) cellSize
3053{
3054  return _cellSize;
3055}
3056
3057/** <p>Returns the space size between cells.</p>
3058    <p>See Also: -setIntercellSpacing:</p>
3059 */
3060- (NSSize) intercellSpacing
3061{
3062  return _intercell;
3063}
3064
3065/** <p>Sets the background color to <var>aColor</var> and marks self for
3066    display. The background color is used to display the NSMatrix color
3067    ( the space between the cells), not the cells ( uses
3068    -setCellBackgroundColor: for that)</p><p>See Also: -backgroundColor
3069    -setCellBackgroundColor: -cellBackgroundColor -drawsBackground
3070    -setDrawsBackground:</p>
3071 */
3072- (void) setBackgroundColor: (NSColor*)aColor
3073{
3074  ASSIGN(_backgroundColor, aColor);
3075  [self setNeedsDisplay: YES];
3076}
3077
3078/** <p>Returns the background color The background color is used to display
3079    the NSMatrix color ( the space between the cells), not the cells ( uses
3080    -setCellBackgroundColor: for that)</p> <p>See Also: -setBackgroundColor:
3081    -setCellBackgroundColor: -cellBackgroundColor -drawsBackground
3082    -setDrawsBackground:</p>
3083 */
3084- (NSColor*) backgroundColor
3085{
3086  return _backgroundColor;
3087}
3088
3089/** <p>Sets the background color of the NSMatrix's cells to <var>aColor</var>
3090    and marks self for display. </p><p>See Also: -cellBackgroundColor
3091    -backgroundColor -setBackgroundColor: -setDrawsCellBackground:
3092    -drawsCellBackground</p>
3093 */
3094- (void) setCellBackgroundColor: (NSColor*)aColor
3095{
3096  ASSIGN(_cellBackgroundColor, aColor);
3097  [self setNeedsDisplay: YES];
3098}
3099
3100/** <p>Returns the background color of the NSMatrix's cells.</p><p>See Also:
3101    -setCellBackgroundColor:  -backgroundColor -setBackgroundColor: </p>
3102 */
3103- (NSColor*) cellBackgroundColor
3104{
3105  return _cellBackgroundColor;
3106}
3107
3108/**<p>Sets the delegate to <var>anObject</var>. The delegate is used
3109   when editing a cell</p><p>See Also: -delegate -textDidEndEditing:
3110   -textDidBeginEditing: -textDidChange:</p>
3111 */
3112- (void) setDelegate: (id)anObject
3113{
3114  if (_delegate)
3115    [nc removeObserver: _delegate name: nil object: self];
3116  _delegate = anObject;
3117
3118#define SET_DELEGATE_NOTIFICATION(notif_name) \
3119  if ([_delegate respondsToSelector: @selector(controlText##notif_name:)]) \
3120    [nc addObserver: _delegate \
3121      selector: @selector(controlText##notif_name:) \
3122      name: NSControlText##notif_name##Notification object: self]
3123
3124  if (_delegate)
3125    {
3126      SET_DELEGATE_NOTIFICATION(DidBeginEditing);
3127      SET_DELEGATE_NOTIFICATION(DidEndEditing);
3128      SET_DELEGATE_NOTIFICATION(DidChange);
3129    }
3130}
3131
3132
3133/**<p>Returns the NSMatrix's delegate. delegate is used when editing a cell</p>
3134   <p>See Also: -setDelegate: -textDidEndEditing:  -textDidBeginEditing:
3135   -textDidChange:</p>
3136 */
3137- (id) delegate
3138{
3139  return _delegate;
3140}
3141
3142- (void) setTarget: anObject
3143{
3144  _target = anObject;
3145}
3146
3147- (id) target
3148{
3149  return _target;
3150}
3151
3152/**
3153 * Sets the message to send when a single click occurs.<br />
3154 */
3155- (void) setAction: (SEL)aSelector
3156{
3157  _action = aSelector;
3158}
3159
3160- (SEL) action
3161{
3162  return _action;
3163}
3164
3165/** <p>Sets the message to send when a double click occurs.
3166    NB: In GNUstep the following method does *not* set
3167    ignoresMultiClick to NO as in the MacOS-X spec.
3168    It simply sets the doubleAction, as in OpenStep spec.</p>
3169    <p>-doubleAction</p>
3170 */
3171- (void) setDoubleAction: (SEL)aSelector
3172{
3173  _doubleAction = aSelector;
3174}
3175
3176/** <p>Returns the action method, used when the user double clicks</p>
3177    <p>See Also: -setDoubleAction:</p>
3178 */
3179- (SEL) doubleAction
3180{
3181  return _doubleAction;
3182}
3183
3184/**<p>Sets the error action method to <var>aSelector</var>. This error method
3185   is used when in -textShouldEndEditing: if the selected cell doe not
3186   have a valid text object</p>
3187   <p>See Also: -errorAction</p>
3188 */
3189- (void) setErrorAction: (SEL)aSelector
3190{
3191  _errorAction = aSelector;
3192}
3193
3194/**<p>Returns the error action method to <var>aSelector</var>This error method
3195   is used when in -textShouldEndEditing: if the selected cell doe not
3196   have a valid text object</p>
3197   <p>See Also: -setErrorAction:</p>
3198 */
3199- (SEL) errorAction
3200{
3201  return _errorAction;
3202}
3203
3204/**<p> Enables or disables all cells of the receiver. </p>
3205 */
3206- (void) setEnabled: (BOOL)flag
3207{
3208  NSInteger i, j;
3209
3210  for (i = 0; i < _numRows; i++)
3211    {
3212      for (j = 0; j < _numCols; j++)
3213	{
3214	  [_cells[i][j] setEnabled: flag];
3215	}
3216    }
3217}
3218
3219/**<p> Sets a flag to indicate whether the matrix should permit empty
3220   selections or should force one or mor cells to be selected at all times.
3221   </p><p>See Also: -allowsEmptySelection</p>
3222 */
3223- (void) setAllowsEmptySelection: (BOOL)flag
3224{
3225  _allowsEmptySelection = flag;
3226}
3227
3228/**<p>Returns whether the matrix should permit empty selections or should
3229   force one or mor cells to be selected at all times.</p>
3230   <p>See Also: -setAllowsEmptySelection:</p>
3231 */
3232- (BOOL) allowsEmptySelection
3233{
3234  return _allowsEmptySelection;
3235}
3236
3237- (void) setSelectionByRect: (BOOL)flag
3238{
3239  _selectionByRect = flag;
3240}
3241
3242/**
3243 */
3244- (BOOL) isSelectionByRect
3245{
3246  return _selectionByRect;
3247}
3248
3249/** <p>Sets whether the NSMatrix draws its background and marks self for
3250    display.</p>
3251    <p>See Also: -drawsBackground -setDrawsCellBackground:</p>
3252 */
3253- (void) setDrawsBackground: (BOOL)flag
3254{
3255  _drawsBackground = flag;
3256  [self setNeedsDisplay: YES];
3257}
3258
3259/** <p>Returns whether the NSMatrix draws its background</p>
3260    <p>See Also: -setDrawsBackground: -drawsCellBackground</p>
3261 */
3262- (BOOL) drawsBackground
3263{
3264  return _drawsBackground;
3265}
3266
3267/**<p>Sets whether the NSMatrix draws cells backgrounds and marks self for
3268   display</p><p>See Also: -drawsCellBackground -setDrawsBackground:</p>
3269 */
3270- (void) setDrawsCellBackground: (BOOL)flag
3271{
3272  _drawsCellBackground = flag;
3273  [self setNeedsDisplay: YES];
3274}
3275
3276/**<p>Returns whether the NSMatrix draws cells backgrounds</p>
3277   <p>See Also: -setDrawsCellBackground: -drawsBackground</p>
3278 */
3279- (BOOL) drawsCellBackground
3280{
3281  return _drawsCellBackground;
3282}
3283
3284/** <p>Sets whether the NSMatrix resizes its cells automatically</p>
3285    <p>See Also: -autosizesCells</p>
3286 */
3287- (void) setAutosizesCells: (BOOL)flag
3288{
3289  _autosizesCells = flag;
3290}
3291
3292/** <p>Returns whether the NSMatrix resizes its cells automatically</p>
3293    <p>See Also: -autosizesCells</p>
3294 */
3295- (BOOL) autosizesCells
3296{
3297  return _autosizesCells;
3298}
3299
3300- (BOOL) isAutoscroll
3301{
3302  return _autoscroll;
3303}
3304
3305/**<p>Returns the number of rows of the NSMatrix</p>
3306   <p>See Also: -numberOfColumns</p>
3307 */
3308- (NSInteger) numberOfRows
3309{
3310  return _numRows;
3311}
3312
3313/**<p>Returns the number of columns of the NSMatrix</p>
3314   <p>See Also: -numberOfRows</p>
3315 */
3316- (NSInteger) numberOfColumns
3317{
3318  return _numCols;
3319}
3320
3321- (id) selectedCell
3322{
3323  return _selectedCell;
3324}
3325
3326/**<p>Returns the column number of the selected cell or -1
3327   if no cell is selected</p><p>See Also: -selectedRow -selectedCell</p>
3328 */
3329- (NSInteger) selectedColumn
3330{
3331  return _selectedColumn;
3332}
3333
3334
3335/**<p>Returns the row number of the selected cell or -1
3336   if no cell is selected</p><p>See Also: -selectedColumn -selectedCell</p>
3337 */
3338- (NSInteger) selectedRow
3339{
3340  return _selectedRow;
3341}
3342
3343- (NSInteger) mouseDownFlags
3344{
3345  return mouseDownFlags;
3346}
3347
3348- (BOOL) isFlipped
3349{
3350  return YES;
3351}
3352
3353- (void) _rebuildLayoutAfterResizing
3354{
3355  if (_autosizesCells)
3356    {
3357      /* Keep the intercell as it is, and adjust the cell size to fit.  */
3358      if (_numRows > 1)
3359	{
3360	  _cellSize.height = _bounds.size.height - ((_numRows - 1) * _intercell.height);
3361	  _cellSize.height = _cellSize.height / _numRows;
3362	  if (_cellSize.height < 0)
3363	    {
3364	      _cellSize.height = 0;
3365	    }
3366	}
3367      else
3368	{
3369	  _cellSize.height = _bounds.size.height;
3370	}
3371
3372      if (_numCols > 1)
3373	{
3374	  _cellSize.width = _bounds.size.width - ((_numCols - 1) * _intercell.width);
3375	  _cellSize.width = _cellSize.width / _numCols;
3376	  if (_cellSize.width < 0)
3377	    {
3378	      _cellSize.width = 0;
3379	    }
3380	}
3381      else
3382	{
3383	  _cellSize.width = _bounds.size.width;
3384	}
3385    }
3386}
3387
3388- (void) setFrame: (NSRect)aFrame
3389{
3390  [super setFrame: aFrame];
3391  [self _rebuildLayoutAfterResizing];
3392}
3393
3394- (void) setFrameSize: (NSSize)aSize
3395{
3396  [super setFrameSize: aSize];
3397  [self _rebuildLayoutAfterResizing];
3398}
3399
3400- (void) _move: (unichar)pos
3401{
3402  BOOL selectCell = NO;
3403  NSInteger h, i, lastDottedRow, lastDottedColumn;
3404
3405  if (_mode == NSRadioModeMatrix || _mode == NSListModeMatrix)
3406    selectCell = YES;
3407
3408  if (_dottedRow == -1 || _dottedColumn == -1)
3409    {
3410      if (pos == NSUpArrowFunctionKey || pos == NSDownArrowFunctionKey)
3411	{
3412	  for (h = 0; h < _numCols; h++)
3413	    {
3414	      for (i = 0; i < _numRows; i++)
3415		{
3416		  if ([_cells[i][h] acceptsFirstResponder])
3417		    {
3418		      _dottedRow = i;
3419		      _dottedColumn = h;
3420		      break;
3421		    }
3422		}
3423
3424	      if (i == _dottedRow)
3425		break;
3426	    }
3427	}
3428      else
3429	{
3430	  for (i = 0; i < _numRows; i++)
3431	    {
3432	      for (h = 0; h < _numCols; h++)
3433		{
3434		  if ([_cells[i][h] acceptsFirstResponder])
3435		    {
3436		      _dottedRow = i;
3437		      _dottedColumn = h;
3438		      break;
3439		    }
3440		}
3441
3442	      if (h == _dottedColumn)
3443		break;
3444	    }
3445	}
3446
3447      if (_dottedRow == -1 || _dottedColumn == -1)
3448	return;
3449
3450      if (selectCell)
3451	{
3452	  if (_selectedCell)
3453	    {
3454	      [self deselectAllCells];
3455	    }
3456
3457	  [self selectCellAtRow: _dottedRow column: _dottedColumn];
3458	}
3459      else
3460	[self setNeedsDisplayInRect: [self cellFrameAtRow: _dottedRow
3461					   column: _dottedColumn]];
3462    }
3463  else
3464    {
3465      lastDottedRow = _dottedRow;
3466      lastDottedColumn = _dottedColumn;
3467
3468      if (pos == NSUpArrowFunctionKey)
3469	{
3470	  if (_dottedRow <= 0)
3471	    return;
3472
3473	  for (i = _dottedRow-1; i >= 0; i--)
3474	    {
3475	      if ([_cells[i][_dottedColumn] acceptsFirstResponder])
3476		{
3477		  _dottedRow = i;
3478		  break;
3479		}
3480	    }
3481	}
3482      else if (pos == NSDownArrowFunctionKey)
3483	{
3484	  if (_dottedRow >= _numRows-1)
3485	    return;
3486
3487	  for (i = _dottedRow+1; i < _numRows; i++)
3488	    {
3489	      if ([_cells[i][_dottedColumn] acceptsFirstResponder])
3490		{
3491		  _dottedRow = i;
3492		  break;
3493		}
3494	    }
3495	}
3496      else if (pos == NSLeftArrowFunctionKey)
3497	{
3498	  if (_dottedColumn <= 0)
3499	    return;
3500
3501	  for (i = _dottedColumn-1; i >= 0; i--)
3502	    {
3503	      if ([_cells[_dottedRow][i] acceptsFirstResponder])
3504		{
3505		  _dottedColumn = i;
3506		  break;
3507		}
3508	    }
3509	}
3510      else
3511	{
3512	  if (_dottedColumn >= _numCols-1)
3513	    return;
3514
3515	  for (i = _dottedColumn+1; i < _numCols; i++)
3516	    {
3517	      if ([_cells[_dottedRow][i] acceptsFirstResponder])
3518		{
3519		  _dottedColumn = i;
3520		  break;
3521		}
3522	    }
3523	}
3524
3525      if ((pos == NSUpArrowFunctionKey || pos == NSDownArrowFunctionKey)
3526	  && _dottedRow != i)
3527	return;
3528
3529      if ((pos == NSLeftArrowFunctionKey || pos == NSRightArrowFunctionKey)
3530	  && _dottedColumn != i)
3531	return;
3532
3533      if (selectCell)
3534	{
3535	  if (_mode == NSRadioModeMatrix)
3536	    {
3537	      /* FIXME */
3538	      /*
3539	      NSCell *aCell = _cells[lastDottedRow][lastDottedColumn];
3540	      BOOL    isHighlighted = [aCell isHighlighted];
3541
3542	      if ([aCell state] || isHighlighted)
3543		{
3544		  [aCell setState: NSOffState];
3545		  _selectedCells[lastDottedRow][lastDottedColumn] = NO;
3546		  _selectedRow = _selectedColumn = -1;
3547		  _selectedCell = nil;
3548
3549		  if (isHighlighted)
3550		    [self highlightCell: NO
3551			  atRow: lastDottedRow
3552			  column: lastDottedColumn];
3553		  else
3554		    [self drawCell: aCell];
3555		}
3556	      */
3557	    }
3558	  else
3559	    [self deselectAllCells];
3560
3561	  [self selectCellAtRow: _dottedRow column: _dottedColumn];
3562	}
3563      else
3564	{
3565	  [self setNeedsDisplayInRect: [self cellFrameAtRow: lastDottedRow
3566					     column: lastDottedColumn]];
3567	  [self setNeedsDisplayInRect: [self cellFrameAtRow: _dottedRow
3568					     column: _dottedColumn]];
3569	}
3570    }
3571
3572  if (selectCell)
3573    {
3574      [self displayIfNeeded];
3575      [self performClick: self];
3576    }
3577}
3578
3579- (void) moveUp: (id)sender
3580{
3581  [self _move: NSUpArrowFunctionKey];
3582}
3583
3584- (void) moveDown: (id)sender
3585{
3586  [self _move: NSDownArrowFunctionKey];
3587}
3588
3589- (void) moveLeft: (id)sender
3590{
3591  [self _move: NSLeftArrowFunctionKey];
3592}
3593
3594- (void) moveRight: (id)sender
3595{
3596  [self _move: NSRightArrowFunctionKey];
3597}
3598
3599- (void) _shiftModifier: (unichar)character
3600{
3601  int i, lastDottedRow, lastDottedColumn;
3602
3603  lastDottedRow = _dottedRow;
3604  lastDottedColumn = _dottedColumn;
3605
3606  if (character == NSUpArrowFunctionKey)
3607    {
3608      if (_dottedRow <= 0)
3609	return;
3610
3611      for (i = _dottedRow-1; i >= 0; i--)
3612	{
3613	  if ([_cells[i][_dottedColumn] acceptsFirstResponder])
3614	    {
3615	      _dottedRow = i;
3616	      break;
3617	    }
3618	}
3619
3620      if (_dottedRow != i)
3621	return;
3622    }
3623  else if (character == NSDownArrowFunctionKey)
3624    {
3625      if (_dottedRow < 0 || _dottedRow >= _numRows-1)
3626	return;
3627
3628      for (i = _dottedRow+1; i < _numRows; i++)
3629	{
3630	  if ([_cells[i][_dottedColumn] acceptsFirstResponder])
3631	    {
3632	      _dottedRow = i;
3633	      break;
3634	    }
3635	}
3636    }
3637  else if (character == NSLeftArrowFunctionKey)
3638    {
3639      if (_dottedColumn <= 0)
3640	return;
3641
3642      for (i = _dottedColumn-1; i >= 0; i--)
3643	{
3644	  if ([_cells[_dottedRow][i] acceptsFirstResponder])
3645	    {
3646	      _dottedColumn = i;
3647	      break;
3648	    }
3649	}
3650    }
3651  else
3652    {
3653      if (_dottedColumn < 0 || _dottedColumn >= _numCols-1)
3654	return;
3655
3656      for (i = _dottedColumn+1; i < _numCols; i++)
3657	{
3658	  if ([_cells[_dottedRow][i] acceptsFirstResponder])
3659	    {
3660	      _dottedColumn = i;
3661	      break;
3662	    }
3663	}
3664    }
3665
3666  [self lockFocus];
3667  [self drawCell: _cells[lastDottedRow][lastDottedColumn]];
3668  [self drawCell: _cells[_dottedRow][_dottedColumn]];
3669  [self unlockFocus];
3670  [_window flushWindow];
3671
3672  [self performClick: self];
3673}
3674
3675- (void) _altModifier: (unichar)character
3676{
3677  switch (character)
3678    {
3679    case NSUpArrowFunctionKey:
3680      if (_dottedRow <= 0)
3681	return;
3682
3683      _dottedRow--;
3684      break;
3685
3686    case NSDownArrowFunctionKey:
3687      if (_dottedRow < 0 || _dottedRow >= _numRows-1)
3688	return;
3689
3690      _dottedRow++;
3691      break;
3692
3693    case NSLeftArrowFunctionKey:
3694      if (_dottedColumn <= 0)
3695	return;
3696
3697      _dottedColumn--;
3698      break;
3699
3700    case NSRightArrowFunctionKey:
3701      if (_dottedColumn < 0 || _dottedColumn >= _numCols-1)
3702	return;
3703
3704      _dottedColumn++;
3705      break;
3706    }
3707
3708  [self setSelectionFrom: INDEX_FROM_COORDS(_selectedColumn, _selectedRow)
3709	to: INDEX_FROM_COORDS(_dottedColumn, _dottedRow)
3710	anchor: INDEX_FROM_COORDS(_selectedColumn, _selectedRow)
3711	highlight: YES];
3712
3713  [self displayIfNeeded];
3714  [self performClick: self];
3715}
3716
3717- (void) keyDown: (NSEvent *)theEvent
3718{
3719  NSString *characters = [theEvent characters];
3720  NSUInteger modifiers = [theEvent modifierFlags];
3721  unichar  character = 0;
3722
3723  if ([characters length] > 0)
3724    {
3725      character = [characters characterAtIndex: 0];
3726    }
3727
3728  switch (character)
3729    {
3730    case NSCarriageReturnCharacter:
3731    case NSNewlineCharacter:
3732    case NSEnterCharacter:
3733      [self selectText: self];
3734      break;
3735
3736    case ' ':
3737      if (_dottedRow != -1 && _dottedColumn != -1)
3738	{
3739	  if (modifiers & NSAlternateKeyMask)
3740	    [self _altModifier: character];
3741	  else
3742	    {
3743	      switch (_mode)
3744		{
3745		case NSTrackModeMatrix:
3746		case NSHighlightModeMatrix:
3747		case NSRadioModeMatrix:
3748		  [self selectCellAtRow: _dottedRow column: _dottedColumn];
3749		  break;
3750
3751		case NSListModeMatrix:
3752		  if (!(modifiers & NSShiftKeyMask))
3753		    [self deselectAllCells];
3754		  break;
3755		}
3756
3757	      [self displayIfNeeded];
3758	      [self performClick: self];
3759	    }
3760	  return;
3761	}
3762      break;
3763
3764    case NSLeftArrowFunctionKey:
3765    case NSRightArrowFunctionKey:
3766      if (_numCols <= 1)
3767	break;
3768
3769    case NSUpArrowFunctionKey:
3770    case NSDownArrowFunctionKey:
3771      if (modifiers & NSShiftKeyMask)
3772	[self _shiftModifier: character];
3773      else if (modifiers & NSAlternateKeyMask)
3774	[self _altModifier: character];
3775      else
3776	{
3777	  if (character == NSUpArrowFunctionKey)
3778	    [self moveUp: self];
3779	  else if (character == NSDownArrowFunctionKey)
3780	    [self moveDown: self];
3781	  else if (character == NSLeftArrowFunctionKey)
3782	    [self moveLeft: self];
3783	  else
3784	    [self moveRight: self];
3785	}
3786      return;
3787
3788    case NSBackTabCharacter:
3789      if (_tabKeyTraversesCells)
3790	{
3791          if ([self _selectNextSelectableCellAfterRow: _selectedRow
3792                                               column: _selectedColumn])
3793            return;
3794        }
3795      break;
3796
3797    case NSTabCharacter:
3798      if (_tabKeyTraversesCells)
3799	{
3800	  if ([theEvent modifierFlags] & NSShiftKeyMask)
3801	    {
3802	      if ([self _selectNextSelectableCellAfterRow: _selectedRow
3803		       column: _selectedColumn])
3804		return;
3805	    }
3806	  else
3807	    {
3808	      if ([self _selectPreviousSelectableCellBeforeRow: _selectedRow
3809		       column: _selectedColumn])
3810		return;
3811	    }
3812	}
3813      break;
3814
3815    default:
3816      break;
3817    }
3818
3819  [super keyDown: theEvent];
3820}
3821
3822- (void) performClick: (id)sender
3823{
3824  [super sendAction: _action to: _target];
3825}
3826
3827- (BOOL) acceptsFirstResponder
3828{
3829  // We gratefully accept keyboard events.
3830  return YES;
3831}
3832
3833- (void) _setNeedsDisplayDottedCell
3834{
3835  if (_dottedRow != -1 && _dottedColumn != -1)
3836    {
3837      [self setNeedsDisplayInRect: [self cellFrameAtRow: _dottedRow
3838					 column: _dottedColumn]];
3839    }
3840}
3841
3842- (BOOL) becomeFirstResponder
3843{
3844  [self _setNeedsDisplayDottedCell];
3845
3846  return YES;
3847}
3848
3849- (BOOL) resignFirstResponder
3850{
3851  [self _setNeedsDisplayDottedCell];
3852
3853  return YES;
3854}
3855
3856- (void) becomeKeyWindow
3857{
3858  [self _setNeedsDisplayDottedCell];
3859}
3860
3861- (void) resignKeyWindow
3862{
3863  [self _setNeedsDisplayDottedCell];
3864}
3865
3866- (BOOL) abortEditing
3867{
3868  if (_textObject)
3869    {
3870      [_selectedCell endEditing: _textObject];
3871      _textObject = nil;
3872      return YES;
3873    }
3874  else
3875    return NO;
3876}
3877
3878- (NSText *) currentEditor
3879{
3880  if (_textObject && ([_window firstResponder] == _textObject))
3881    return _textObject;
3882  else
3883    return nil;
3884}
3885
3886- (void) validateEditing
3887{
3888  if (_textObject)
3889    {
3890      NSFormatter *formatter;
3891      NSString *string;
3892
3893      formatter = [_selectedCell formatter];
3894      string = AUTORELEASE ([[_textObject text] copy]);
3895
3896      if (formatter == nil)
3897	{
3898	  [_selectedCell setStringValue: string];
3899	}
3900      else
3901	{
3902	  id newObjectValue;
3903	  NSString *error;
3904
3905	  if ([formatter getObjectValue: &newObjectValue
3906			 forString: string
3907			 errorDescription: &error] == YES)
3908	    {
3909	      [_selectedCell setObjectValue: newObjectValue];
3910	    }
3911	  else
3912	    {
3913	      if ([_delegate control: self
3914			     didFailToFormatString: string
3915			     errorDescription: error] == YES)
3916		{
3917		  [_selectedCell setStringValue: string];
3918		}
3919
3920	    }
3921	}
3922    }
3923}
3924
3925- (void) setValue: (id)anObject forKey: (NSString*)aKey
3926{
3927  if ([aKey isEqual: NSSelectedTagBinding])
3928    {
3929      [self selectCellWithTag: [anObject integerValue]];
3930    }
3931  else
3932    {
3933      [super setValue: anObject forKey: aKey];
3934    }
3935}
3936
3937- (id) valueForKey: (NSString*)aKey
3938{
3939  if ([aKey isEqual: NSSelectedTagBinding])
3940    {
3941      return [NSNumber numberWithInteger: [self selectedTag]];
3942    }
3943  else
3944    {
3945      return [super valueForKey: aKey];
3946    }
3947}
3948
3949@end
3950
3951
3952@implementation NSMatrix (PrivateMethods)
3953
3954/*
3955 * Renew rows and columns, but when expanding the matrix, refrain from
3956 * creating rowSpace  items in the last row and colSpace items in the
3957 * last column.  When inserting the contents of an array into the matrix,
3958 * this avoids creation of new cless which would immediately be replaced
3959 * by those from the array.
3960 * NB. new spaces in the matrix are pre-initialised with nil values so
3961 * that replacing them doesn't cause attempts to release random memory.
3962 */
3963- (void) _renewRows: (NSInteger)row
3964	    columns: (NSInteger)col
3965	   rowSpace: (NSInteger)rowSpace
3966	   colSpace: (NSInteger)colSpace
3967{
3968  NSInteger i, j;
3969  NSInteger oldMaxC;
3970  NSInteger oldMaxR;
3971  SEL mkSel = @selector(makeCellAtRow:column:);
3972  IMP mkImp = [self methodForSelector: mkSel];
3973
3974//NSLog(@"%x - mr: %d mc:%d nr:%d nc:%d r:%d c:%d", (unsigned)self, _maxRows, _maxCols, _numRows, _numCols, row, col);
3975  if (row < 0)
3976    {
3977#if	NSMATRIX_STRICT_CHECKING == 0
3978      NSLog(@"renew negative row (%d) in matrix", (int)row);
3979#else
3980      [NSException raise: NSRangeException
3981		  format: @"renew negative row (%d) in matrix", (int)row];
3982#endif
3983      row = 0;
3984    }
3985  if (col < 0)
3986    {
3987#if	NSMATRIX_STRICT_CHECKING == 0
3988      NSLog(@"renew negative column (%d) in matrix", (int)col);
3989#else
3990      [NSException raise: NSRangeException
3991		  format: @"renew negative column (%d) in matrix", (int)col];
3992#endif
3993      col = 0;
3994    }
3995
3996  /*
3997   * Update matrix dimension before we actually change it - so that
3998   * makeCellAtRow:column: doesn't think we are trying to make a cell
3999   * outside the array bounds.
4000   * Our implementation doesn't care, but a subclass might use
4001   * putCell:atRow:column: to implement it, and that checks bounds.
4002   */
4003  oldMaxC = _maxCols;
4004  _numCols = col;
4005  if (col > _maxCols)
4006    _maxCols = col;
4007  oldMaxR = _maxRows;
4008  _numRows = row;
4009  if (row > _maxRows)
4010    _maxRows = row;
4011
4012  if (col > oldMaxC)
4013    {
4014      NSInteger end = col - 1;
4015
4016      for (i = 0; i < oldMaxR; i++)
4017	{
4018	  _cells[i] = NSZoneRealloc(_myZone, _cells[i], col * sizeof(id));
4019	  _selectedCells[i] = NSZoneRealloc(_myZone, _selectedCells[i],
4020                                            col * sizeof(BOOL));
4021
4022	  for (j = oldMaxC; j < col; j++)
4023	    {
4024	      _cells[i][j] = nil;
4025	      _selectedCells[i][j] = NO;
4026	      if (j == end && colSpace > 0)
4027		{
4028		  colSpace--;
4029		}
4030	      else
4031		{
4032		  (*mkImp)(self, mkSel, i, j);
4033		}
4034	    }
4035	}
4036    }
4037
4038  if (row > oldMaxR)
4039    {
4040      NSInteger end = row - 1;
4041
4042      _cells = NSZoneRealloc(_myZone, _cells, row * sizeof(id*));
4043      _selectedCells
4044	= NSZoneRealloc(_myZone, _selectedCells, row * sizeof(BOOL*));
4045
4046      /* Allocate the new rows and fill them */
4047      for (i = oldMaxR; i < row; i++)
4048	{
4049	  _cells[i] = NSZoneMalloc(_myZone, _maxCols * sizeof(id));
4050	  _selectedCells[i] = NSZoneMalloc(_myZone, _maxCols * sizeof(BOOL));
4051
4052	  if (i == end)
4053	    {
4054	      for (j = 0; j < _maxCols; j++)
4055		{
4056		  _cells[i][j] = nil;
4057		  _selectedCells[i][j] = NO;
4058		  if (rowSpace > 0)
4059		    {
4060		      rowSpace--;
4061		    }
4062		  else
4063		    {
4064		      (*mkImp)(self, mkSel, i, j);
4065		    }
4066		}
4067	    }
4068	  else
4069	    {
4070	      for (j = 0; j < _maxCols; j++)
4071		{
4072		  _cells[i][j] = nil;
4073		  _selectedCells[i][j] = NO;
4074		  (*mkImp)(self, mkSel, i, j);
4075		}
4076	    }
4077	}
4078    }
4079
4080  [self deselectAllCells];
4081//NSLog(@"%x - end mr: %d mc:%d nr:%d nc:%d r:%d c:%d", (unsigned)self, _maxRows, _maxCols, _numRows, _numCols, row, col);
4082}
4083
4084- (void) _setState: (NSInteger)state
4085	 highlight: (BOOL)highlight
4086	startIndex: (NSInteger)start
4087	  endIndex: (NSInteger)end
4088{
4089  NSInteger i;
4090  MPoint startPoint = POINT_FROM_INDEX(start);
4091  MPoint endPoint = POINT_FROM_INDEX(end);
4092
4093  for (i = startPoint.y; i <= endPoint.y; i++)
4094    {
4095      NSInteger j;
4096      NSInteger colLimit;
4097
4098      if (_selectionByRect || i == startPoint.y)
4099	{
4100	  j = startPoint.x;
4101	}
4102      else
4103	{
4104	  j = 0;
4105	}
4106
4107      if (_selectionByRect || i == endPoint.y)
4108	colLimit = endPoint.x;
4109      else
4110	colLimit = _numCols - 1;
4111
4112      for (; j <= colLimit; j++)
4113	{
4114	  NSCell *aCell = _cells[i][j];
4115
4116          if ([aCell isEnabled]
4117	    && ([aCell state] != state || [aCell isHighlighted] != highlight
4118	      || (state == NSOffState && _selectedCells[i][j] != NO)
4119	      || (state != NSOffState && _selectedCells[i][j] == NO)))
4120            {
4121	      [aCell setState: state];
4122
4123	      if (state == NSOffState)
4124	        _selectedCells[i][j] = NO;
4125	      else
4126	        _selectedCells[i][j] = YES;
4127
4128	      [aCell setHighlighted: highlight];
4129	      [self setNeedsDisplayInRect: [self cellFrameAtRow: i column: j]];
4130            }
4131	}
4132    }
4133}
4134
4135// Return YES on success; NO if no selectable cell found.
4136-(BOOL) _selectNextSelectableCellAfterRow: (NSInteger)row
4137				   column: (NSInteger)column
4138{
4139  NSInteger i, j;
4140
4141  if (row > -1)
4142    {
4143      // First look for cells in the same row
4144      for (j = column + 1; j < _numCols; j++)
4145	{
4146	  if ([_cells[row][j] isEnabled] && [_cells[row][j] isSelectable])
4147	    {
4148	      _selectedCell = [self selectTextAtRow: row column: j];
4149	      _selectedRow = row;
4150	      _selectedColumn = j;
4151	      _selectedCells[row][j] = YES;
4152	      return YES;
4153	    }
4154	}
4155    }
4156  // Otherwise, make the big cycle.
4157  for (i = row + 1; i < _numRows; i++)
4158    {
4159      for (j = 0; j < _numCols; j++)
4160	{
4161	  if ([_cells[i][j] isEnabled] && [_cells[i][j] isSelectable])
4162	    {
4163	      _selectedCell = [self selectTextAtRow: i column: j];
4164	      _selectedRow = i;
4165	      _selectedColumn = j;
4166	      _selectedCells[i][j] = YES;
4167	      return YES;
4168	    }
4169	}
4170    }
4171  return NO;
4172}
4173
4174-(BOOL) _selectPreviousSelectableCellBeforeRow: (NSInteger)row
4175					column: (NSInteger)column
4176{
4177  NSInteger i,j;
4178
4179  if (row < _numRows)
4180    {
4181      // First look for cells in the same row
4182      for (j = column - 1; j > -1; j--)
4183	{
4184	  if ([_cells[row][j] isEnabled] && [_cells[row][j] isSelectable])
4185	    {
4186	      _selectedCell = [self selectTextAtRow: row column: j];
4187	      _selectedRow = row;
4188	      _selectedColumn = j;
4189	      _selectedCells[row][j] = YES;
4190	      return YES;
4191	    }
4192	}
4193    }
4194  // Otherwise, make the big cycle.
4195  for (i = row - 1; i > -1; i--)
4196    {
4197      for (j = _numCols - 1; j > -1; j--)
4198	{
4199	  if ([_cells[i][j] isEnabled] && [_cells[i][j] isSelectable])
4200	    {
4201	      _selectedCell = [self selectTextAtRow: i column: j];
4202	      _selectedRow = i;
4203	      _selectedColumn = j;
4204	      _selectedCells[i][j] = YES;
4205	      return YES;
4206	    }
4207	}
4208    }
4209  return NO;
4210}
4211
4212- (void) _setKeyRow: (NSInteger)row column: (NSInteger)column
4213{
4214  if (_dottedRow == row && _dottedColumn == column)
4215    {
4216      return;
4217    }
4218  if ([_cells[row][column] acceptsFirstResponder])
4219    {
4220      if (_dottedRow != -1 && _dottedColumn != -1)
4221        {
4222          [self setNeedsDisplayInRect: [self cellFrameAtRow: _dottedRow
4223                                             column: _dottedColumn]];
4224	}
4225      _dottedRow = row;
4226      _dottedColumn = column;
4227      [self setNeedsDisplayInRect: [self cellFrameAtRow: _dottedRow
4228                                         column: _dottedColumn]];
4229    }
4230}
4231
4232@end
4233