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"];
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;
47 _mode=NSListModeMatrix;
49 _mode=NSRadioModeMatrix;
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){
57 NSLog(@"Unknown cell class '%@' in NSMatrix, using NSCell",name);
59 _cellClass=[NSCell class];
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];
70 [NSException raise:NSInvalidArgumentException format:@"-[%@ %s] is not implemented for coder %@",isa,sel_getName(_cmd),coder];
75 -initWithFrame:(NSRect)frame {
76 [super initWithFrame:frame];
80 _font=[[NSFont userFontOfSize:0] retain];
81 _cells=[NSMutableArray new];
86 _cellSize=NSMakeSize(frame.size.width,20);
87 _intercellSpacing=NSMakeSize(0,0);
89 _cellClass=[NSCell class];
90 _mode=NSListModeMatrix;
91 _tabKeyTraversesCells=YES;
96 -initWithFrame:(NSRect)frame mode:(int)mode prototype:(NSCell *)prototype numberOfRows:(int)rows numberOfColumns:(int)columns {
99 [self initWithFrame:frame];
102 _prototype=[prototype copy];
104 _numberOfColumns=columns;
105 for(i=0;i<rows*columns;i++)
106 [_cells addObject:[[_prototype copy] autorelease]];
111 -initWithFrame:(NSRect)frame mode:(int)mode cellClass:(Class)cls numberOfRows:(int)rows numberOfColumns:(int)columns {
114 [self initWithFrame:frame];
119 _numberOfColumns=columns;
120 for(i=0;i<rows*columns;i++)
121 [_cells addObject:[[[cls alloc] init] autorelease]];
128 [_backgroundColor release];
129 [_cellBackgroundColor release];
131 [_prototype release];
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];
152 NSRect frame=[self frame];
154 if(_autoresizingMask&NSViewWidthSizable){
155 if(_numberOfColumns==0)
156 _cellSize.width=frame.size.width;
158 _cellSize.width=(frame.size.width-
159 (_numberOfColumns-1)*_intercellSpacing.width)/_numberOfColumns;
162 if(_autoresizingMask& NSViewHeightSizable){
164 _cellSize.height= frame.size.height;
166 _cellSize.height=(frame.size.height-
167 (_numberOfRows-1)*_intercellSpacing.height)/_numberOfRows;
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];
187 -(void)resetCursorRects {
188 NSRect visibleRect=[self visibleRect];
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];
207 -(void)setFont:(NSFont *)font {
226 return _doubleAction;
241 -cellWithTag:(int)tag {
242 int i,count=[_cells count];
244 for(i=0;i<count;i++){
245 NSCell *cell=[_cells objectAtIndex:i];
253 -cellAtRow:(int)row column:(int)column {
254 unsigned index=row*_numberOfColumns+column;
256 if(index>=[_cells count])
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)
263 if (column < 0 || column >= _numberOfColumns)
266 return [_cells objectAtIndex:index];
269 -(NSRect)cellFrameAtRow:(int)row column:(int)column {
272 result.origin.x=column*_cellSize.width+column*_intercellSpacing.width;
273 result.origin.y=row*_cellSize.height+row*_intercellSpacing.height;
274 result.size=_cellSize;
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;
291 -(BOOL)getRow:(int *)row column:(int *)column forPoint:(NSPoint)point {
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]);
303 return _numberOfRows;
306 -(int)numberOfColumns {
307 return _numberOfColumns;
310 -(void)getNumberOfRows:(int *)rows columns:(int *)columns {
312 *columns=_numberOfColumns;
315 -(NSString *)toolTipForCell:(NSCell *)cell {
316 NSUnimplementedMethod();
321 if(_keyCellIndex==-1)
324 return [_cells objectAtIndex:_keyCellIndex];
327 -(NSMatrixMode)mode {
331 -(BOOL)allowsEmptySelection {
332 return _allowsEmptySelection;
335 -(BOOL)tabKeyTraversesCells {
336 return _tabKeyTraversesCells;
339 -(BOOL)autosizesCells {
340 return _autosizesCells;
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;
372 if(_selectedIndex<0 || _numberOfRows==0 || _numberOfColumns==0)
375 return _selectedIndex/_numberOfColumns;
378 -(int)selectedColumn {
379 if(_selectedIndex<0 || _numberOfRows==0 || _numberOfColumns==0)
382 return _selectedIndex%_numberOfColumns;
389 // FIX shouldn't need this check if _selectedIndex is kept in sync
390 if(_selectedIndex>=[_cells count])
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];
410 -(void)setDelegate:delegate {
411 if(_delegate==delegate)
414 NSNotificationCenter *center=[NSNotificationCenter defaultCenter];
419 { @selector(controlTextDidBeginEditing:), NSControlTextDidBeginEditingNotification },
420 { @selector(controlTextDidChange:), NSControlTextDidChangeNotification },
421 { @selector(controlTextDidEndEditing:), NSControlTextDidEndEditingNotification },
427 for(i=0;notes[i].selector!=NULL;i++)
428 [center removeObserver:_delegate name:notes[i].name object:self];
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 {
441 -(void)setAction:(SEL)action {
445 -(void)setDoubleAction:(SEL)action {
446 _doubleAction=action;
449 -(void)setCellClass:(Class)class {
453 -(void)setPrototype:(NSCell *)cell {
455 [_prototype release];
459 -(void)_deselectAllCells {
460 int i,count=[_cells count];
466 [[_cells objectAtIndex:i] setState:NSOffState];
469 -(void)renewRows:(int)rows columns:(int)columns {
470 while(_numberOfRows<rows)
472 while(_numberOfRows>rows)
473 [self removeRow:_numberOfRows-1];
474 while(_numberOfColumns<columns)
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];
486 result=[[[_cellClass alloc] init] autorelease];
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]];
501 for(i=0;i<_numberOfColumns;i++)
502 [_cells addObject:[self makeCellAtRow:_numberOfRows column:i]];
507 -(void)insertRow:(int)row {
510 for(i=0;i<_numberOfColumns;i++){
511 [_cells insertObject:[self makeCellAtRow:row column:i]
512 atIndex:row*_numberOfColumns];
517 -(void)insertRow:(int)row withCells:(NSArray *)cells {
520 for(i=0;i<_numberOfColumns;i++)
521 [_cells insertObject:[cells objectAtIndex:i] atIndex:row*_numberOfColumns+i];
526 -(void)removeRow:(int)row {
529 for(i=0;i<_numberOfColumns;i++){
530 [_cells removeObjectAtIndex:row*_numberOfColumns];
539 NSCell *cell=[self makeCellAtRow:i-1 column:_numberOfColumns];
541 [_cells insertObject:cell atIndex:i*_numberOfColumns];
547 -(void)removeColumn:(int)col {
550 for(i=_numberOfRows;i>=1;i--)
551 [_cells removeObjectAtIndex:(i*col)];
556 -(void)setToolTip:(NSString *)tip forCell:(NSCell *)cell {
559 -(void)setKeyCell:cell {
560 _keyCellIndex=[_cells indexOfObjectIdenticalTo:cell];
563 -(void)setMode:(NSMatrixMode)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 {
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 {
613 -(void)selectCellAtRow:(int)row column:(int)column {
616 if(row<0 || row>=_numberOfRows)
618 else if(column<0 || column>=_numberOfColumns)
621 cell=[_cells objectAtIndex:row*_numberOfColumns+column];
623 [self selectCell:cell];
626 -(void)_setSelectedIndexFromCell:(NSCell *)select {
627 [self willChangeValueForKey:@"selectedTag"];
628 [self willChangeValueForKey:@"selectedIndex"];
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)
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
683 [self _deselectAllCells];
685 NSCell *cell = [_cells objectAtIndex:from];
687 [self _selectCell:cell deselectOthers:NO];
689 [cell setHighlighted:YES];
694 -(void)_deselectAllCellsInLockFocus {
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];
709 -(void)deselectAllCells {
710 if([self mode]==NSRadioModeMatrix && [self allowsEmptySelection]==NO)
714 [self _deselectAllCellsInLockFocus];
716 [[self window] flushWindow];
722 -(void)deselectSelectedCell {
725 if ([self mode] == NSRadioModeMatrix && [self allowsEmptySelection] == NO)
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];
743 -(void)setState:(int)state atRow:(int)row column:(int)column {
744 NSCell *cell=[self cellAtRow:row column:column];
747 NSRect frame=[self cellFrameAtRow:row column:column];
749 [cell setState:state];
750 [self setNeedsDisplayInRect:frame];
754 -(void)highlightCell:(BOOL)highlight atRow:(int)row column:(int)column {
755 NSCell *cell=[self cellAtRow:row column:column];
758 NSRect frame=[self cellFrameAtRow:row column:column];
761 [cell highlight:highlight withFrame:frame inView:self];
763 [[self window] flushWindow];
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];
779 NSCell *cell=[self selectedCell];
780 SEL action=[cell action];
781 id target=[cell target];
784 action=[self action];
785 target=[self target];
787 else if(target==nil){
788 target=[self target];
791 return [NSApp sendAction:action to:target from:self];
794 -(void)sendDoubleAction {
795 SEL action=[self doubleAction];
796 id target=[self target];
799 action=[[self selectedCell] action];
804 action=[self action];
805 target=[self target];
808 [NSApp sendAction:action to:target from:self];
812 -(void)setEnabled:(BOOL)enabled {
813 int count=[_cells count];
816 [[_cells objectAtIndex:count] setEnabled:enabled];
818 [self setNeedsDisplay:YES];
823 return [self drawsBackground];
826 -(void)updateCell:(NSCell *)cell {
829 if([self getRow:&row column:&column ofCell:cell]){
830 NSRect frame=[self cellFrameAtRow:row column:column];
832 [self setNeedsDisplayInRect:frame];
836 -(void)updateCellInside:(NSCell *)cell {
839 if([self getRow:&row column:&column ofCell:cell]){
840 NSRect frame=[self cellFrameAtRow:row column:column];
842 [self setNeedsDisplayInRect:frame];
849 -(void)drawCell:(NSCell *)cell {
852 if([self getRow:&row column:&column ofCell:cell]){
853 NSRect frame=[self cellFrameAtRow:row column:column];
855 [cell setControlView:self];
856 [cell drawWithFrame:frame inView:self];
858 [[self window] flushWindow];
864 -(void)drawRect:(NSRect)rect {
867 if([self drawsBackground]){
868 [[self backgroundColor] setFill];
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];
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 {
895 if(![cell isEditable])
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];
923 -(BOOL)_mouseDownRadio:(NSEvent *)event {
925 NSEvent *lastMouse=event;
928 NSPoint point=[self convertPoint:[lastMouse locationInWindow] fromView:nil];
932 [[self superview] autoscroll:lastMouse];
934 if((cell=[self _enabledCellAtPoint:point row:&row column:&column])!=nil){
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];
944 [[self window] flushWindow];
945 event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|
946 NSLeftMouseDraggedMask|NSPeriodicMask];
947 if([event type]!=NSPeriodic)
950 }while([event type]!=NSLeftMouseUp);
952 [self highlightCell:NO atRow:[self selectedRow] column:[self selectedColumn]];
957 -(BOOL)_mouseDownHighlight:(NSEvent *)event {
959 NSEvent *lastMouse=event;
961 NSPoint point=[self convertPoint:[lastMouse locationInWindow] fromView:nil];
965 [[self superview] autoscroll:lastMouse];
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];
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];
985 [cell setState:currentState];
986 [cell highlight:NO withFrame:cellFrame inView:self];
990 [[self window] flushWindow];
991 event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|
992 NSLeftMouseDraggedMask|NSPeriodicMask];
993 if([event type]!=NSPeriodic)
995 }while([event type]!=NSLeftMouseUp);
1000 -(BOOL)_mouseDownList:(NSEvent *)event {
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];
1011 [self _deselectAllCellsInLockFocus];
1014 else if(!([event modifierFlags]&NSControlKeyMask))
1015 [self deselectAllCells];
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];
1027 previousRow=firstRow=row;
1028 previousColumn=firstColumn=column;
1029 if([lastMouse modifierFlags]&NSControlKeyMask)
1030 firstHighlight=![cell isHighlighted];
1031 // [self _selectCell:cell];
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];
1061 [[self window] flushWindow];
1064 previousColumn=column;
1067 event=[[self window] nextEventMatchingMask:NSLeftMouseUpMask|NSLeftMouseDraggedMask|NSPeriodicMask];
1068 if([event type]!=NSPeriodic)
1070 }while([event type]!=NSLeftMouseUp);
1075 -(BOOL)_mouseDownTrack:(NSEvent *)event {
1077 NSPoint point=[self convertPoint:[event locationInWindow] fromView:nil];
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];
1096 -(void)mouseDown:(NSEvent *)event {
1097 NSPoint point=[self convertPoint:[event locationInWindow] fromView:nil];
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];
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;
1120 [NSEvent stopPeriodicEvents];
1123 if([event clickCount]==2)
1124 [self sendDoubleAction];
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;
1136 if ([self tabKeyTraversesCells] && [self mode] != NSRadioModeMatrix){
1138 if(_keyCellIndex >= [_cells count])
1139 _keyCellIndex = [_cells count]-1;
1141 selectNextKeyView = NO;
1143 [self setNeedsDisplay:YES];
1146 if (selectNextKeyView)
1147 [[self window] selectNextKeyView:sender];
1150 - (void)insertBacktab:sender {
1151 BOOL selectPreviousKeyView = YES;
1153 if ([self tabKeyTraversesCells] && [self mode] != NSRadioModeMatrix){
1155 if(_keyCellIndex < 0)
1158 selectPreviousKeyView = NO;
1160 [self setNeedsDisplay:YES];
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];
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];
1197 [self getRow:&row column:&column ofCell:[self keyCell]];
1199 nextCell = [self cellAtRow:row-1 column:column];
1202 if ([self mode] == NSRadioModeMatrix)
1203 [self selectCell:nextCell];
1205 [self setKeyCell:nextCell];
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];
1222 [self getRow:&row column:&column ofCell:[self keyCell]];
1224 nextCell = [self cellAtRow:row+1 column:column];
1227 if ([self mode] == NSRadioModeMatrix)
1228 [self selectCell:nextCell];
1230 [self setKeyCell:nextCell];
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];
1247 [self getRow:&row column:&column ofCell:[self keyCell]];
1249 nextCell = [self cellAtRow:row column:column-1];
1252 if ([self mode] == NSRadioModeMatrix)
1253 [self selectCell:nextCell];
1255 [self setKeyCell:nextCell];
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];
1272 [self getRow:&row column:&column ofCell:[self keyCell]];
1274 nextCell = [self cellAtRow:row column:column+1];
1277 if ([self mode] == NSRadioModeMatrix)
1278 [self selectCell:nextCell];
1280 [self setKeyCell:nextCell];
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]];
1294 -(void)textDidEndEditing:(NSNotification *)note {
1295 int movement=[[[note userInfo] objectForKey:@"NSTextMovement"] intValue];
1297 [super textDidEndEditing:note];
1299 if(movement==NSReturnTextMovement || [[self selectedCell] sendsActionOnEndEditing])
1302 if(movement==NSTabTextMovement){
1305 if(_keyCellIndex>=[_cells count]){
1306 NSView *next=[self nextValidKeyView];
1311 [[self window] makeFirstResponder:next];
1315 if(_keyCellIndex>=[_cells count])
1318 [self _selectTextCell:[self keyCell]];
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];
1342 - (int) _selectedTag {
1343 return [[self selectedCell] tag];
1345 - (void) _setSelectedTag:(int)selectedTag {
1346 [self selectCellWithTag:selectedTag];