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