Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / AppKit / NSMatrix.m
blob09fa58e5cc3aa4c36d3f02abae83c12573d4bae3
1 /* Copyright (c) 2006-2007 Christopher J. W. Lloyd
3 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
9 #import <AppKit/NSMatrix.h>
10 #import <AppKit/NSCell.h>
11 #import <AppKit/NSFont.h>
12 #import <AppKit/NSApplication.h>
13 #import <AppKit/NSEvent.h>
14 #import <AppKit/NSColor.h>
15 #import <AppKit/NSGraphics.h>
16 #import <AppKit/NSWindow.h>
17 #import <Foundation/NSKeyedArchiver.h>
18 #import <AppKit/NSRaise.h>
20 @implementation NSMatrix
22 -(void)encodeWithCoder:(NSCoder *)coder {
23    NSUnimplementedMethod();
26 -initWithCoder:(NSCoder *)coder {
27    [super initWithCoder:coder];
29    if([coder allowsKeyedCoding]){
30     NSKeyedUnarchiver *keyed=(NSKeyedUnarchiver *)coder;
31     int flags=[keyed decodeIntForKey:@"NSMatrixFlags"];
32     NSString *name;
33     
34     _numberOfRows=[keyed decodeIntForKey:@"NSNumRows"];
35     _numberOfColumns=[keyed decodeIntForKey:@"NSNumCols"];
36     _cellSize=[keyed decodeSizeForKey:@"NSCellSize"];
37     _intercellSpacing=[keyed decodeSizeForKey:@"NSIntercellSpacing"];
38     _tabKeyTraversesCells=(flags&0x00200000)?YES:NO;
39     _autosizesCells=(flags&0x00800000)?YES:NO;
40     _drawsBackground=(flags&0x01000000)?YES:NO;
41     _drawsCellBackground=(flags&0x02000000)?YES:NO;
42     _selectionByRect=(flags&0x04000000)?YES:NO;
43     _isAutoscroll=(flags&0x08000000)?YES:NO;
44     _allowsEmptySelection=(flags&0x10000000)?YES:NO;
45     _mode=NSTrackModeMatrix;
46     if(flags&0x20000000)
47      _mode=NSListModeMatrix;
48     if(flags&0x40000000)
49      _mode=NSRadioModeMatrix;
50     if(flags&0x80000000)
51      _mode=NSHighlightModeMatrix;
52     _backgroundColor=[[keyed decodeObjectForKey:@"NSBackgroundColor"] retain];
53     _cellBackgroundColor=[[keyed decodeObjectForKey:@"NSCellBackgroundColor"] retain];
54     name=[keyed decodeObjectForKey:@"NSCellClass"];
55     if((_cellClass=NSClassFromString(name))==Nil){
56         if (name) {
57             NSLog(@"Unknown cell class '%@' in NSMatrix, using NSCell",name);
58         }
59      _cellClass=[NSCell class];
60     }
61     _prototype=[[keyed decodeObjectForKey:@"NSProtoCell"] retain];
62     _cells=[[NSMutableArray new] initWithArray:[keyed decodeObjectForKey:@"NSCells"]];
63     id selectedCell=[keyed decodeObjectForKey:@"NSSelectedCell"];
64     if ((_selectedIndex=[_cells indexOfObjectIdenticalTo:selectedCell]) != NSNotFound)
65       [self selectCell:selectedCell];
66     else
67       _selectedIndex=-1;
68    }
69    else {
70     [NSException raise:NSInvalidArgumentException format:@"-[%@ %s] is not implemented for coder %@",isa,sel_getName(_cmd),coder];
71    }
72    return self;
75 -initWithFrame:(NSRect)frame {
76    [super initWithFrame:frame];
77    _target=nil;
78    _action=NULL;
79    _doubleAction=NULL;
80    _font=[[NSFont userFontOfSize:0] retain];
81    _cells=[NSMutableArray new];
82    _numberOfRows=0;
83    _numberOfColumns=1;
84    _selectedIndex=-1;
85    _keyCellIndex=-1;
86    _cellSize=NSMakeSize(frame.size.width,20);
87    _intercellSpacing=NSMakeSize(0,0);
88    _prototype=nil;
89    _cellClass=[NSCell class];
90    _mode=NSListModeMatrix;
91    _tabKeyTraversesCells=YES;
93    return self;
96 -initWithFrame:(NSRect)frame mode:(int)mode prototype:(NSCell *)prototype numberOfRows:(int)rows numberOfColumns:(int)columns {
97    int i;
98    
99    [self initWithFrame:frame];
100    
101    _mode=mode;
102    _prototype=[prototype copy];
103    _numberOfRows=rows;
104    _numberOfColumns=columns;
105    for(i=0;i<rows*columns;i++)
106     [_cells addObject:[[_prototype copy] autorelease]];
107     
108    return self;
111 -initWithFrame:(NSRect)frame mode:(int)mode cellClass:(Class)cls numberOfRows:(int)rows numberOfColumns:(int)columns {
112    int i;
113    
114    [self initWithFrame:frame];
115    
116    _mode=mode;
117    _cellClass=cls;
118    _numberOfRows=rows;
119    _numberOfColumns=columns;
120    for(i=0;i<rows*columns;i++)
121     [_cells addObject:[[[cls alloc] init] autorelease]];
122     
123    return self;
126 -(void)dealloc {
127    [_font release];
128    [_backgroundColor release];
129    [_cellBackgroundColor release];
130    [_cells release];
131    [_prototype release];
132    [super dealloc];
135 -(BOOL)resignFirstResponder {
136    [self setNeedsDisplay:YES];
137    return [super resignFirstResponder];
140 -(void)setRefusesFirstResponder:(BOOL)flag {
141    _refusesFirstResponder=flag;
144 -(BOOL)refusesFirstResponder {
145    return _refusesFirstResponder;
148 -(void)resizeWithOldSuperviewSize:(NSSize)oldSize {
149    [super resizeWithOldSuperviewSize:oldSize];
151    if(_autosizesCells){
152     NSRect frame=[self frame];
154     if(_autoresizingMask&NSViewWidthSizable){
155      if(_numberOfColumns==0)
156       _cellSize.width=frame.size.width;
157      else
158       _cellSize.width=(frame.size.width-
159          (_numberOfColumns-1)*_intercellSpacing.width)/_numberOfColumns;
160     }
162     if(_autoresizingMask& NSViewHeightSizable){
163      if(_numberOfRows==0)
164       _cellSize.height= frame.size.height;
165      else
166       _cellSize.height=(frame.size.height-
167         (_numberOfRows-1)*_intercellSpacing.height)/_numberOfRows;
168     }
169    }
172 -(void)sizeToFit {
173    NSSize size;
175    size.width=_cellSize.width*_numberOfColumns+
176        _intercellSpacing.width*(_numberOfColumns-1);
177    size.height=_cellSize.height*_numberOfRows+
178        _intercellSpacing.height*(_numberOfRows-1);
180    [self setFrameSize:size];
183 -(BOOL)isFlipped {
184    return YES;
187 -(void)resetCursorRects {
188    NSRect visibleRect=[self visibleRect];
189    int    row,col;
191    for(row=0;row<_numberOfRows;row++)
192     for(col=0;col<_numberOfColumns;col++){
193      NSRect frame=[self cellFrameAtRow:row column:col];
195      if(NSIntersectsRect(frame,visibleRect)){
196       NSCell *cell=[self cellAtRow:row column:col];
198       [cell resetCursorRect:frame inView:self];
199      }
200     } 
203 -(NSFont *)font {
204    return _font;
207 -(void)setFont:(NSFont *)font {
208    font=[font retain];
209    [_font release];
210    _font=font;
213 -target {
214    return _target;
217 -(SEL)action {
218    return _action;
221 -delegate {
222    return _delegate;
225 -(SEL)doubleAction {
226    return _doubleAction;
229 -(Class)cellClass {
230    return _cellClass;
233 -prototype {
234    return _prototype;
237 -(NSArray *)cells {
238    return _cells;
241 -cellWithTag:(int)tag {
242    int i,count=[_cells count];
244    for(i=0;i<count;i++){
245     NSCell *cell=[_cells objectAtIndex:i];
246     if([cell tag]==tag)
247      return cell;
248    }
250    return nil;
253 -cellAtRow:(int)row column:(int)column {
254    unsigned index=row*_numberOfColumns+column;
256    if(index>=[_cells count])
257     return nil;
259    // bounds checking added for keyboard spatial movement, hopefully doesn't blow up anything
260    // which relies on broken behavior
261    if (row < 0 || row >= _numberOfRows)
262        return nil;
263    if (column < 0 || column >= _numberOfColumns)
264        return nil;
266    return [_cells objectAtIndex:index];
269 -(NSRect)cellFrameAtRow:(int)row column:(int)column {
270    NSRect result;
272    result.origin.x=column*_cellSize.width+column*_intercellSpacing.width;
273    result.origin.y=row*_cellSize.height+row*_intercellSpacing.height;
274    result.size=_cellSize;
276    return result;
279 -(BOOL)getRow:(int *)row column:(int *)column ofCell:(NSCell *)cell {
280    int index=[_cells indexOfObjectIdenticalTo:cell];
282    if(index!=NSNotFound){
283     *row=index/_numberOfColumns;
284     *column=index%_numberOfColumns;
285     return YES;
286    }
288    return NO;
291 -(BOOL)getRow:(int *)row column:(int *)column forPoint:(NSPoint)point {
292    NSRect cellFrame;
294    *row=point.y/(_cellSize.height+_intercellSpacing.height);
295    *column=point.x/(_cellSize.width+_intercellSpacing.width);
297    cellFrame=[self cellFrameAtRow:*row column:*column];
299    return NSMouseInRect(point,cellFrame,[self isFlipped]);
302 -(int)numberOfRows {
303    return _numberOfRows;
306 -(int)numberOfColumns {
307    return _numberOfColumns;
310 -(void)getNumberOfRows:(int *)rows columns:(int *)columns {
311    *rows=_numberOfRows;
312    *columns=_numberOfColumns;
315 -(NSString *)toolTipForCell:(NSCell *)cell {
316    NSUnimplementedMethod();
317    return nil;
320 -keyCell {
321    if(_keyCellIndex==-1)
322     return nil;
324    return [_cells objectAtIndex:_keyCellIndex];
327 -(NSMatrixMode)mode {
328    return _mode;
331 -(BOOL)allowsEmptySelection {
332    return _allowsEmptySelection;
335 -(BOOL)tabKeyTraversesCells {
336    return _tabKeyTraversesCells;
339 -(BOOL)autosizesCells {
340    return _autosizesCells;
343 -(NSSize)cellSize {
344    return _cellSize;
347 -(NSSize)intercellSpacing {
348    return _intercellSpacing;
351 -(BOOL)drawsBackground {
352    return _drawsBackground;
355 -(NSColor *)backgroundColor {
356    return _backgroundColor;
359 -(BOOL)drawsCellBackground {
360    return _drawsCellBackground;
363 -(NSColor *)cellBackgroundColor {
364    return _cellBackgroundColor;
367 -(BOOL)isAutoscroll {
368    return _isAutoscroll;
371 -(int)selectedRow {
372    if(_selectedIndex<0 || _numberOfRows==0 || _numberOfColumns==0)
373     return -1;
375    return _selectedIndex/_numberOfColumns;
378 -(int)selectedColumn {
379    if(_selectedIndex<0 || _numberOfRows==0 || _numberOfColumns==0)
380     return -1;
382    return _selectedIndex%_numberOfColumns;
385 -selectedCell {
386    if(_selectedIndex<0)
387     return nil;
389 // FIX shouldn't need this check if _selectedIndex is kept in sync
390    if(_selectedIndex>=[_cells count])
391     return nil;
393    return [_cells objectAtIndex:_selectedIndex];
396 -(NSArray *)selectedCells {
397    NSMutableArray *result=[NSMutableArray array];
398    int i,count=[_cells count];
400    for(i=0;i<count;i++){
401     NSCell *cell=[_cells objectAtIndex:i];
403     if([cell isHighlighted])
404      [result addObject:cell];
405    }
407    return result;
410 -(void)setDelegate:delegate {
411    if(_delegate==delegate)
412     return;
413     
414    NSNotificationCenter *center=[NSNotificationCenter defaultCenter];
415    struct {
416     SEL       selector;
417     NSString *name;
418    } notes[]={
419     { @selector(controlTextDidBeginEditing:), NSControlTextDidBeginEditingNotification },
420     { @selector(controlTextDidChange:), NSControlTextDidChangeNotification },
421     { @selector(controlTextDidEndEditing:), NSControlTextDidEndEditingNotification },
422     { NULL, nil }
423    };
424    int i;
426    if(_delegate!=nil)
427     for(i=0;notes[i].selector!=NULL;i++)
428      [center removeObserver:_delegate name:notes[i].name object:self];
430    _delegate=delegate;
432    for(i=0;notes[i].selector!=NULL;i++)
433     if([_delegate respondsToSelector:notes[i].selector])
434      [center addObserver:_delegate selector:notes[i].selector name:notes[i].name object:self];
437 -(void)setTarget:target {
438    _target=target;
441 -(void)setAction:(SEL)action {
442    _action=action;
445 -(void)setDoubleAction:(SEL)action {
446    _doubleAction=action;
449 -(void)setCellClass:(Class)class {
450    _cellClass=class;
453 -(void)setPrototype:(NSCell *)cell {
454    cell=[cell retain];
455    [_prototype release];
456    _prototype=cell;
459 -(void)_deselectAllCells {
460    int i,count=[_cells count];
462    _selectedIndex=-1;
463    _keyCellIndex=-1;
465    for(i=0;i<count;i++)
466     [[_cells objectAtIndex:i] setState:NSOffState];
469 -(void)renewRows:(int)rows columns:(int)columns {
470    while(_numberOfRows<rows)
471     [self addRow];
472    while(_numberOfRows>rows)
473     [self removeRow:_numberOfRows-1];
474    while(_numberOfColumns<columns)
475     [self addColumn];
476    while(_numberOfColumns>columns)
477     [self removeColumn:_numberOfColumns-1];
479    [self _deselectAllCells];
482 -(NSCell *)makeCellAtRow:(int)row column:(int)column {
483    NSCell *result=[[_prototype copy] autorelease];
485    if(result==nil)
486     result=[[[_cellClass alloc] init] autorelease];
488    return result;
491 -(void)putCell:(NSCell *)cell atRow:(int)row column:(int)column {
492    unsigned index=row*_numberOfColumns+column;
494    [_cells replaceObjectAtIndex:index withObject:cell];
495    [self setNeedsDisplayInRect:[self cellFrameAtRow:row column:column]];
498 -(void)addRow {
499    int i;
501    for(i=0;i<_numberOfColumns;i++)
502     [_cells addObject:[self makeCellAtRow:_numberOfRows column:i]];
504    _numberOfRows++;
507 -(void)insertRow:(int)row {
508    int i;
510    for(i=0;i<_numberOfColumns;i++){
511     [_cells insertObject:[self makeCellAtRow:row column:i]
512                  atIndex:row*_numberOfColumns];
513    }
514    _numberOfRows++;
517 -(void)insertRow:(int)row withCells:(NSArray *)cells {
518    int i;
520    for(i=0;i<_numberOfColumns;i++)
521     [_cells insertObject:[cells objectAtIndex:i] atIndex:row*_numberOfColumns+i];
522     
523    _numberOfRows++;
526 -(void)removeRow:(int)row {
527    int i;
529    for(i=0;i<_numberOfColumns;i++){
530     [_cells removeObjectAtIndex:row*_numberOfColumns];
531    }
533    _numberOfRows--;
536 -(void)addColumn {
537    int i=_numberOfRows;
538    for(;i>0;i--){
539     NSCell *cell=[self makeCellAtRow:i-1 column:_numberOfColumns];
541     [_cells insertObject:cell atIndex:i*_numberOfColumns];
542    }
544    _numberOfColumns++;
547 -(void)removeColumn:(int)col {
548    int i;
550    for(i=_numberOfRows;i>=1;i--)
551     [_cells removeObjectAtIndex:(i*col)];
553    _numberOfColumns--;
556 -(void)setToolTip:(NSString *)tip forCell:(NSCell *)cell {
559 -(void)setKeyCell:cell {
560    _keyCellIndex=[_cells indexOfObjectIdenticalTo:cell];
563 -(void)setMode:(NSMatrixMode)mode {
564    _mode=mode;
567 -(void)setAllowsEmptySelection:(BOOL)flag {
568    _allowsEmptySelection=flag;
571 -(void)setTabKeyTraversesCells:(BOOL)flag {
572    _tabKeyTraversesCells=flag;
575 -(void)setAutosizesCells:(BOOL)flag {
576    _autosizesCells=flag;
579 -(void)setCellSize:(NSSize)size {
580    _cellSize=size;
583 -(void)setIntercellSpacing:(NSSize)size {
584    _intercellSpacing=size;
587 -(void)setDrawsBackground:(BOOL)flag {
588    _drawsBackground=flag;
591 -(void)setBackgroundColor:(NSColor *)color {
592    color=[color retain];
593    [_backgroundColor release];
594    _backgroundColor=color;
595    [self setNeedsDisplay:YES];
598 -(void)setDrawsCellBackground:(BOOL)flag {
599    _drawsCellBackground=flag;
602 -(void)setCellBackgroundColor:(NSColor *)color {
603    color=[color retain];
604    [_cellBackgroundColor release];
605    _cellBackgroundColor=color;
606    [self setNeedsDisplay:YES];
609 -(void)setAutoscroll:(BOOL)flag {
610    _isAutoscroll=flag;
613 -(void)selectCellAtRow:(int)row column:(int)column {
614    NSCell *cell;
616    if(row<0 || row>=_numberOfRows)
617     cell=nil;
618    else if(column<0 || column>=_numberOfColumns)
619     cell=nil;
620    else 
621     cell=[_cells objectAtIndex:row*_numberOfColumns+column];
623    [self selectCell:cell];
626 -(void)_setSelectedIndexFromCell:(NSCell *)select {
627         [self willChangeValueForKey:@"selectedTag"];
628         [self willChangeValueForKey:@"selectedIndex"];
629    if(select==nil)
630     _selectedIndex=-1;
631    else
632     _selectedIndex=[_cells indexOfObjectIdenticalTo:select];
634    _keyCellIndex=_selectedIndex;
635         [self didChangeValueForKey:@"selectedIndex"];
636         [self didChangeValueForKey:@"selectedTag"];
639 -(void)_selectCell:(NSCell *)select deselectOthers:(BOOL)deselectOthers {
640    if([self mode]==NSRadioModeMatrix){
641     if(![self allowsEmptySelection] && select==nil)
642      return;
643    }
645    if(deselectOthers)
646     [self _deselectAllCells];
648    [self _setSelectedIndexFromCell:select];
649    [select setState:NSOnState];
650    if([self mode]==NSListModeMatrix)
651     [select setHighlighted:YES];
653    [self setNeedsDisplay:YES];
656 -(void)selectCell:(NSCell *)select {
657    [self _selectCell:select deselectOthers:YES];
660 -(BOOL)selectCellWithTag:(int)tag {
661    NSCell *cell=[self cellWithTag:tag];
663    [self selectCell:cell];
665    return (cell!=nil)?YES:NO;
668 -(void)selectAll:sender {
669     int i, count = [_cells count];
671     for (i=0; i < count; ++i)
672         [self _selectCell:[_cells objectAtIndex:i] deselectOthers:NO];
675 -(void)setSelectionFrom:(int)from to:(int)to anchor:(int)anchor highlight:(BOOL)highlight {
676     if (anchor != -1) { // no anchor, i.e., no selected cell
677         if (anchor < from)
678             from = anchor;
679         if (anchor > to)
680             to = anchor;
681     }
683     [self _deselectAllCells];
684     while (from < to) {
685         NSCell *cell = [_cells objectAtIndex:from];
686         
687         [self _selectCell:cell deselectOthers:NO];
688         if (highlight)
689             [cell setHighlighted:YES];
690         from++;
691     }
694 -(void)_deselectAllCellsInLockFocus {
695    int row,column;
697    for(row=0;row<_numberOfRows;row++)
698     for(column=0;column<_numberOfColumns;column++){
699      NSCell *cell=[self cellAtRow:row column:column];
700      NSRect  cellFrame=[self cellFrameAtRow:row column:column];
702      [cell setControlView:self];
703      [cell setState:NSOffState];
704      [cell setHighlighted:NO];
705      [cell drawWithFrame:cellFrame inView:self];
706     }
709 -(void)deselectAllCells {
710    if([self mode]==NSRadioModeMatrix && [self allowsEmptySelection]==NO)
711     return;
713    [self lockFocus];
714    [self _deselectAllCellsInLockFocus];
715    [self unlockFocus];
716    [[self window] flushWindow];
718    _selectedIndex=-1;
719    _keyCellIndex=-1;
722 -(void)deselectSelectedCell {
723    int row,column;
725    if ([self mode] == NSRadioModeMatrix && [self allowsEmptySelection] == NO)
726        return;
728    for(row=0;row<_numberOfRows;row++)
729     for(column=0;column<_numberOfColumns;column++){
730      NSCell *cell=[self cellAtRow:row column:column];
732      [cell setState:NSOffState];
733      [self drawCellAtRow:row column:column];
734     }
735     _selectedIndex=-1;
736     _keyCellIndex=-1;
739 -(void)sizeToCells {
740    [self sizeToFit];
743 -(void)setState:(int)state atRow:(int)row column:(int)column {
744    NSCell *cell=[self cellAtRow:row column:column];
746    if(cell!=nil){
747     NSRect frame=[self cellFrameAtRow:row column:column];
748     
749     [cell setState:state];
750     [self setNeedsDisplayInRect:frame];
751    }
754 -(void)highlightCell:(BOOL)highlight atRow:(int)row column:(int)column {
755    NSCell *cell=[self cellAtRow:row column:column];
757    if(cell!=nil){
758     NSRect frame=[self cellFrameAtRow:row column:column];
760     [self lockFocus];
761     [cell highlight:highlight withFrame:frame inView:self];
762     [self unlockFocus];
763     [[self window] flushWindow];
764    }
767 -(void)drawCellAtRow:(int)row column:(int)column {
768    NSCell *cell=[self cellAtRow:row column:column];
770    [self drawCell:cell];
773 -(void)scrollCellToVisibleAtRow:(int)row column:(int)column {
774    NSRect frame=[self cellFrameAtRow:row column:column];
775    [self scrollRectToVisible:frame];
778 -(BOOL)sendAction {
779    NSCell *cell=[self selectedCell];
780    SEL     action=[cell action];
781    id      target=[cell target];
783    if(action==NULL){
784     action=[self action];
785     target=[self target];
786    }
787    else if(target==nil){
788     target=[self target];
789    }
791    return [NSApp sendAction:action to:target from:self];
794 -(void)sendDoubleAction {
795    SEL action=[self doubleAction];
796    id  target=[self target];
798    if(action==NULL){
799     action=[[self selectedCell] action];
800     target=nil;
801    }
803    if(action==NULL){
804     action=[self action];
805     target=[self target];
806    }
808    [NSApp sendAction:action to:target from:self];
812 -(void)setEnabled:(BOOL)enabled {
813    int count=[_cells count];
815    while(--count>=0){
816     [[_cells objectAtIndex:count] setEnabled:enabled];
817    }
818    [self setNeedsDisplay:YES];
822 -(BOOL)isOpaque {
823    return [self drawsBackground];
826 -(void)updateCell:(NSCell *)cell {
827    int row,column;
829    if([self getRow:&row column:&column ofCell:cell]){
830     NSRect frame=[self cellFrameAtRow:row column:column];
832     [self setNeedsDisplayInRect:frame];
833    }
836 -(void)updateCellInside:(NSCell *)cell {
837    int row,column;
839    if([self getRow:&row column:&column ofCell:cell]){
840     NSRect frame=[self cellFrameAtRow:row column:column];
842     [self setNeedsDisplayInRect:frame];
843    }
849 -(void)drawCell:(NSCell *)cell {
850    int row,column;
852    if([self getRow:&row column:&column ofCell:cell]){
853     NSRect frame=[self cellFrameAtRow:row column:column];
854     [self lockFocus];
855     [cell setControlView:self];
856     [cell drawWithFrame:frame inView:self];
857     [self unlockFocus];
858     [[self window] flushWindow];
859    }
864 -(void)drawRect:(NSRect)rect {
865    int row,col;
867    if([self drawsBackground]){
868     [[self backgroundColor] setFill];
869     NSRectFill(rect);
870    }
872    for(row=0;row<_numberOfRows;row++)
873     for(col=0;col<_numberOfColumns;col++){
874      NSRect frame=[self cellFrameAtRow:row column:col];
876      if(NSIntersectsRect(frame,rect)){
877       NSCell *cell=[self cellAtRow:row column:col];
878       [cell setControlView:self];
879       [cell drawWithFrame:frame inView:self];
880      }
881     } 
884 -(void)_fieldEditCell:(NSCell *)cell row:(int)row column:(int)column {
885    [self selectCell:cell];
887    NSText* editor =[[self window] fieldEditor:YES forObject:self];
888    _currentEditor=[[cell setUpFieldEditorAttributes: editor] retain];
891 -(void)_selectTextCell:(NSCell *)cell {
892    int    row,column;
893    NSRect cellFrame;
895    if(![cell isEditable])
896     return;
898    [self getRow:&row column:&column ofCell:cell];
899    cellFrame=[self cellFrameAtRow:row column:column];
901    [self _fieldEditCell:cell row:row column:column];
902    [cell selectWithFrame:cellFrame inView:self editor:_currentEditor delegate:self start:0 length:[[cell stringValue] length]];
905 -(void)_editTextCell:(NSCell *)cell row:(int)row column:(int)column event:(NSEvent *)event {
906    NSRect cellFrame=[self cellFrameAtRow:row column:column];
908    [self _fieldEditCell:cell row:row column:column];
909    [cell editWithFrame:cellFrame inView:self editor:_currentEditor delegate:self event:event];
912 -(NSCell *)_enabledCellAtPoint:(NSPoint)point row:(int *)row column:(int *)column {
913    if([self getRow:row column:column forPoint:point]){
914     NSCell *cell=[self cellAtRow:*row column:*column];
916     if([cell isEnabled])
917      return cell;
918    }
920    return nil;
923 -(BOOL)_mouseDownRadio:(NSEvent *)event {
924    BOOL     result=NO;
925    NSEvent *lastMouse=event;
927    do {
928     NSPoint point=[self convertPoint:[lastMouse locationInWindow] fromView:nil];
929     int     row,column;
930     NSCell *cell;
932     [[self superview] autoscroll:lastMouse];
933     [self lockFocus];
934     if((cell=[self _enabledCellAtPoint:point row:&row column:&column])!=nil){
935      result=YES;
937      if([self selectedCell]!=cell)
938       [self highlightCell:NO atRow:[self selectedRow] column:[self selectedColumn]];
940      [self highlightCell:YES atRow:row column:column];
941      [self selectCell:cell];
942     }
943     [self unlockFocus];
944     [[self window] flushWindow];
945     event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|
946                           NSLeftMouseDraggedMask|NSPeriodicMask];
947     if([event type]!=NSPeriodic)
948      lastMouse=event;
950    }while([event type]!=NSLeftMouseUp);
952    [self highlightCell:NO atRow:[self selectedRow] column:[self selectedColumn]];
954    return result;
957 -(BOOL)_mouseDownHighlight:(NSEvent *)event {
958    BOOL     result=NO;
959    NSEvent *lastMouse=event;
960    do {
961     NSPoint point=[self convertPoint:[lastMouse locationInWindow] fromView:nil];
962     int     row,column;
963     NSCell *cell;
965     [[self superview] autoscroll:lastMouse];
966     [self lockFocus];
967     if((cell=[self _enabledCellAtPoint:point row:&row column:&column])!=nil){
968      NSRect  cellFrame=[self cellFrameAtRow:row column:column];
969      int     currentState=[cell state];
970      int     nextState=[cell nextState];
972      result=YES;
973      [cell highlight:YES withFrame:cellFrame inView:self];
975      [cell setState:nextState];
977      if([cell trackMouse:lastMouse inRect:cellFrame ofView:self untilMouseUp:NO]){
978       _selectedIndex=[_cells indexOfObjectIdenticalTo:cell];
979       _keyCellIndex=_selectedIndex;
980       [cell highlight:NO withFrame:cellFrame inView:self];
981       [self unlockFocus];
982       break;
983      }
984      else {
985       [cell setState:currentState];
986       [cell highlight:NO withFrame:cellFrame inView:self];
987      }
988     }
989     [self unlockFocus];
990     [[self window] flushWindow];
991     event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|
992                           NSLeftMouseDraggedMask|NSPeriodicMask];
993     if([event type]!=NSPeriodic)
994      lastMouse=event;
995    }while([event type]!=NSLeftMouseUp);
997    return result;
1000 -(BOOL)_mouseDownList:(NSEvent *)event {
1001    BOOL     result=YES;
1002    NSEvent *lastMouse=event;
1003    int  firstRow=-1,firstColumn=-1;
1004    int  previousRow=-1,previousColumn=-1;
1005    BOOL firstHighlight=YES;
1007    if([event modifierFlags]&NSShiftKeyMask){
1008     firstRow=previousRow=[self selectedRow];
1009     firstColumn=previousColumn=[self selectedColumn];
1010     [self lockFocus];
1011     [self _deselectAllCellsInLockFocus];
1012     [self unlockFocus];
1013    }
1014    else if(!([event modifierFlags]&NSControlKeyMask))
1015     [self deselectAllCells];
1017    do {
1018     NSPoint point=[self convertPoint:[lastMouse locationInWindow] fromView:nil];
1019     int     row,column,minSelRow,maxSelRow,minSelColumn,maxSelColumn,r,c;
1021     [[self superview] autoscroll:lastMouse];
1023     if([self getRow:&row column:&column forPoint:point]){
1024      NSCell *cell=[self cellAtRow:row column:column];
1026      if(firstRow==-1){
1027       previousRow=firstRow=row;
1028       previousColumn=firstColumn=column;
1029       if([lastMouse modifierFlags]&NSControlKeyMask)
1030        firstHighlight=![cell isHighlighted];
1031     //  [self _selectCell:cell];
1032      }
1035      [self lockFocus];
1037      minSelRow=MIN(firstRow,row);
1038      maxSelRow=MAX(firstRow,row);
1039      minSelColumn=MIN(firstColumn,column);
1040      maxSelColumn=MAX(firstColumn,column);
1041      for(r=MIN(minSelRow,previousRow);r<=MAX(maxSelRow,previousRow);r++){
1042       for(c=MIN(minSelColumn,previousColumn);c<=MAX(maxSelColumn,previousColumn);c++){
1043        cell=[self cellAtRow:r column:c];
1045        if([cell isEnabled]){
1046         NSRect cellFrame=[self cellFrameAtRow:r column:c];
1047         BOOL   inSelection=(r>=minSelRow && r<=maxSelRow && c>=minSelColumn && c<=maxSelColumn);
1048         BOOL   highlight=inSelection?firstHighlight:!firstHighlight;
1050         [cell setState:highlight?NSOnState:NSOffState];
1051         [cell setHighlighted:highlight];
1052         [self _setSelectedIndexFromCell:cell];
1054         //[self setNeedsDisplay:YES];
1055         [cell setControlView:self];
1056         [cell drawWithFrame:cellFrame inView:self];
1057        }
1058       }
1059      }
1060      [self unlockFocus];
1061      [[self window] flushWindow];
1063      previousRow=row;
1064      previousColumn=column;
1065     }
1067     event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask];
1068     if([event type]!=NSPeriodic)
1069      lastMouse=event;
1070    }while([event type]!=NSLeftMouseUp);
1072    return result;
1075 -(BOOL)_mouseDownTrack:(NSEvent *)event {
1076    BOOL    result=NO;
1077    NSPoint point=[self convertPoint:[event locationInWindow] fromView:nil];
1078    int     row,column;
1079    NSCell *cell;
1081    [self lockFocus];
1082     if((cell=[self _enabledCellAtPoint:point row:&row column:&column])!=nil){
1083      NSRect  cellFrame=[self cellFrameAtRow:row column:column];
1085      if([cell trackMouse:event inRect:cellFrame ofView:self untilMouseUp:YES]){
1086       [cell setState:[cell nextState]];
1087       [self selectCell:cell];
1088      }
1089      result=YES;
1090     }
1091    [self unlockFocus];
1093    return result;
1096 -(void)mouseDown:(NSEvent *)event {
1097    NSPoint point=[self convertPoint:[event locationInWindow] fromView:nil];
1098    int     row,column;
1099    BOOL    sendAction=NO;
1101    if([self getRow:&row column:&column forPoint:point]){
1102     NSCell *cell=[self cellAtRow:row column:column];
1104     if([cell isEditable]){
1105      if([cell isEnabled])
1106       [self _editTextCell:cell row:row column:column event:event];
1107      return;
1108     }
1109    }
1111    [NSEvent startPeriodicEventsAfterDelay:0.1 withPeriod:0.2];
1113    switch([self mode]){
1114     case NSRadioModeMatrix:     sendAction=[self _mouseDownRadio:event]; break;
1115     case NSHighlightModeMatrix: sendAction=[self _mouseDownHighlight:event]; break;
1116     case NSListModeMatrix:      sendAction=[self _mouseDownList:event]; break;
1117     case NSTrackModeMatrix:     sendAction=[self _mouseDownTrack:event]; break;
1118    }
1120    [NSEvent stopPeriodicEvents];
1122    if(sendAction){
1123     if([event clickCount]==2)
1124      [self sendDoubleAction];
1125     else
1126      [self sendAction];
1127    }
1129    [[self window] flushWindow];
1132 // n.b. now moves to next/previous view on last/first cell, should not wrap (according to spec)
1133 - (void)insertTab:sender {
1134     BOOL selectNextKeyView = YES;
1135     
1136     if ([self tabKeyTraversesCells] && [self mode] != NSRadioModeMatrix){
1137         _keyCellIndex++;
1138         if(_keyCellIndex >= [_cells count])
1139             _keyCellIndex = [_cells count]-1;
1140         else
1141             selectNextKeyView = NO;
1142         
1143         [self setNeedsDisplay:YES];
1144     }
1146     if (selectNextKeyView)
1147         [[self window] selectNextKeyView:sender];
1150 - (void)insertBacktab:sender {
1151     BOOL selectPreviousKeyView = YES;
1152     
1153     if ([self tabKeyTraversesCells] && [self mode] != NSRadioModeMatrix){
1154         _keyCellIndex--;
1155         if(_keyCellIndex < 0)
1156             _keyCellIndex = 0;
1157         else
1158             selectPreviousKeyView = NO;
1159         
1160         [self setNeedsDisplay:YES];
1161     }
1163     if (selectPreviousKeyView)
1164         [[self window] selectPreviousKeyView:sender];
1167 - (void)performClick:sender {
1168     if([self mode]==NSHighlightModeMatrix){
1169         NSCell *cell=[self keyCell];
1170         int     nextState=[cell nextState];
1172         [self selectCell:cell];
1173         [cell setState:nextState];
1174         [self drawCell:cell];
1175         [self sendAction];
1176         return;
1177     }
1180 - (void)keyDown:(NSEvent *)event {
1181     [self interpretKeyEvents:[NSArray arrayWithObject:event]];
1184 // bounds checking is done at selectCellAtRow:column:
1185 // revised to pass keyboard events down to cells if possible
1186 // hmm. if we're in radio mode, movement == selection
1187 // otherwise, movement = keyboard focus
1188 -(void)moveUp:sender {
1189     NSCell *nextCell = nil;
1190     int row = -1, column = -1;
1192     if ([self mode] == NSRadioModeMatrix) {
1193         row = [self selectedRow];
1194         column = [self selectedColumn];
1195     }
1196     else
1197         [self getRow:&row column:&column ofCell:[self keyCell]];
1199     nextCell = [self cellAtRow:row-1 column:column];
1201     if (nextCell) {
1202         if ([self mode] == NSRadioModeMatrix)
1203             [self selectCell:nextCell];
1204         
1205         [self setKeyCell:nextCell];
1206     }
1207     else if ([[self keyCell] respondsToSelector:@selector(moveUp:)])
1208         [[self keyCell] moveUp:sender];
1210     [self setNeedsDisplay:YES];
1213 -(void)moveDown:sender {
1214     NSCell *nextCell = nil;
1215     int row = -1, column = -1;
1217     if ([self mode] == NSRadioModeMatrix) {
1218         row = [self selectedRow];
1219         column = [self selectedColumn];
1220     }
1221     else
1222         [self getRow:&row column:&column ofCell:[self keyCell]];
1224     nextCell = [self cellAtRow:row+1 column:column];
1226     if (nextCell) {
1227         if ([self mode] == NSRadioModeMatrix)
1228             [self selectCell:nextCell];
1229         
1230         [self setKeyCell:nextCell];
1231     }
1232     else if ([[self keyCell] respondsToSelector:@selector(moveDown:)])
1233         [[self keyCell] moveDown:sender];
1235     [self setNeedsDisplay:YES];
1238 -(void)moveLeft:sender {
1239     NSCell *nextCell = nil;
1240     int row = -1, column = -1;
1242     if ([self mode] == NSRadioModeMatrix) {
1243         row = [self selectedRow];
1244         column = [self selectedColumn];
1245     }
1246     else
1247         [self getRow:&row column:&column ofCell:[self keyCell]];
1249     nextCell = [self cellAtRow:row column:column-1];
1251     if (nextCell) {
1252         if ([self mode] == NSRadioModeMatrix)
1253             [self selectCell:nextCell];
1254         
1255         [self setKeyCell:nextCell];
1256     }
1257     else if ([[self keyCell] respondsToSelector:@selector(moveLeft:)])
1258         [[self keyCell] moveLeft:sender];
1260     [self setNeedsDisplay:YES];
1263 -(void)moveRight:sender {
1264     NSCell *nextCell = nil;
1265     int row = -1, column = -1;
1267     if ([self mode] == NSRadioModeMatrix) {
1268         row = [self selectedRow];
1269         column = [self selectedColumn];
1270     }
1271     else
1272         [self getRow:&row column:&column ofCell:[self keyCell]];
1274     nextCell = [self cellAtRow:row column:column+1];
1276     if (nextCell) {
1277         if ([self mode] == NSRadioModeMatrix)
1278             [self selectCell:nextCell];
1279         
1280         [self setKeyCell:nextCell];
1281     }
1282     else if ([[self keyCell] respondsToSelector:@selector(moveRight:)])
1283         [[self keyCell] moveRight:sender];
1285     [self setNeedsDisplay:YES];
1288 -(BOOL)becomeFirstResponder {
1289    [self _selectTextCell:[self keyCell]];
1291    return YES;
1294 -(void)textDidEndEditing:(NSNotification *)note {
1295    int movement=[[[note userInfo] objectForKey:@"NSTextMovement"] intValue];
1297    [super textDidEndEditing:note];
1299    if(movement==NSReturnTextMovement || [[self selectedCell] sendsActionOnEndEditing])
1300     [self sendAction];
1302    if(movement==NSTabTextMovement){
1304     _keyCellIndex++;
1305     if(_keyCellIndex>=[_cells count]){
1306      NSView *next=[self nextValidKeyView];
1308      _keyCellIndex=0;
1310      if(next!=nil){
1311       [[self window] makeFirstResponder:next];
1312       return;
1313      }
1314     }
1315     if(_keyCellIndex>=[_cells count])
1316      _keyCellIndex=-1;
1318      [self _selectTextCell:[self keyCell]];
1319    }
1322 @end
1325 @implementation NSMatrix (Bindings)
1327 - (int)_selectedIndex
1329         return _selectedIndex;
1332 - (void)_setSelectedIndex:(int)index
1334         if (_selectedIndex != index) {
1335                 if (index < [_cells count]) {
1336                         NSCell* cell = [_cells objectAtIndex: index];
1337                         [self selectCell: cell];
1338                 }
1339         }
1342 - (int) _selectedTag {
1343     return [[self selectedCell] tag];
1345 - (void) _setSelectedTag:(int)selectedTag {
1346     [self selectCellWithTag:selectedTag];
1349 @end