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