1/** <title>NSCell</title> 2 3 <abstract>The abstract cell class</abstract> 4 5 Copyright (C) 1996-2012,2019 Free Software Foundation, Inc. 6 7 Author: Scott Christley <scottc@net-community.com> 8 Date: 1996 9 Modifications: Felipe A. Rodriguez <far@ix.netcom.com> 10 Date: August 1998 11 Rewrite: Multiple authors 12 Date: 1999 13 Editing, formatters: Nicola Pero <nicola@brainstorm.co.uk> 14 Date: 2000 15 16 This file is part of the GNUstep GUI Library. 17 18 This library is free software; you can redistribute it and/or 19 modify it under the terms of the GNU Lesser General Public 20 License as published by the Free Software Foundation; either 21 version 2 of the License, or (at your option) any later version. 22 23 This library is distributed in the hope that it will be useful, 24 but WITHOUT ANY WARRANTY; without even the implied warranty of 25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 26 Lesser General Public License for more details. 27 28 You should have received a copy of the GNU Lesser General Public 29 License along with this library; see the file COPYING.LIB. 30 If not, see <http://www.gnu.org/licenses/> or write to the 31 Free Software Foundation, 51 Franklin Street, Fifth Floor, 32 Boston, MA 02110-1301, USA. 33*/ 34 35#include <math.h> 36 37#import "config.h" 38#import <Foundation/NSCoder.h> 39#import <Foundation/NSDebug.h> 40#import <Foundation/NSException.h> 41#import <Foundation/NSFormatter.h> 42#import <Foundation/NSValue.h> 43#import <Foundation/NSNotification.h> 44#import <Foundation/NSNumberFormatter.h> 45#import <Foundation/NSRunLoop.h> 46#import <Foundation/NSString.h> 47#import <Foundation/NSGeometry.h> 48 49#import "AppKit/AppKitExceptions.h" 50#import "AppKit/NSAttributedString.h" 51#import "AppKit/NSApplication.h" 52#import "AppKit/NSControl.h" 53#import "AppKit/NSCell.h" 54#import "AppKit/NSClipView.h" 55#import "AppKit/NSColor.h" 56#import "AppKit/NSCursor.h" 57#import "AppKit/NSEvent.h" 58#import "AppKit/NSFont.h" 59#import "AppKit/NSGraphics.h" 60#import "AppKit/NSImage.h" 61#import "AppKit/NSMenu.h" 62#import "AppKit/NSParagraphStyle.h" 63#import "AppKit/NSStringDrawing.h" 64#import "AppKit/NSTextView.h" 65#import "AppKit/NSTextContainer.h" 66#import "AppKit/NSView.h" 67#import "AppKit/NSWindow.h" 68#import "AppKit/NSKeyValueBinding.h" 69#import "GSBindingHelpers.h" 70#import "GNUstepGUI/GSTheme.h" 71#import "GSGuiPrivate.h" 72 73static Class colorClass; 74static Class cellClass; 75static Class fontClass; 76static Class imageClass; 77 78static NSColor *txtCol; 79static NSColor *dtxtCol; 80 81@interface NSCell (PrivateColor) 82+ (void) _systemColorsChanged: (NSNotification*)n; 83@end 84 85 86@implementation NSCell (PrivateColor) 87+ (void) _systemColorsChanged: (NSNotification*)n 88{ 89 ASSIGN (txtCol, [colorClass controlTextColor]); 90 ASSIGN (dtxtCol, [colorClass disabledControlTextColor]); 91} 92@end 93 94 95/** 96 *<p> TODO Desctiption</p> 97 */ 98 99@implementation NSCell 100 101/* 102 * Class methods 103 */ 104+ (void) initialize 105{ 106 if (self == [NSCell class]) 107 { 108 [self setVersion: 4]; 109 colorClass = [NSColor class]; 110 cellClass = [NSCell class]; 111 fontClass = [NSFont class]; 112 imageClass = [NSImage class]; 113 /* 114 * Watch for changes to system colors, and simulate an initial change 115 * in order to set up our defaults. 116 */ 117 [[NSNotificationCenter defaultCenter] 118 addObserver: self 119 selector: @selector(_systemColorsChanged:) 120 name: NSSystemColorsDidChangeNotification 121 object: nil]; 122 [self _systemColorsChanged: nil]; 123#if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST) 124 [self exposeBinding: NSTitleBinding]; 125#endif 126 } 127} 128 129+ (NSMenu*) defaultMenu 130{ 131 return nil; 132} 133 134+ (NSFocusRingType) defaultFocusRingType 135{ 136 return NSFocusRingTypeDefault; 137} 138 139/**<p>This class method returns NO. This method should be overrided by 140 subclasses.</p> 141 */ 142+ (BOOL) prefersTrackingUntilMouseUp 143{ 144 return NO; 145} 146 147/* 148 * Instance methods 149 */ 150- (id) init 151{ 152 return [self initTextCell: @""]; 153} 154 155/**<p>Initializes and returns a new NSCell with a NSImage <var>anImage</var>. 156 This method sets the image position to <ref type="type" 157 id="NSCellImagePosition">NSImageOnly</ref> and the cell's type to 158 <ref type="type" id="NSCellType">NSImageCellType</ref>.</p> 159 <p>See Also: -initTextCell: </p> 160 */ 161- (id) initImageCell: (NSImage*)anImage 162{ 163 _cell.type = NSImageCellType; 164 _cell_image = RETAIN (anImage); 165 _cell.image_position = NSImageOnly; 166 _font = RETAIN ([fontClass systemFontOfSize: 0]); 167 168 // Implicitly set by allocation: 169 // 170 //_font = nil; 171 //_cell.contents_is_attributed_string = NO; 172 //_cell.is_highlighted = NO; 173 //_cell.is_disabled = NO; 174 //_cell.is_editable = NO; 175 //_cell.is_rich_text = NO; 176 //_cell.imports_graphics = NO; 177 //_cell.shows_first_responder = NO; 178 //_cell.refuses_first_responder = NO; 179 //_cell.sends_action_on_end_editing = NO; 180 //_cell.is_bordered = NO; 181 //_cell.is_bezeled = NO; 182 //_cell.is_scrollable = NO; 183 //_cell.is_selectable = NO; 184 //_cell.state = 0; 185 //_cell.line_break_mode = NSLineBreakByWordWrapping; 186 _action_mask = NSLeftMouseUpMask; 187 _menu = [object_getClass(self) defaultMenu]; 188 [self setFocusRingType: [object_getClass(self) defaultFocusRingType]]; 189 190 return self; 191} 192/**<p>Initializes and returns a new NSCell with a NSString aString. 193 This method sets the cell's type to <ref type="type" id="NSCellType"> 194 NSTextCellType</ref>.</p> 195 <p>See Also: -initImageCell: </p> 196 */ 197- (id) initTextCell: (NSString*)aString 198{ 199 _cell.type = NSTextCellType; 200 _contents = RETAIN (aString); 201 _font = RETAIN ([fontClass systemFontOfSize: 0]); 202 203 // Implicitly set by allocation: 204 // 205 //_cell.contents_is_attributed_string = NO; 206 //_cell_image = nil; 207 //_cell.image_position = NSNoImage; 208 //_cell.is_disabled = NO; 209 //_cell.state = 0; 210 //_cell.is_highlighted = NO; 211 //_cell.is_editable = NO; 212 //_cell.is_bordered = NO; 213 //_cell.is_bezeled = NO; 214 //_cell.is_scrollable = NO; 215 //_cell.is_selectable = NO; 216 //_cell.line_break_mode = NSLineBreakByWordWrapping; 217 _action_mask = NSLeftMouseUpMask; 218 _menu = [object_getClass(self) defaultMenu]; 219 [self setFocusRingType: [object_getClass(self) defaultFocusRingType]]; 220 221 return self; 222} 223 224- (void) dealloc 225{ 226 // Remove all key value bindings for this object. 227 [GSKeyValueBinding unbindAllForObject: self]; 228 TEST_RELEASE (_contents); 229 TEST_RELEASE (_cell_image); 230 TEST_RELEASE (_font); 231 TEST_RELEASE (_represented_object); 232 TEST_RELEASE (_object_value); 233 TEST_RELEASE (_formatter); 234 TEST_RELEASE (_menu); 235 236 [super dealloc]; 237} 238 239/* 240 * Setting the NSCell's Value 241 */ 242- (id) objectValue 243{ 244 if (_cell.has_valid_object_value) 245 { 246 return _object_value; 247 } 248 else 249 { 250 return nil; 251 } 252} 253 254- (BOOL) hasValidObjectValue 255{ 256 return _cell.has_valid_object_value; 257} 258 259/**<p>Returns the NSCell's value as a double. </p> 260 *<p>See Also: -setDoubleValue: </p> 261 */ 262- (double) doubleValue 263{ 264 if ((_cell.has_valid_object_value == YES) && 265 ([_object_value respondsToSelector: @selector(doubleValue)])) 266 { 267 return [_object_value doubleValue]; 268 } 269 else 270 { 271 return [[self stringValue] doubleValue]; 272 } 273} 274 275/**<p>Returns the cell's value as a float. </p> 276 *<p>See Also: -setFloatValue: </p> 277 */ 278- (float) floatValue 279{ 280 if ((_cell.has_valid_object_value == YES) && 281 ([_object_value respondsToSelector: @selector(floatValue)])) 282 { 283 return [_object_value floatValue]; 284 } 285 else 286 { 287 return [[self stringValue] floatValue]; 288 } 289} 290 291/**<p>Returns the cell's value as an int. </p> 292 *<p>See Also: -setIntValue:</p> 293 */ 294- (int) intValue 295{ 296 if ((_cell.has_valid_object_value == YES) && 297 ([_object_value respondsToSelector: @selector(intValue)])) 298 { 299 return [_object_value intValue]; 300 } 301 else 302 { 303 return [[self stringValue] intValue]; 304 } 305} 306 307/**<p>Returns the cell's value as an NSInteger. </p> 308 *<p>See Also: -setIntegerValue:</p> 309 */ 310- (NSInteger) integerValue 311{ 312 if ((_cell.has_valid_object_value == YES) && 313 ([_object_value respondsToSelector: @selector(integerValue)])) 314 { 315 return [_object_value integerValue]; 316 } 317 else 318 { 319 return [[self stringValue] integerValue]; 320 } 321} 322 323/**<p>Returns the cell's value as a NSString.</p> 324 *<p>See Also: -setStringValue: </p> 325 */ 326- (NSString*) stringValue 327{ 328 if (nil == _contents) 329 { 330 return @""; 331 } 332 333 if (_cell.contents_is_attributed_string == NO) 334 { 335 // If we have a formatter this is also the string of the _object_value 336 return (NSString *)_contents; 337 } 338 else 339 { 340 return [(NSAttributedString *)_contents string]; 341 } 342} 343 344- (void) setObjectValue: (id)object 345{ 346 id newContents; 347 348 ASSIGN (_object_value, object); 349 if (_formatter == nil) 350 { 351 if (object == nil || [object isKindOfClass: [NSString class]] == YES) 352 { 353 newContents = object; 354 _cell.contents_is_attributed_string = NO; 355 _cell.has_valid_object_value = YES; 356 357 // If we are in single line mode, trim the new line characters 358 if(_cell.uses_single_line_mode == YES) 359 { 360 newContents = [object stringByTrimmingCharactersInSet: 361 [NSCharacterSet newlineCharacterSet]]; 362 } 363 } 364 else if ([object isKindOfClass: [NSAttributedString class]] == YES) 365 { 366 newContents = object; 367 _cell.contents_is_attributed_string = YES; 368 _cell.has_valid_object_value = YES; 369 370 // If we are in single line mode, trim the new line characters 371 if(_cell.uses_single_line_mode == YES) 372 { 373 newContents = [object stringByTrimmingCharactersInSet: 374 [NSCharacterSet newlineCharacterSet]]; 375 } 376 } 377 else if ([_object_value respondsToSelector: @selector(attributedStringValue)]) 378 { 379 newContents = [_object_value attributedStringValue]; 380 _cell.contents_is_attributed_string = YES; 381 _cell.has_valid_object_value = YES; 382 } 383 else if ([_object_value respondsToSelector: @selector(stringValue)]) 384 { 385 // If the thing that was assigned is not a string, but 386 // responds to stringValue then get that. 387 newContents = [_object_value stringValue]; 388 _cell.contents_is_attributed_string = NO; 389 _cell.has_valid_object_value = YES; 390 } 391 else 392 { 393 newContents = [_object_value description]; 394 _cell.contents_is_attributed_string = NO; 395 _cell.has_valid_object_value = YES; 396 } 397 } 398 else 399 { 400 newContents = [_formatter stringForObjectValue: _object_value]; 401 _cell.contents_is_attributed_string = NO; 402 if (newContents != nil) 403 { 404 _cell.has_valid_object_value = YES; 405 } 406 else 407 { 408 _cell.has_valid_object_value = NO; 409 } 410 } 411 412 ASSIGNCOPY(_contents, newContents); 413} 414 415 416/**<p>Sets the NSCell's value to aDouble.</p> 417 *<p>See Also: -doubleValue</p> 418 */ 419- (void) setDoubleValue: (double)aDouble 420{ 421 NSNumber *number; 422 423 // NB: GNUstep can set a double value for an image cell 424 425 number = [NSNumber numberWithDouble: aDouble]; 426 [self setObjectValue: number]; 427} 428 429/** 430 *<p>Sets the NSCell's value to a aFloat. This used for example in 431 NSSliderCell</p> 432 *<p>See Also: -floatValue</p> 433 */ 434- (void) setFloatValue: (float)aFloat 435{ 436 NSNumber *number; 437 438 // NB: GNUstep can set a float value for an image cell. 439 // NSSliderCell is an example of it! 440 441 number = [NSNumber numberWithFloat: aFloat]; 442 [self setObjectValue: number]; 443} 444 445 446/** 447 *<p>Sets the NSCell's value to anInt.</p> 448 *<p>See Also: -intValue</p> 449 */ 450- (void) setIntValue: (int)anInt 451{ 452 NSNumber *number; 453 454 // NB: GNUstep can set an int value for an image cell. 455 456 number = [NSNumber numberWithInt: anInt]; 457 [self setObjectValue: number]; 458} 459 460/** 461 *<p>Sets the NSCell's value to anInt.</p> 462 *<p>See Also: -integerValue</p> 463 */ 464- (void) setIntegerValue: (NSInteger)anInt 465{ 466 NSNumber *number; 467 468 // NB: GNUstep can set an int value for an image cell. 469 470 number = [NSNumber numberWithInteger: anInt]; 471 [self setObjectValue: number]; 472} 473 474/**<p>Sets the cell's value to a NSString. 475 The NSCell's type is set to NSTextCellType if needed</p> 476 <p>See Also: -stringValue</p> 477 */ 478- (void) setStringValue: (NSString*)aString 479{ 480 /* We warn about nil for compatibiliy with MacOS X, which refuses 481 nil. */ 482 if (aString == nil) 483 { 484 NSDebugMLLog (@"MacOSXCompatibility", 485 @"Attempt to use nil as string value"); 486 } 487 488 if (_cell.type != NSTextCellType) 489 { 490 [self setType: NSTextCellType]; 491 } 492 493 if (_formatter == nil) 494 { 495 [self setObjectValue: aString]; 496 } 497 else 498 { 499 id newObjectValue; 500 501 if ([_formatter getObjectValue: &newObjectValue 502 forString: aString 503 errorDescription: NULL]) 504 { 505 [self setObjectValue: newObjectValue]; 506 } 507 else 508 { 509 ASSIGNCOPY(_contents, aString); 510 _cell.contents_is_attributed_string = NO; 511 _cell.has_valid_object_value = NO; 512 } 513 } 514} 515 516/**<p>Returns some NSCell's attributes for the specified <ref type="type" 517 id="NSCellAttribute">NSCellAttribute</ref></p> 518 <p>See Also: -setCellAttribute:to:</p> 519 */ 520- (NSInteger) cellAttribute: (NSCellAttribute)aParameter 521{ 522 switch (aParameter) 523 { 524 case NSCellDisabled: return _cell.is_disabled; 525 case NSCellState: return _cell.state; 526 case NSCellEditable: return _cell.is_editable; 527 case NSCellHighlighted: return _cell.is_highlighted; 528 case NSCellIsBordered: return _cell.is_bordered; 529 case NSCellAllowsMixedState: return _cell.allows_mixed_state; 530 531 /* 532 case NSPushInCell: return 0; 533 case NSChangeGrayCell: return 0; 534 case NSCellLightsByContents: return 0; 535 case NSCellLightsByGray: return 0; 536 case NSChangeBackgroundCell: return 0; 537 case NSCellLightsByBackground: return 0; 538 case NSCellChangesContents: return 0; 539 case NSCellIsInsetButton: return 0; 540 */ 541 case NSCellHasOverlappingImage: 542 { 543 return _cell.image_position == NSImageOverlaps; 544 } 545 case NSCellHasImageHorizontal: 546 { 547 return (_cell.image_position == NSImageRight) 548 || (_cell.image_position == NSImageLeft); 549 } 550 case NSCellHasImageOnLeftOrBottom: 551 { 552 return (_cell.image_position == NSImageBelow) 553 || (_cell.image_position == NSImageLeft); 554 } 555 default: 556 { 557 NSWarnLog (@"cell attribute %d not supported", (int)aParameter); 558 break; 559 } 560 } 561 562 return 0; 563} 564 565 566/**<p>TODO</p> 567 *<p>See Also: -cellAttribute:</p> 568 */ 569- (void) setCellAttribute: (NSCellAttribute)aParameter to: (NSInteger)value 570{ 571 switch (aParameter) 572 { 573 case NSCellDisabled: 574 { 575 _cell.is_disabled = value; 576 break; 577 } 578 case NSCellState: 579 { 580 _cell.state = value; 581 break; 582 } 583 case NSCellEditable: 584 { 585 _cell.is_editable = value; 586 break; 587 } 588 case NSCellHighlighted: 589 { 590 _cell.is_highlighted = value; 591 break; 592 } 593 case NSCellHasOverlappingImage: 594 { 595 if (value) 596 { 597 _cell.image_position = NSImageOverlaps; 598 } 599 else 600 { 601 if (_cell.image_position == NSImageOverlaps) 602 { 603 _cell.image_position = NSImageLeft; 604 } 605 } 606 break; 607 } 608 case NSCellHasImageHorizontal: 609 { 610 if (value) 611 { 612 if (_cell.image_position != NSImageLeft 613 && _cell.image_position != NSImageRight) 614 { 615 _cell.image_position = NSImageLeft; 616 } 617 } 618 else 619 { 620 if (_cell.image_position == NSImageLeft) 621 { 622 _cell.image_position = NSImageAbove; 623 } 624 else if (_cell.image_position == NSImageRight) 625 { 626 _cell.image_position = NSImageBelow; 627 } 628 } 629 break; 630 } 631 case NSCellHasImageOnLeftOrBottom: 632 { 633 if (value) 634 { 635 if (_cell.image_position == NSImageAbove) 636 { 637 _cell.image_position = NSImageBelow; 638 } 639 else 640 { 641 _cell.image_position = NSImageLeft; 642 } 643 } 644 else 645 { 646 if (_cell.image_position == NSImageBelow) 647 { 648 _cell.image_position = NSImageAbove; 649 } 650 else 651 { 652 _cell.image_position = NSImageRight; 653 } 654 } 655 break; 656 } 657 /* 658 case NSCellChangesContents: 659 _cell. = value; 660 break; 661 case NSCellIsInsetButton: 662 _cell. = value; 663 break; 664*/ 665 case NSCellIsBordered: 666 { 667 _cell.is_bordered = value; 668 break; 669 } 670 case NSCellAllowsMixedState: 671 { 672 _cell.allows_mixed_state = value; 673 break; 674 } 675 default: 676 { 677 NSWarnLog (@"cell attribute %d not supported", (int)aParameter); 678 break; 679 } 680 } 681} 682 683/**<p>Sets the NSCell's type. See <ref type="type" id="NSCellType">NSCellType 684 </ref>.If the cell is set to NSTextCellType, the cell is given 685 a default title and is reset to the default system font.</p> 686 <p>See Also: -type</p> 687*/ 688- (void) setType: (NSCellType)aType 689{ 690 if (_cell.type == aType) 691 { 692 return; 693 } 694 695 _cell.type = aType; 696 switch (_cell.type) 697 { 698 case NSTextCellType: 699 { 700 ASSIGN (_contents, @"title"); 701 _cell.contents_is_attributed_string = NO; 702 /* Doc says we have to reset the font too. */ 703 ASSIGN (_font, [fontClass systemFontOfSize: 0]); 704 break; 705 } 706 case NSImageCellType: 707 { 708 TEST_RELEASE (_cell_image); 709 _cell_image = nil; 710 break; 711 } 712 } 713} 714 715/**<p>Returns the cell's type. Returns NSNullCellType if the 716 cell's type flag is set to NSImageCellType and if the cell's image 717 is nil. See <ref type="type" id="NSCellType">NSCellType</ref> for more 718 information.</p><p>See Also -setType:</p> 719 */ 720- (NSCellType) type 721{ 722 if (_cell.type == NSImageCellType && _cell_image == nil) 723 return NSNullCellType; 724 725 return _cell.type; 726} 727 728 729/**<p>Returns whether the NSCell can respond to mouse events.</p> 730 *<p>See Also: -setEnabled:</p> 731 */ 732- (BOOL) isEnabled 733{ 734 return !_cell.is_disabled; 735} 736 737/**<p>Sets whether the NSCell can respond to mouse events</p> 738 <p>See Also: -isEnabled</p> 739 */ 740- (void) setEnabled: (BOOL)flag 741{ 742 _cell.is_disabled = !flag; 743} 744 745/**<p>Returns whether the NSCell has a bezeled border. By default a NSCell 746 has no bezeled border</p><p>See Also: -setBezeled:</p> 747 */ 748- (BOOL) isBezeled 749{ 750 return _cell.is_bezeled; 751} 752 753/**<p>Returns whether the NSCell has a border. By default a NSCell has 754 border</p><p>See Also: -setBordered: -setBezeled: -isBezeled</p> 755 */ 756- (BOOL) isBordered 757{ 758 return _cell.is_bordered; 759} 760 761/**<p>Returns whether the cell is opaque. Return NO by default</p> 762 */ 763- (BOOL) isOpaque 764{ 765 return NO; 766} 767 768/**<p>Sets whether the cell has a bezeled border. 769 If this method is called, the bordered flag is turn off. 770 By default a NSCell has no bezeled border</p> 771 <p>See Also: -isBezeled -setBordered: -isBordered</p> 772 */ 773- (void) setBezeled: (BOOL)flag 774{ 775 _cell.is_bezeled = flag; 776 _cell.is_bordered = NO; 777} 778 779/**<p>Sets whether the cell has a border. If this method is called, 780 the bezeled flag is turn off. By default a NSCell has no border</p> 781 <p>See Also: -isBordered -setBezeled: -isBezeled</p> 782 */ 783- (void) setBordered: (BOOL)flag 784{ 785 _cell.is_bordered = flag; 786 _cell.is_bezeled = NO; 787} 788 789- (NSFocusRingType) focusRingType 790{ 791 return _cell.focus_ring_type; 792} 793 794- (void) setFocusRingType: (NSFocusRingType)type 795{ 796 _cell.focus_ring_type = type; 797} 798 799/**<p>Sets the NSCell's state. Please use always symbolic constants when 800 calling this method. The integer values could be changed in the this 801 implementation. (Currently they match the Cocoa values but they are 802 quite strange)</p> <p>See Also: -state</p> 803 */ 804- (void) setState: (NSInteger)value 805{ 806 /* We do exactly as in macosx when value is not NSOnState, 807 * NSOffState, NSMixedState, even if their behaviour (value < 0 ==> 808 * NSMixedState) is a bit strange. We could decide to do 809 * differently in the future, so please use always symbolic 810 * constants when calling this method, this way your code won't be 811 * broken by changes. */ 812 if (value > 0 || (value < 0 && _cell.allows_mixed_state == NO)) 813 { 814 _cell.state = NSOnState; 815 } 816 else if (value == 0) 817 { 818 _cell.state = NSOffState; 819 } 820 else 821 { 822 _cell.state = NSMixedState; 823 } 824} 825 826/**<p>Returns the NSCell's state</p> 827 <p>See Also: -setState: </p> 828*/ 829- (NSInteger) state 830{ 831 return _cell.state; 832} 833 834- (BOOL) allowsMixedState 835{ 836 return _cell.allows_mixed_state; 837} 838 839- (void) setAllowsMixedState: (BOOL)flag 840{ 841 _cell.allows_mixed_state = flag; 842 if (!flag && _cell.state == NSMixedState) 843 { 844 [self setNextState]; 845 } 846} 847 848- (NSInteger) nextState 849{ 850 switch (_cell.state) 851 { 852 case NSOnState: 853 { 854 return NSOffState; 855 } 856 case NSOffState: 857 { 858 if (_cell.allows_mixed_state) 859 { 860 return NSMixedState; 861 } 862 else 863 { 864 return NSOnState; 865 } 866 } 867 case NSMixedState: 868 default: 869 { 870 return NSOnState; 871 } 872 } 873} 874 875- (void) setNextState 876{ 877 [self setState: [self nextState]]; 878} 879 880/**<p>Returns the alignment of the text used in the NSCell. See 881 <ref type="type" id="NSTextAlignment">NSTextAlignment</ref> for more 882 informations. By default the text alignment is <ref type="type" 883 id="NSTextAlignment">NSJustifiedTextAlignment</ref></p> 884 <p>See Also: -setAlignment:</p> 885 */ 886- (NSTextAlignment) alignment 887{ 888 return _cell.text_align; 889} 890 891/** <p>Returns the font of the text used in the NSCell</p> 892 <p>See Also: -setFont:</p> 893 */ 894- (NSFont*) font 895{ 896 return _font; 897} 898 899/**<p>Returns whether the cell is editable.By default a NSCell is not editable. 900 </p><p>See Also: -setEditable:</p> 901 */ 902- (BOOL) isEditable 903{ 904 return _cell.is_editable; 905} 906 907/**<p>Returns whether the cell is selectable. This method returns YES if 908 the cell is selectable or editable. NO otherwise</p> 909 <p>See Also: -setSelectable: -isEditable -setEditable: </p> 910 */ 911- (BOOL) isSelectable 912{ 913 return _cell.is_selectable || _cell.is_editable; 914} 915 916/**<p>Returns whether the NSCell is scrollable. By default a NSCell is not 917 scrollable</p><p>See Also: -setScrollable:</p> 918 */ 919- (BOOL) isScrollable 920{ 921 return _cell.is_scrollable; 922} 923 924/**<p>Sets the alignment of the text. See <ref type="type" 925 id="NSTextAlignment">NSTextAlignment</ref>.</p><p>See Also: -alignment </p> 926 */ 927- (void) setAlignment: (NSTextAlignment)mode 928{ 929 // This does not have any influence on attributed strings 930 _cell.text_align = mode; 931} 932 933/**<p>Sets whether the NSCell's text is editable.</p> 934 <p>See Also: -isEditable -setSelectable: -isSelectable</p> 935*/ 936- (void) setEditable: (BOOL)flag 937{ 938 /* 939 * The cell_editable flag is also checked to see if the cell is 940 * selectable so turning edit on also turns selectability on (until 941 * edit is turned off again). 942 */ 943 _cell.is_editable = flag; 944} 945 946/**<p>Sets the text font. The NSCell's type is set to NSTextCellType if needed 947 </p><p>See Also: -font -setType: -type</p> 948 */ 949- (void) setFont: (NSFont*)fontObject 950{ 951 if (_cell.type != NSTextCellType) 952 { 953 [self setType: NSTextCellType]; 954 } 955 956 // This does not have any influence on attributed strings 957 ASSIGN (_font, fontObject); 958} 959 960/**<p>Sets whether the cell selectable. Making a cell unselectable also 961 * makes it uneditable until a -setEditable: re-enables it.</p> 962 *<p>See Also: -isSelectable -setEditable: -isEditable</p> 963 */ 964- (void) setSelectable: (BOOL)flag 965{ 966 _cell.is_selectable = flag; 967 968 if (!flag) 969 _cell.is_editable = NO; 970} 971 972/**<p>Sets whether the NCell is scrollable. By default a NSCell is not 973 scrollable</p><p>See Also: -isSelectable</p> 974 */ 975- (void) setScrollable: (BOOL)flag 976{ 977 _cell.is_scrollable = flag; 978 if (flag) 979 { 980 [self setWraps: NO]; 981 } 982} 983 984- (void) setWraps: (BOOL)flag 985{ 986 if (flag) 987 { 988 if (![self wraps]) 989 [self setLineBreakMode: NSLineBreakByWordWrapping]; 990 } 991 else 992 { 993 if ([self wraps]) 994 [self setLineBreakMode: NSLineBreakByClipping]; 995 } 996} 997 998- (BOOL) wraps 999{ 1000 return _cell.line_break_mode == NSLineBreakByWordWrapping 1001 || _cell.line_break_mode == NSLineBreakByCharWrapping; 1002} 1003 1004- (void) setAttributedStringValue: (NSAttributedString*)attribStr 1005{ 1006 /* Hmm. FIXME. Not sure what to do here. */ 1007 if (_formatter != nil) 1008 { 1009 id newObjectValue; 1010 1011 if ([_formatter getObjectValue: &newObjectValue 1012 forString: [attribStr string] 1013 errorDescription: NULL] == YES) 1014 { 1015 [self setObjectValue: newObjectValue]; 1016 /* What about the attributed string ? We are loosing it. */ 1017 return; 1018 } 1019 _cell.has_valid_object_value = NO; 1020 } 1021 else 1022 { 1023 _cell.has_valid_object_value = YES; 1024 ASSIGN (_object_value, attribStr); 1025 } 1026 1027 ASSIGN (_contents, attribStr); 1028 _cell.contents_is_attributed_string = YES; 1029} 1030 1031- (NSAttributedString*) attributedStringValue 1032{ 1033 if (_formatter != nil) 1034 { 1035 NSDictionary *attributes; 1036 NSAttributedString *attrStr; 1037 1038 attributes = [self _nonAutoreleasedTypingAttributes]; 1039 attrStr = [_formatter attributedStringForObjectValue: _object_value 1040 withDefaultAttributes: attributes]; 1041 RELEASE(attributes); 1042 if (attrStr != nil) 1043 { 1044 return attrStr; 1045 } 1046 } 1047 1048 /* In all other cases */ 1049 if (_cell.contents_is_attributed_string && 1050 nil != _contents) 1051 { 1052 return (NSAttributedString *)_contents; 1053 } 1054 else 1055 { 1056 NSDictionary *dict; 1057 NSAttributedString *attrStr; 1058 1059 dict = [self _nonAutoreleasedTypingAttributes]; 1060 attrStr = [[NSAttributedString alloc] initWithString: [self stringValue] 1061 attributes: dict]; 1062 RELEASE(dict); 1063 return AUTORELEASE(attrStr); 1064 } 1065} 1066 1067- (void) setAllowsEditingTextAttributes: (BOOL)flag 1068{ 1069 _cell.is_rich_text = flag; 1070 if (!flag) 1071 _cell.imports_graphics = NO; 1072} 1073 1074- (BOOL) allowsEditingTextAttributes 1075{ 1076 return _cell.is_rich_text; 1077} 1078 1079- (void) setImportsGraphics: (BOOL)flag 1080{ 1081 _cell.imports_graphics = flag; 1082 if (flag) 1083 _cell.is_rich_text = YES; 1084} 1085 1086- (BOOL) importsGraphics 1087{ 1088 return _cell.imports_graphics; 1089} 1090 1091- (NSString*) title 1092{ 1093 return [self stringValue]; 1094} 1095 1096- (void) setTitle: (NSString*)aString 1097{ 1098 [self setStringValue: aString]; 1099} 1100 1101- (NSLineBreakMode) lineBreakMode 1102{ 1103 return _cell.line_break_mode; 1104} 1105 1106- (void) setLineBreakMode: (NSLineBreakMode)mode 1107{ 1108 if (mode == NSLineBreakByCharWrapping || mode == NSLineBreakByWordWrapping) 1109 { 1110 _cell.is_scrollable = NO; 1111 } 1112 _cell.line_break_mode = mode; 1113} 1114 1115- (NSWritingDirection) baseWritingDirection 1116{ 1117 return _cell.base_writing_direction; 1118} 1119 1120- (void) setBaseWritingDirection: (NSWritingDirection)direction 1121{ 1122 _cell.base_writing_direction = direction; 1123} 1124 1125/**<p>Implemented by subclasses to return the action method. 1126 The NSCell implementaiton returns NULL.</p> 1127 <p>See Also: -setAction: -setTarget: -target</p> 1128 */ 1129- (SEL) action 1130{ 1131 return NULL; 1132} 1133 1134/** <p>Implemented by subclasses to set the action method. 1135 The NSCell implementation raises a NSInternalInconsistencyException</p> 1136 <p>See Also: -action -setTarget: -target</p> 1137*/ 1138- (void) setAction: (SEL)aSelector 1139{ 1140 [NSException raise: NSInternalInconsistencyException 1141 format: @"attempt to set an action in an NSCell"]; 1142} 1143 1144/**<p>Implemented by subclasses to set the target object. 1145 The NSCell implementation raises a NSInternalInconsistencyException</p> 1146 <p>See Also: -target -setAction: -action</p> 1147 */ 1148- (void) setTarget: (id)anObject 1149{ 1150 [NSException raise: NSInternalInconsistencyException 1151 format: @"attempt to set a target in an NSCell"]; 1152} 1153 1154/**<p>Implemented by subclass to return the target object. 1155 The NSCell implementation returns nil</p> 1156 <p>See Also: -setTarget: -setAction: -action</p> 1157 */ 1158- (id) target 1159{ 1160 return nil; 1161} 1162 1163/**<p>Returns whether the cell can continuously send its action messages.</p> 1164 <p>See Also: -setContinuous:</p> 1165 */ 1166- (BOOL) isContinuous 1167{ 1168 // Some subclasses should redefine this with NSLeftMouseDraggedMask 1169 return (_action_mask & NSPeriodicMask) != 0; 1170} 1171 1172/**<p>Sets whether the cell can continuously send its action messages.</p> 1173 *<p>See Also: -isContinuous</p> 1174 */ 1175- (void) setContinuous: (BOOL)flag 1176{ 1177 // Some subclasses should redefine this with NSLeftMouseDraggedMask 1178 if (flag) 1179 { 1180 _action_mask |= NSPeriodicMask; 1181 } 1182 else 1183 { 1184 _action_mask &= ~NSPeriodicMask; 1185 } 1186} 1187 1188/**<p>TODO Explain</p> 1189 */ 1190- (NSInteger) sendActionOn: (NSInteger)mask 1191{ 1192 NSUInteger previousMask = _action_mask; 1193 1194 _action_mask = mask; 1195 1196 return previousMask; 1197} 1198 1199/**<p>Returns the NSCell's image if the NSCell's type is <ref type="type" 1200 id="NSCellType">NSImageCellType</ref>, 1201 returns nil otherwise.</p> 1202 <p>See Also: -setImage: -setType: -type</p> 1203 */ 1204- (NSImage*) image 1205{ 1206 if (_cell.type == NSImageCellType) 1207 { 1208 return _cell_image; 1209 } 1210 else 1211 return nil; 1212} 1213 1214/**<p>Sets the NSCell's image to anImage. This method sets the cell's type 1215 to NSImageCellType if needed. Raises an NSInvalidArgumentException if 1216 the anImage is not an NSImage (sub)class. The new image is retained and the 1217 old one is released</p><p>See Also: -image</p> 1218 */ 1219- (void) setImage: (NSImage*)anImage 1220{ 1221 if (anImage) 1222 { 1223 NSAssert ([anImage isKindOfClass: imageClass], 1224 NSInvalidArgumentException); 1225 } 1226 1227 if (_cell.type != NSImageCellType) 1228 { 1229 [self setType: NSImageCellType]; 1230 } 1231 1232 ASSIGN (_cell_image, anImage); 1233} 1234 1235/**<p>Implemented by sublclasses to assigns the tag <var>anInt</var>. 1236 The NSCell implementation raises an NSInvalidArgumentException.</p> 1237 <p>See Also: -tag</p> 1238 */ 1239- (void) setTag: (NSInteger)anInt 1240{ 1241 [NSException raise: NSInternalInconsistencyException 1242 format: @"attempt to set a tag in an NSCell"]; 1243} 1244 1245/**<p>Implemented by subclasses to Return the tag. 1246 The NSCell implementation returns -1 </p><p>See Also: -setTag:</p> 1247 */ 1248- (NSInteger) tag 1249{ 1250 return -1; 1251} 1252 1253/* 1254 * Formatting Data 1255 */ 1256- (void) setFloatingPointFormat: (BOOL)autoRange 1257 left: (NSUInteger)leftDigits 1258 right: (NSUInteger)rightDigits 1259{ 1260 NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; 1261 NSMutableString *format = [[NSMutableString alloc] init]; 1262 1263 if (autoRange) 1264 { 1265 NSUInteger fieldWidth = leftDigits + rightDigits + 1; 1266 1267 // FIXME: this does not fully match the documentation. 1268 while (fieldWidth--) 1269 { 1270 [format appendString: @"#"]; 1271 } 1272 } 1273 else 1274 { 1275 while (leftDigits--) 1276 { 1277 [format appendString: @"#"]; 1278 } 1279 [format appendString: @"."]; 1280 while (rightDigits--) 1281 { 1282 [format appendString: @"0"]; 1283 } 1284 } 1285 1286 [formatter setFormat: format]; 1287 RELEASE(format); 1288 [self setFormatter: formatter]; 1289 RELEASE(formatter); 1290} 1291 1292- (void) setFormatter: (NSFormatter*)newFormatter 1293{ 1294 ASSIGN(_formatter, newFormatter); 1295} 1296 1297- (id) formatter 1298{ 1299 return _formatter; 1300} 1301 1302/**<p> TODO</p> 1303 */ 1304- (NSInteger) entryType 1305{ 1306 return _cell.entry_type; 1307} 1308 1309/** <p>TODO</p> 1310 */ 1311- (void) setEntryType: (NSInteger)aType 1312{ 1313 [self setType: NSTextCellType]; 1314 // TODO: This should select a suitable formatter 1315 _cell.entry_type = aType; 1316} 1317 1318- (BOOL) isEntryAcceptable: (NSString*)aString 1319{ 1320 if ((_formatter != nil) && ![aString isEqualToString: @""]) 1321 { 1322 id newObjectValue; 1323 1324 return [_formatter getObjectValue: &newObjectValue 1325 forString: aString 1326 errorDescription: NULL]; 1327 } 1328 else 1329 { 1330 return YES; 1331 } 1332} 1333 1334/* 1335 * Menu 1336 */ 1337- (void) setMenu: (NSMenu*)aMenu 1338{ 1339 ASSIGN (_menu, aMenu); 1340} 1341 1342- (NSMenu*) menu 1343{ 1344 return _menu; 1345} 1346 1347- (NSMenu*) menuForEvent: (NSEvent*)anEvent 1348 inRect: (NSRect)cellFrame 1349 ofView: (NSView*)aView 1350{ 1351 return [self menu]; 1352} 1353 1354/** 1355 * Compares the reciever to another to another NSCell. 1356 * The argument must be an NSCell sublclass and have 1357 * the NSCellType NSTextCellType. Returns the result 1358 * of the comparison of each cell's stringValue. 1359 */ 1360- (NSComparisonResult) compare: (id)otherCell 1361{ 1362 if ([otherCell isKindOfClass: cellClass] == NO) 1363 { 1364 [NSException raise: NSBadComparisonException 1365 format: @"NSCell comparison with non-NSCell"]; 1366 } 1367 if (_cell.type != NSTextCellType 1368 || ((NSCell*)otherCell)->_cell.type != NSTextCellType) 1369 { 1370 [NSException raise: NSBadComparisonException 1371 format: @"Comparison between non-text cells"]; 1372 } 1373 /* We shouldn't access instance variables directly as subclasses 1374 may override stringValue to retrieve the value from somewhere else. */ 1375 return [[self stringValue] compare: [(NSCell*)otherCell stringValue]]; 1376} 1377 1378/* 1379 * Should this cell respond to keyboard input? 1380 */ 1381- (BOOL) acceptsFirstResponder 1382{ 1383 return _cell.is_disabled == NO && _cell.refuses_first_responder == NO; 1384} 1385 1386- (void) setShowsFirstResponder: (BOOL)flag 1387{ 1388 _cell.shows_first_responder = flag; 1389} 1390 1391- (BOOL) showsFirstResponder 1392{ 1393 return _cell.shows_first_responder; 1394} 1395 1396- (void) setTitleWithMnemonic: (NSString*)aString 1397{ 1398 NSRange r = [aString rangeOfString: @"&"]; 1399 1400 if (r.length > 0) 1401 { 1402 NSUInteger location = r.location; 1403 1404 [self setTitle: [[aString substringToIndex: location] 1405 stringByAppendingString: 1406 [aString substringFromIndex: NSMaxRange(r)]]]; 1407 // TODO: We should underline this character 1408 [self setMnemonicLocation: location]; 1409 } 1410} 1411 1412- (NSString*) mnemonic 1413{ 1414 NSUInteger location = [self mnemonicLocation]; 1415 NSString *c = [self title]; 1416 1417 if ((location == NSNotFound) || location >= [c length]) 1418 return @""; 1419 1420 return [c substringWithRange: NSMakeRange (location, 1)]; 1421} 1422 1423- (void) setMnemonicLocation: (NSUInteger)location 1424{ 1425 _cell.mnemonic_location = location; 1426} 1427 1428- (NSUInteger) mnemonicLocation 1429{ 1430 return _cell.mnemonic_location; 1431} 1432 1433- (BOOL) refusesFirstResponder 1434{ 1435 return _cell.refuses_first_responder; 1436} 1437 1438- (void) setRefusesFirstResponder: (BOOL)flag 1439{ 1440 _cell.refuses_first_responder = flag; 1441} 1442 1443/** 1444 * Simulates a single click in the cell (only works with controls which have 1445 * no more than one cell). This method is deprecated, 1446 * performClickWithFrame:inView: is the right method to use now. 1447 */ 1448- (void) performClick: (id)sender 1449{ 1450 NSView *cv = [self controlView]; 1451 1452 if (cv != nil) 1453 [self performClickWithFrame: [cv bounds] inView: cv]; 1454} 1455 1456/* 1457 * Helper method used to send actions. Sender normally is [self controlView]. 1458 */ 1459- (BOOL) _sendActionFrom: (id)sender 1460{ 1461 SEL action = [self action]; 1462 1463 if ([sender respondsToSelector: @selector(sendAction:to:)]) 1464 { 1465 return [sender sendAction: action to: [self target]]; 1466 } 1467 else 1468 { 1469 if (sender == nil) 1470 sender = self; 1471 1472 if (action) 1473 { 1474 return [NSApp sendAction: action to: [self target] from: sender]; 1475 } 1476 } 1477 1478 return NO; 1479} 1480 1481/** 1482 * Simulates a single click in the cell. 1483 * The display of the cell with this event 1484 * occurs in the area delimited by <var>cellFrame</var> within 1485 * <var>controlView</var>. 1486 */ 1487- (void) performClickWithFrame: (NSRect)cellFrame inView: (NSView *)controlView 1488{ 1489 if (_cell.is_disabled == YES) 1490 { 1491 return; 1492 } 1493 1494 [self setNextState]; 1495 1496 if ((controlView != nil) && [controlView canDraw]) 1497 { 1498 NSWindow *cvWin = [controlView window]; 1499 NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 0.1]; 1500 1501 [controlView lockFocus]; 1502 [self highlight: YES withFrame: cellFrame inView: controlView]; 1503 [cvWin flushWindow]; 1504 1505 // Wait approx 1/10 seconds 1506 [[NSRunLoop currentRunLoop] runUntilDate: limit]; 1507 1508 [self highlight: NO withFrame: cellFrame inView: controlView]; 1509 [cvWin flushWindow]; 1510 [controlView unlockFocus]; 1511 } 1512 1513 [self _sendActionFrom: controlView]; 1514} 1515 1516/* 1517 * Deriving values from other objects (not necessarily cells) 1518 */ 1519- (void) takeObjectValueFrom: (id)sender 1520{ 1521 [self setObjectValue: [sender objectValue]]; 1522} 1523 1524/** <p>Sets the NSCell's double value to sender's double value</p> 1525 <p>See Also: -setDoubleValue:</p> 1526 */ 1527- (void) takeDoubleValueFrom: (id)sender 1528{ 1529 [self setDoubleValue: [sender doubleValue]]; 1530} 1531 1532/** <p>Sets the NSCell's float value to sender's float value</p> 1533 <p>See Also: -setFloatValue:</p> 1534 */ 1535- (void) takeFloatValueFrom: (id)sender 1536{ 1537 [self setFloatValue: [sender floatValue]]; 1538} 1539 1540/** <p>Sets the NSCell's int value to sender's int value</p> 1541 <p>See Also: -setIntValue:</p> 1542 */ 1543- (void) takeIntValueFrom: (id)sender 1544{ 1545 [self setIntValue: [sender intValue]]; 1546} 1547 1548/** <p>Sets the NSCell's NSInteger value to sender's NSInteger value</p> 1549 <p>See Also: -setIntegerValue:</p> 1550 */ 1551- (void) takeIntegerValueFrom: (id)sender 1552{ 1553 [self setIntegerValue: [sender integerValue]]; 1554} 1555 1556/** <p>Sets the NSCell's NSString value to sender's NSSting value</p> 1557 <p>See Also: -setStringValue:</p> 1558 */ 1559- (void) takeStringValueFrom: (id)sender 1560{ 1561 [self setStringValue: [sender stringValue]]; 1562} 1563 1564/** <p>Returns the NSCell's represented object</p> 1565 <p>See Also: -setRepresentedObject:</p> 1566 */ 1567- (id) representedObject 1568{ 1569 return _represented_object; 1570} 1571 1572/** <p>Sets the NSCell's represented object to <var>anObject</var>. 1573 anObject will be retain.</p><p>See Also: -representedObject</p> 1574 */ 1575- (void) setRepresentedObject: (id)anObject 1576{ 1577 /* Ahm - not nice - the RETAIN here could cause retain cycles - anyway. */ 1578 ASSIGN (_represented_object, anObject); 1579} 1580 1581- (NSBackgroundStyle)backgroundStyle 1582{ 1583 return(_cell.background_style); 1584} 1585 1586- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle 1587{ 1588 _cell.background_style = backgroundStyle; 1589} 1590 1591/**<p>Returns the mouse flags. This flags are usally sets in 1592 the -trackMouse:inRect:ofView:untilMouseUp: method</p> 1593 */ 1594- (NSInteger) mouseDownFlags 1595{ 1596 return _mouse_down_flags; 1597} 1598 1599/**<p>Gets the NSCell's <var>delay</var> and the <var>interval</var> 1600 parameters used when NSCell sends continouly action messages. 1601 The NSCell implementation sets <var>delay</var> to 0.2 and <var>interval</var> 1602 to 0.025.</p> 1603 <p>See Also: -trackMouse:inRect:ofView:untilMouseUp:</p> 1604 */ 1605- (void) getPeriodicDelay: (float*)delay interval: (float*)interval 1606{ 1607 *delay = 0.2; 1608 *interval = 0.025; 1609} 1610 1611/**<p>Returns whether tracking starts. The NSCell implementation 1612 returns YES when the <var>startPoint</var> is into the control view 1613 retangle, NO otherwise. This method is call at the early stage of 1614 -trackMouse:inRect:ofView:untilMouseUp:</p><p>See Also: 1615 [NSView-mouse:inRect:] -trackMouse:inRect:ofView:untilMouseUp: 1616 </p> 1617 */ 1618- (BOOL) startTrackingAt: (NSPoint)startPoint inView: (NSView*)controlView 1619{ 1620 if ([self isContinuous] || (_action_mask & NSLeftMouseDraggedMask)) 1621 { 1622 return YES; 1623 } 1624 else 1625 { 1626 return NO; 1627 } 1628} 1629 1630/** <p>Returns whether the mouse dragging should continue for the cell. 1631 Subclasses should overrided this method if you want 1632 stop tracking the mouse. This method is call in the 1633 -trackMouse:inRect:ofView:untilMouseUp: main loop.</p> 1634 <p>See Also: -trackMouse:inRect:ofView:untilMouseUp:</p> 1635 */ 1636- (BOOL) continueTracking: (NSPoint)lastPoint 1637 at: (NSPoint)currentPoint 1638 inView: (NSView*)controlView 1639{ 1640 if ([self isContinuous] || (_action_mask & NSLeftMouseDraggedMask)) 1641 { 1642 return YES; 1643 } 1644 else 1645 { 1646 return NO; 1647 } 1648} 1649 1650/**<p>Default implementation of this method in NSCell does nothing.</p> 1651 */ 1652- (void) stopTracking: (NSPoint)lastPoint 1653 at: (NSPoint)stopPoint 1654 inView: (NSView*)controlView 1655 mouseIsUp: (BOOL)flag 1656{ 1657} 1658 1659- (BOOL) trackMouse: (NSEvent*)theEvent 1660 inRect: (NSRect)cellFrame 1661 ofView: (NSView*)controlView 1662 untilMouseUp: (BOOL)flag 1663{ 1664 NSApplication *theApp = [NSApplication sharedApplication]; 1665 NSUInteger event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask 1666 | NSMouseMovedMask | NSLeftMouseDraggedMask | NSOtherMouseDraggedMask 1667 | NSRightMouseDraggedMask; 1668 NSPoint location = [theEvent locationInWindow]; 1669 NSPoint point = [controlView convertPoint: location fromView: nil]; 1670 NSPoint last_point = point; 1671 BOOL mouseWentUp = NO; 1672 BOOL tracking; 1673 unsigned periodCount = 0; 1674 1675 NSDebugLLog(@"NSCell", @"cell start tracking in rect %@ initial point %f %f", 1676 NSStringFromRect(cellFrame), point.x, point.y); 1677 1678 _mouse_down_flags = [theEvent modifierFlags]; 1679 1680 if (![self isEnabled]) 1681 { 1682 return NO; 1683 } 1684 1685 if (![controlView mouse: point inRect: cellFrame]) 1686 { 1687 // point is not in cell 1688 return NO; 1689 } 1690 1691 tracking = [self startTrackingAt: point inView: controlView]; 1692 1693 if (_action_mask & NSEventMaskFromType([theEvent type])) 1694 { 1695 [self _sendActionFrom: controlView]; 1696 } 1697 1698 if ([self isContinuous]) 1699 { 1700 float delay; 1701 float interval; 1702 1703 [self getPeriodicDelay: &delay interval: &interval]; 1704 [NSEvent startPeriodicEventsAfterDelay: delay withPeriod: interval]; 1705 event_mask |= NSPeriodicMask; 1706 } 1707 1708 NSDebugLLog(@"NSCell", @"cell get mouse events\n"); 1709 if (theEvent != [NSApp currentEvent]) 1710 { 1711 theEvent = [NSApp currentEvent]; 1712 } 1713 else 1714 { 1715 theEvent = [theApp nextEventMatchingMask: event_mask 1716 untilDate: [NSDate distantFuture] 1717 inMode: NSEventTrackingRunLoopMode 1718 dequeue: YES]; 1719 } 1720 1721 while (YES) 1722 { 1723 NSEventType eventType; 1724 1725 eventType = [theEvent type]; 1726 1727 // Did the mouse go up? 1728 if (eventType == NSLeftMouseUp) 1729 { 1730 NSDebugLLog(@"NSCell", @"cell mouse went up\n"); 1731 mouseWentUp = YES; 1732 break; 1733 } 1734 else if (eventType == NSPeriodic) 1735 { 1736 NSDebugLLog (@"NSCell", @"cell got a periodic event"); 1737 if (periodCount == 4) 1738 { 1739 NSWindow *w = [controlView window]; 1740 1741 /* 1742 * Too many periodic events in succession - 1743 * update the mouse location and reset the counter. 1744 */ 1745 location = [w mouseLocationOutsideOfEventStream]; 1746 last_point = point; 1747 point = [controlView convertPoint: location fromView: nil]; 1748 periodCount = 0; 1749 } 1750 else 1751 { 1752 periodCount++; 1753 } 1754 } 1755 else 1756 { 1757 location = [theEvent locationInWindow]; 1758 last_point = point; 1759 point = [controlView convertPoint: location fromView: nil]; 1760 } 1761 1762 if (!flag && ![controlView mouse: point inRect: cellFrame]) 1763 { 1764 NSDebugLLog(@"NSCell", @"point not in cell frame\n"); 1765 break; 1766 } 1767 1768 if (tracking) 1769 { 1770 // should continue tracking? 1771 tracking = [self continueTracking: last_point 1772 at: point 1773 inView: controlView]; 1774 NSDebugLLog(@"NSCell", @"cell continue tracking %d\n", tracking); 1775 } 1776 1777 if (_action_mask & NSEventMaskFromType([theEvent type])) 1778 { 1779 [self _sendActionFrom: controlView]; 1780 } 1781 1782 theEvent = [theApp nextEventMatchingMask: event_mask 1783 untilDate: [NSDate distantFuture] 1784 inMode: NSEventTrackingRunLoopMode 1785 dequeue: YES]; 1786 } 1787 1788 if (tracking) 1789 { 1790 // Hook called when stop tracking 1791 [self stopTracking: last_point 1792 at: point 1793 inView: controlView 1794 mouseIsUp: mouseWentUp]; 1795 } 1796 1797 if ([self isContinuous]) 1798 { 1799 [NSEvent stopPeriodicEvents]; 1800 } 1801 1802 if (mouseWentUp) 1803 { 1804 [self setNextState]; 1805 if (_action_mask & NSEventMaskFromType([theEvent type])) 1806 { 1807 [self _sendActionFrom: controlView]; 1808 } 1809 } 1810 1811 // Return YES only if the mouse went up within the cell or flag was true 1812 if (mouseWentUp && (flag || [controlView mouse: point inRect: cellFrame])) 1813 { 1814 NSDebugLLog(@"NSCell", @"mouse went up in cell\n"); 1815 return YES; 1816 } 1817 else 1818 { 1819 // Otherwise return NO 1820 NSDebugLLog(@"NSCell", @"mouse did not go up in cell\n"); 1821 return NO; 1822 } 1823} 1824 1825- (NSUInteger) hitTestForEvent: (NSEvent*)event 1826 inRect: (NSRect)cellFrame 1827 ofView: (NSView*)controlView 1828{ 1829 if (_cell.type == NSImageCellType) 1830 { 1831 if ((_cell_image != nil) && 1832 NSMouseInRect([controlView convertPoint: [event locationInWindow] 1833 fromView: nil], 1834 [self imageRectForBounds: [controlView bounds]], 1835 [controlView isFlipped])) 1836 { 1837 return NSCellHitContentArea; 1838 } 1839 else 1840 { 1841 return NSCellHitNone; 1842 } 1843 } 1844 else if (_cell.type == NSTextCellType) 1845 { 1846 if (_contents == nil) 1847 { 1848 return NSCellHitNone; 1849 } 1850 else if (_cell.is_disabled == NO) 1851 { 1852 return NSCellHitContentArea | NSCellHitEditableTextArea; 1853 } 1854 else 1855 { 1856 return NSCellHitContentArea; 1857 } 1858 } 1859 else 1860 { 1861 if (_cell.is_disabled == NO) 1862 { 1863 return NSCellHitContentArea | NSCellHitTrackableArea; 1864 } 1865 else 1866 { 1867 return NSCellHitContentArea; 1868 } 1869 } 1870} 1871 1872/** <p>TODO</p> 1873 */ 1874- (void) resetCursorRect: (NSRect)cellFrame inView: (NSView*)controlView 1875{ 1876 if (_cell.type == NSTextCellType && _cell.is_disabled == NO 1877 && (_cell.is_selectable == YES || _cell.is_editable == YES)) 1878 { 1879 static NSCursor *cursor = nil; 1880 NSRect rect; 1881 1882 if (cursor== nil) 1883 { 1884 cursor = RETAIN([NSCursor IBeamCursor]); 1885 } 1886 rect = NSIntersectionRect(cellFrame, [controlView visibleRect]); 1887 /* 1888 * Here we depend on an undocumented feature of NSCursor which may or 1889 * may not exist in OPENSTEP or MacOS-X ... 1890 * If we add a cursor rect to a view and don't set it to be set on 1891 * either entry to or exit from the view, we push it on entry and 1892 * pop it from the cursor stack on exit. 1893 */ 1894 [controlView addCursorRect: rect cursor: cursor]; 1895 } 1896} 1897 1898/**<p>Implemented by subclasses to returns the key equivalent. 1899 The NSCell implementation returns an empty NSString. </p> 1900 */ 1901- (NSString*) keyEquivalent 1902{ 1903 return @""; 1904} 1905 1906/**<p>Does nothing. This method is used by subclasses to recalculate sizes</p> 1907 <p>It is usally called from a NSControl object</p> 1908 <p>See Also: [NSControl-calcSize]</p> 1909 */ 1910- (void) calcDrawInfo: (NSRect)aRect 1911{ 1912} 1913 1914/**Returns the minimun size needed to display the NSCell. 1915 This size is calculate by adding : 1916 <list> 1917 <item> the borders (plain or bezeled) size</item> 1918 <item> the spacing between the border and inside the cell</item> 1919 <item> the TODO ... if the cell is type of NSTextCellType 1920 or the image size if the cell has a NSImageCellType type.</item> 1921 </list> 1922 <p>This method returns NSZeroSize if the cell has a NSNullCellType type 1923 (Cocoa returns a very big size instead). 1924 </p> 1925 */ 1926- (NSSize) cellSize 1927{ 1928 NSSize borderSize, s; 1929 NSBorderType aType; 1930 1931 // Get border size 1932 if (_cell.is_bordered) 1933 aType = NSLineBorder; 1934 else if (_cell.is_bezeled) 1935 aType = NSBezelBorder; 1936 else 1937 aType = NSNoBorder; 1938 1939 borderSize = [[GSTheme theme] sizeForBorderType: aType]; 1940 1941 // Add spacing between border and inside 1942 if (_cell.is_bordered || _cell.is_bezeled) 1943 { 1944 borderSize.height += 1; 1945 borderSize.width += 3; 1946 } 1947 1948 // Get Content Size 1949 switch (_cell.type) 1950 { 1951 case NSTextCellType: 1952 { 1953 NSAttributedString *attrStr; 1954 1955 attrStr = [self attributedStringValue]; 1956 if ([attrStr length] != 0) 1957 { 1958 s = [attrStr size]; 1959 } 1960 else 1961 { 1962 s = [self _sizeText: @"A"]; 1963 } 1964 } 1965 break; 1966 1967 case NSImageCellType: 1968 if (_cell_image == nil) 1969 { 1970 s = NSZeroSize; 1971 } 1972 else 1973 { 1974 s = [_cell_image size]; 1975 } 1976 break; 1977 1978 default: 1979 case NSNullCellType: 1980 // macosx instead returns a 'very big size' here; we return NSZeroSize 1981 s = NSZeroSize; 1982 break; 1983 } 1984 1985 // Add in border size 1986 s.width += 2 * borderSize.width; 1987 s.height += 2 * borderSize.height; 1988 1989 return s; 1990} 1991 1992/**<p>TODO. Currently the GNUstep implementation returns -cellSize</p> 1993 <p>See Also: -cellSize</p> 1994 */ 1995- (NSSize) cellSizeForBounds: (NSRect)aRect 1996{ 1997 if (_cell.type == NSTextCellType) 1998 { 1999 // TODO: Resize the text to fit 2000 } 2001 2002 return [self cellSize]; 2003} 2004 2005/**<p>TODO</p> 2006 */ 2007- (NSRect) drawingRectForBounds: (NSRect)theRect 2008{ 2009 NSSize borderSize; 2010 NSBorderType aType; 2011 2012 // Get border size 2013 if (_cell.is_bordered) 2014 aType = NSLineBorder; 2015 else if (_cell.is_bezeled) 2016 aType = NSBezelBorder; 2017 else 2018 aType = NSNoBorder; 2019 2020 borderSize = [[GSTheme theme] sizeForBorderType: aType]; 2021 return NSInsetRect(theRect, borderSize.width, borderSize.height); 2022} 2023 2024/**<p>Frame the image gets drawn in</p> 2025 */ 2026- (NSRect) imageRectForBounds: (NSRect)theRect 2027{ 2028 if (_cell.type == NSImageCellType) 2029 { 2030 NSRect frame = [self drawingRectForBounds: theRect]; 2031 2032 // Add spacing between border and inside 2033 if (_cell.is_bordered || _cell.is_bezeled) 2034 { 2035 frame.origin.x += 3; 2036 frame.size.width -= 6; 2037 frame.origin.y += 1; 2038 frame.size.height -= 2; 2039 } 2040 return frame; 2041 } 2042 else 2043 { 2044 return theRect; 2045 } 2046} 2047 2048/** <p>Frame the title gets drawn in</p> 2049 */ 2050- (NSRect) titleRectForBounds: (NSRect)theRect 2051{ 2052 if (_cell.type == NSTextCellType) 2053 { 2054 NSRect frame = [self drawingRectForBounds: theRect]; 2055 2056 // Add spacing between border and inside 2057 if (_cell.is_bordered || _cell.is_bezeled) 2058 { 2059 frame.origin.x += 3; 2060 frame.size.width -= 6; 2061 frame.origin.y += 1; 2062 frame.size.height -= 2; 2063 } 2064 return frame; 2065 } 2066 else 2067 { 2068 return theRect; 2069 } 2070} 2071 2072- (void) setControlSize: (NSControlSize)controlSize 2073{ 2074 _cell.control_size = controlSize; 2075} 2076 2077- (NSControlSize) controlSize 2078{ 2079 return _cell.control_size; 2080} 2081 2082- (void) setControlTint: (NSControlTint)controlTint 2083{ 2084 _cell.control_tint = controlTint; 2085} 2086 2087- (NSControlTint) controlTint 2088{ 2089 return _cell.control_tint; 2090} 2091 2092/**<p>This method is used by subclasses to get the control view. 2093 This method returns nil.</p> 2094 */ 2095- (NSView*) controlView 2096{ 2097 return nil; 2098} 2099 2100/**<p>This method is used by subclasses to specify the control view.</p> 2101 */ 2102- (void) setControlView: (NSView*)view 2103{ 2104 // Do nothing 2105} 2106 2107/** <p>This drawing is minimal and with no background, 2108 * to make it easier for subclass to customize drawing. </p> 2109 */ 2110- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView 2111{ 2112 switch (_cell.type) 2113 { 2114 case NSTextCellType: 2115 if (_cell.in_editing) 2116 [self _drawEditorWithFrame: cellFrame inView: controlView]; 2117 else 2118 [self _drawAttributedText: [self _drawAttributedString] 2119 inFrame: [self titleRectForBounds: cellFrame]]; 2120 break; 2121 2122 case NSImageCellType: 2123 if (_cell_image) 2124 { 2125 NSSize size; 2126 NSPoint position; 2127 NSRect drawingRect = [self imageRectForBounds: cellFrame]; 2128 NSRect rect; 2129 2130 size = [_cell_image size]; 2131 position.x = MAX(NSMidX(drawingRect) - (size.width/2.),0.); 2132 position.y = MAX(NSMidY(drawingRect) - (size.height/2.),0.); 2133 rect = NSMakeRect(position.x, position.y, size.width, size.height); 2134 2135 if (nil != controlView) 2136 { 2137 rect = [controlView centerScanRect: rect]; 2138 } 2139 2140 [_cell_image drawInRect: rect 2141 fromRect: NSZeroRect 2142 operation: NSCompositeSourceOver 2143 fraction: 1.0 2144 respectFlipped: YES 2145 hints: nil]; 2146 } 2147 break; 2148 2149 case NSNullCellType: 2150 break; 2151 } 2152 2153 // NB: We don't do any highlighting to make it easier for subclasses 2154 // to reuse this code while doing their own custom highlighting and 2155 // prettyfying 2156} 2157 2158/**<p>Draws the cell in <var>controlView</var></p> 2159 */ 2160- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView 2161{ 2162 // do nothing if cell's frame rect is zero 2163 if (NSIsEmptyRect(cellFrame)) 2164 return; 2165 2166 // draw the border if needed 2167 [self _drawBorderAndBackgroundWithFrame: cellFrame inView: controlView]; 2168 // draw interior 2169 [self drawInteriorWithFrame: cellFrame inView: controlView]; 2170 // Draw first responder 2171 [self _drawFocusRingWithFrame: cellFrame inView: controlView]; 2172} 2173 2174/**<p>Sets whether the NSCell is highlighted.</p> 2175 <p>See Also: -isHighlighted</p> 2176 */ 2177- (void) setHighlighted: (BOOL) flag 2178{ 2179 _cell.is_highlighted = flag; 2180} 2181 2182/**<p>Returns whether the cell is highlighted. By default NO</p> 2183 <p>See Also: -setHighlighted:</p> 2184 */ 2185- (BOOL) isHighlighted 2186{ 2187 return _cell.is_highlighted; 2188} 2189 2190/** 2191 *<p>TODO explain</p> 2192 */ 2193 2194- (void) highlight: (BOOL)lit 2195 withFrame: (NSRect)cellFrame 2196 inView: (NSView*)controlView 2197{ 2198 if (_cell.is_highlighted != lit) 2199 { 2200 _cell.is_highlighted = lit; 2201 2202 // Disabling this because when combined with making 2203 // -[NSButtonCell isOpaque] return NO, it was causing scroller buttons 2204 // to stay stuck down. --Eric (2013-09-28) 2205#if 0 2206 /* 2207 * NB: This has a visible effect only if subclasses override 2208 * drawWithFrame:inView: to draw something special when the 2209 * cell is highlighted. 2210 * NSCell simply draws border+text/image and makes no highlighting, 2211 * for easier subclassing. 2212 */ 2213 if ([self isOpaque] == NO) 2214 { 2215 /* FIXME - This looks like potentially generating an 2216 * infinite loop! The control asking the cell to draw 2217 * itself in the rect, the cell asking the control to draw 2218 * the rect, the control asking the cell to draw itself in 2219 * the rect, the cell ... 2220 * 2221 * I think we should remove it. The control is responsible 2222 * for using the cell to draw, not vice versa. 2223 */ 2224 [controlView displayRect: cellFrame]; 2225 } 2226#endif 2227 [self drawWithFrame: cellFrame inView: controlView]; 2228 } 2229} 2230 2231- (NSColor*) highlightColorWithFrame: (NSRect)cellFrame 2232 inView: (NSView *)controlView 2233{ 2234 return [NSColor selectedControlColor]; 2235} 2236 2237- (NSText*) setUpFieldEditorAttributes: (NSText*)textObject 2238{ 2239 NSDictionary *attr; 2240 2241 // Reset the string to have a well defined state. The real string gets set later on. 2242 [textObject setString: @""]; 2243 2244 [textObject setTextColor: [self textColor]]; 2245 if ([self isBezeled]) 2246 { 2247 [textObject setBackgroundColor: [NSColor textBackgroundColor]]; 2248 [textObject setDrawsBackground: YES]; 2249 } 2250 else 2251 { 2252 [textObject setDrawsBackground: NO]; 2253 } 2254 [textObject setFont: [self font]]; 2255 [textObject setAlignment: [self alignment]]; 2256 // FIXME: Add base writing direction 2257 2258 [textObject setEditable: [self isEditable]]; 2259 [textObject setSelectable: [self isSelectable]]; 2260 [textObject setRichText: [self allowsEditingTextAttributes]]; 2261 [textObject setImportsGraphics: [self importsGraphics]]; 2262 [(NSTextView*)textObject setAllowsUndo: [self allowsUndo]]; 2263 attr = [self _nonAutoreleasedTypingAttributes]; 2264 [(NSTextView*)textObject setTypingAttributes: attr]; 2265 RELEASE(attr); 2266 2267 return textObject; 2268} 2269 2270- (void) _setupTextWithFrame: (NSRect)aRect 2271 inView: (NSView*)controlView 2272 editor: (NSText*)textObject 2273 delegate: (id)anObject 2274 range: (NSRange)selection 2275{ 2276 BOOL needsClipView; 2277 BOOL wraps = [self wraps]; 2278 NSTextContainer *ct; 2279 NSSize maxSize; 2280 NSRect titleRect = [self titleRectForBounds: aRect]; 2281 2282 /* We always add a clip view if the cell is editable so that the user 2283 can edit the whole contents even if the cell's contents normally is 2284 clipped. */ 2285 needsClipView = [self isScrollable] || [self isEditable]; 2286 if (needsClipView) 2287 { 2288 NSClipView *cv; 2289 2290 cv = [[NSClipView alloc] initWithFrame: titleRect]; 2291 [cv setDocumentView: textObject]; 2292 [controlView addSubview: cv]; 2293 RELEASE(cv); 2294 } 2295 else 2296 [controlView addSubview: textObject]; 2297 2298 /* Note: The order of statements matters here. We must set the text object's 2299 horizontallyResizable and verticallyResizable attributes before setting 2300 its frame size. Otherwise, the text object's width and/or height might 2301 incorrectly be reduced to zero (since the text object has no contents at 2302 this point) if the text object was resizable by its text container before. 2303 Of course we could also have set the text object's minimum size to the 2304 intended frame size, but then we must update the minimum size whenever 2305 the field editor's frame is changed in -_drawEditorWithFrame:inView:. 2306 Note that the minimum size is not relevant when a clip view is used. */ 2307 [textObject setMinSize: NSZeroSize]; 2308 [textObject setMaxSize: NSMakeSize(1e6, 1e6)]; 2309 [textObject setHorizontallyResizable: needsClipView && !wraps]; 2310 [textObject setVerticallyResizable: needsClipView]; 2311 [textObject setFrame: titleRect]; 2312 if (needsClipView) 2313 [textObject setAutoresizingMask: NSViewWidthSizable + NSViewHeightSizable]; 2314 else 2315 [textObject setAutoresizingMask: NSViewNotSizable]; 2316 2317 /* Note: Order of statements matters again. The heightTracksTextView 2318 and widthTracksTextView container attributes must be set after setting 2319 the horizontallyResizable and verticallyResizable text view attributes 2320 because NSTextView's setter methods include "safety code", which 2321 always updates the container attributes along with the text view 2322 attributes. 2323 FIXME Fix NSTextView to only reset the text container attributes, but 2324 never set them. However note that this may break some sloppily written 2325 code which forgets to set the text container attributes. */ 2326 /* See comments in NSStringDrawing.m about the choice of maximum size. */ 2327 ct = [(NSTextView*)textObject textContainer]; 2328 if (wraps) 2329 maxSize = NSMakeSize(NSWidth(titleRect), 1e6); 2330 else 2331 maxSize = NSMakeSize(1e6, 1e6); 2332 [ct setContainerSize: maxSize]; 2333 [ct setWidthTracksTextView: wraps]; 2334 [ct setHeightTracksTextView: NO]; 2335 2336 [self _updateFieldEditor: textObject]; 2337 2338 [textObject setSelectedRange: selection]; 2339 [textObject scrollRangeToVisible: selection]; 2340 2341 [textObject setDelegate: anObject]; 2342 [[controlView window] makeFirstResponder: textObject]; 2343 _cell.in_editing = YES; 2344} 2345 2346/**<p>Ends any text editing. This method sets the text object's delegate 2347 to nil, and remove the NSClipView and the text object used for editing</p> 2348 <p>See Also: -editWithFrame:inView:editor:delegate:event:</p> 2349 */ 2350- (void) endEditing: (NSText*)textObject 2351{ 2352 NSClipView *clipView; 2353 2354 _cell.in_editing = NO; 2355 [textObject setString: @""]; 2356 [textObject setDelegate: nil]; 2357 2358 clipView = (NSClipView*)[textObject superview]; 2359 if ([clipView isKindOfClass: [NSClipView class]]) 2360 { 2361 [clipView setDocumentView: nil]; 2362 [clipView removeFromSuperview]; 2363 } 2364 else 2365 [textObject removeFromSuperview]; 2366} 2367 2368/* 2369 * Editing Text 2370 */ 2371/** <p>.This method does nothing if a the <var>controlView</var> is nil, 2372 if text object does not exist or if the cell's type is not <ref type="type" 2373 id="NSCellType">NSTextCellType</ref></p> 2374 */ 2375- (void) editWithFrame: (NSRect)aRect 2376 inView: (NSView*)controlView 2377 editor: (NSText*)textObject 2378 delegate: (id)anObject 2379 event: (NSEvent*)theEvent 2380{ 2381 if (!controlView || !textObject || (_cell.type != NSTextCellType)) 2382 return; 2383 2384 [self _setupTextWithFrame: aRect 2385 inView: controlView 2386 editor: textObject 2387 delegate: anObject 2388 range: NSMakeRange(0, 0)]; 2389 2390 if ([theEvent type] == NSLeftMouseDown) 2391 { 2392 [textObject mouseDown: theEvent]; 2393 } 2394} 2395 2396/** <p> This method does nothing if the <var>controlView</var> is nil, 2397 if text object does not exist or if the cell's type is not <ref type="type" 2398 id="NSCellType">NSTextCellType</ref> </p> 2399 */ 2400- (void) selectWithFrame: (NSRect)aRect 2401 inView: (NSView*)controlView 2402 editor: (NSText*)textObject 2403 delegate: (id)anObject 2404 start: (NSInteger)selStart 2405 length: (NSInteger)selLength 2406{ 2407 if (!controlView || !textObject || (_cell.type != NSTextCellType)) 2408 return; 2409 2410 [self _setupTextWithFrame: aRect 2411 inView: controlView 2412 editor: textObject 2413 delegate: anObject 2414 range: NSMakeRange(selStart, selLength)]; 2415} 2416 2417- (BOOL) sendsActionOnEndEditing 2418{ 2419 return _cell.sends_action_on_end_editing; 2420} 2421 2422- (void) setSendsActionOnEndEditing: (BOOL)flag 2423{ 2424 _cell.sends_action_on_end_editing = flag; 2425} 2426 2427- (BOOL) allowsUndo 2428{ 2429 return _cell.allows_undo; 2430} 2431 2432- (void) setAllowsUndo: (BOOL)flag 2433{ 2434 _cell.allows_undo = flag; 2435} 2436 2437/* 2438 * Copying 2439 */ 2440- (id) copyWithZone: (NSZone*)zone 2441{ 2442 NSCell *c = (NSCell*)NSCopyObject (self, 0, zone); 2443 2444 /* Hmmm. */ 2445 c->_contents = [_contents copyWithZone: zone]; 2446 /* Because of performance issues (and because so the doc says) only 2447 pointers to the objects are copied. We need to RETAIN them all 2448 though. */ 2449 _font = TEST_RETAIN (_font); 2450 _object_value = TEST_RETAIN (_object_value); 2451 _menu = TEST_RETAIN (_menu); 2452 _cell_image = TEST_RETAIN (_cell_image); 2453 _formatter = TEST_RETAIN (_formatter); 2454 _represented_object = TEST_RETAIN (_represented_object); 2455 2456 return c; 2457} 2458 2459/* 2460 * NSCoding protocol 2461 */ 2462- (void) encodeWithCoder: (NSCoder*)aCoder 2463{ 2464 if ([aCoder allowsKeyedCoding]) 2465 { 2466 unsigned long cFlags = 0; 2467 unsigned int cFlags2 = 0; 2468 id contents = _contents; 2469 2470 // encode contents 2471 [aCoder encodeObject: contents forKey: @"NSContents"]; 2472 2473 // flags 2474 cFlags |= [self focusRingType]; 2475 cFlags |= [self showsFirstResponder] ? 0x4 : 0; 2476 cFlags |= (_action_mask & NSLeftMouseUpMask) ? 0 : 0x20; 2477 cFlags |= [self wraps] ? 0x40 : 0; 2478 cFlags |= (_action_mask & NSLeftMouseDraggedMask) ? 0x100 : 0; 2479 cFlags |= (_action_mask & NSLeftMouseDownMask) ? 0x40000 : 0; 2480 cFlags |= [self isContinuous] ? 0x80000 : 0; 2481 cFlags |= [self isScrollable] ? 0x100000 : 0; 2482 cFlags |= [self isSelectable] ? 0x200000 : 0; 2483 cFlags |= [self isBezeled] ? 0x400000 : 0; 2484 cFlags |= [self isBordered] ? 0x800000 : 0; 2485 cFlags |= ([self type] << 26); 2486 cFlags |= [self isEditable] ? 0x10000000 : 0; 2487 cFlags |= ([self isEnabled] == NO) ? 0x20000000 : 0; 2488 cFlags |= [self isHighlighted] ? 0x40000000 : 0; 2489 cFlags |= ([self state] == NSOnState) ? 0x80000000 : 0; 2490 [aCoder encodeInt: cFlags forKey: @"NSCellFlags"]; 2491 2492 // flags part 2 2493 cFlags2 |= ([self usesSingleLineMode] ? 0x40 : 0); 2494 cFlags2 |= (([self allowsUndo] == NO) ? 0x1000 : 0); 2495 cFlags2 |= ([self controlTint] << 5); 2496 cFlags2 |= ([self lineBreakMode] << 9); 2497 cFlags2 |= ([self controlSize] << 17); 2498 cFlags2 |= [self sendsActionOnEndEditing] ? 0x400000 : 0; 2499 cFlags2 |= [self allowsMixedState] ? 0x1000000 : 0; 2500 cFlags2 |= [self refusesFirstResponder] ? 0x2000000 : 0; 2501 cFlags2 |= ([self alignment] << 26); 2502 cFlags2 |= [self importsGraphics] ? 0x20000000 : 0; 2503 cFlags2 |= [self allowsEditingTextAttributes] ? 0x40000000 : 0; 2504 [aCoder encodeInt: cFlags2 forKey: @"NSCellFlags2"]; 2505 2506 if (_cell.type == NSTextCellType) 2507 { 2508 // font and formatter. 2509 if ([self font]) 2510 { 2511 [aCoder encodeObject: [self font] forKey: @"NSSupport"]; 2512 } 2513 2514 if ([self formatter]) 2515 { 2516 [aCoder encodeObject: [self formatter] forKey: @"NSFormatter"]; 2517 } 2518 } 2519 else if ([self image]) 2520 { 2521 [aCoder encodeObject: [self image] forKey: @"NSSupport"]; 2522 } 2523 } 2524 else 2525 { 2526 BOOL flag; 2527 unsigned int tmp_int; 2528 2529 [aCoder encodeObject: _contents]; 2530 [aCoder encodeObject: _cell_image]; 2531 [aCoder encodeObject: _font]; 2532 [aCoder encodeObject: _object_value]; 2533 flag = _cell.contents_is_attributed_string; 2534 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2535 flag = _cell.is_highlighted; 2536 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2537 flag = _cell.is_disabled; 2538 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2539 flag = _cell.is_editable; 2540 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2541 flag = _cell.is_rich_text; 2542 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2543 flag = _cell.imports_graphics; 2544 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2545 flag = _cell.shows_first_responder; 2546 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2547 flag = _cell.refuses_first_responder; 2548 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2549 flag = _cell.sends_action_on_end_editing; 2550 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2551 flag = _cell.is_bordered; 2552 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2553 flag = _cell.is_bezeled; 2554 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2555 flag = _cell.is_scrollable; 2556 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2557 flag = _cell.is_selectable; 2558 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2559 // This used to be is_continuous, which has been replaced. 2560 /* Ayers 20.03.2003: But we must continue to encode it for backward 2561 compatibility or current releases will have undefined behavior when 2562 decoding archives (i.e. .gorm files) encoded by this version. */ 2563 flag = [self isContinuous]; 2564 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2565 flag = _cell.allows_mixed_state; 2566 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2567 flag = [self wraps]; 2568 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag]; 2569 tmp_int = _cell.text_align; 2570 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2571 tmp_int = _cell.type; 2572 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2573 tmp_int = _cell.image_position; 2574 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2575 tmp_int = _cell.entry_type; 2576 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2577 // FIXME: State may be -1, why do we encode it as unsigned? 2578 tmp_int = _cell.state; 2579 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2580 tmp_int = _cell.mnemonic_location; 2581 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2582 [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_mouse_down_flags]; 2583 [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_action_mask]; 2584 [aCoder encodeValueOfObjCType: @encode(id) at: &_formatter]; 2585 [aCoder encodeValueOfObjCType: @encode(id) at: &_menu]; 2586 [aCoder encodeValueOfObjCType: @encode(id) at: &_represented_object]; 2587 2588 tmp_int = _cell.allows_undo; 2589 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2590 tmp_int = _cell.line_break_mode; 2591 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2592 tmp_int = _cell.control_tint; 2593 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2594 tmp_int = _cell.control_size; 2595 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2596 tmp_int = _cell.focus_ring_type; 2597 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2598 tmp_int = _cell.base_writing_direction; 2599 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2600 tmp_int = _cell.uses_single_line_mode; 2601 [aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2602 } 2603} 2604 2605- (id) initWithCoder: (NSCoder*)aDecoder 2606{ 2607 if ([aDecoder allowsKeyedCoding]) 2608 { 2609 id contents = [aDecoder decodeObjectForKey: @"NSContents"]; 2610 2611 // initialize based on content... 2612 if ([contents isKindOfClass: [NSString class]]) 2613 { 2614 self = [self initTextCell: contents]; 2615 } 2616 else if ([contents isKindOfClass: [NSImage class]]) 2617 { 2618 self = [self initImageCell: contents]; 2619 } 2620 else 2621 { 2622 self = [self init]; 2623 [self setObjectValue: contents]; 2624 } 2625 2626 if ([aDecoder containsValueForKey: @"NSCellFlags"]) 2627 { 2628 unsigned long cFlags; 2629 NSUInteger mask = 0; 2630 cFlags = [aDecoder decodeIntForKey: @"NSCellFlags"]; 2631 2632 [self setFocusRingType: (cFlags & 0x3)]; 2633 [self setShowsFirstResponder: ((cFlags & 0x4) == 0x4)]; 2634 // This bit flag is the other way around! 2635 if ((cFlags & 0x20) != 0x20) 2636 mask |= NSLeftMouseUpMask; 2637 // This bit flag is the other way around! 2638 [self setWraps: ((cFlags & 0x40) != 0x40)]; 2639 if ((cFlags & 0x100) == 0x100) 2640 mask |= NSLeftMouseDraggedMask; 2641 if ((cFlags & 0x40000) == 0x40000) 2642 mask |= NSLeftMouseDownMask; 2643 if ((cFlags & 0x80000) == 0x80000) 2644 mask |= NSPeriodicMask; 2645 [self sendActionOn: mask]; 2646 [self setScrollable: ((cFlags & 0x100000) == 0x100000)]; 2647 [self setSelectable: ((cFlags & 0x200000) == 0x200000)]; 2648 [self setBezeled: ((cFlags & 0x400000) == 0x400000)]; 2649 [self setBordered: ((cFlags & 0x800000) == 0x800000)]; 2650 [self setType: ((cFlags & 0xC000000) >> 26)]; 2651 [self setEditable: ((cFlags & 0x10000000) == 0x10000000)]; 2652 // This bit flag is the other way around! 2653 [self setEnabled: ((cFlags & 0x20000000) != 0x20000000)]; 2654 [self setHighlighted: ((cFlags & 0x40000000) == 0x40000000)]; 2655 [self setState: ((cFlags & 0x80000000) == 0x80000000) ? NSOnState : NSOffState]; 2656 } 2657 if ([aDecoder containsValueForKey: @"NSCellFlags2"]) 2658 { 2659 int cFlags2; 2660 2661 cFlags2 = [aDecoder decodeIntForKey: @"NSCellFlags2"]; 2662 [self setUsesSingleLineMode: (cFlags2 & 0x40)]; 2663 [self setControlTint: ((cFlags2 & 0xE0) >> 5)]; 2664 [self setLineBreakMode: ((cFlags2 & 0xE00) >> 9)]; 2665 [self setControlSize: ((cFlags2 & 0xE0000) >> 17)]; 2666 [self setSendsActionOnEndEditing: ((cFlags2 & 0x400000) == 0x400000)]; 2667 [self setAllowsMixedState: ((cFlags2 & 0x1000000) == 0x1000000)]; 2668 [self setRefusesFirstResponder: ((cFlags2 & 0x2000000) == 0x2000000)]; 2669 [self setAlignment: ((cFlags2 & 0x1C000000) >> 26)]; 2670 [self setImportsGraphics: ((cFlags2 & 0x20000000) == 0x20000000)]; 2671 [self setAllowsEditingTextAttributes: ((cFlags2 & 0x40000000) == 0x40000000)]; 2672 } 2673 if ([aDecoder containsValueForKey: @"NSSupport"]) 2674 { 2675 id support = [aDecoder decodeObjectForKey: @"NSSupport"]; 2676 2677 if ([support isKindOfClass: [NSFont class]]) 2678 { 2679 [self setFont: support]; 2680 } 2681 else if ([support isKindOfClass: [NSImage class]]) 2682 { 2683 [self setImage: support]; 2684 } 2685 } 2686 if ([aDecoder containsValueForKey: @"NSFormatter"]) 2687 { 2688 NSFormatter *formatter = [aDecoder decodeObjectForKey: @"NSFormatter"]; 2689 2690 [self setFormatter: formatter]; 2691 } 2692 } 2693 else 2694 { 2695 BOOL flag, wraps; 2696 unsigned int tmp_int; 2697 id formatter, menu; 2698 int version = [aDecoder versionForClassName: @"NSCell"]; 2699 2700 [aDecoder decodeValueOfObjCType: @encode(id) at: &_contents]; 2701 [aDecoder decodeValueOfObjCType: @encode(id) at: &_cell_image]; 2702 [aDecoder decodeValueOfObjCType: @encode(id) at: &_font]; 2703 [aDecoder decodeValueOfObjCType: @encode(id) at: &_object_value]; 2704 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2705 _cell.contents_is_attributed_string = flag; 2706 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2707 _cell.is_highlighted = flag; 2708 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2709 _cell.is_disabled = flag; 2710 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2711 _cell.is_editable = flag; 2712 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2713 _cell.is_rich_text = flag; 2714 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2715 _cell.imports_graphics = flag; 2716 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2717 _cell.shows_first_responder = flag; 2718 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2719 _cell.refuses_first_responder = flag; 2720 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2721 _cell.sends_action_on_end_editing = flag; 2722 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2723 _cell.is_bordered = flag; 2724 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2725 _cell.is_bezeled = flag; 2726 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2727 _cell.is_scrollable = flag; 2728 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2729 _cell.is_selectable = flag; 2730 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2731 // This used to be is_continuous, which has been replaced. 2732 //_cell.is_continuous = flag; 2733 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2734 _cell.allows_mixed_state = flag; 2735 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag]; 2736 /* The wraps attribute has been superseded by lineBreakMode. However, 2737 we may need it to set lineBreakMode when reading old archives. */ 2738 wraps = flag; 2739 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2740 _cell.text_align = tmp_int; 2741 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2742 _cell.type = tmp_int; 2743 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2744 _cell.image_position = tmp_int; 2745 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2746 _cell.entry_type = tmp_int; 2747 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2748 _cell.state = tmp_int; 2749 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2750 _cell.mnemonic_location = tmp_int; 2751 [aDecoder decodeValueOfObjCType: @encode(NSUInteger) 2752 at: &_mouse_down_flags]; 2753 [aDecoder decodeValueOfObjCType: @encode(NSUInteger) at: &_action_mask]; 2754 if (version < 3) 2755 { 2756 unsigned int mask = 0; 2757 2758 // Convert old GNUstep mask value to Cocoa values 2759 if ((_action_mask & 0x1) == 0x1) 2760 { 2761 mask |= NSLeftMouseDownMask; 2762 } 2763 if ((_action_mask & 0x2) == 0x2) 2764 { 2765 mask |= NSLeftMouseUpMask; 2766 } 2767 if ((_action_mask & 0x4) == 0x4) 2768 { 2769 mask |= NSOtherMouseDownMask; 2770 } 2771 if ((_action_mask & 0x8) == 0x8) 2772 { 2773 mask |= NSOtherMouseUpMask; 2774 } 2775 if ((_action_mask & 0x10) == 0x10) 2776 { 2777 mask |= NSRightMouseDownMask; 2778 } 2779 if ((_action_mask & 0x20) == 0x20) 2780 { 2781 mask |= NSRightMouseUpMask; 2782 } 2783 if ((_action_mask & 0x40) == 0x40) 2784 { 2785 mask |= NSMouseMovedMask; 2786 } 2787 if ((_action_mask & 0x80) == 0x80) 2788 { 2789 mask |= NSLeftMouseDraggedMask; 2790 } 2791 if ((_action_mask & 0x100) == 0x100) 2792 { 2793 mask |= NSOtherMouseDraggedMask; 2794 } 2795 if ((_action_mask & 0x200) == 0x200) 2796 { 2797 mask |= NSRightMouseDraggedMask; 2798 } 2799 if ((_action_mask & 0x400) == 0x400) 2800 { 2801 mask |= NSMouseEnteredMask; 2802 } 2803 if ((_action_mask & 0x800) == 0x800) 2804 { 2805 mask |= NSMouseExitedMask; 2806 } 2807 if ((_action_mask & 0x1000) == 0x1000) 2808 { 2809 mask |= NSKeyDownMask; 2810 } 2811 if ((_action_mask & 0x2000) == 0x2000) 2812 { 2813 mask |= NSKeyUpMask; 2814 } 2815 if ((_action_mask & 0x4000) == 0x4000) 2816 { 2817 mask |= NSFlagsChangedMask; 2818 } 2819 if ((_action_mask & 0x8000) == 0x8000) 2820 { 2821 mask |= NSAppKitDefinedMask; 2822 } 2823 if ((_action_mask & 0x10000) == 0x10000) 2824 { 2825 mask |= NSSystemDefinedMask; 2826 } 2827 if ((_action_mask & 0x20000) == 0x20000) 2828 { 2829 mask |= NSApplicationDefinedMask; 2830 } 2831 if ((_action_mask & 0x40000) == 0x40000) 2832 { 2833 mask |= NSPeriodicMask; 2834 } 2835 if ((_action_mask & 0x80000) == 0x80000) 2836 { 2837 mask |= NSCursorUpdateMask; 2838 } 2839 if ((_action_mask & 0x100000) == 0x100000) 2840 { 2841 mask |= NSScrollWheelMask; 2842 } 2843 _action_mask = mask; 2844 } 2845 _action_mask |= NSLeftMouseUpMask; 2846 [aDecoder decodeValueOfObjCType: @encode(id) at: &formatter]; 2847 [self setFormatter: formatter]; 2848 [aDecoder decodeValueOfObjCType: @encode(id) at: &menu]; 2849 [self setMenu: menu]; 2850 [aDecoder decodeValueOfObjCType: @encode(id) at: &_represented_object]; 2851 2852 if (_formatter != nil) 2853 { 2854 NSString *contents; 2855 2856 contents = [_formatter stringForObjectValue: _object_value]; 2857 if (contents != nil) 2858 { 2859 _cell.has_valid_object_value = YES; 2860 ASSIGN (_contents, contents); 2861 _cell.contents_is_attributed_string = NO; 2862 } 2863 } 2864 2865 if (version >= 2) 2866 { 2867 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2868 _cell.allows_undo = tmp_int; 2869 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2870 _cell.line_break_mode = tmp_int; 2871 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2872 _cell.control_tint = tmp_int; 2873 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2874 _cell.control_size = tmp_int; 2875 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2876 _cell.focus_ring_type = tmp_int; 2877 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2878 _cell.base_writing_direction = tmp_int; 2879 } 2880 else 2881 { 2882 /* Backward compatibility: Derive lineBreakMode from the superseded 2883 wraps attribute. */ 2884 [self setWraps: wraps]; 2885 } 2886 2887 if (version >= 4) 2888 { 2889 [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int]; 2890 _cell.uses_single_line_mode = tmp_int; 2891 } 2892 2893 } 2894 return self; 2895} 2896 2897- (NSUserInterfaceLayoutDirection) userInterfaceLayoutDirection 2898{ 2899 // FIXME 2900 return NSUserInterfaceLayoutDirectionLeftToRight; 2901} 2902 2903- (void) setUserInterfaceLayoutDirection: (NSUserInterfaceLayoutDirection)dir 2904{ 2905 // FIXME: implement this 2906 return; 2907} 2908 2909- (void) setUsesSingleLineMode: (BOOL)flag 2910{ 2911 _cell.uses_single_line_mode = flag; 2912} 2913 2914- (BOOL) usesSingleLineMode 2915{ 2916 return _cell.uses_single_line_mode; 2917} 2918 2919@end 2920 2921@implementation NSCell (PrivateMethods) 2922 2923- (NSColor*) textColor 2924{ 2925 if (_cell.is_disabled) 2926 return dtxtCol; 2927 else 2928 return txtCol; 2929} 2930 2931/* This method is an exception and returns a non-autoreleased 2932 dictionary, so that calling methods can deallocate it immediately 2933 using release. Otherwise if many cells are drawn/their size 2934 computed, we pile up hundreds or thousands of these objects before they 2935 are deallocated at the end of the run loop. */ 2936- (NSDictionary*) _nonAutoreleasedTypingAttributes 2937{ 2938 NSDictionary *attr; 2939 NSColor *color; 2940 NSMutableParagraphStyle *paragraphStyle; 2941 2942 color = [self textColor]; 2943 /* Note: There are only a few possible paragraph styles for cells. 2944 TODO: Cache them and reuse them for the whole app lifetime. */ 2945 paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 2946 [paragraphStyle setLineBreakMode: [self lineBreakMode]]; 2947 [paragraphStyle setBaseWritingDirection: [self baseWritingDirection]]; 2948 [paragraphStyle setAlignment: [self alignment]]; 2949 2950 attr = [[NSDictionary alloc] initWithObjectsAndKeys: 2951 _font, NSFontAttributeName, 2952 color, NSForegroundColorAttributeName, 2953 paragraphStyle, NSParagraphStyleAttributeName, 2954 nil]; 2955 RELEASE (paragraphStyle); 2956 return attr; 2957} 2958 2959- (NSSize) _sizeText: (NSString*)title 2960{ 2961 NSSize size; 2962 NSDictionary *dict; 2963 2964 if (title == nil) 2965 { 2966 return NSMakeSize (0,0); 2967 } 2968 2969 dict = [self _nonAutoreleasedTypingAttributes]; 2970 size = [title sizeWithAttributes: dict]; 2971 RELEASE (dict); 2972 return size; 2973} 2974 2975/** 2976 * Private internal method, returns an attributed string to display. 2977 */ 2978- (NSAttributedString*) _drawAttributedString 2979{ 2980 if (!_cell.is_disabled) 2981 { 2982 return [self attributedStringValue]; 2983 } 2984 else 2985 { 2986 NSAttributedString *attrStr = [self attributedStringValue]; 2987 NSDictionary *attribs; 2988 NSMutableDictionary *newAttribs; 2989 2990 attribs = [attrStr attributesAtIndex: 0 2991 effectiveRange: NULL]; 2992 newAttribs = [NSMutableDictionary dictionaryWithDictionary: attribs]; 2993 [newAttribs setObject: [NSColor disabledControlTextColor] 2994 forKey: NSForegroundColorAttributeName]; 2995 2996 return AUTORELEASE([[NSAttributedString alloc] 2997 initWithString: [attrStr string] 2998 attributes: newAttribs]); 2999 } 3000} 3001 3002 3003- (BOOL) _shouldShortenStringForRect: (NSRect)titleRect size: (NSSize)titleSize length: (NSUInteger)length 3004{ 3005 NSLineBreakMode mode = [self lineBreakMode]; 3006 3007 return ((titleSize.width > titleRect.size.width) && (length > 4) && 3008 (mode == NSLineBreakByTruncatingHead || 3009 mode == NSLineBreakByTruncatingTail || 3010 mode == NSLineBreakByTruncatingMiddle)); 3011} 3012 3013- (NSAttributedString*) _resizeAttributedString: (NSAttributedString*)attrstring forRect: (NSRect)titleRect 3014{ 3015 // Redo string based on selected truncation mask... 3016 NSMutableAttributedString *mutableString = AUTORELEASE([attrstring mutableCopy]); 3017 NSString *ellipsis = @"..."; 3018 NSLineBreakMode mode = [self lineBreakMode]; 3019 // This code shortens the string one character at a time. 3020 // To speed it up we start off proportional: 3021 CGFloat width = [mutableString size].width; 3022 int cut = MAX(floor([mutableString length] * (width - titleRect.size.width) / width), 4); 3023 3024 do 3025 { 3026 NSRange replaceRange; 3027 if (mode == NSLineBreakByTruncatingHead) 3028 replaceRange = NSMakeRange(0, cut); 3029 else if (mode == NSLineBreakByTruncatingTail) 3030 replaceRange = NSMakeRange([mutableString length] - cut, cut); 3031 else 3032 replaceRange = NSMakeRange(([mutableString length] / 2) - (cut / 2), cut); 3033 [mutableString replaceCharactersInRange: replaceRange withString: ellipsis]; 3034 cut = 4; 3035 } while ([mutableString length] > 4 && [mutableString size].width > titleRect.size.width); 3036 3037 // Return the modified attributed string... 3038 return mutableString; 3039} 3040 3041/** 3042 * Private internal method to display an attributed string. 3043 */ 3044- (void) _drawAttributedText: (NSAttributedString*)aString 3045 inFrame: (NSRect)aRect 3046{ 3047 NSSize titleSize; 3048 3049 if (aString == nil) 3050 return; 3051 3052 titleSize = [aString size]; 3053 if ([self _shouldShortenStringForRect: aRect size: titleSize length: [aString length]]) 3054 { 3055 aString = [self _resizeAttributedString: aString forRect: aRect]; 3056 titleSize = [aString size]; 3057 } 3058 3059 /** Important: text should always be vertically centered without 3060 * considering descender [as if descender did not exist]. 3061 * This is particularly important for single line texts. 3062 * Please make sure the output remains always correct. 3063 */ 3064 aRect.origin.y = NSMidY (aRect) - titleSize.height/2; 3065 aRect.size.height = titleSize.height; 3066 3067 [aString drawInRect: aRect]; 3068} 3069 3070- (void) _drawText: (NSString*)aString inFrame: (NSRect)cellFrame 3071{ 3072 NSSize titleSize; 3073 NSDictionary *attributes; 3074 3075 if (aString == nil) 3076 return; 3077 3078 attributes = [self _nonAutoreleasedTypingAttributes]; 3079 titleSize = [aString sizeWithAttributes: attributes]; 3080 if ([self _shouldShortenStringForRect: cellFrame size: titleSize length: [aString length]]) 3081 { 3082 NSAttributedString *attrstring = AUTORELEASE([[NSAttributedString alloc] initWithString: aString 3083 attributes: attributes]); 3084 return [self _drawAttributedText: attrstring inFrame: cellFrame]; 3085 } 3086 3087 /** Important: text should always be vertically centered without 3088 * considering descender [as if descender did not exist]. 3089 * This is particularly important for single line texts. 3090 * Please make sure the output remains always correct. 3091 */ 3092 cellFrame.origin.y = NSMidY (cellFrame) - titleSize.height/2; 3093 cellFrame.size.height = titleSize.height; 3094 3095 [aString drawInRect: cellFrame withAttributes: attributes]; 3096 RELEASE (attributes); 3097} 3098 3099// Private helper method overridden in subclasses 3100- (void) _drawBorderAndBackgroundWithFrame: (NSRect)cellFrame 3101 inView: (NSView*)controlView 3102{ 3103 NSBorderType aType; 3104 3105 // Get border size 3106 if (_cell.is_bordered) 3107 aType = NSLineBorder; 3108 else if (_cell.is_bezeled) 3109 aType = NSBezelBorder; 3110 else 3111 aType = NSNoBorder; 3112 3113 [[GSTheme theme] drawBorderType: aType frame: cellFrame view: controlView]; 3114} 3115 3116// Private helper method 3117- (void) _drawFocusRingWithFrame: (NSRect)cellFrame inView: (NSView*)controlView 3118{ 3119 if (_cell.shows_first_responder 3120 && [[controlView window] firstResponder] == controlView) 3121 { 3122 switch (_cell.focus_ring_type) 3123 { 3124 case NSFocusRingTypeDefault: 3125 [[GSTheme theme] drawFocusFrame: [self drawingRectForBounds: 3126 cellFrame] 3127 view: controlView]; 3128 break; 3129 case NSFocusRingTypeExterior: 3130 [[GSTheme theme] drawFocusFrame: cellFrame 3131 view: controlView]; 3132 break; 3133 case NSFocusRingTypeNone: 3134 default: 3135 break; 3136 } 3137 } 3138} 3139 3140- (void) _drawEditorWithFrame: (NSRect)cellFrame 3141 inView: (NSView *)controlView 3142{ 3143 /* Look Ma', no drawing here... */ 3144 3145 if ([controlView isKindOfClass: [NSControl class]]) 3146 { 3147 /* Adjust the text editor's frame to match cell's frame (w/o border) */ 3148 NSRect titleRect = [self titleRectForBounds: cellFrame]; 3149 NSText *textObject = [(NSControl*)controlView currentEditor]; 3150 NSView *clipView = [textObject superview]; 3151 3152 if ([clipView isKindOfClass: [NSClipView class]]) 3153 { 3154 [clipView setFrame: titleRect]; 3155 } 3156 else 3157 { 3158 [textObject setFrame: titleRect]; 3159 } 3160 } 3161} 3162 3163- (BOOL) _sendsActionOn:(NSUInteger)eventTypeMask 3164{ 3165 return (_action_mask & eventTypeMask); 3166} 3167 3168- (void) _setInEditing: (BOOL)flag 3169{ 3170 _cell.in_editing = flag; 3171} 3172 3173- (BOOL) _inEditing 3174{ 3175 return _cell.in_editing; 3176} 3177 3178- (void) _updateFieldEditor: (NSText*)textObject 3179{ 3180 if (_formatter != nil) 3181 { 3182 NSString *contents; 3183 3184 contents = [_formatter editingStringForObjectValue: _object_value]; 3185 if (contents == nil) 3186 { 3187 // We cannot call the stringValue method as this will call 3188 // validateEditing in NSActionCell subclasses 3189 if (nil == _contents) 3190 { 3191 contents = @""; 3192 } 3193 else 3194 { 3195 if (_cell.contents_is_attributed_string == NO) 3196 { 3197 contents = (NSString *)_contents; 3198 } 3199 else 3200 { 3201 contents = [(NSAttributedString *)_contents string]; 3202 } 3203 } 3204 } 3205 if (![contents isEqualToString: [textObject string]]) 3206 [textObject setText: contents]; 3207 } 3208 else 3209 { 3210 NSString *contents; 3211 3212 if (nil == _contents) 3213 { 3214 contents = @""; 3215 } 3216 else 3217 { 3218 if (_cell.contents_is_attributed_string == NO) 3219 { 3220 contents = (NSString *)_contents; 3221 } 3222 else 3223 { 3224 contents = [(NSAttributedString *)_contents string]; 3225 } 3226 } 3227 if (![contents isEqualToString: [textObject string]]) 3228 { 3229 if (_cell.contents_is_attributed_string == NO) 3230 { 3231 [textObject setText: contents]; 3232 } 3233 else 3234 { 3235 // FIXME what about attribute changes? 3236 NSRange range = NSMakeRange(0, [[textObject string] length]); 3237 [textObject replaceCharactersInRange: range 3238 withAttributedString: (NSAttributedString *)_contents]; 3239 } 3240 } 3241 } 3242} 3243 3244/** 3245 * Private method used by NSImageCell and NSButtonCell for calculating 3246 * scaled image size 3247 */ 3248 3249static inline NSSize 3250scaleProportionally(NSSize imageSize, NSSize canvasSize, BOOL scaleUpOrDown) 3251{ 3252 CGFloat ratio; 3253 3254 if (imageSize.width <= 0 3255 || imageSize.height <= 0) 3256 { 3257 return NSMakeSize(0, 0); 3258 } 3259 3260 /* Get the smaller ratio and scale the image size by it. */ 3261 ratio = MIN(canvasSize.width / imageSize.width, 3262 canvasSize.height / imageSize.height); 3263 3264 /* Only scale down, unless scaleUpOrDown is YES */ 3265 if (ratio < 1.0 || scaleUpOrDown) 3266 { 3267 imageSize.width *= ratio; 3268 imageSize.height *= ratio; 3269 } 3270 3271 return imageSize; 3272} 3273 3274- (NSSize) _scaleImageWithSize: (NSSize)imageSize 3275 toFitInSize: (NSSize)canvasSize 3276 scalingType: (NSImageScaling)scalingType 3277{ 3278 NSSize result; 3279 switch (scalingType) 3280 { 3281 case NSImageScaleProportionallyDown: // == NSScaleProportionally 3282 { 3283 result = scaleProportionally (imageSize, canvasSize, NO); 3284 break; 3285 } 3286 case NSImageScaleAxesIndependently: // == NSScaleToFit 3287 { 3288 result = canvasSize; 3289 break; 3290 } 3291 default: 3292 case NSImageScaleNone: // == NSScaleNone 3293 { 3294 result = imageSize; 3295 break; 3296 } 3297 case NSImageScaleProportionallyUpOrDown: 3298 { 3299 result = scaleProportionally (imageSize, canvasSize, YES); 3300 break; 3301 } 3302 } 3303 return result; 3304} 3305 3306@end 3307