1/** <title>NSView</title> 2 3 <abstract>The view class which encapsulates all drawing functionality</abstract> 4 5 Copyright (C) 1996 Free Software Foundation, Inc. 6 7 Author: Scott Christley <scottc@net-community.com> 8 Date: 1996 9 Author: Ovidiu Predescu <ovidiu@net-community.com> 10 Date: 1997 Author: Felipe A. Rodriguez <far@ix.netcom.com> 11 Date: August 1998 12 Author: Richard Frith-Macdonald <richard@brainstorm.co.uk> 13 Date: January 1999 14 15 This file is part of the GNUstep GUI Library. 16 17 This library is free software; you can redistribute it and/or 18 modify it under the terms of the GNU Lesser General Public 19 License as published by the Free Software Foundation; either 20 version 2 of the License, or (at your option) any later version. 21 22 This library is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25 Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public 28 License along with this library; see the file COPYING.LIB. 29 If not, see <http://www.gnu.org/licenses/> or write to the 30 Free Software Foundation, 51 Franklin Street, Fifth Floor, 31 Boston, MA 02110-1301, USA. 32*/ 33 34#import "config.h" 35#include <math.h> 36#include <float.h> 37 38#import <Foundation/NSString.h> 39#import <Foundation/NSBundle.h> 40#import <Foundation/NSCalendarDate.h> 41#import <Foundation/NSCoder.h> 42#import <Foundation/NSKeyedArchiver.h> 43#import <Foundation/NSDictionary.h> 44#import <Foundation/NSThread.h> 45#import <Foundation/NSLock.h> 46#import <Foundation/NSArray.h> 47#import <Foundation/NSNotification.h> 48#import <Foundation/NSValue.h> 49#import <Foundation/NSData.h> 50#import <Foundation/NSDebug.h> 51#import <Foundation/NSPathUtilities.h> 52#import <Foundation/NSSet.h> 53 54#import "AppKit/NSAffineTransform.h" 55#import "AppKit/NSApplication.h" 56#import "AppKit/NSBezierPath.h" 57#import "AppKit/NSBitmapImageRep.h" 58#import "AppKit/NSCursor.h" 59#import "AppKit/NSDocumentController.h" 60#import "AppKit/NSDocument.h" 61#import "AppKit/NSClipView.h" 62#import "AppKit/NSFont.h" 63#import "AppKit/NSGraphics.h" 64#import "AppKit/NSKeyValueBinding.h" 65#import "AppKit/NSMenu.h" 66#import "AppKit/NSPasteboard.h" 67#import "AppKit/NSPrintInfo.h" 68#import "AppKit/NSPrintOperation.h" 69#import "AppKit/NSScrollView.h" 70#import "AppKit/NSView.h" 71#import "AppKit/NSWindow.h" 72#import "AppKit/NSWorkspace.h" 73#import "AppKit/PSOperators.h" 74#import "GNUstepGUI/GSDisplayServer.h" 75#import "GNUstepGUI/GSTrackingRect.h" 76#import "GNUstepGUI/GSNibLoading.h" 77#import "GSToolTips.h" 78#import "GSBindingHelpers.h" 79#import "GSGuiPrivate.h" 80#import "NSViewPrivate.h" 81 82/* 83 * We need a fast array that can store objects without retain/release ... 84 */ 85#define GSI_ARRAY_TYPES GSUNION_OBJ 86#define GSI_ARRAY_NO_RELEASE 1 87#define GSI_ARRAY_NO_RETAIN 1 88 89#ifdef GSIArray 90#undef GSIArray 91#endif 92#include <GNUstepBase/GSIArray.h> 93 94#define nKV(O) ((GSIArray)(O->_nextKeyView)) 95#define pKV(O) ((GSIArray)(O->_previousKeyView)) 96 97/* Variable tells this view and subviews that we're printing. Not really 98 a class variable because we want it visible to subviews also 99*/ 100NSView *viewIsPrinting = nil; 101 102/** 103 <unit> 104 <heading>NSView</heading> 105 106 <p>NSView is an abstract class which provides facilities for drawing 107 in a window and receiving events. It is the superclass of many of 108 the visual elements of the GUI.</p> 109 110 <p>In order to display itself, a view must be placed in a window 111 (represented by an NSWindow object). Within the window is a 112 hierarchy of NSViews, headed by the window's content view. Every 113 other view in a window is a descendant of this view.</p> 114 115 <p>Subclasses can override -drawRect: in order to 116 implement their appearance. Other methods of NSView and NSResponder 117 can also be overridden to handle user generated events.</p> 118 119 </unit> 120*/ 121 122@implementation NSView 123 124/* 125 * Class variables */ 126static Class rectClass; 127static Class viewClass; 128 129static NSAffineTransform *flip = nil; 130 131static NSNotificationCenter *nc = nil; 132 133static SEL preSel; 134static SEL invalidateSel; 135 136static void (*preImp)(NSAffineTransform*, SEL, NSAffineTransform*); 137static void (*invalidateImp)(NSView*, SEL); 138 139/* 140 * Stuff to maintain a map table so we know what views are 141 * registered for drag and drop - we don't store the info in 142 * the view directly 'cot it would take up a pointer in each 143 * view and the vast majority of views wouldn't use it. 144 * Types are not registered/unregistered often enough for the 145 * performance of this mechanism to be an issue. 146 */ 147static NSMapTable *typesMap = 0; 148static NSLock *typesLock = nil; 149 150/* 151 * This is the only external interface to the drag types info. 152 */ 153NSArray* 154GSGetDragTypes(NSView *obj) 155{ 156 NSArray *t; 157 158 [typesLock lock]; 159 t = (NSArray*)NSMapGet(typesMap, (void*)(gsaddr)obj); 160 [typesLock unlock]; 161 return t; 162} 163 164static void 165GSRemoveDragTypes(NSView* obj) 166{ 167 [typesLock lock]; 168 NSMapRemove(typesMap, (void*)(gsaddr)obj); 169 [typesLock unlock]; 170} 171 172static NSArray* 173GSSetDragTypes(NSView* obj, NSArray *types) 174{ 175 NSUInteger count = [types count]; 176 NSString *strings[count]; 177 NSArray *t; 178 NSUInteger i; 179 180 /* 181 * Make a new array with copies of the type strings so we don't get 182 * them mutated by someone else. 183 */ 184 [types getObjects: strings]; 185 for (i = 0; i < count; i++) 186 { 187 strings[i] = [strings[i] copy]; 188 } 189 t = [NSArray arrayWithObjects: strings count: count]; 190 for (i = 0; i < count; i++) 191 { 192 RELEASE(strings[i]); 193 } 194 /* 195 * Store it. 196 */ 197 [typesLock lock]; 198 NSMapInsert(typesMap, (void*)(gsaddr)obj, (void*)(gsaddr)t); 199 [typesLock unlock]; 200 return t; 201} 202 203 204/* 205 * Private methods. 206 */ 207 208 209/* 210 * The [-_invalidateCoordinates] method marks the coordinate mapping 211 * matrices (matrixFromWindow and _matrixToWindow) and the cached visible 212 * rectangle as invalid. It recursively invalidates the coordinates for 213 * all subviews as well. 214 * This method must be called whenever the size, shape or position of 215 * the view is changed in any way. 216 */ 217- (void) _invalidateCoordinates 218{ 219 if (_coordinates_valid == YES) 220 { 221 NSUInteger count; 222 223 _coordinates_valid = NO; 224 if (_rFlags.valid_rects != 0) 225 { 226 [_window invalidateCursorRectsForView: self]; 227 } 228 if (_rFlags.has_subviews) 229 { 230 count = [_sub_views count]; 231 if (count > 0) 232 { 233 NSView* array[count]; 234 NSUInteger i; 235 236 [_sub_views getObjects: array]; 237 for (i = 0; i < count; i++) 238 { 239 NSView *sub = array[i]; 240 241 if (sub->_coordinates_valid == YES) 242 { 243 (*invalidateImp)(sub, invalidateSel); 244 } 245 } 246 } 247 } 248 [self renewGState]; 249 } 250} 251 252/* 253 * The [-_matrixFromWindow] method returns a matrix that can be used to 254 * map coordinates in the windows coordinate system to coordinates in the 255 * views coordinate system. It rebuilds the mapping matrices and 256 * visible rectangle cache if necessary. 257 * All coordinate transformations use this matrix. 258 */ 259- (NSAffineTransform*) _matrixFromWindow 260{ 261 [self _rebuildCoordinates]; 262 return _matrixFromWindow; 263} 264 265/* 266 * The [-_matrixToWindow] method returns a matrix that can be used to 267 * map coordinates in the views coordinate system to coordinates in the 268 * windows coordinate system. It rebuilds the mapping matrices and 269 * visible rectangle cache if necessary. 270 * All coordinate transformations use this matrix. 271 */ 272- (NSAffineTransform*) _matrixToWindow 273{ 274 [self _rebuildCoordinates]; 275 return _matrixToWindow; 276} 277 278/* 279 * The [-_rebuildCoordinates] method rebuilds the coordinate mapping 280 * matrices (matrixFromWindow and _matrixToWindow) and the cached visible 281 * rectangle if they have been invalidated. 282 */ 283- (void) _rebuildCoordinates 284{ 285 BOOL isFlipped = [self isFlipped]; 286 BOOL lastFlipped = _rFlags.flipped_view; 287 288 if ((_coordinates_valid == NO) || (isFlipped != lastFlipped)) 289 { 290 _coordinates_valid = YES; 291 _rFlags.flipped_view = isFlipped; 292 293 if (!_window && !_super_view) 294 { 295 _visibleRect = _bounds; 296 [_matrixToWindow makeIdentityMatrix]; 297 [_matrixFromWindow makeIdentityMatrix]; 298 } 299 else 300 { 301 NSRect superviewsVisibleRect; 302 BOOL superFlipped; 303 NSAffineTransform *pMatrix; 304 NSAffineTransformStruct ts; 305 306 if (_super_view != nil) 307 { 308 superFlipped = [_super_view isFlipped]; 309 pMatrix = [_super_view _matrixToWindow]; 310 } 311 else 312 { 313 superFlipped = NO; 314 pMatrix = [NSAffineTransform transform]; 315 } 316 317 ts = [pMatrix transformStruct]; 318 319 /* prepend translation */ 320 ts.tX = NSMinX(_frame) * ts.m11 + NSMinY(_frame) * ts.m21 + ts.tX; 321 ts.tY = NSMinX(_frame) * ts.m12 + NSMinY(_frame) * ts.m22 + ts.tY; 322 [_matrixToWindow setTransformStruct: ts]; 323 324 /* prepend rotation */ 325 if (_frameMatrix != nil) 326 { 327 (*preImp)(_matrixToWindow, preSel, _frameMatrix); 328 } 329 330 if (isFlipped != superFlipped) 331 { 332 /* 333 * The flipping process must result in a coordinate system that 334 * exactly overlays the original. To do that, we must translate 335 * the origin by the height of the view. 336 */ 337 ts = [flip transformStruct]; 338 ts.tY = _frame.size.height; 339 [flip setTransformStruct: ts]; 340 (*preImp)(_matrixToWindow, preSel, flip); 341 } 342 if (_boundsMatrix != nil) 343 { 344 (*preImp)(_matrixToWindow, preSel, _boundsMatrix); 345 } 346 ts = [_matrixToWindow transformStruct]; 347 [_matrixFromWindow setTransformStruct: ts]; 348 [_matrixFromWindow invert]; 349 350 if (_super_view != nil) 351 { 352 superviewsVisibleRect = [self convertRect: [_super_view visibleRect] 353 fromView: _super_view]; 354 355 _visibleRect = NSIntersectionRect(superviewsVisibleRect, _bounds); 356 } 357 else 358 { 359 _visibleRect = _bounds; 360 } 361 } 362 } 363} 364 365- (void) _viewDidMoveToWindow 366{ 367 [self viewDidMoveToWindow]; 368 if (_rFlags.has_subviews) 369 { 370 NSUInteger count = [_sub_views count]; 371 372 if (count > 0) 373 { 374 NSUInteger i; 375 NSView *array[count]; 376 377 [_sub_views getObjects: array]; 378 for (i = 0; i < count; ++i) 379 { 380 [array[i] _viewDidMoveToWindow]; 381 } 382 } 383 } 384} 385 386- (void) _viewWillMoveToWindow: (NSWindow*)newWindow 387{ 388 BOOL old_allocate_gstate; 389 390 [self viewWillMoveToWindow: newWindow]; 391 if (_coordinates_valid) 392 { 393 (*invalidateImp)(self, invalidateSel); 394 } 395 if (_rFlags.has_currects != 0) 396 { 397 [self discardCursorRects]; 398 } 399 400 if (newWindow == _window) 401 { 402 return; 403 } 404 405 // This call also reset _allocate_gstate, so we have 406 // to store this value and set it again. 407 // This way we keep the logic in one place. 408 old_allocate_gstate = _allocate_gstate; 409 [self releaseGState]; 410 _allocate_gstate = old_allocate_gstate; 411 412 if (_rFlags.has_draginfo) 413 { 414 NSArray *t = GSGetDragTypes(self); 415 416 if (_window != nil) 417 { 418 [GSDisplayServer removeDragTypes: t fromWindow: _window]; 419 if ([_window autorecalculatesKeyViewLoop]) 420 { 421 [_window recalculateKeyViewLoop]; 422 } 423 } 424 if (newWindow != nil) 425 { 426 [GSDisplayServer addDragTypes: t toWindow: newWindow]; 427 if ([newWindow autorecalculatesKeyViewLoop]) 428 { 429 [newWindow recalculateKeyViewLoop]; 430 } 431 } 432 } 433 434 _window = newWindow; 435 436 if (_rFlags.has_subviews) 437 { 438 NSUInteger count = [_sub_views count]; 439 440 if (count > 0) 441 { 442 NSUInteger i; 443 NSView *array[count]; 444 445 [_sub_views getObjects: array]; 446 for (i = 0; i < count; ++i) 447 { 448 [array[i] _viewWillMoveToWindow: newWindow]; 449 } 450 } 451 } 452} 453 454- (void) _viewWillMoveToSuperview: (NSView*)newSuper 455{ 456 [self viewWillMoveToSuperview: newSuper]; 457 _super_view = newSuper; 458} 459 460/* 461 * Extend in super view covered by the frame of a view. 462 * When the frame is rotated, this is different from the frame. 463 */ 464- (NSRect) _frameExtend 465{ 466 NSRect frame = _frame; 467 468 if (_frameMatrix != nil) 469 { 470 NSRect r; 471 472 r.origin = NSZeroPoint; 473 r.size = frame.size; 474 [_frameMatrix boundingRectFor: r result: &r]; 475 frame = NSOffsetRect(r, NSMinX(frame), 476 NSMinY(frame)); 477 } 478 479 return frame; 480} 481 482 483- (NSString*) _subtreeDescriptionWithPrefix: (NSString*)prefix 484{ 485 NSMutableString *desc = [[NSMutableString alloc] init]; 486 NSEnumerator *e; 487 NSView *v; 488 489 [desc appendFormat: @"%@%@\n", prefix, [self description], nil]; 490 491 prefix = [prefix stringByAppendingString: @" "]; 492 e = [_sub_views objectEnumerator]; 493 while ((v = (NSView*)[e nextObject]) != nil) 494 { 495 [desc appendString: [v _subtreeDescriptionWithPrefix: prefix]]; 496 } 497 498 return AUTORELEASE(desc); 499} 500 501/* 502 * Unofficial Cocoa method for debugging a view hierarchy. 503 */ 504- (NSString*) _subtreeDescription 505{ 506 return [self _subtreeDescriptionWithPrefix: @""]; 507} 508 509- (NSString*) _flagDescription 510{ 511 return @""; 512} 513 514- (NSString*) _resizeDescription 515{ 516 return [NSString stringWithFormat: @"h=%c%c%c v=%c%c%c", 517 (_autoresizingMask & NSViewMinXMargin) ? '&' : '-', 518 (_autoresizingMask & NSViewWidthSizable) ? '&' : '-', 519 (_autoresizingMask & NSViewMaxXMargin) ? '&' : '-', 520 (_autoresizingMask & NSViewMinYMargin) ? '&' : '-', 521 (_autoresizingMask & NSViewHeightSizable) ? '&' : '-', 522 (_autoresizingMask & NSViewMaxYMargin) ? '&' : '-', 523 nil]; 524} 525 526- (NSString*) description 527{ 528 return [NSString stringWithFormat: @"%@ %@ %@ f=%@ b=%@", 529 [self _flagDescription], 530 [self _resizeDescription], [super description], 531 NSStringFromRect(_frame), NSStringFromRect(_bounds), nil]; 532} 533 534/* 535 * Class methods 536 */ 537+ (void) initialize 538{ 539 if (self == [NSView class]) 540 { 541 Class matrixClass = [NSAffineTransform class]; 542 NSAffineTransformStruct ats = { 1, 0, 0, -1, 0, 1 }; 543 544 typesMap = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, 545 NSObjectMapValueCallBacks, 0); 546 typesLock = [NSLock new]; 547 548 preSel = @selector(prependTransform:); 549 invalidateSel = @selector(_invalidateCoordinates); 550 551 preImp = (void (*)(NSAffineTransform*, SEL, NSAffineTransform*)) 552 [matrixClass instanceMethodForSelector: preSel]; 553 554 invalidateImp = (void (*)(NSView*, SEL)) 555 [self instanceMethodForSelector: invalidateSel]; 556 557 flip = [matrixClass new]; 558 [flip setTransformStruct: ats]; 559 560 nc = [NSNotificationCenter defaultCenter]; 561 562 viewClass = [NSView class]; 563 rectClass = [GSTrackingRect class]; 564 NSDebugLLog(@"NSView", @"Initialize NSView class\n"); 565 [self setVersion: 1]; 566 567 // expose bindings 568 [self exposeBinding: NSToolTipBinding]; 569 [self exposeBinding: NSHiddenBinding]; 570 } 571} 572 573/** 574 Return the view at the top of graphics contexts stack 575 or nil if none is focused. 576 */ 577+ (NSView*) focusView 578{ 579 return [GSCurrentContext() focusView]; 580} 581 582/* 583 * Instance methods 584 */ 585- (id) init 586{ 587 return [self initWithFrame: NSZeroRect]; 588} 589 590- (id) initWithFrame: (NSRect)frameRect 591{ 592 self = [super init]; 593 if (!self) 594 return self; 595 596 if (frameRect.size.width < 0) 597 { 598 NSWarnMLog(@"given negative width"); 599 frameRect.size.width = 0; 600 } 601 if (frameRect.size.height < 0) 602 { 603 NSWarnMLog(@"given negative height"); 604 frameRect.size.height = 0; 605 } 606 _frame = frameRect; // Set frame rectangle 607 _bounds.origin = NSZeroPoint; // Set bounds rectangle 608 _bounds.size = _frame.size; 609 610 // _frameMatrix = [NSAffineTransform new]; // Map fromsuperview to frame 611 // _boundsMatrix = [NSAffineTransform new]; // Map from superview to bounds 612 _matrixToWindow = [NSAffineTransform new]; // Map to window coordinates 613 _matrixFromWindow = [NSAffineTransform new]; // Map from window coordinates 614 615 _sub_views = [NSMutableArray new]; 616 _tracking_rects = [NSMutableArray new]; 617 _cursor_rects = [NSMutableArray new]; 618 619 // Some values are already set by initialisation 620 //_super_view = nil; 621 //_window = nil; 622 //_is_rotated_from_base = NO; 623 //_is_rotated_or_scaled_from_base = NO; 624 _rFlags.needs_display = YES; 625 _post_bounds_changes = YES; 626 _post_frame_changes = YES; 627 _autoresizes_subviews = YES; 628 _autoresizingMask = NSViewNotSizable; 629 //_coordinates_valid = NO; 630 //_nextKeyView = 0; 631 //_previousKeyView = 0; 632 633 _alphaValue = 1.0; 634 635 return self; 636} 637 638- (void) dealloc 639{ 640 NSView *tmp; 641 NSUInteger count; 642 643 // Remove all key value bindings for this view. 644 [GSKeyValueBinding unbindAllForObject: self]; 645 646 /* 647 * Remove self from view chain. Try to mimic MacOS-X behavior ... 648 * We send setNextKeyView: messages to all view for which we are the 649 * next key view, setting their next key view to nil. 650 * 651 * First we do the obvious stuff using the standard methods. 652 */ 653 [self setNextKeyView: nil]; 654 tmp = [self previousKeyView]; 655 if ([tmp nextKeyView] == self) 656 [tmp setNextKeyView: nil]; 657 658 /* 659 * Now, we locate any remaining cases where a view has us as its next 660 * view, and ask the view to change that. 661 */ 662 if (pKV(self) != 0) 663 { 664 count = GSIArrayCount(pKV(self)); 665 while (count-- > 0) 666 { 667 tmp = GSIArrayItemAtIndex(pKV(self), count).obj; 668 if ([tmp nextKeyView] == self) 669 { 670 [tmp setNextKeyView: nil]; 671 } 672 } 673 } 674 675 /* 676 * Now we clean up the previous view array, in case subclasses have 677 * overridden the default -setNextKeyView: method and broken things. 678 * We also relase the memory we used. 679 */ 680 if (pKV(self) != 0) 681 { 682 count = GSIArrayCount(pKV(self)); 683 while (count-- > 0) 684 { 685 tmp = GSIArrayItemAtIndex(pKV(self), count).obj; 686 if (tmp != nil && nKV(tmp) != 0) 687 { 688 NSUInteger otherCount = GSIArrayCount(nKV(tmp)); 689 690 while (otherCount-- > 1) 691 { 692 if (GSIArrayItemAtIndex(nKV(tmp), otherCount).obj == self) 693 { 694 GSIArrayRemoveItemAtIndex(nKV(tmp), otherCount); 695 } 696 } 697 if (GSIArrayItemAtIndex(nKV(tmp), 0).obj == self) 698 { 699 GSIArraySetItemAtIndex(nKV(tmp), (GSIArrayItem)nil, 0); 700 } 701 } 702 } 703 GSIArrayClear(pKV(self)); 704 NSZoneFree(NSDefaultMallocZone(), pKV(self)); 705 _previousKeyView = 0; 706 } 707 708 /* 709 * Now we clean up all views which have us as their previous view. 710 * We also release the memory we used. 711 */ 712 if (nKV(self) != 0) 713 { 714 count = GSIArrayCount(nKV(self)); 715 while (count-- > 0) 716 { 717 tmp = GSIArrayItemAtIndex(nKV(self), count).obj; 718 if (tmp != nil && pKV(tmp) != 0) 719 { 720 NSUInteger otherCount = GSIArrayCount(pKV(tmp)); 721 722 while (otherCount-- > 1) 723 { 724 if (GSIArrayItemAtIndex(pKV(tmp), otherCount).obj == self) 725 { 726 GSIArrayRemoveItemAtIndex(pKV(tmp), otherCount); 727 } 728 } 729 if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self) 730 { 731 GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0); 732 } 733 } 734 } 735 GSIArrayClear(nKV(self)); 736 NSZoneFree(NSDefaultMallocZone(), nKV(self)); 737 _nextKeyView = 0; 738 } 739 740 /* 741 * Now remove our subviews, AFTER cleaning up the view chain, in case 742 * any of our subviews were in the chain. 743 */ 744 while ([_sub_views count] > 0) 745 { 746 [[_sub_views lastObject] removeFromSuperviewWithoutNeedingDisplay]; 747 } 748 749 RELEASE(_matrixToWindow); 750 RELEASE(_matrixFromWindow); 751 TEST_RELEASE(_frameMatrix); 752 TEST_RELEASE(_boundsMatrix); 753 TEST_RELEASE(_sub_views); 754 if (_rFlags.has_tooltips != 0) 755 { 756 [GSToolTips removeTipsForView: self]; 757 } 758 if (_rFlags.has_currects != 0) 759 { 760 [self discardCursorRects]; // Handle release of cursors 761 } 762 TEST_RELEASE(_cursor_rects); 763 TEST_RELEASE(_tracking_rects); 764 [self unregisterDraggedTypes]; 765 [self releaseGState]; 766 767 [super dealloc]; 768} 769 770/** 771 * Adds aView as a subview of the receiver. 772 */ 773- (void) addSubview: (NSView*)aView 774{ 775 [self addSubview: aView 776 positioned: NSWindowAbove 777 relativeTo: nil]; 778} 779 780- (void) addSubview: (NSView*)aView 781 positioned: (NSWindowOrderingMode)place 782 relativeTo: (NSView*)otherView 783{ 784 NSUInteger index; 785 786 if (aView == nil) 787 { 788 return; 789 } 790 if ([self isDescendantOf: aView]) 791 { 792 [NSException raise: NSInvalidArgumentException 793 format: @"addSubview:positioned:relativeTo: creates a " 794 @"loop in the views tree!"]; 795 } 796 797 if (aView == otherView) 798 return; 799 800 RETAIN(aView); 801 [aView removeFromSuperview]; 802 803 // Do this after the removeFromSuperview, as aView may already 804 // be a subview and the index could change. 805 if (otherView == nil) 806 { 807 index = NSNotFound; 808 } 809 else 810 { 811 index = [_sub_views indexOfObjectIdenticalTo: otherView]; 812 } 813 if (index == NSNotFound) 814 { 815 if (place == NSWindowBelow) 816 index = 0; 817 else 818 index = [_sub_views count]; 819 } 820 else if (place != NSWindowBelow) 821 { 822 index += 1; 823 } 824 825 [aView _viewWillMoveToWindow: _window]; 826 [aView _viewWillMoveToSuperview: self]; 827 [aView setNextResponder: self]; 828 [_sub_views insertObject: aView atIndex: index]; 829 _rFlags.has_subviews = 1; 830 [aView resetCursorRects]; 831 [aView setNeedsDisplay: YES]; 832 [aView _viewDidMoveToWindow]; 833 [aView viewDidMoveToSuperview]; 834 [self didAddSubview: aView]; 835 RELEASE(aView); 836} 837 838/** 839 * Returns self if aView is the receiver or aView is a subview of the receiver, 840 * the ancestor view shared by aView and the receiver if any, or 841 * aView if it is an ancestor of the receiver, otherwise returns nil. 842 */ 843- (NSView*) ancestorSharedWithView: (NSView*)aView 844{ 845 if (self == aView) 846 return self; 847 848 if ([self isDescendantOf: aView]) 849 return aView; 850 851 if ([aView isDescendantOf: self]) 852 return self; 853 854 /* 855 * If neither are descendants of each other and either does not have a 856 * superview then they cannot have a common ancestor 857 */ 858 if (!_super_view) 859 return nil; 860 861 if (![aView superview]) 862 return nil; 863 864 /* Find the common ancestor of superviews */ 865 return [_super_view ancestorSharedWithView: [aView superview]]; 866} 867 868/** 869 * Returns YES if aView is an ancestor of the receiver. 870 */ 871- (BOOL) isDescendantOf: (NSView*)aView 872{ 873 if (aView == self) 874 return YES; 875 876 if (!_super_view) 877 return NO; 878 879 if (_super_view == aView) 880 return YES; 881 882 return [_super_view isDescendantOf: aView]; 883} 884 885- (NSView*) opaqueAncestor 886{ 887 NSView *next = _super_view; 888 NSView *current = self; 889 890 while (next != nil) 891 { 892 if ([current isOpaque] == YES) 893 { 894 break; 895 } 896 current = next; 897 next = current->_super_view; 898 } 899 return current; 900} 901 902/** 903 * Removes the receiver from its superviews list of subviews. 904 */ 905- (void) removeFromSuperviewWithoutNeedingDisplay 906{ 907 if (_super_view != nil) 908 { 909 [_super_view removeSubview: self]; 910 } 911} 912 913/** 914 <p> Removes the receiver from its superviews list of subviews 915 and marks the rectangle that the reciever occupied in the 916 superview as needing redisplay. </p> 917 918 <p> This is dangerous to use during display, since it alters the 919 rectangles needing display. In this case, you can use the 920 -removeFromSuperviewWithoutNeedingDisplay method instead.</p> */ 921- (void) removeFromSuperview 922{ 923 if (_super_view != nil) 924 { 925 [_super_view setNeedsDisplayInRect: _frame]; 926 [self removeFromSuperviewWithoutNeedingDisplay]; 927 } 928} 929 930/** 931 <p> Removes aSubview from the receivers list of subviews and from 932 the responder chain. </p> 933 934 <p> Also invokes -viewWillMoveToWindow: on aView with a nil argument, 935 to handle 936 removal of aView (and recursively, its children) from its window - 937 performing tidyup by invalidating cursor rects etc. </p> 938*/ 939- (void) removeSubview: (NSView*)aView 940{ 941 id view; 942 /* 943 * This must be first because it invokes -resignFirstResponder:, 944 * which assumes the view is still in the view hierarchy 945 */ 946 for (view = [_window firstResponder]; 947 view != nil && [view respondsToSelector: @selector(superview)]; 948 view = [view superview]) 949 { 950 if (view == aView) 951 { 952 [_window makeFirstResponder: _window]; 953 break; 954 } 955 } 956 [self willRemoveSubview: aView]; 957 aView->_super_view = nil; 958 [aView _viewWillMoveToWindow: nil]; 959 [aView _viewWillMoveToSuperview: nil]; 960 [aView setNextResponder: nil]; 961 RETAIN(aView); 962 [_sub_views removeObjectIdenticalTo: aView]; 963 [aView setNeedsDisplay: NO]; 964 [aView _viewDidMoveToWindow]; 965 [aView viewDidMoveToSuperview]; 966 RELEASE(aView); 967 if ([_sub_views count] == 0) 968 { 969 _rFlags.has_subviews = 0; 970 } 971} 972 973/** 974 * Removes oldView, which should be a subview of the receiver, from the 975 * receiver and places newView in its place. If newView is nil, just 976 * removes oldView. If oldView is nil, just adds newView. 977 */ 978- (void) replaceSubview: (NSView*)oldView with: (NSView*)newView 979{ 980 if (newView == oldView) 981 { 982 return; 983 } 984 /* 985 * NB. we implement the replacement in full rather than calling addSubview: 986 * since classes like NSBox override these methods but expect to be able to 987 * call [super replaceSubview:with:] safely. 988 */ 989 if (oldView == nil) 990 { 991 /* 992 * Strictly speaking, the docs say that if 'oldView' is not a subview 993 * of the receiver then we do nothing - but here we add newView anyway. 994 * So a replacement with no oldView is an addition. 995 */ 996 RETAIN(newView); 997 [newView removeFromSuperview]; 998 [newView _viewWillMoveToWindow: _window]; 999 [newView _viewWillMoveToSuperview: self]; 1000 [newView setNextResponder: self]; 1001 [_sub_views addObject: newView]; 1002 _rFlags.has_subviews = 1; 1003 [newView resetCursorRects]; 1004 [newView setNeedsDisplay: YES]; 1005 [newView _viewDidMoveToWindow]; 1006 [newView viewDidMoveToSuperview]; 1007 [self didAddSubview: newView]; 1008 RELEASE(newView); 1009 } 1010 else if ([_sub_views indexOfObjectIdenticalTo: oldView] != NSNotFound) 1011 { 1012 if (newView == nil) 1013 { 1014 /* 1015 * If there is no new view to add - we just remove the old one. 1016 * So a replacement with no newView is a removal. 1017 */ 1018 [oldView removeFromSuperview]; 1019 } 1020 else 1021 { 1022 NSUInteger index; 1023 1024 /* 1025 * Ok - the standard case - we remove the newView from wherever it 1026 * was (which may have been in this view), locate the position of 1027 * the oldView (which may have changed due to the removal of the 1028 * newView), remove the oldView, and insert the newView in it's 1029 * place. 1030 */ 1031 RETAIN(newView); 1032 [newView removeFromSuperview]; 1033 index = [_sub_views indexOfObjectIdenticalTo: oldView]; 1034 [oldView removeFromSuperview]; 1035 [newView _viewWillMoveToWindow: _window]; 1036 [newView _viewWillMoveToSuperview: self]; 1037 [newView setNextResponder: self]; 1038 [_sub_views insertObject: newView 1039 atIndex: index]; 1040 _rFlags.has_subviews = 1; 1041 [newView resetCursorRects]; 1042 [newView setNeedsDisplay: YES]; 1043 [newView _viewDidMoveToWindow]; 1044 [newView viewDidMoveToSuperview]; 1045 [self didAddSubview: newView]; 1046 RELEASE(newView); 1047 } 1048 } 1049} 1050 1051- (void) setSubviews: (NSArray *)newSubviews 1052{ 1053 NSEnumerator *en; 1054 NSView *aView; 1055 NSMutableArray *uniqNew = [NSMutableArray array]; 1056 1057 if (nil == newSubviews) 1058 { 1059 [NSException raise: NSInvalidArgumentException 1060 format: @"Setting nil as new subviews."]; 1061 } 1062 1063 // Use a copy as we remove from the subviews array 1064 en = [[NSArray arrayWithArray: _sub_views] objectEnumerator]; 1065 while ((aView = [en nextObject])) 1066 { 1067 if (NO == [newSubviews containsObject: aView]) 1068 { 1069 [aView removeFromSuperview]; 1070 } 1071 } 1072 1073 en = [newSubviews objectEnumerator]; 1074 while ((aView = [en nextObject])) 1075 { 1076 id supersub = [aView superview]; 1077 1078 if (supersub != nil && supersub != self) 1079 { 1080 [NSException raise: NSInvalidArgumentException 1081 format: @"Superviews of new subviews must be either nil or receiver."]; 1082 } 1083 1084 if ([uniqNew containsObject: aView]) 1085 { 1086 [NSException raise: NSInvalidArgumentException 1087 format: @"Duplicated new subviews."]; 1088 } 1089 1090 if (NO == [_sub_views containsObject: aView]) 1091 { 1092 [self addSubview: aView]; 1093 } 1094 1095 [uniqNew addObject: aView]; 1096 } 1097 1098 ASSIGN(_sub_views, uniqNew); 1099 1100 // The order of the subviews may have changed 1101 [self setNeedsDisplay: YES]; 1102} 1103 1104- (void) sortSubviewsUsingFunction: (NSComparisonResult (*)(id ,id ,void*))compare 1105 context: (void*)context 1106{ 1107 [_sub_views sortUsingFunction: compare context: context]; 1108} 1109 1110/** 1111 * Notifies the receiver that its superview is being changed to newSuper. 1112 */ 1113- (void) viewWillMoveToSuperview: (NSView*)newSuper 1114{ 1115} 1116 1117/** 1118 * Notifies the receiver that it will now be a view of newWindow. 1119 * Note, this method is also used when removing a view from a window 1120 * (in which case, newWindow is nil) to let all the subviews know 1121 * that they have also been removed from the window. 1122 */ 1123- (void) viewWillMoveToWindow: (NSWindow*)newWindow 1124{ 1125} 1126 1127- (void) didAddSubview: (NSView *)subview 1128{} 1129 1130- (void) viewDidMoveToSuperview 1131{} 1132 1133- (void) viewDidMoveToWindow 1134{} 1135 1136- (void) willRemoveSubview: (NSView *)subview 1137{} 1138 1139static NSSize _computeScale(NSSize fs, NSSize bs) 1140{ 1141 NSSize scale; 1142 1143 if (bs.width == 0) 1144 { 1145 if (fs.width == 0) 1146 scale.width = 1; 1147 else 1148 scale.width = FLT_MAX; 1149 } 1150 else 1151 { 1152 scale.width = fs.width / bs.width; 1153 } 1154 if (bs.height == 0) 1155 { 1156 if (fs.height == 0) 1157 scale.height = 1; 1158 else 1159 scale.height = FLT_MAX; 1160 } 1161 else 1162 { 1163 scale.height = fs.height / bs.height; 1164 } 1165 1166 return scale; 1167} 1168 1169- (void) _setFrameAndClearAutoresizingError: (NSRect)frameRect 1170{ 1171 _frame = frameRect; 1172 _autoresizingFrameError = NSZeroRect; 1173} 1174 1175- (void) setFrame: (NSRect)frameRect 1176{ 1177 BOOL changedOrigin = NO; 1178 BOOL changedSize = NO; 1179 NSSize old_size = _frame.size; 1180 1181 if (frameRect.size.width < 0) 1182 { 1183 NSWarnMLog(@"given negative width"); 1184 frameRect.size.width = 0; 1185 } 1186 if (frameRect.size.height < 0) 1187 { 1188 NSWarnMLog(@"given negative height"); 1189 frameRect.size.height = 0; 1190 } 1191 1192 if (NSEqualPoints(_frame.origin, frameRect.origin) == NO) 1193 { 1194 changedOrigin = YES; 1195 } 1196 if (NSEqualSizes(_frame.size, frameRect.size) == NO) 1197 { 1198 changedSize = YES; 1199 } 1200 1201 if (changedSize == YES || changedOrigin == YES) 1202 { 1203 [self _setFrameAndClearAutoresizingError: frameRect]; 1204 1205 if (changedSize == YES) 1206 { 1207 if (_is_rotated_or_scaled_from_base == YES) 1208 { 1209 NSAffineTransform *matrix; 1210 NSRect frame = _frame; 1211 1212 frame.origin = NSMakePoint(0, 0); 1213 matrix = [_boundsMatrix copy]; 1214 [matrix invert]; 1215 [matrix boundingRectFor: frame result: &_bounds]; 1216 RELEASE(matrix); 1217 } 1218 else 1219 { 1220 _bounds.size = frameRect.size; 1221 } 1222 } 1223 1224 if (_coordinates_valid) 1225 { 1226 (*invalidateImp)(self, invalidateSel); 1227 } 1228 [self resetCursorRects]; 1229 [self resizeSubviewsWithOldSize: old_size]; 1230 if (_post_frame_changes) 1231 { 1232 [nc postNotificationName: NSViewFrameDidChangeNotification 1233 object: self]; 1234 } 1235 } 1236} 1237 1238- (void) setFrameOrigin: (NSPoint)newOrigin 1239{ 1240 if (NSEqualPoints(_frame.origin, newOrigin) == NO) 1241 { 1242 NSRect newFrame = _frame; 1243 newFrame.origin = newOrigin; 1244 1245 if (_coordinates_valid) 1246 { 1247 (*invalidateImp)(self, invalidateSel); 1248 } 1249 [self _setFrameAndClearAutoresizingError: newFrame]; 1250 [self resetCursorRects]; 1251 if (_post_frame_changes) 1252 { 1253 [nc postNotificationName: NSViewFrameDidChangeNotification 1254 object: self]; 1255 } 1256 } 1257} 1258 1259- (void) setFrameSize: (NSSize)newSize 1260{ 1261 NSRect newFrame = _frame; 1262 if (newSize.width < 0) 1263 { 1264 NSWarnMLog(@"given negative width"); 1265 newSize.width = 0; 1266 } 1267 if (newSize.height < 0) 1268 { 1269 NSWarnMLog(@"given negative height"); 1270 newSize.height = 0; 1271 } 1272 if (NSEqualSizes(_frame.size, newSize) == NO) 1273 { 1274 NSSize old_size = _frame.size; 1275 1276 if (_is_rotated_or_scaled_from_base) 1277 { 1278 if (_boundsMatrix == nil) 1279 { 1280 CGFloat sx = _bounds.size.width / _frame.size.width; 1281 CGFloat sy = _bounds.size.height / _frame.size.height; 1282 1283 newFrame.size = newSize; 1284 [self _setFrameAndClearAutoresizingError: newFrame]; 1285 _bounds.size.width = _frame.size.width * sx; 1286 _bounds.size.height = _frame.size.height * sy; 1287 } 1288 else 1289 { 1290 NSAffineTransform *matrix; 1291 NSRect frame; 1292 1293 newFrame.size = newSize; 1294 [self _setFrameAndClearAutoresizingError: newFrame]; 1295 1296 frame = _frame; 1297 frame.origin = NSMakePoint(0, 0); 1298 matrix = [_boundsMatrix copy]; 1299 [matrix invert]; 1300 [matrix boundingRectFor: frame result: &_bounds]; 1301 RELEASE(matrix); 1302 } 1303 } 1304 else 1305 { 1306 newFrame.size = _bounds.size = newSize; 1307 [self _setFrameAndClearAutoresizingError: newFrame]; 1308 } 1309 1310 if (_coordinates_valid) 1311 { 1312 (*invalidateImp)(self, invalidateSel); 1313 } 1314 [self resetCursorRects]; 1315 [self resizeSubviewsWithOldSize: old_size]; 1316 if (_post_frame_changes) 1317 { 1318 [nc postNotificationName: NSViewFrameDidChangeNotification 1319 object: self]; 1320 } 1321 } 1322} 1323 1324- (void) setFrameRotation: (CGFloat)angle 1325{ 1326 CGFloat oldAngle = [self frameRotation]; 1327 1328 if (oldAngle != angle) 1329 { 1330 /* no frame matrix, create one since it is needed for rotation */ 1331 if (_frameMatrix == nil) 1332 { 1333 // Map from superview to frame 1334 _frameMatrix = [NSAffineTransform new]; 1335 } 1336 1337 [_frameMatrix rotateByDegrees: angle - oldAngle]; 1338 _is_rotated_from_base = _is_rotated_or_scaled_from_base = YES; 1339 1340 if (_coordinates_valid) 1341 { 1342 (*invalidateImp)(self, invalidateSel); 1343 } 1344 [self resetCursorRects]; 1345 if (_post_frame_changes) 1346 { 1347 [nc postNotificationName: NSViewFrameDidChangeNotification 1348 object: self]; 1349 } 1350 } 1351} 1352 1353- (BOOL) isRotatedFromBase 1354{ 1355 if (_is_rotated_from_base) 1356 { 1357 return YES; 1358 } 1359 else if (_super_view) 1360 { 1361 return [_super_view isRotatedFromBase]; 1362 } 1363 else 1364 { 1365 return NO; 1366 } 1367} 1368 1369- (BOOL) isRotatedOrScaledFromBase 1370{ 1371 if (_is_rotated_or_scaled_from_base) 1372 { 1373 return YES; 1374 } 1375 else if (_super_view) 1376 { 1377 return [_super_view isRotatedOrScaledFromBase]; 1378 } 1379 else 1380 { 1381 return NO; 1382 } 1383} 1384 1385- (void) setBounds: (NSRect)aRect 1386{ 1387 NSDebugLLog(@"NSView", @"setBounds %@", NSStringFromRect(aRect)); 1388 if (aRect.size.width < 0) 1389 { 1390 NSWarnMLog(@"given negative width"); 1391 aRect.size.width = 0; 1392 } 1393 if (aRect.size.height < 0) 1394 { 1395 NSWarnMLog(@"given negative height"); 1396 aRect.size.height = 0; 1397 } 1398 1399 if (_is_rotated_from_base || (NSEqualRects(_bounds, aRect) == NO)) 1400 { 1401 NSAffineTransform *matrix; 1402 NSPoint oldOrigin; 1403 NSSize scale; 1404 1405 if (_boundsMatrix == nil) 1406 { 1407 _boundsMatrix = [NSAffineTransform new]; 1408 } 1409 1410 // Adjust scale 1411 scale = _computeScale(_frame.size, aRect.size); 1412 if (scale.width != 1 || scale.height != 1) 1413 { 1414 _is_rotated_or_scaled_from_base = YES; 1415 } 1416 [_boundsMatrix scaleTo: scale.width : scale.height]; 1417 { 1418 matrix = [_boundsMatrix copy]; 1419 [matrix invert]; 1420 oldOrigin = [matrix transformPoint: NSMakePoint(0, 0)]; 1421 RELEASE(matrix); 1422 } 1423 [_boundsMatrix translateXBy: oldOrigin.x - aRect.origin.x 1424 yBy: oldOrigin.y - aRect.origin.y]; 1425 if (!_is_rotated_from_base) 1426 { 1427 // Adjust bounds 1428 _bounds = aRect; 1429 } 1430 else 1431 { 1432 // Adjust bounds 1433 NSRect frame = _frame; 1434 1435 frame.origin = NSMakePoint(0, 0); 1436 matrix = [_boundsMatrix copy]; 1437 [matrix invert]; 1438 [matrix boundingRectFor: frame result: &_bounds]; 1439 RELEASE(matrix); 1440 } 1441 1442 if (_coordinates_valid) 1443 { 1444 (*invalidateImp)(self, invalidateSel); 1445 } 1446 [self resetCursorRects]; 1447 if (_post_bounds_changes) 1448 { 1449 [nc postNotificationName: NSViewBoundsDidChangeNotification 1450 object: self]; 1451 } 1452 } 1453} 1454 1455- (void) setBoundsOrigin: (NSPoint)newOrigin 1456{ 1457 NSPoint oldOrigin; 1458 1459 if (_boundsMatrix == nil) 1460 { 1461 oldOrigin = NSMakePoint(NSMinX(_bounds), NSMinY(_bounds)); 1462 } 1463 else 1464 { 1465 NSAffineTransform *matrix = [_boundsMatrix copy]; 1466 1467 [matrix invert]; 1468 oldOrigin = [matrix transformPoint: NSMakePoint(0, 0)]; 1469 RELEASE(matrix); 1470 } 1471 [self translateOriginToPoint: NSMakePoint(oldOrigin.x - newOrigin.x, 1472 oldOrigin.y - newOrigin.y)]; 1473} 1474 1475- (void) setBoundsSize: (NSSize)newSize 1476{ 1477 NSSize scale; 1478 1479 NSDebugLLog(@"NSView", @"%@ setBoundsSize: %@", self, 1480 NSStringFromSize(newSize)); 1481 1482 if (newSize.width < 0) 1483 { 1484 NSWarnMLog(@"given negative width"); 1485 newSize.width = 0; 1486 } 1487 if (newSize.height < 0) 1488 { 1489 NSWarnMLog(@"given negative height"); 1490 newSize.height = 0; 1491 } 1492 1493 scale = _computeScale(_frame.size, newSize); 1494 if (scale.width != 1 || scale.height != 1) 1495 { 1496 _is_rotated_or_scaled_from_base = YES; 1497 } 1498 1499 if (_boundsMatrix == nil) 1500 { 1501 _boundsMatrix = [NSAffineTransform new]; 1502 } 1503 [_boundsMatrix scaleTo: scale.width : scale.height]; 1504 if (!_is_rotated_from_base) 1505 { 1506 scale = _computeScale(_bounds.size, newSize); 1507 _bounds.origin.x = _bounds.origin.x / scale.width; 1508 _bounds.origin.y = _bounds.origin.y / scale.height; 1509 _bounds.size = newSize; 1510 } 1511 else 1512 { 1513 NSAffineTransform *matrix; 1514 NSRect frame = _frame; 1515 1516 frame.origin = NSMakePoint(0, 0); 1517 matrix = [_boundsMatrix copy]; 1518 [matrix invert]; 1519 [matrix boundingRectFor: frame result: &_bounds]; 1520 RELEASE(matrix); 1521 } 1522 1523 if (_coordinates_valid) 1524 { 1525 (*invalidateImp)(self, invalidateSel); 1526 } 1527 [self resetCursorRects]; 1528 if (_post_bounds_changes) 1529 { 1530 [nc postNotificationName: NSViewBoundsDidChangeNotification 1531 object: self]; 1532 } 1533} 1534 1535- (void) setBoundsRotation: (CGFloat)angle 1536{ 1537 [self rotateByAngle: angle - [self boundsRotation]]; 1538} 1539 1540- (void) translateOriginToPoint: (NSPoint)point 1541{ 1542 NSDebugLLog(@"NSView", @"%@ translateOriginToPoint: %@", self, 1543 NSStringFromPoint(point)); 1544 if (NSEqualPoints(NSZeroPoint, point) == NO) 1545 { 1546 if (_boundsMatrix == nil) 1547 { 1548 _boundsMatrix = [NSAffineTransform new]; 1549 } 1550 [_boundsMatrix translateXBy: point.x 1551 yBy: point.y]; 1552 // Adjust bounds 1553 _bounds.origin.x -= point.x; 1554 _bounds.origin.y -= point.y; 1555 1556 if (_coordinates_valid) 1557 { 1558 (*invalidateImp)(self, invalidateSel); 1559 } 1560 [self resetCursorRects]; 1561 if (_post_bounds_changes) 1562 { 1563 [nc postNotificationName: NSViewBoundsDidChangeNotification 1564 object: self]; 1565 } 1566 } 1567} 1568 1569- (void) scaleUnitSquareToSize: (NSSize)newSize 1570{ 1571 if (newSize.width != 1.0 || newSize.height != 1.0) 1572 { 1573 if (newSize.width < 0) 1574 { 1575 NSWarnMLog(@"given negative width"); 1576 newSize.width = 0; 1577 } 1578 if (newSize.height < 0) 1579 { 1580 NSWarnMLog(@"given negative height"); 1581 newSize.height = 0; 1582 } 1583 1584 if (_boundsMatrix == nil) 1585 { 1586 _boundsMatrix = [NSAffineTransform new]; 1587 } 1588 [_boundsMatrix scaleXBy: newSize.width yBy: newSize.height]; 1589 // Adjust bounds 1590 _bounds.origin.x = _bounds.origin.x / newSize.width; 1591 _bounds.origin.y = _bounds.origin.y / newSize.height; 1592 _bounds.size.width = _bounds.size.width / newSize.width; 1593 _bounds.size.height = _bounds.size.height / newSize.height; 1594 1595 _is_rotated_or_scaled_from_base = YES; 1596 1597 if (_coordinates_valid) 1598 { 1599 (*invalidateImp)(self, invalidateSel); 1600 } 1601 [self resetCursorRects]; 1602 if (_post_bounds_changes) 1603 { 1604 [nc postNotificationName: NSViewBoundsDidChangeNotification 1605 object: self]; 1606 } 1607 } 1608} 1609 1610- (void) rotateByAngle: (CGFloat)angle 1611{ 1612 if (angle != 0.0) 1613 { 1614 NSAffineTransform *matrix; 1615 NSRect frame = _frame; 1616 1617 frame.origin = NSMakePoint(0, 0); 1618 if (_boundsMatrix == nil) 1619 { 1620 _boundsMatrix = [NSAffineTransform new]; 1621 } 1622 [_boundsMatrix rotateByDegrees: angle]; 1623 // Adjust bounds 1624 matrix = [_boundsMatrix copy]; 1625 [matrix invert]; 1626 [matrix boundingRectFor: frame result: &_bounds]; 1627 RELEASE(matrix); 1628 1629 _is_rotated_from_base = _is_rotated_or_scaled_from_base = YES; 1630 1631 if (_coordinates_valid) 1632 { 1633 (*invalidateImp)(self, invalidateSel); 1634 } 1635 [self resetCursorRects]; 1636 if (_post_bounds_changes) 1637 { 1638 [nc postNotificationName: NSViewBoundsDidChangeNotification 1639 object: self]; 1640 } 1641 } 1642} 1643 1644 1645- (CGFloat) alphaValue 1646{ 1647 return _alphaValue; 1648} 1649 1650- (void)setAlphaValue: (CGFloat)alpha 1651{ 1652 _alphaValue = alpha; 1653} 1654 1655- (CGFloat) frameCenterRotation 1656{ 1657 // FIXME this is dummy, we don't have layers yet 1658 return 0.0; 1659} 1660 1661- (void) setFrameCenterRotation:(CGFloat)rot; 1662{ 1663 // FIXME this is dummy, we don't have layers yet 1664 // we probably need a Matrix akin frame rotation. 1665} 1666 1667 1668- (NSRect) centerScanRect: (NSRect)aRect 1669{ 1670 NSAffineTransform *matrix; 1671 CGFloat x_org; 1672 CGFloat y_org; 1673 1674 /* 1675 * Hmm - we assume that the windows coordinate system is centered on the 1676 * pixels of the screen - this may not be correct of course. 1677 * Plus - this is all pretty meaningless is we are not in a window! 1678 */ 1679 matrix = [self _matrixToWindow]; 1680 aRect.origin = [matrix transformPoint: aRect.origin]; 1681 aRect.size = [matrix transformSize: aRect.size]; 1682 if (aRect.size.height < 0.0) 1683 { 1684 aRect.size.height = -aRect.size.height; 1685 } 1686 1687 x_org = aRect.origin.x; 1688 y_org = aRect.origin.y; 1689 aRect.origin.x = GSRoundTowardsInfinity(aRect.origin.x); 1690 aRect.origin.y = [self isFlipped] ? GSRoundTowardsNegativeInfinity(aRect.origin.y) : GSRoundTowardsInfinity(aRect.origin.y); 1691 aRect.size.width = GSRoundTowardsInfinity(aRect.size.width + (x_org - aRect.origin.x) / 2.0); 1692 aRect.size.height = GSRoundTowardsInfinity(aRect.size.height + (y_org - aRect.origin.y) / 2.0); 1693 1694 matrix = [self _matrixFromWindow]; 1695 aRect.origin = [matrix transformPoint: aRect.origin]; 1696 aRect.size = [matrix transformSize: aRect.size]; 1697 if (aRect.size.height < 0.0) 1698 { 1699 aRect.size.height = -aRect.size.height; 1700 } 1701 1702 return aRect; 1703} 1704 1705- (NSPoint) convertPoint: (NSPoint)aPoint fromView: (NSView*)aView 1706{ 1707 NSPoint inBase; 1708 1709 if (aView == self) 1710 { 1711 return aPoint; 1712 } 1713 1714 if (aView != nil) 1715 { 1716 NSAssert(_window == [aView window], NSInvalidArgumentException); 1717 inBase = [[aView _matrixToWindow] transformPoint: aPoint]; 1718 } 1719 else 1720 { 1721 inBase = aPoint; 1722 } 1723 1724 return [[self _matrixFromWindow] transformPoint: inBase]; 1725} 1726 1727- (NSPoint) convertPoint: (NSPoint)aPoint toView: (NSView*)aView 1728{ 1729 NSPoint inBase; 1730 1731 if (aView == self) 1732 return aPoint; 1733 1734 inBase = [[self _matrixToWindow] transformPoint: aPoint]; 1735 1736 if (aView != nil) 1737 { 1738 NSAssert(_window == [aView window], NSInvalidArgumentException); 1739 return [[aView _matrixFromWindow] transformPoint: inBase]; 1740 } 1741 else 1742 { 1743 return inBase; 1744 } 1745} 1746 1747 1748/* Helper for -convertRect:fromView: and -convertRect:toView:. */ 1749static NSRect 1750convert_rect_using_matrices(NSRect aRect, NSAffineTransform *matrix1, 1751 NSAffineTransform *matrix2) 1752{ 1753 NSRect r; 1754 NSPoint p[4], min, max; 1755 int i; 1756 1757 for (i = 0; i < 4; i++) 1758 p[i] = aRect.origin; 1759 p[1].x += aRect.size.width; 1760 p[2].y += aRect.size.height; 1761 p[3].x += aRect.size.width; 1762 p[3].y += aRect.size.height; 1763 1764 for (i = 0; i < 4; i++) 1765 p[i] = [matrix1 transformPoint: p[i]]; 1766 1767 min = max = p[0] = [matrix2 transformPoint: p[0]]; 1768 for (i = 1; i < 4; i++) 1769 { 1770 p[i] = [matrix2 transformPoint: p[i]]; 1771 min.x = MIN(min.x, p[i].x); 1772 min.y = MIN(min.y, p[i].y); 1773 max.x = MAX(max.x, p[i].x); 1774 max.y = MAX(max.y, p[i].y); 1775 } 1776 1777 r.origin = min; 1778 r.size.width = max.x - min.x; 1779 r.size.height = max.y - min.y; 1780 1781 return r; 1782} 1783 1784/** 1785 * Converts aRect from the coordinate system of aView to the coordinate 1786 * system of the receiver, ie. returns the bounding rectangle in the 1787 * receiver of aRect in aView. 1788 * <br /> 1789 * aView and the receiver must be in the same window. If aView is nil, 1790 * converts from the receiver's window's coordinate system. 1791 */ 1792- (NSRect) convertRect: (NSRect)aRect fromView: (NSView*)aView 1793{ 1794 NSAffineTransform *matrix1, *matrix2; 1795 1796 if (aView == self || _window == nil || (aView != nil && [aView window] == nil)) 1797 { 1798 return aRect; 1799 } 1800 1801 if (aView != nil) 1802 { 1803 NSAssert(_window == [aView window], NSInvalidArgumentException); 1804 matrix1 = [aView _matrixToWindow]; 1805 } 1806 else 1807 { 1808 matrix1 = [NSAffineTransform transform]; 1809 } 1810 1811 matrix2 = [self _matrixFromWindow]; 1812 1813 return convert_rect_using_matrices(aRect, matrix1, matrix2); 1814} 1815 1816/** 1817 * Converts aRect from the coordinate system of the receiver to the 1818 * coordinate system of aView, ie. returns the bounding rectangle in 1819 * aView of aRect in the receiver. 1820 * <br /> 1821 * aView and the receiver must be in the same window. If aView is nil, 1822 * converts to the receiver's window's coordinate system. 1823 */ 1824- (NSRect) convertRect: (NSRect)aRect toView: (NSView*)aView 1825{ 1826 NSAffineTransform *matrix1, *matrix2; 1827 1828 if (aView == self || _window == nil || (aView != nil && [aView window] == nil)) 1829 { 1830 return aRect; 1831 } 1832 1833 matrix1 = [self _matrixToWindow]; 1834 1835 if (aView != nil) 1836 { 1837 NSAssert(_window == [aView window], NSInvalidArgumentException); 1838 matrix2 = [aView _matrixFromWindow]; 1839 } 1840 else 1841 { 1842 matrix2 = [NSAffineTransform transform]; 1843 } 1844 1845 return convert_rect_using_matrices(aRect, matrix1, matrix2); 1846} 1847 1848- (NSSize) convertSize: (NSSize)aSize fromView: (NSView*)aView 1849{ 1850 NSSize inBase; 1851 NSSize inSelf; 1852 1853 if (aView) 1854 { 1855 NSAssert(_window == [aView window], NSInvalidArgumentException); 1856 inBase = [[aView _matrixToWindow] transformSize: aSize]; 1857 if (inBase.height < 0.0) 1858 { 1859 inBase.height = -inBase.height; 1860 } 1861 } 1862 else 1863 { 1864 inBase = aSize; 1865 } 1866 1867 inSelf = [[self _matrixFromWindow] transformSize: inBase]; 1868 if (inSelf.height < 0.0) 1869 { 1870 inSelf.height = -inSelf.height; 1871 } 1872 return inSelf; 1873} 1874 1875- (NSSize) convertSize: (NSSize)aSize toView: (NSView*)aView 1876{ 1877 NSSize inBase = [[self _matrixToWindow] transformSize: aSize]; 1878 if (inBase.height < 0.0) 1879 { 1880 inBase.height = -inBase.height; 1881 } 1882 1883 if (aView) 1884 { 1885 NSSize inOther; 1886 NSAssert(_window == [aView window], NSInvalidArgumentException); 1887 inOther = [[aView _matrixFromWindow] transformSize: inBase]; 1888 if (inOther.height < 0.0) 1889 { 1890 inOther.height = -inOther.height; 1891 } 1892 return inOther; 1893 } 1894 else 1895 { 1896 return inBase; 1897 } 1898} 1899 1900- (NSPoint) convertPointFromBase: (NSPoint)aPoint 1901{ 1902 return [self convertPoint: aPoint fromView: nil]; 1903} 1904 1905- (NSPoint) convertPointToBase: (NSPoint)aPoint 1906{ 1907 return [self convertPoint: aPoint toView: nil]; 1908} 1909 1910- (NSRect) convertRectFromBase: (NSRect)aRect 1911{ 1912 return [self convertRect: aRect fromView: nil]; 1913} 1914 1915- (NSRect) convertRectToBase: (NSRect)aRect 1916{ 1917 return [self convertRect: aRect toView: nil]; 1918} 1919 1920- (NSSize) convertSizeFromBase: (NSSize)aSize 1921{ 1922 return [self convertSize: aSize fromView: nil]; 1923} 1924 1925- (NSSize) convertSizeToBase: (NSSize)aSize 1926{ 1927 return [self convertSize: aSize toView: nil]; 1928} 1929 1930/** 1931 * Sets whether the receiver should post NSViewFrameDidChangeNotification 1932 * when its frame changed. 1933 */ 1934- (void) setPostsFrameChangedNotifications: (BOOL)flag 1935{ 1936 _post_frame_changes = flag; 1937} 1938 1939/** 1940 * Sets whether the receiver should post NSViewBoundsDidChangeNotification 1941 * when its bound changed. 1942 */ 1943- (void) setPostsBoundsChangedNotifications: (BOOL)flag 1944{ 1945 _post_bounds_changes = flag; 1946} 1947 1948/* 1949 * resize subviews only if we are supposed to and we have never been rotated 1950 */ 1951- (void) resizeSubviewsWithOldSize: (NSSize)oldSize 1952{ 1953 if (_rFlags.has_subviews) 1954 { 1955 id e, o; 1956 1957 if (_autoresizes_subviews == NO || _is_rotated_from_base == YES) 1958 return; 1959 1960 e = [_sub_views objectEnumerator]; 1961 o = [e nextObject]; 1962 while (o) 1963 { 1964 [o resizeWithOldSuperviewSize: oldSize]; 1965 o = [e nextObject]; 1966 } 1967 } 1968} 1969 1970static void autoresize(CGFloat oldContainerSize, 1971 CGFloat newContainerSize, 1972 CGFloat *contentPositionInOut, 1973 CGFloat *contentSizeInOut, 1974 BOOL minMarginFlexible, 1975 BOOL sizeFlexible, 1976 BOOL maxMarginFlexible) 1977{ 1978 const CGFloat change = newContainerSize - oldContainerSize; 1979 const CGFloat oldContentSize = *contentSizeInOut; 1980 const CGFloat oldContentPosition = *contentPositionInOut; 1981 CGFloat flexibleSpace = 0.0; 1982 1983 // See how much flexible space we have to distrube the change over 1984 1985 if (sizeFlexible) 1986 flexibleSpace += oldContentSize; 1987 1988 if (minMarginFlexible) 1989 flexibleSpace += oldContentPosition; 1990 1991 if (maxMarginFlexible) 1992 flexibleSpace += oldContainerSize - oldContentPosition - oldContentSize; 1993 1994 1995 if (flexibleSpace <= 0.0) 1996 { 1997 /** 1998 * In this code path there is no flexible space so we divide 1999 * the available space equally among the flexible portions of the view 2000 */ 2001 int subdivisions = (sizeFlexible ? 1 : 0) + 2002 (minMarginFlexible ? 1 : 0) + 2003 (maxMarginFlexible ? 1 : 0); 2004 2005 if (subdivisions > 0) 2006 { 2007 const CGFloat changePerOption = change / subdivisions; 2008 2009 if (sizeFlexible) 2010 { 2011 *contentSizeInOut += changePerOption; 2012 } 2013 if (minMarginFlexible) 2014 { 2015 *contentPositionInOut += changePerOption; 2016 } 2017 } 2018 } 2019 else 2020 { 2021 /** 2022 * In this code path we distribute the change proportionately 2023 * over the flexible spaces 2024 */ 2025 const CGFloat changePerPoint = change / flexibleSpace; 2026 2027 if (sizeFlexible) 2028 { 2029 *contentSizeInOut += changePerPoint * oldContentSize; 2030 } 2031 if (minMarginFlexible) 2032 { 2033 *contentPositionInOut += changePerPoint * oldContentPosition; 2034 } 2035 } 2036} 2037 2038- (void) resizeWithOldSuperviewSize: (NSSize)oldSize 2039{ 2040 NSSize superViewFrameSize; 2041 NSRect newFrame = _frame; 2042 NSRect newFrameRounded; 2043 2044 if (_autoresizingMask == NSViewNotSizable) 2045 return; 2046 2047 if (!NSEqualRects(NSZeroRect, _autoresizingFrameError)) 2048 { 2049 newFrame.origin.x -= _autoresizingFrameError.origin.x; 2050 newFrame.origin.y -= _autoresizingFrameError.origin.y; 2051 newFrame.size.width -= _autoresizingFrameError.size.width; 2052 newFrame.size.height -= _autoresizingFrameError.size.height; 2053 } 2054 2055 superViewFrameSize = NSMakeSize(0,0); 2056 if (_super_view) 2057 superViewFrameSize = [_super_view frame].size; 2058 2059 autoresize(oldSize.width, 2060 superViewFrameSize.width, 2061 &newFrame.origin.x, 2062 &newFrame.size.width, 2063 (_autoresizingMask & NSViewMinXMargin), 2064 (_autoresizingMask & NSViewWidthSizable), 2065 (_autoresizingMask & NSViewMaxXMargin)); 2066 2067 { 2068 const BOOL flipped = (_super_view && [_super_view isFlipped]); 2069 2070 autoresize(oldSize.height, 2071 superViewFrameSize.height, 2072 &newFrame.origin.y, 2073 &newFrame.size.height, 2074 flipped ? (_autoresizingMask & NSViewMaxYMargin) : (_autoresizingMask & NSViewMinYMargin), 2075 (_autoresizingMask & NSViewHeightSizable), 2076 flipped ? (_autoresizingMask & NSViewMinYMargin) : (_autoresizingMask & NSViewMaxYMargin)); 2077 } 2078 2079 newFrameRounded = newFrame; 2080 2081 /** 2082 * Perform rounding to pixel-align the frame if we are not rotated 2083 */ 2084 if (![self isRotatedFromBase] && [self superview] != nil) 2085 { 2086 newFrameRounded = [[self superview] centerScanRect: newFrameRounded]; 2087 } 2088 2089 [self setFrame: newFrameRounded]; 2090 2091 _autoresizingFrameError.origin.x = (newFrameRounded.origin.x - newFrame.origin.x); 2092 _autoresizingFrameError.origin.y = (newFrameRounded.origin.y - newFrame.origin.y); 2093 _autoresizingFrameError.size.width = (newFrameRounded.size.width - newFrame.size.width); 2094 _autoresizingFrameError.size.height = (newFrameRounded.size.height - newFrame.size.height); 2095} 2096 2097- (void) _lockFocusInContext: (NSGraphicsContext *)ctxt inRect: (NSRect)rect 2098{ 2099 NSRect wrect; 2100 NSInteger window_gstate = 0; 2101 2102 if (viewIsPrinting == nil) 2103 { 2104 NSAssert(_window != nil, NSInternalInconsistencyException); 2105 /* Check for deferred window */ 2106 if ((window_gstate = [_window gState]) == 0) 2107 { 2108 return; 2109 } 2110 } 2111 2112 if (ctxt == nil) 2113 { 2114 if (viewIsPrinting != nil) 2115 { 2116 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 2117 2118 ctxt = [printOp context]; 2119 } 2120 else 2121 { 2122 ctxt = [_window graphicsContext]; 2123 } 2124 } 2125 2126 // Set current context 2127 [NSGraphicsContext saveGraphicsState]; 2128 [NSGraphicsContext setCurrentContext: ctxt]; 2129 2130 [ctxt lockFocusView: self inRect: rect]; 2131 wrect = [self convertRect: rect toView: nil]; 2132 NSDebugLLog(@"NSView", @"-lockFocusInRect: %@\n" 2133 @"\t for view %@ in window %p (%@)\n" 2134 @"\t frame %@, flip %d", 2135 NSStringFromRect(wrect), 2136 self, _window, NSStringFromRect([_window frame]), 2137 NSStringFromRect(_frame), [self isFlipped]); 2138 if (viewIsPrinting == nil) 2139 { 2140 [_window->_rectsBeingDrawn addObject: [NSValue valueWithRect: wrect]]; 2141 } 2142 2143 /* Make sure we don't modify superview's gstate */ 2144 DPSgsave(ctxt); 2145 2146 if (viewIsPrinting != nil) 2147 { 2148 if (viewIsPrinting == self) 2149 { 2150 /* Make sure coordinates are valid, then fake that we don't have 2151 a superview so we get printed correctly */ 2152 [self _matrixToWindow]; 2153 [_matrixToWindow makeIdentityMatrix]; 2154 } 2155 else 2156 { 2157 [[self _matrixToWindow] concat]; 2158 } 2159 2160 /* Allow subclases to make other modifications */ 2161 [self setUpGState]; 2162 } 2163 else 2164 { 2165 if (_gstate && !_renew_gstate) 2166 { 2167 DPSsetgstate(ctxt, _gstate); 2168 DPSgsave(ctxt); 2169 } 2170 else 2171 { 2172 // This only works, when the context comes from the window 2173 DPSsetgstate(ctxt, window_gstate); 2174 DPSgsave(ctxt); 2175 [[self _matrixToWindow] concat]; 2176 2177 /* Allow subclases to make other modifications */ 2178 [self setUpGState]; 2179 _renew_gstate = NO; 2180 if (_allocate_gstate) 2181 { 2182 if (_gstate) 2183 { 2184 GSReplaceGState(ctxt, _gstate); 2185 } 2186 else 2187 { 2188 _gstate = GSDefineGState(ctxt); 2189 } 2190 /* Balance the previous gsave and install our own gstate */ 2191 DPSgrestore(ctxt); 2192 DPSsetgstate(ctxt, _gstate); 2193 DPSgsave(ctxt); 2194 } 2195 } 2196 } 2197 2198 if ([self wantsDefaultClipping]) 2199 { 2200 /* 2201 * Clip to the visible rectangle - which will never be greater 2202 * than the bounds of the view. This prevents drawing outside 2203 * our bounds. 2204 */ 2205 // Normally the second test is not needed, it can differ only 2206 // when the view is loaded from a NIB file. 2207 if (_is_rotated_from_base && (_boundsMatrix != nil)) 2208 { 2209 // When the view is rotated, we clip to the frame. 2210 NSAffineTransform *matrix; 2211 NSRect frame = _frame; 2212 NSBezierPath *bp; 2213 2214 frame.origin = NSMakePoint(0, 0); 2215 bp = [NSBezierPath bezierPathWithRect: frame]; 2216 2217 matrix = [_boundsMatrix copy]; 2218 [matrix invert]; 2219 [bp transformUsingAffineTransform: matrix]; 2220 [bp addClip]; 2221 RELEASE(matrix); 2222 } 2223 else 2224 { 2225 // FIXME: Should we use _bounds or visibleRect here? 2226 DPSrectclip(ctxt, NSMinX(rect), NSMinY(rect), 2227 NSWidth(rect), NSHeight(rect)); 2228 } 2229 } 2230 2231 /* Tell backends that images are drawn upside down. Obsolete? 2232 This is needed when a backend is able to handle full image transformation. */ 2233 GSWSetViewIsFlipped(ctxt, [self isFlipped]); 2234} 2235 2236- (void) _setIgnoresBacking: (BOOL) flag 2237{ 2238 _rFlags.ignores_backing = flag; 2239} 2240 2241- (BOOL) _ignoresBacking 2242{ 2243 return _rFlags.ignores_backing; 2244} 2245 2246- (void) unlockFocusNeedsFlush: (BOOL)flush 2247{ 2248 NSGraphicsContext *ctxt = GSCurrentContext(); 2249 2250 NSDebugLLog(@"NSView_details", @"-unlockFocusNeedsFlush: %i for view %@\n", 2251 flush, self); 2252 2253 if (viewIsPrinting == nil) 2254 { 2255 NSAssert(_window != nil, NSInternalInconsistencyException); 2256 /* Check for deferred window */ 2257 if ([_window gState] == 0) 2258 return; 2259 2260 /* Restore our original gstate */ 2261 DPSgrestore(ctxt); 2262 } 2263 2264 /* Restore state of nesting lockFocus */ 2265 DPSgrestore(ctxt); 2266 if (!_allocate_gstate) 2267 _gstate = 0; 2268 2269 if (viewIsPrinting == nil) 2270 { 2271 NSRect rect; 2272 if (flush && !_rFlags.ignores_backing) 2273 { 2274 rect = [[_window->_rectsBeingDrawn lastObject] rectValue]; 2275 _window->_rectNeedingFlush = 2276 NSUnionRect(_window->_rectNeedingFlush, rect); 2277 _window->_f.needs_flush = YES; 2278 } 2279 [_window->_rectsBeingDrawn removeLastObject]; 2280 } 2281 [ctxt unlockFocusView: self needsFlush: YES ]; 2282 [NSGraphicsContext restoreGraphicsState]; 2283} 2284 2285/** 2286 <p> Tell the view to maintain a private gstate object which 2287 encapsulates all the information about drawing, such as coordinate 2288 transforms, line widths, etc. If you do not invoke this method, a 2289 gstate object is constructed each time the view is lockFocused. 2290 Allocating a private gstate may improve the performance of views 2291 that are focused a lot and have a lot of customized drawing 2292 parameters. </p> 2293 2294 <p> View subclasses should override the 2295 setUpGstate method to set these custom parameters. 2296 </p> 2297*/ 2298- (void) allocateGState 2299{ 2300 _allocate_gstate = YES; 2301 _renew_gstate = YES; 2302} 2303 2304/** 2305 Frees the gstate object, if there is one. 2306*/ 2307- (void) releaseGState 2308{ 2309 if (_allocate_gstate && _gstate && 2310 _window && ([_window graphicsContext] != nil)) 2311 { 2312 GSUndefineGState([_window graphicsContext], _gstate); 2313 } 2314 _gstate = 0; 2315 _allocate_gstate = NO; 2316} 2317 2318/** 2319 Returns an identifier that represents the view's gstate object, 2320 which is used to encapsulate drawing information about the view. 2321 Most of the time a gstate object is created from scratch when the 2322 view is focused, so if the view is not currently focused or 2323 allocateGState has not been called, then this method will return 0. 2324 FIXME: The above is what the OpenStep and Cocoa specification say, but 2325 gState is 0 unless allocateGState has been called. 2326*/ 2327- (NSInteger) gState 2328{ 2329 if (_allocate_gstate && (!_gstate || _renew_gstate)) 2330 { 2331 // Set the gstate by locking and unlocking focus. 2332 [self lockFocus]; 2333 [self unlockFocusNeedsFlush: NO]; 2334 } 2335 2336 return _gstate; 2337} 2338 2339/** 2340 Invalidates the view's gstate object so it will be set up again 2341 using setUpGState the next time the view is focused. */ 2342- (void) renewGState 2343{ 2344 _renew_gstate = YES; 2345 /* Note that the next time we lock focus, we'll realloc a gstate (if 2346 _allocate_gstate). This seems to make sense, and also allows us 2347 to call this method each time we invalidate the coordinates */ 2348} 2349 2350/* Overridden by subclasses to setup custom gstate */ 2351- (void) setUpGState 2352{ 2353} 2354 2355- (void) lockFocusInRect: (NSRect)rect 2356{ 2357 [self _lockFocusInContext: nil inRect: rect]; 2358} 2359 2360- (void) lockFocus 2361{ 2362 [self lockFocusInRect: [self visibleRect]]; 2363} 2364 2365- (void) unlockFocus 2366{ 2367 [self unlockFocusNeedsFlush: YES]; 2368} 2369 2370- (BOOL) lockFocusIfCanDraw 2371{ 2372 return [self lockFocusIfCanDrawInContext: nil]; 2373} 2374 2375- (BOOL) lockFocusIfCanDrawInContext: (NSGraphicsContext *)context 2376{ 2377 if ([self canDraw]) 2378 { 2379 [self _lockFocusInContext: context inRect: [self visibleRect]]; 2380 return YES; 2381 } 2382 else 2383 { 2384 return NO; 2385 } 2386} 2387 2388- (BOOL) canDraw 2389{ 2390 if (((viewIsPrinting != nil) && [self isDescendantOf: viewIsPrinting]) || 2391 ((_window != nil) && ([_window windowNumber] != 0) && 2392 ![self isHiddenOrHasHiddenAncestor])) 2393 { 2394 return YES; 2395 } 2396 else 2397 { 2398 return NO; 2399 } 2400} 2401 2402/* 2403 * The following display* methods work based on these invariants: 2404 * - When a view is marked as needing display, all views above it 2405 * in the hierarchy are marked as well. 2406 * - When a view has an invalid rectangle, all views above it up 2407 * to the next opaque view also include this invalid rectangle. 2408 * 2409 * After drawing an area in a view give, subviews a chance to draw 2410 * there too. 2411 * When drawing a non-opaque subview we need to make sure any area 2412 * we draw in has been drawn by the opaque superview as well. 2413 * 2414 * When drawing the invalid area of a view, we need to make sure 2415 * that invalid areas in opaque subviews get drawn as well. These 2416 * areas will not be included in the invalid area of the view. 2417 * 2418 * IfNeeded means we only draw if the view is marked as needing display 2419 * and will only draw in the _invalidRect of this view and that of all 2420 * the opaque subviews. For non-opaque subviews we need to draw where 2421 * ever a superview has already drawn. 2422 * 2423 * InRect means we will only draw in this rectangle. If non is given the 2424 * visibleRect gets used. 2425 * 2426 * IgnoringOpacity means we start drawing at the current view. Otherwise 2427 * we go up to the next opaque view. 2428 * 2429 */ 2430 2431- (void) display 2432{ 2433 [self displayRect: [self visibleRect]]; 2434} 2435 2436- (void) displayIfNeeded 2437{ 2438 if (_rFlags.needs_display == YES) 2439 { 2440 [self displayIfNeededInRect: [self visibleRect]]; 2441 } 2442} 2443 2444- (void) displayIfNeededIgnoringOpacity 2445{ 2446 if (_rFlags.needs_display == YES) 2447 { 2448 [self displayIfNeededInRectIgnoringOpacity: [self visibleRect]]; 2449 } 2450} 2451 2452- (void) displayIfNeededInRect: (NSRect)aRect 2453{ 2454 if (_rFlags.needs_display == YES) 2455 { 2456 if ([self isOpaque] == YES) 2457 { 2458 [self displayIfNeededInRectIgnoringOpacity: aRect]; 2459 } 2460 else 2461 { 2462 NSView *firstOpaque = [self opaqueAncestor]; 2463 2464 aRect = [firstOpaque convertRect: aRect fromView: self]; 2465 [firstOpaque displayIfNeededInRectIgnoringOpacity: aRect]; 2466 } 2467 } 2468} 2469 2470- (void) displayIfNeededInRectIgnoringOpacity: (NSRect)aRect 2471{ 2472 if (_rFlags.needs_display == YES) 2473 { 2474 NSRect rect; 2475 2476 /* 2477 * Restrict the drawing of self onto the invalid rectangle. 2478 */ 2479 rect = NSIntersectionRect(aRect, _invalidRect); 2480 [self displayRectIgnoringOpacity: rect]; 2481 2482 /* 2483 * If we still need display after displaying the invalid rectangle, 2484 * this means that some subviews still need to display. 2485 * For opaque subviews their invalid rectangle may even overlap the 2486 * original aRect. 2487 * Display any subview that need display. 2488 */ 2489 if (_rFlags.needs_display == YES) 2490 { 2491 NSEnumerator *enumerator = [_sub_views objectEnumerator]; 2492 NSView *subview; 2493 BOOL subviewNeedsDisplay = NO; 2494 2495 while ((subview = [enumerator nextObject]) != nil) 2496 { 2497 if (subview->_rFlags.needs_display) 2498 { 2499 NSRect subviewFrame = [subview _frameExtend]; 2500 NSRect isect; 2501 2502 isect = NSIntersectionRect(aRect, subviewFrame); 2503 if (NSIsEmptyRect(isect) == NO) 2504 { 2505 isect = [subview convertRect: isect fromView: self]; 2506 [subview displayIfNeededInRectIgnoringOpacity: isect]; 2507 } 2508 2509 if (subview->_rFlags.needs_display) 2510 { 2511 subviewNeedsDisplay = YES; 2512 } 2513 } 2514 } 2515 /* 2516 * Make sure our needs_display flag matches that of the subviews. 2517 * Only set to NO when there is no _invalidRect. 2518 */ 2519 if (NSIsEmptyRect(_invalidRect)) 2520 { 2521 _rFlags.needs_display = subviewNeedsDisplay; 2522 } 2523 } 2524 } 2525} 2526 2527/** 2528 * Causes the area of the view specified by aRect to be displayed. 2529 * This is done by moving up the view hierarchy until an opaque view 2530 * is found, then asking that view to update the appropriate area. 2531 */ 2532- (void) displayRect: (NSRect)aRect 2533{ 2534 if ([self isOpaque] == YES) 2535 { 2536 [self displayRectIgnoringOpacity: aRect]; 2537 } 2538 else 2539 { 2540 NSView *firstOpaque = [self opaqueAncestor]; 2541 2542 aRect = [firstOpaque convertRect: aRect fromView: self]; 2543 [firstOpaque displayRectIgnoringOpacity: aRect]; 2544 } 2545} 2546 2547- (void) displayRectIgnoringOpacity: (NSRect)aRect 2548{ 2549 [self displayRectIgnoringOpacity: aRect inContext: nil]; 2550} 2551 2552- (void) displayRectIgnoringOpacity: (NSRect)aRect 2553 inContext: (NSGraphicsContext *)context 2554{ 2555 NSGraphicsContext *wContext; 2556 BOOL flush = NO; 2557 BOOL subviewNeedsDisplay = NO; 2558 2559 if (![self canDraw]) 2560 { 2561 return; 2562 } 2563 2564 wContext = [_window graphicsContext]; 2565 if (context == nil) 2566 { 2567 context = wContext; 2568 } 2569 2570 if (context == wContext) 2571 { 2572 NSRect neededRect; 2573 NSRect visibleRect = [self visibleRect]; 2574 2575 flush = YES; 2576 [_window disableFlushWindow]; 2577 aRect = NSIntersectionRect(aRect, visibleRect); 2578 neededRect = NSIntersectionRect(_invalidRect, visibleRect); 2579 2580 /* 2581 * If the rect we are going to display contains the _invalidRect 2582 * then we can empty _invalidRect. Do this before the drawing, 2583 * as drawRect: may change this value. 2584 * FIXME: If the drawn rectangle cuts of a complete part of the 2585 * _invalidRect, we should try to reduce this. 2586 */ 2587 if (NSEqualRects(aRect, NSUnionRect(neededRect, aRect)) == YES) 2588 { 2589 _invalidRect = NSZeroRect; 2590 _rFlags.needs_display = NO; 2591 } 2592 } 2593 2594 if (NSIsEmptyRect(aRect) == NO) 2595 { 2596 /* 2597 * Now we draw this view. 2598 */ 2599 [self _lockFocusInContext: context inRect: aRect]; 2600 [self drawRect: aRect]; 2601 [self unlockFocusNeedsFlush: flush]; 2602 } 2603 2604 /* 2605 * Even when aRect is empty we need to loop over the subviews to see, 2606 * if there is anything left to draw. 2607 */ 2608 if (_rFlags.has_subviews == YES) 2609 { 2610 NSUInteger count = [_sub_views count]; 2611 2612 if (count > 0) 2613 { 2614 NSView *array[count]; 2615 NSUInteger i; 2616 2617 [_sub_views getObjects: array]; 2618 2619 for (i = 0; i < count; ++i) 2620 { 2621 NSView *subview = array[i]; 2622 NSRect subviewFrame = [subview _frameExtend]; 2623 NSRect isect; 2624 2625 /* 2626 * Having drawn ourself into the rect, we must make sure that 2627 * subviews overlapping the area are redrawn. 2628 */ 2629 isect = NSIntersectionRect(aRect, subviewFrame); 2630 if (NSIsEmptyRect(isect) == NO) 2631 { 2632 isect = [subview convertRect: isect fromView: self]; 2633 [subview displayRectIgnoringOpacity: isect 2634 inContext: context]; 2635 } 2636 /* 2637 * Is there still something to draw in the subview? 2638 * This keeps the invariant that views further up are marked 2639 * for redraw when ever a view further down needs to redraw. 2640 */ 2641 if (subview->_rFlags.needs_display == YES) 2642 { 2643 subviewNeedsDisplay = YES; 2644 } 2645 } 2646 } 2647 } 2648 2649 if (context == wContext) 2650 { 2651 if (subviewNeedsDisplay) 2652 { 2653 /* 2654 * If not all subviews have been fully displayed, we cannot turn off 2655 * the 'needs_display' flag. This is to keep the invariant that when 2656 * a view is marked as needing to display, all its ancestors will be 2657 * marked too. 2658 */ 2659 _rFlags.needs_display = YES; 2660 } 2661 [_window enableFlushWindow]; 2662 [_window flushWindowIfNeeded]; 2663 } 2664} 2665 2666/** 2667 This method is invoked to handle drawing inside the view. The 2668 default NSView's implementation does nothing; subclasses might 2669 override it to draw something inside the view. Since NSView's 2670 implementation is guaranteed to be empty, you should not call 2671 super's implementation when you override it in subclasses. 2672 drawRect: is invoked when the focus has already been locked on the 2673 view; you can use arbitrary postscript functions in drawRect: to 2674 draw inside your view; the coordinate system in which you draw is 2675 the view's own coordinate system (this means for example that you 2676 should refer to the rectangle covered by the view using its bounds, 2677 and not its frame). The argument of drawRect: is the rectangle 2678 which needs to be redrawn. In a lossy implementation, you can 2679 ignore the argument and redraw the whole view; if you are aiming at 2680 performance, you may want to redraw only what is inside the 2681 rectangle which needs to be redrawn; this usually improves drawing 2682 performance considerably. */ 2683- (void) drawRect: (NSRect)rect 2684{} 2685 2686- (NSRect) visibleRect 2687{ 2688 if ([self isHiddenOrHasHiddenAncestor]) 2689 { 2690 return NSZeroRect; 2691 } 2692 2693 if (_coordinates_valid == NO) 2694 { 2695 [self _rebuildCoordinates]; 2696 } 2697 return _visibleRect; 2698} 2699 2700- (BOOL) wantsDefaultClipping 2701{ 2702 return YES; 2703} 2704 2705- (BOOL) needsToDrawRect: (NSRect)aRect 2706{ 2707 const NSRect *rects; 2708 NSInteger i, count; 2709 2710 [self getRectsBeingDrawn: &rects count: &count]; 2711 for (i = 0; i < count; i++) 2712 { 2713 if (NSIntersectsRect(aRect, rects[i])) 2714 return YES; 2715 } 2716 return NO; 2717} 2718 2719- (void) getRectsBeingDrawn: (const NSRect **)rects count: (NSInteger *)count 2720{ 2721 // FIXME 2722 static NSRect rect; 2723 2724 rect = [[_window->_rectsBeingDrawn lastObject] rectValue]; 2725 rect = [self convertRect: rect fromView: nil]; 2726 2727 if (rects != NULL) 2728 { 2729 *rects = ▭ 2730 } 2731 2732 if (count != NULL) 2733 { 2734 *count = 1; 2735 } 2736} 2737 2738- (NSBitmapImageRep *) bitmapImageRepForCachingDisplayInRect: (NSRect)rect 2739{ 2740 NSBitmapImageRep *bitmap; 2741 2742 [self lockFocus]; 2743 bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect: rect]; 2744 [self unlockFocus]; 2745 2746 return AUTORELEASE(bitmap); 2747} 2748 2749- (void) cacheDisplayInRect: (NSRect)rect 2750 toBitmapImageRep: (NSBitmapImageRep *)bitmap 2751{ 2752 NSDictionary *dict; 2753 NSData *imageData; 2754 2755 [self lockFocus]; 2756 dict = [GSCurrentContext() GSReadRect: rect]; 2757 [self unlockFocus]; 2758 imageData = [dict objectForKey: @"Data"]; 2759 2760 if (imageData != nil) 2761 { 2762 // Copy the image data to the bitmap 2763 memcpy([bitmap bitmapData], [imageData bytes], [imageData length]); 2764 } 2765} 2766 2767 2768extern NSThread *GSAppKitThread; /* TODO */ 2769 2770/* 2771For -setNeedsDisplay*, the real work is done in the ..._real methods, and 2772the actual public method simply calls it, but makes sure that the call is 2773in the main thread. 2774*/ 2775 2776- (void) _setNeedsDisplay_real: (NSNumber *)n 2777{ 2778 BOOL flag = [n boolValue]; 2779 2780 if (flag) 2781 { 2782 [self setNeedsDisplayInRect: _bounds]; 2783 } 2784 else 2785 { 2786 _rFlags.needs_display = NO; 2787 _invalidRect = NSZeroRect; 2788 } 2789} 2790 2791/** 2792 * As an exception to the general rules for threads and gui, this 2793 * method is thread-safe and may be called from any thread. Display 2794 * will always be done in the main thread. (Note that other methods are 2795 * in general not thread-safe; if you want to access other properties of 2796 * views from multiple threads, you need to provide the synchronization.) 2797 */ 2798- (void) setNeedsDisplay: (BOOL)flag 2799{ 2800 NSNumber *n = [[NSNumber alloc] initWithBool: flag]; 2801 if (GSCurrentThread() != GSAppKitThread) 2802 { 2803 NSDebugMLLog (@"MacOSXCompatibility", 2804 @"setNeedsDisplay: called on secondary thread"); 2805 [self performSelectorOnMainThread: @selector(_setNeedsDisplay_real:) 2806 withObject: n 2807 waitUntilDone: NO]; 2808 } 2809 else 2810 { 2811 [self _setNeedsDisplay_real: n]; 2812 } 2813 DESTROY(n); 2814} 2815 2816 2817- (void) _setNeedsDisplayInRect_real: (NSValue *)v 2818{ 2819 NSRect invalidRect = [v rectValue]; 2820 NSView *currentView = _super_view; 2821 2822 /* 2823 * Limit to bounds, combine with old _invalidRect, and then check to see 2824 * if the result is the same as the old _invalidRect - if it isn't then 2825 * set the new _invalidRect. 2826 */ 2827 invalidRect = NSIntersectionRect(invalidRect, _bounds); 2828 invalidRect = NSUnionRect(_invalidRect, invalidRect); 2829 if (NSEqualRects(invalidRect, _invalidRect) == NO) 2830 { 2831 NSView *firstOpaque = [self opaqueAncestor]; 2832 2833 _rFlags.needs_display = YES; 2834 _invalidRect = invalidRect; 2835 if (firstOpaque == self) 2836 { 2837 /** 2838 * Enlarge (if necessary) _invalidRect so it lies on integral device pixels 2839 */ 2840 const NSRect inBase = [self convertRectToBase: _invalidRect]; 2841 const NSRect inBaseRounded = NSIntegralRect(inBase); 2842 _invalidRect = [self convertRectFromBase: inBaseRounded]; 2843 2844 [_window setViewsNeedDisplay: YES]; 2845 } 2846 else 2847 { 2848 invalidRect = [firstOpaque convertRect: _invalidRect fromView: self]; 2849 [firstOpaque setNeedsDisplayInRect: invalidRect]; 2850 } 2851 } 2852 2853 /* 2854 * Must make sure that superviews know that we need display. 2855 * NB. we may have been marked as needing display and then moved to another 2856 * parent, so we can't assume that our parent is marked simply because we are. 2857 */ 2858 while (currentView) 2859 { 2860 currentView->_rFlags.needs_display = YES; 2861 currentView = currentView->_super_view; 2862 } 2863 // Also mark the window, as this may not happen above 2864 [_window setViewsNeedDisplay: YES]; 2865} 2866 2867/** 2868 * Inform the view system that the specified rectangle is invalid and 2869 * requires updating. This automatically informs any superviews of 2870 * any updating they need to do. 2871 * 2872 * As an exception to the general rules for threads and gui, this 2873 * method is thread-safe and may be called from any thread. Display 2874 * will always be done in the main thread. (Note that other methods are 2875 * in general not thread-safe; if you want to access other properties of 2876 * views from multiple threads, you need to provide the synchronization.) 2877 */ 2878- (void) setNeedsDisplayInRect: (NSRect)invalidRect 2879{ 2880 NSValue *v; 2881 2882 if (NSIsEmptyRect(invalidRect)) 2883 return; // avoid unnecessary work when rectangle is empty 2884 2885 v = [[NSValue alloc] 2886 initWithBytes: &invalidRect 2887 objCType: @encode(NSRect)]; 2888 2889 if (GSCurrentThread() != GSAppKitThread) 2890 { 2891 NSDebugMLLog (@"MacOSXCompatibility", 2892 @"setNeedsDisplayInRect: called on secondary thread"); 2893 [self performSelectorOnMainThread: @selector(_setNeedsDisplayInRect_real:) 2894 withObject: v 2895 waitUntilDone: NO]; 2896 } 2897 else 2898 { 2899 [self _setNeedsDisplayInRect_real: v]; 2900 } 2901 DESTROY(v); 2902} 2903 2904+ (NSFocusRingType) defaultFocusRingType 2905{ 2906 return NSFocusRingTypeDefault; 2907} 2908 2909- (void) setKeyboardFocusRingNeedsDisplayInRect: (NSRect)rect 2910{ 2911 // FIXME For external type special handling is needed 2912 [self setNeedsDisplayInRect: rect]; 2913} 2914 2915- (void) setFocusRingType: (NSFocusRingType)focusRingType 2916{ 2917 _focusRingType = focusRingType; 2918} 2919 2920- (NSFocusRingType) focusRingType 2921{ 2922 return _focusRingType; 2923} 2924 2925/* 2926 * Hidding Views 2927 */ 2928- (void) setHidden: (BOOL)flag 2929{ 2930 id view; 2931 2932 if (_is_hidden == flag) 2933 return; 2934 2935 _is_hidden = flag; 2936 2937 if (_is_hidden) 2938 { 2939 for (view = [_window firstResponder]; 2940 view != nil && [view respondsToSelector: @selector(superview)]; 2941 view = [view superview]) 2942 { 2943 if (view == self) 2944 { 2945 [_window makeFirstResponder: [self nextValidKeyView]]; 2946 break; 2947 } 2948 } 2949 if (_rFlags.has_draginfo) 2950 { 2951 if (_window != nil) 2952 { 2953 NSArray *t = GSGetDragTypes(self); 2954 2955 [GSDisplayServer removeDragTypes: t fromWindow: _window]; 2956 } 2957 } 2958 [[self superview] setNeedsDisplay: YES]; 2959 } 2960 else 2961 { 2962 if (_rFlags.has_draginfo) 2963 { 2964 if (_window != nil) 2965 { 2966 NSArray *t = GSGetDragTypes(self); 2967 2968 [GSDisplayServer addDragTypes: t toWindow: _window]; 2969 } 2970 } 2971 if (_rFlags.has_subviews) 2972 { 2973 // The _visibleRect of subviews will be NSZeroRect, because when they 2974 // were calculated in -[_rebuildCoordinates], they were intersected 2975 // with the result of calling -[visibleRect] on the hidden superview, 2976 // which returns NSZeroRect for hidden views. 2977 // 2978 // So, recalculate the subview coordinates now to make them correct. 2979 2980 [_sub_views makeObjectsPerformSelector: 2981 @selector(_invalidateCoordinates)]; 2982 } 2983 [self setNeedsDisplay: YES]; 2984 } 2985} 2986 2987- (BOOL) isHidden 2988{ 2989 return _is_hidden; 2990} 2991 2992- (BOOL) isHiddenOrHasHiddenAncestor 2993{ 2994 return ([self isHidden] || [_super_view isHiddenOrHasHiddenAncestor]); 2995} 2996 2997/* 2998 * Live resize support 2999 */ 3000- (BOOL) inLiveResize 3001{ 3002 return _in_live_resize; 3003} 3004 3005- (void) viewWillStartLiveResize 3006{ 3007 // FIXME 3008 _in_live_resize = YES; 3009} 3010 3011- (void) viewDidEndLiveResize 3012{ 3013 // FIXME 3014 _in_live_resize = NO; 3015} 3016 3017- (BOOL) preservesContentDuringLiveResize 3018{ 3019 return NO; 3020} 3021 3022- (void) getRectsExposedDuringLiveResize: (NSRect[4])exposedRects count: (NSInteger *)count 3023{ 3024 // FIXME 3025 if (count != NULL) 3026 { 3027 *count = 1; 3028 } 3029 exposedRects[0] = _bounds; 3030} 3031 3032- (NSRect) rectPreservedDuringLiveResize 3033{ 3034 return NSZeroRect; 3035} 3036 3037/* 3038 * Scrolling 3039 */ 3040- (NSRect) adjustScroll: (NSRect)newVisible 3041{ 3042 return newVisible; 3043} 3044 3045/** 3046 * Finds the nearest enclosing NSClipView and, if the location of the event 3047 * is outside it, scrolls the NSClipView in the direction of the event. The 3048 * amount scrolled is proportional to how far outside the NSClipView the 3049 * event's location is. 3050 * 3051 * This method is suitable for calling periodically from a modal event 3052 * tracking loop when the mouse is dragged outside the tracking view. The 3053 * suggested period of the calls is 0.1 seconds. 3054 */ 3055- (BOOL) autoscroll: (NSEvent*)theEvent 3056{ 3057 if (_super_view) 3058 return [_super_view autoscroll: theEvent]; 3059 3060 return NO; 3061} 3062 3063- (void) reflectScrolledClipView: (NSClipView*)aClipView 3064{ 3065} 3066 3067- (void) scrollClipView: (NSClipView*)aClipView toPoint: (NSPoint)aPoint 3068{ 3069 [aClipView scrollToPoint: aPoint]; 3070} 3071 3072- (NSClipView*) _enclosingClipView 3073{ 3074 static Class clipViewClass; 3075 id aView = [self superview]; 3076 3077 if (!clipViewClass) 3078 { 3079 clipViewClass = [NSClipView class]; 3080 } 3081 3082 while (aView != nil) 3083 { 3084 if ([aView isKindOfClass: clipViewClass]) 3085 { 3086 break; 3087 } 3088 aView = [aView superview]; 3089 } 3090 3091 return aView; 3092} 3093 3094- (void) scrollPoint: (NSPoint)aPoint 3095{ 3096 NSClipView *s = [self _enclosingClipView]; 3097 3098 if (s == nil) 3099 return; 3100 3101 aPoint = [self convertPoint: aPoint toView: s]; 3102 if (NSEqualPoints(aPoint, [s bounds].origin) == NO) 3103 { 3104 [s scrollToPoint: aPoint]; 3105 } 3106} 3107 3108/** 3109 Copy on scroll method, should be called from [NSClipView setBoundsOrigin]. 3110 */ 3111- (void) scrollRect: (NSRect)aRect by: (NSSize)delta 3112{ 3113 NSPoint destPoint; 3114 3115 aRect = NSIntersectionRect(aRect, _bounds); // Don't copy stuff outside. 3116 destPoint = aRect.origin; 3117 destPoint.x += delta.width; 3118 destPoint.y += delta.height; 3119 if ([self isFlipped]) 3120 { 3121 destPoint.y += aRect.size.height; 3122 } 3123 3124 //NSLog(@"destPoint %@ in %@", NSStringFromPoint(destPoint), NSStringFromRect(_bounds)); 3125 3126 [self lockFocus]; 3127 //NSCopyBits(0, aRect, destPoint); 3128 NSCopyBits([[self window] gState], [self convertRect: aRect toView: nil], destPoint); 3129 [self unlockFocus]; 3130} 3131 3132/** 3133Scrolls the nearest enclosing clip view the minimum required distance 3134necessary to make aRect (or as much of it possible) in the receiver visible. 3135Returns YES iff any scrolling was done. 3136*/ 3137- (BOOL) scrollRectToVisible: (NSRect)aRect 3138{ 3139 NSClipView *s = [self _enclosingClipView]; 3140 3141 if (s != nil) 3142 { 3143 NSRect vRect = [s documentVisibleRect]; 3144 NSPoint aPoint = vRect.origin; 3145 // Ok we assume that the rectangle is origined at the bottom left 3146 // and goes to the top and right as it grows in size for the naming 3147 // of these variables 3148 CGFloat ldiff, rdiff, tdiff, bdiff; 3149 3150 if (vRect.size.width == 0 && vRect.size.height == 0) 3151 return NO; 3152 3153 aRect = [self convertRect: aRect toView: [s documentView]]; 3154 3155 // Find the differences on each side. 3156 ldiff = NSMinX(vRect) - NSMinX(aRect); 3157 rdiff = NSMaxX(aRect) - NSMaxX(vRect); 3158 bdiff = NSMinY(vRect) - NSMinY(aRect); 3159 tdiff = NSMaxY(aRect) - NSMaxY(vRect); 3160 3161 // If the diff's have the same sign then nothing needs to be scrolled 3162 if ((ldiff * rdiff) >= 0.0) ldiff = rdiff = 0.0; 3163 if ((bdiff * tdiff) >= 0.0) bdiff = tdiff = 0.0; 3164 3165 // Move the smallest difference 3166 aPoint.x += (fabs(ldiff) < fabs(rdiff)) ? (-ldiff) : rdiff; 3167 aPoint.y += (fabs(bdiff) < fabs(tdiff)) ? (-bdiff) : tdiff; 3168 3169 if (aPoint.x != vRect.origin.x || aPoint.y != vRect.origin.y) 3170 { 3171 aPoint = [[s documentView] convertPoint: aPoint toView: s]; 3172 [s scrollToPoint: aPoint]; 3173 return YES; 3174 } 3175 } 3176 return NO; 3177} 3178 3179- (NSScrollView*) enclosingScrollView 3180{ 3181 static Class scrollViewClass; 3182 id aView = [self superview]; 3183 3184 if (!scrollViewClass) 3185 { 3186 scrollViewClass = [NSScrollView class]; 3187 } 3188 3189 while (aView != nil) 3190 { 3191 if ([aView isKindOfClass: scrollViewClass]) 3192 { 3193 break; 3194 } 3195 aView = [aView superview]; 3196 } 3197 3198 return aView; 3199} 3200 3201/* 3202 * Managing the Cursor 3203 * 3204 * We use the tracking rectangle class to maintain the cursor rects 3205 */ 3206- (void) addCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject 3207{ 3208 if (_window != nil) 3209 { 3210 GSTrackingRect *m; 3211 3212 aRect = [self convertRect: aRect toView: nil]; 3213 m = [rectClass allocWithZone: NSDefaultMallocZone()]; 3214 m = [m initWithRect: aRect 3215 tag: 0 3216 owner: RETAIN(anObject) 3217 userData: NULL 3218 inside: YES]; 3219 [_cursor_rects addObject: m]; 3220 RELEASE(m); 3221 _rFlags.has_currects = 1; 3222 _rFlags.valid_rects = 1; 3223 } 3224} 3225 3226- (void) discardCursorRects 3227{ 3228 if (_rFlags.has_currects != 0) 3229 { 3230 NSUInteger count = [_cursor_rects count]; 3231 3232 if (count > 0) 3233 { 3234 GSTrackingRect *rects[count]; 3235 3236 [_cursor_rects getObjects: rects]; 3237 if (_rFlags.valid_rects != 0) 3238 { 3239 NSPoint loc = _window->_lastPoint; 3240 NSUInteger i; 3241 3242 for (i = 0; i < count; ++i) 3243 { 3244 GSTrackingRect *r = rects[i]; 3245 if (NSMouseInRect(loc, r->rectangle, NO)) 3246 { 3247 [r->owner mouseExited: nil]; 3248 } 3249 [r invalidate]; 3250 } 3251 _rFlags.valid_rects = 0; 3252 } 3253 while (count-- > 0) 3254 { 3255 RELEASE([rects[count] owner]); 3256 } 3257 [_cursor_rects removeAllObjects]; 3258 } 3259 _rFlags.has_currects = 0; 3260 } 3261} 3262 3263- (void) removeCursorRect: (NSRect)aRect cursor: (NSCursor*)anObject 3264{ 3265 id e = [_cursor_rects objectEnumerator]; 3266 GSTrackingRect *o; 3267 NSCursor *c; 3268 NSPoint loc = [_window mouseLocationOutsideOfEventStream]; 3269 3270 /* Base remove test upon cursor object */ 3271 o = [e nextObject]; 3272 while (o) 3273 { 3274 c = [o owner]; 3275 if (c == anObject) 3276 { 3277 if (NSMouseInRect(loc, o->rectangle, NO)) 3278 { 3279 [c mouseExited: nil]; 3280 } 3281 [o invalidate]; 3282 [_cursor_rects removeObject: o]; 3283 if ([_cursor_rects count] == 0) 3284 { 3285 _rFlags.has_currects = 0; 3286 _rFlags.valid_rects = 0; 3287 } 3288 RELEASE(c); 3289 break; 3290 } 3291 else 3292 { 3293 o = [e nextObject]; 3294 } 3295 } 3296} 3297 3298- (void) resetCursorRects 3299{ 3300} 3301 3302static NSView* findByTag(NSView *view, NSInteger aTag, NSUInteger *level) 3303{ 3304 NSUInteger i, count; 3305 NSArray *sub = [view subviews]; 3306 3307 count = [sub count]; 3308 if (count > 0) 3309 { 3310 NSView *array[count]; 3311 3312 [sub getObjects: array]; 3313 3314 for (i = 0; i < count; i++) 3315 { 3316 if ([array[i] tag] == aTag) 3317 return array[i]; 3318 } 3319 *level += 1; 3320 for (i = 0; i < count; i++) 3321 { 3322 NSView *v; 3323 3324 v = findByTag(array[i], aTag, level); 3325 if (v != nil) 3326 return v; 3327 } 3328 *level -= 1; 3329 } 3330 return nil; 3331} 3332 3333- (id) viewWithTag: (NSInteger)aTag 3334{ 3335 NSView *view = nil; 3336 3337 /* 3338 * If we have the specified tag - return self. 3339 */ 3340 if ([self tag] == aTag) 3341 { 3342 view = self; 3343 } 3344 else if (_rFlags.has_subviews) 3345 { 3346 NSUInteger count = [_sub_views count]; 3347 3348 if (count > 0) 3349 { 3350 NSView *array[count]; 3351 NSUInteger i; 3352 3353 [_sub_views getObjects: array]; 3354 3355 /* 3356 * Quick check to see if any of our direct descendents has the tag. 3357 */ 3358 for (i = 0; i < count; i++) 3359 { 3360 NSView *subView = array[i]; 3361 3362 if ([subView tag] == aTag) 3363 { 3364 view = subView; 3365 break; 3366 } 3367 } 3368 3369 if (view == nil) 3370 { 3371 NSUInteger level = 0xffffffff; 3372 3373 /* 3374 * Ok - do it the long way - search the whole tree for each of 3375 * our descendents and see which has the closest view matching 3376 * the tag. 3377 */ 3378 for (i = 0; i < count; i++) 3379 { 3380 NSUInteger l = 0; 3381 NSView *v; 3382 3383 v = findByTag(array[i], aTag, &l); 3384 3385 if (v != nil && l < level) 3386 { 3387 view = v; 3388 level = l; 3389 } 3390 } 3391 } 3392 } 3393 } 3394 return view; 3395} 3396 3397/* 3398 * Aiding Event Handling 3399 */ 3400 3401/** 3402 * Returns YES if the view object will accept the first 3403 * click received when in an inactive window, and NO 3404 * otherwise. 3405 */ 3406- (BOOL) acceptsFirstMouse: (NSEvent*)theEvent 3407{ 3408 return NO; 3409} 3410 3411/** 3412 * Returns the subview, lowest in the receiver's hierarchy, which 3413 * contains aPoint, or nil if there is no such view. 3414 */ 3415- (NSView*) hitTest: (NSPoint)aPoint 3416{ 3417 NSPoint p; 3418 NSView *v = nil, *w; 3419 3420 /* If not within our frame then it can't be a hit. 3421 3422 As a special case, always assume that it's a hit if our _super_view is nil, 3423 ie. if we're the top-level view in a window. 3424 */ 3425 3426 if ([self isHidden]) 3427 { 3428 return nil; 3429 } 3430 3431 if (_is_rotated_or_scaled_from_base) 3432 { 3433 p = [self convertPoint: aPoint fromView: _super_view]; 3434 if (!NSPointInRect (p, _bounds)) 3435 { 3436 return nil; 3437 } 3438 } 3439 else if (_super_view && ![_super_view mouse: aPoint inRect: _frame]) 3440 { 3441 return nil; 3442 } 3443 else 3444 { 3445 p = [self convertPoint: aPoint fromView: _super_view]; 3446 } 3447 3448 if (_rFlags.has_subviews) 3449 { 3450 NSUInteger count; 3451 3452 count = [_sub_views count]; 3453 if (count > 0) 3454 { 3455 NSView *array[count]; 3456 3457 [_sub_views getObjects: array]; 3458 3459 while (count > 0) 3460 { 3461 w = array[--count]; 3462 v = [w hitTest: p]; 3463 if (v) 3464 break; 3465 } 3466 } 3467 } 3468 /* 3469 * mouse is either in the subview or within self 3470 */ 3471 if (v) 3472 return v; 3473 else 3474 return self; 3475} 3476 3477/** 3478 * Returns whether or not aPoint lies within aRect. 3479 */ 3480- (BOOL) mouse: (NSPoint)aPoint inRect: (NSRect)aRect 3481{ 3482 return NSMouseInRect (aPoint, aRect, [self isFlipped]); 3483} 3484 3485- (BOOL) performKeyEquivalent: (NSEvent*)theEvent 3486{ 3487 NSUInteger i; 3488 3489 for (i = 0; i < [_sub_views count]; i++) 3490 if ([[_sub_views objectAtIndex: i] performKeyEquivalent: theEvent] == YES) 3491 return YES; 3492 return NO; 3493} 3494 3495- (BOOL) performMnemonic: (NSString *)aString 3496{ 3497 NSUInteger i; 3498 3499 for (i = 0; i < [_sub_views count]; i++) 3500 if ([[_sub_views objectAtIndex: i] performMnemonic: aString] == YES) 3501 return YES; 3502 return NO; 3503} 3504 3505- (BOOL) mouseDownCanMoveWindow 3506{ 3507 return ![self isOpaque]; 3508} 3509 3510- (void) removeTrackingRect: (NSTrackingRectTag)tag 3511{ 3512 NSUInteger i, j; 3513 GSTrackingRect *m; 3514 3515 j = [_tracking_rects count]; 3516 for (i = 0;i < j; ++i) 3517 { 3518 m = (GSTrackingRect*)[_tracking_rects objectAtIndex: i]; 3519 if ([m tag] == tag) 3520 { 3521 [m invalidate]; 3522 [_tracking_rects removeObjectAtIndex: i]; 3523 if ([_tracking_rects count] == 0) 3524 { 3525 _rFlags.has_trkrects = 0; 3526 } 3527 return; 3528 } 3529 } 3530} 3531 3532- (BOOL) shouldDelayWindowOrderingForEvent: (NSEvent*)anEvent 3533{ 3534 return NO; 3535} 3536 3537- (NSTrackingRectTag) addTrackingRect: (NSRect)aRect 3538 owner: (id)anObject 3539 userData: (void*)data 3540 assumeInside: (BOOL)flag 3541{ 3542 NSTrackingRectTag t; 3543 NSUInteger i, j; 3544 GSTrackingRect *m; 3545 3546 t = 0; 3547 j = [_tracking_rects count]; 3548 for (i = 0; i < j; ++i) 3549 { 3550 m = (GSTrackingRect*)[_tracking_rects objectAtIndex: i]; 3551 if ([m tag] > t) 3552 t = [m tag]; 3553 } 3554 ++t; 3555 3556 m = [[rectClass alloc] initWithRect: aRect 3557 tag: t 3558 owner: anObject 3559 userData: data 3560 inside: flag]; 3561 [_tracking_rects addObject: m]; 3562 RELEASE(m); 3563 _rFlags.has_trkrects = 1; 3564 return t; 3565} 3566 3567-(BOOL) needsPanelToBecomeKey 3568{ 3569 return NO; 3570} 3571 3572 3573/** 3574 * <p>The effect of the -setNextKeyView: method is to set aView to be the 3575 * value returned by subsequent calls to the receivers -nextKeyView method. 3576 * This also has the effect of setting the previous key view of aView, 3577 * so that subsequent calls to its -previousKeyView method will return 3578 * the receiver. 3579 * </p> 3580 * <p>As a special case, if you pass nil as aView then the -previousKeyView 3581 * of the receivers current -nextKeyView is set to nil as well as the 3582 * receivers -nextKeyView being set to nil.<br /> 3583 * This behavior provides MacOS-X compatibility. 3584 * </p> 3585 * <p>If you pass a non-view object other than nil, an 3586 * NSInternaInconsistencyException is raised. 3587 * </p> 3588 * <p><strong>NB</strong> This method does <em>NOT</em> cause aView to be 3589 * retained, and if aView is deallocated, the [NSView-dealloc] method will 3590 * automatically remove it from the key view chain it is in. 3591 * </p> 3592 * <p>For keyboard navigation, views are linked together in a chain, so that 3593 * the current first responder view can be changed by stepping backward 3594 * and forward in that chain. This is the method for building and modifying 3595 * that chain. 3596 * </p> 3597 * <p>The MacOS-X documentation refers to this chain as a <em>loop</em>, but 3598 * the actual implementation is not a loop at all (except as a special case 3599 * when you make the chain into a loop). In fact, while each view may have 3600 * only zero or one <em>next</em> view, and zero or one <em>previous</em> 3601 * view, several views may have their <em>next</em> view set to a single 3602 * view and/or their <em>previous</em> views set to a single view. So the 3603 * actual setup is a directed graph rather than a loop. 3604 * </p> 3605 * <p>While a directed graph is a very powerful and flexible way of managing 3606 * the way views get keyboard focus in response to tabs etc, it can be 3607 * confusing if misused. It is probably best therefore, to set your views 3608 * up as a single loop within each window. 3609 * </p> 3610 * <example> 3611 * [a setNextKeyView: b]; 3612 * [b setNextKeyView: c]; 3613 * [c setNextKeyView: d]; 3614 * [d setNextKeyView: a]; 3615 * </example> 3616 */ 3617- (void) setNextKeyView: (NSView *)aView 3618{ 3619 NSView *tmp; 3620 NSUInteger count; 3621 3622 if (aView != nil && [aView isKindOfClass: viewClass] == NO) 3623 { 3624 [NSException raise: NSInternalInconsistencyException 3625 format: @"[NSView -setNextKeyView:] passed non-view object %@", aView]; 3626 } 3627 3628 if (aView == nil) 3629 { 3630 if (nKV(self) != 0) 3631 { 3632 tmp = GSIArrayItemAtIndex(nKV(self), 0).obj; 3633 if (tmp != nil) 3634 { 3635 /* 3636 * Remove all reference to self from our next key view. 3637 */ 3638 if (pKV(tmp) != 0) 3639 { 3640 count = GSIArrayCount(pKV(tmp)); 3641 while (count-- > 1) 3642 { 3643 if (GSIArrayItemAtIndex(pKV(tmp), count).obj == self) 3644 { 3645 GSIArrayRemoveItemAtIndex(pKV(tmp), count); 3646 } 3647 } 3648 if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self) 3649 { 3650 GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0); 3651 } 3652 } 3653 /* 3654 * Clear link to the next key view. 3655 */ 3656 GSIArraySetItemAtIndex(nKV(self), (GSIArrayItem)nil, 0); 3657 } 3658 } 3659 return; 3660 } 3661 3662 if (nKV(self) == 0) 3663 { 3664 /* 3665 * Create array and ensure that it has a nil item at index 0 ... 3666 * so we always have room for the pointer to the next view. 3667 */ 3668 _nextKeyView = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t)); 3669 GSIArrayInitWithZoneAndCapacity(nKV(self), NSDefaultMallocZone(), 1); 3670 GSIArrayAddItem(nKV(self), (GSIArrayItem)nil); 3671 } 3672 else 3673 { 3674 /* A safety measure against recursion. */ 3675 tmp = GSIArrayItemAtIndex(nKV(self), 0).obj; 3676 if (tmp == aView) 3677 { 3678 return; 3679 } 3680 } 3681 3682 if (pKV(aView) == 0) 3683 { 3684 /* 3685 * Create array and ensure that it has a nil item at index 0 ... 3686 * so we always have room for the pointer to the previous view. 3687 */ 3688 aView->_previousKeyView = NSZoneMalloc(NSDefaultMallocZone(), sizeof(GSIArray_t)); 3689 GSIArrayInitWithZoneAndCapacity(pKV(aView), NSDefaultMallocZone(), 1); 3690 GSIArrayAddItem(pKV(aView), (GSIArrayItem)nil); 3691 } 3692 3693 /* 3694 * Tell the old previous view of aView that aView no longer points to it. 3695 */ 3696 tmp = GSIArrayItemAtIndex(pKV(aView), 0).obj; 3697 if (tmp != nil) 3698 { 3699 count = GSIArrayCount(nKV(tmp)); 3700 while (count-- > 1) 3701 { 3702 if (GSIArrayItemAtIndex(nKV(tmp), count).obj == aView) 3703 { 3704 GSIArrayRemoveItemAtIndex(nKV(tmp), count); 3705 } 3706 } 3707 /* 3708 * If the view still points to aView, make a note of it in the 3709 * 'previous' array of aView while making space for the new link. 3710 */ 3711 if (GSIArrayItemAtIndex(nKV(tmp), 0).obj == aView) 3712 { 3713 GSIArrayInsertItem(pKV(aView), (GSIArrayItem)nil, 0); 3714 } 3715 } 3716 3717 /* 3718 * Set up 'previous' link in aView to point to us. 3719 */ 3720 GSIArraySetItemAtIndex(pKV(aView), (GSIArrayItem)((id)self), 0); 3721 3722 /* 3723 * Tell our current 'next' view that we are no longer pointing to it. 3724 */ 3725 tmp = GSIArrayItemAtIndex(nKV(self), 0).obj; 3726 if (tmp != nil) 3727 { 3728 count = GSIArrayCount(pKV(tmp)); 3729 while (count-- > 1) 3730 { 3731 if (GSIArrayItemAtIndex(pKV(tmp), count).obj == self) 3732 { 3733 GSIArrayRemoveItemAtIndex(pKV(tmp), count); 3734 } 3735 } 3736 if (GSIArrayItemAtIndex(pKV(tmp), 0).obj == self) 3737 { 3738 GSIArraySetItemAtIndex(pKV(tmp), (GSIArrayItem)nil, 0); 3739 } 3740 } 3741 3742 /* 3743 * Set up 'next' link to point to aView. 3744 */ 3745 GSIArraySetItemAtIndex(nKV(self), (GSIArrayItem)((id)aView), 0); 3746} 3747 3748/** 3749 * Returns the next view after the receiver in the key view chain.<br /> 3750 * Returns nil if there is no view after the receiver.<br /> 3751 * The next view is set up using the -setNextKeyView: method.<br /> 3752 * The key view chain is used to determine the order in which views become 3753 * first responder when using keyboard navigation. 3754 */ 3755- (NSView *) nextKeyView 3756{ 3757 if (nKV(self) == 0) 3758 { 3759 return nil; 3760 } 3761 return GSIArrayItemAtIndex(nKV(self), 0).obj; 3762} 3763 3764/** 3765 * Returns the first available view after the receiver which is 3766 * actually able to become first responder. See -nextKeyView and 3767 * [NSResponder-acceptsFirstResponder] 3768 */ 3769- (NSView *) nextValidKeyView 3770{ 3771 NSView *theView; 3772 3773 theView = [self nextKeyView]; 3774 while (1) 3775 { 3776 if ((theView == nil) || (theView == self) || 3777 [theView canBecomeKeyView]) 3778 { 3779 return theView; 3780 } 3781 theView = [theView nextKeyView]; 3782 } 3783} 3784 3785/** 3786 * GNUstep addition ... a conveninece method to insert a view in the 3787 * key view chain before the receiver, using the -previousKeyView and 3788 * -setNextKeyView: methods. 3789 */ 3790- (void) setPreviousKeyView: (NSView *)aView 3791{ 3792 NSView *p = [self previousKeyView]; 3793 3794 if (aView == p || aView == self) 3795 { 3796 return; 3797 } 3798 [p setNextKeyView: aView]; 3799 [aView setNextKeyView: self]; 3800} 3801 3802/** 3803 * Returns the view before the receiver in the key view chain.<br /> 3804 * Returns nil if there is no view before the receiver in the chain.<br /> 3805 * The previous view of the receiver was set up by passing it as the 3806 * argument to a call of -setNextKeyView: on that view.<br /> 3807 * The key view chain is used to determine the order in which views become 3808 * first responder when using keyboard navigation. 3809 */ 3810- (NSView *) previousKeyView 3811{ 3812 if (pKV(self) == 0) 3813 { 3814 return nil; 3815 } 3816 return GSIArrayItemAtIndex(pKV(self), 0).obj; 3817} 3818 3819/** 3820 * Returns the first available view before the receiver which is 3821 * actually able to become first responder. See -nextKeyView and 3822 * [NSResponder-acceptsFirstResponder] 3823 */ 3824- (NSView *) previousValidKeyView 3825{ 3826 NSView *theView; 3827 3828 theView = [self previousKeyView]; 3829 while (1) 3830 { 3831 if ((theView == nil) || (theView == self) || 3832 [theView canBecomeKeyView]) 3833 { 3834 return theView; 3835 } 3836 theView = [theView previousKeyView]; 3837 } 3838} 3839 3840- (BOOL) canBecomeKeyView 3841{ 3842 // FIXME 3843 return [self acceptsFirstResponder] && ![self isHiddenOrHasHiddenAncestor]; 3844} 3845 3846/* 3847 * Dragging 3848 */ 3849- (BOOL) dragFile: (NSString*)filename 3850 fromRect: (NSRect)rect 3851 slideBack: (BOOL)slideFlag 3852 event: (NSEvent*)event 3853{ 3854 NSImage *anImage = [[NSWorkspace sharedWorkspace] iconForFile: filename]; 3855 NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; 3856 3857 if (anImage == nil) 3858 return NO; 3859 3860 [pboard declareTypes: [NSArray arrayWithObject: NSFilenamesPboardType] 3861 owner: self]; 3862 if (![pboard setPropertyList: [NSArray arrayWithObject: filename] 3863 forType: NSFilenamesPboardType]) 3864 return NO; 3865 3866 [self dragImage: anImage 3867 at: rect.origin 3868 offset: NSMakeSize(0, 0) 3869 event: event 3870 pasteboard: pboard 3871 source: self 3872 slideBack: slideFlag]; 3873 return YES; 3874} 3875 3876- (void) dragImage: (NSImage*)anImage 3877 at: (NSPoint)viewLocation 3878 offset: (NSSize)initialOffset 3879 event: (NSEvent*)event 3880 pasteboard: (NSPasteboard*)pboard 3881 source: (id)sourceObject 3882 slideBack: (BOOL)slideFlag 3883{ 3884 [_window dragImage: anImage 3885 at: [self convertPoint: viewLocation toView: nil] 3886 offset: initialOffset 3887 event: event 3888 pasteboard: pboard 3889 source: sourceObject 3890 slideBack: slideFlag]; 3891} 3892 3893/** 3894 * Registers the fact that the receiver should accept dragged data 3895 * of any of the specified types. You need to do this if you want 3896 * your view to support drag and drop. 3897 */ 3898- (void) registerForDraggedTypes: (NSArray*)newTypes 3899{ 3900 NSArray *o; 3901 NSArray *t; 3902 3903 if (newTypes == nil || [newTypes count] == 0) 3904 [NSException raise: NSInvalidArgumentException 3905 format: @"Types information missing"]; 3906 3907 /* 3908 * Get the old drag types for this view if we need to tell the context 3909 * to change the registered types for the window. 3910 */ 3911 if (_rFlags.has_draginfo == 1 && _window != nil) 3912 { 3913 o = TEST_RETAIN(GSGetDragTypes(self)); 3914 } 3915 else 3916 { 3917 o = nil; 3918 } 3919 3920 t = GSSetDragTypes(self, newTypes); 3921 _rFlags.has_draginfo = 1; 3922 if (_window != nil) 3923 { 3924 // Remove the old types first, that way overlapping types stay assigned. 3925 if (o != nil) 3926 { 3927 [GSDisplayServer removeDragTypes: o fromWindow: _window]; 3928 } 3929 [GSDisplayServer addDragTypes: t toWindow: _window]; 3930 } 3931 TEST_RELEASE(o); 3932} 3933 3934- (void) unregisterDraggedTypes 3935{ 3936 if (_rFlags.has_draginfo) 3937 { 3938 if (_window != nil) 3939 { 3940 NSArray *t = GSGetDragTypes(self); 3941 3942 [GSDisplayServer removeDragTypes: t fromWindow: _window]; 3943 } 3944 GSRemoveDragTypes(self); 3945 _rFlags.has_draginfo = 0; 3946 } 3947} 3948 3949- (NSArray *) registeredDraggedTypes 3950{ 3951 return GSGetDragTypes(self); 3952} 3953 3954- (BOOL) dragPromisedFilesOfTypes: (NSArray *)typeArray 3955 fromRect: (NSRect)aRect 3956 source: (id)sourceObject 3957 slideBack: (BOOL)slideBack 3958 event: (NSEvent *)theEvent 3959{ 3960 // FIXME: Where to get the image from? 3961 NSImage *anImage = nil; 3962 NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; 3963 3964 if (anImage == nil) 3965 return NO; 3966 3967 [pboard declareTypes: [NSArray arrayWithObject: NSFilesPromisePboardType] 3968 owner: sourceObject]; 3969 // FIXME: Not sure if this is correct. 3970 if (![pboard setPropertyList: typeArray 3971 forType: NSFilesPromisePboardType]) 3972 return NO; 3973 3974 [self dragImage: anImage 3975 at: aRect.origin 3976 offset: NSMakeSize(0, 0) 3977 event: theEvent 3978 pasteboard: pboard 3979 source: sourceObject 3980 slideBack: slideBack]; 3981 return YES; 3982} 3983 3984/* 3985 * Printing 3986 */ 3987- (void) fax: (id)sender 3988{ 3989 NSPrintInfo *aPrintInfo = [NSPrintInfo sharedPrintInfo]; 3990 3991 [aPrintInfo setJobDisposition: NSPrintFaxJob]; 3992 [[NSPrintOperation printOperationWithView: self 3993 printInfo: aPrintInfo] runOperation]; 3994} 3995 3996- (void) print: (id)sender 3997{ 3998 [[NSPrintOperation printOperationWithView: self] runOperation]; 3999} 4000 4001- (NSData*) dataWithEPSInsideRect: (NSRect)aRect 4002{ 4003 NSMutableData *data = [NSMutableData data]; 4004 4005 if ([[NSPrintOperation EPSOperationWithView: self 4006 insideRect: aRect 4007 toData: data] runOperation]) 4008 { 4009 return data; 4010 } 4011 else 4012 { 4013 return nil; 4014 } 4015} 4016 4017- (void) writeEPSInsideRect: (NSRect)rect 4018 toPasteboard: (NSPasteboard*)pasteboard 4019{ 4020 NSData *data = [self dataWithEPSInsideRect: rect]; 4021 4022 if (data != nil) 4023 [pasteboard setData: data 4024 forType: NSPostScriptPboardType]; 4025} 4026 4027- (NSData *) dataWithPDFInsideRect: (NSRect)aRect 4028{ 4029 NSMutableData *data = [NSMutableData data]; 4030 4031 if ([[NSPrintOperation PDFOperationWithView: self 4032 insideRect: aRect 4033 toData: data] runOperation]) 4034 { 4035 return data; 4036 } 4037 else 4038 { 4039 return nil; 4040 } 4041} 4042 4043- (void) writePDFInsideRect: (NSRect)aRect 4044 toPasteboard: (NSPasteboard *)pboard 4045{ 4046 NSData *data = [self dataWithPDFInsideRect: aRect]; 4047 4048 if (data != nil) 4049 [pboard setData: data 4050 forType: NSPDFPboardType]; 4051} 4052 4053- (NSString *) printJobTitle 4054{ 4055 id doc; 4056 NSString *title; 4057 doc = [[NSDocumentController sharedDocumentController] documentForWindow: 4058 [self window]]; 4059 if (doc) 4060 title = [doc displayName]; 4061 else 4062 title = [[self window] title]; 4063 return title; 4064} 4065 4066/* 4067 * Pagination 4068 */ 4069- (void) adjustPageHeightNew: (CGFloat*)newBottom 4070 top: (CGFloat)oldTop 4071 bottom: (CGFloat)oldBottom 4072 limit: (CGFloat)bottomLimit 4073{ 4074 CGFloat bottom = oldBottom; 4075 4076 if (_rFlags.has_subviews) 4077 { 4078 id e, o; 4079 4080 e = [_sub_views objectEnumerator]; 4081 while ((o = [e nextObject]) != nil) 4082 { 4083 // FIXME: We have to convert this values for the subclass 4084 4085 CGFloat oTop, oBottom, oLimit; 4086 /* Don't ask me why, but gcc-2.91.66 crashes if we use 4087 NSMakePoint in the following expressions. We avoid this 4088 compiler internal bug by using an auxiliary aPoint 4089 variable, and setting it manually to the NSPoints we 4090 need. */ 4091 { 4092 NSPoint aPoint = {0, oldTop}; 4093 oTop = ([self convertPoint: aPoint toView: o]).y; 4094 } 4095 4096 { 4097 NSPoint aPoint = {0, bottom}; 4098 oBottom = ([self convertPoint: aPoint toView: o]).y; 4099 } 4100 4101 { 4102 NSPoint aPoint = {0, bottomLimit}; 4103 oLimit = ([self convertPoint: aPoint toView: o]).y; 4104 } 4105 4106 [o adjustPageHeightNew: &oBottom 4107 top: oTop 4108 bottom: oBottom 4109 limit: oLimit]; 4110 4111 { 4112 NSPoint aPoint = {0, oBottom}; 4113 bottom = ([self convertPoint: aPoint fromView: o]).y; 4114 } 4115 } 4116 } 4117 4118 *newBottom = bottom; 4119} 4120 4121- (void) adjustPageWidthNew: (CGFloat*)newRight 4122 left: (CGFloat)oldLeft 4123 right: (CGFloat)oldRight 4124 limit: (CGFloat)rightLimit 4125{ 4126 CGFloat right = oldRight; 4127 4128 if (_rFlags.has_subviews) 4129 { 4130 id e, o; 4131 4132 e = [_sub_views objectEnumerator]; 4133 while ((o = [e nextObject]) != nil) 4134 { 4135 // FIXME: We have to convert this values for the subclass 4136 4137 /* See comments in adjustPageHeightNew:top:bottom:limit: 4138 about why code is structured in this funny way. */ 4139 CGFloat oLeft, oRight, oLimit; 4140 /* Don't ask me why, but gcc-2.91.66 crashes if we use 4141 NSMakePoint in the following expressions. We avoid this 4142 compiler internal bug by using an auxiliary aPoint 4143 variable, and setting it manually to the NSPoints we 4144 need. */ 4145 { 4146 NSPoint aPoint = {oldLeft, 0}; 4147 oLeft = ([self convertPoint: aPoint toView: o]).x; 4148 } 4149 4150 { 4151 NSPoint aPoint = {right, 0}; 4152 oRight = ([self convertPoint: aPoint toView: o]).x; 4153 } 4154 4155 { 4156 NSPoint aPoint = {rightLimit, 0}; 4157 oLimit = ([self convertPoint: aPoint toView: o]).x; 4158 } 4159 4160 [o adjustPageHeightNew: &oRight 4161 top: oLeft 4162 bottom: oRight 4163 limit: oLimit]; 4164 4165 { 4166 NSPoint aPoint = {oRight, 0}; 4167 right = ([self convertPoint: aPoint fromView: o]).x; 4168 } 4169 } 4170 } 4171 4172 *newRight = right; 4173} 4174 4175- (CGFloat) heightAdjustLimit 4176{ 4177 return 0.0; 4178} 4179 4180- (BOOL) knowsPagesFirst: (int*)firstPageNum last: (int*)lastPageNum 4181{ 4182 return NO; 4183} 4184 4185- (BOOL) knowsPageRange: (NSRange*)range 4186{ 4187 return NO; 4188} 4189 4190- (NSPoint) locationOfPrintRect: (NSRect)aRect 4191{ 4192 int pages; 4193 NSPoint location; 4194 NSRect bounds; 4195 NSMutableDictionary *dict; 4196 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4197 NSPrintInfo *printInfo = [printOp printInfo]; 4198 dict = [printInfo dictionary]; 4199 4200 pages = [[dict objectForKey: @"NSPrintTotalPages"] intValue]; 4201 if ([dict objectForKey: @"NSPrintPaperBounds"]) 4202 bounds = [[dict objectForKey: @"NSPrintPaperBounds"] rectValue]; 4203 else 4204 bounds = aRect; 4205 location = NSMakePoint(0, NSHeight(bounds)-NSHeight(aRect)); 4206 /* FIXME: I can't figure out how the location for a multi-page document 4207 is computed. Just ignore centering? */ 4208 if (pages == 1) 4209 { 4210 if ([printInfo isHorizontallyCentered]) 4211 location.x = (NSWidth(bounds) - NSWidth(aRect))/2; 4212 if ([printInfo isVerticallyCentered]) 4213 location.y = (NSHeight(bounds) - NSHeight(aRect))/2; 4214 } 4215 4216 return location; 4217} 4218 4219- (NSRect) rectForPage: (NSInteger)page 4220{ 4221 return NSZeroRect; 4222} 4223 4224- (CGFloat) widthAdjustLimit 4225{ 4226 return 0.0; 4227} 4228 4229/* 4230 * Writing Conforming PostScript 4231 */ 4232- (void) beginPage: (int)ordinalNum 4233 label: (NSString*)aString 4234 bBox: (NSRect)pageRect 4235 fonts: (NSString*)fontNames 4236{ 4237 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4238 NSGraphicsContext *ctxt = [printOp context]; 4239 4240 [ctxt beginPage: ordinalNum 4241 label: aString 4242 bBox: pageRect 4243 fonts: fontNames]; 4244} 4245 4246- (void) beginPageSetupRect: (NSRect)aRect placement: (NSPoint)location 4247{ 4248 [self beginPageInRect: aRect atPlacement: location]; 4249} 4250 4251- (void) beginPrologueBBox: (NSRect)boundingBox 4252 creationDate: (NSString*)dateCreated 4253 createdBy: (NSString*)anApplication 4254 fonts: (NSString*)fontNames 4255 forWhom: (NSString*)user 4256 pages: (int)numPages 4257 title: (NSString*)aTitle 4258{ 4259 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4260 NSGraphicsContext *ctxt = [printOp context]; 4261 4262 [ctxt beginPrologueBBox: boundingBox 4263 creationDate: dateCreated 4264 createdBy: anApplication 4265 fonts: fontNames 4266 forWhom: user 4267 pages: numPages 4268 title: aTitle]; 4269} 4270 4271- (void) addToPageSetup 4272{ 4273} 4274 4275- (void) beginSetup 4276{ 4277 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4278 NSGraphicsContext *ctxt = [printOp context]; 4279 4280 [ctxt beginSetup]; 4281} 4282 4283- (void) beginTrailer 4284{ 4285 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4286 NSGraphicsContext *ctxt = [printOp context]; 4287 4288 [ctxt beginTrailer]; 4289} 4290 4291- (void) drawPageBorderWithSize: (NSSize)borderSize 4292{ 4293} 4294 4295- (void) drawSheetBorderWithSize: (NSSize)borderSize 4296{ 4297} 4298 4299- (void) endHeaderComments 4300{ 4301 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4302 NSGraphicsContext *ctxt = [printOp context]; 4303 4304 [ctxt endHeaderComments]; 4305} 4306 4307- (void) endPrologue 4308{ 4309 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4310 NSGraphicsContext *ctxt = [printOp context]; 4311 4312 [ctxt endPrologue]; 4313} 4314 4315- (void) endSetup 4316{ 4317 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4318 NSGraphicsContext *ctxt = [printOp context]; 4319 4320 [ctxt endSetup]; 4321} 4322 4323- (void) endPageSetup 4324{ 4325 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4326 NSGraphicsContext *ctxt = [printOp context]; 4327 4328 [ctxt endPageSetup]; 4329} 4330 4331- (void) endPage 4332{ 4333 int nup; 4334 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4335 NSGraphicsContext *ctxt = [printOp context]; 4336 NSDictionary *dict = [[printOp printInfo] dictionary]; 4337 4338 // Balance gsave in beginPageInRect: 4339 DPSgrestore(ctxt); 4340 4341 nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue]; 4342 if (nup > 1) 4343 { 4344 DPSPrintf(ctxt, "__GSpagesaveobject restore\n\n"); 4345 } 4346 4347 // [self unlockFocus]; 4348} 4349 4350- (void) endTrailer 4351{ 4352 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4353 NSGraphicsContext *ctxt = [printOp context]; 4354 4355 [ctxt endTrailer]; 4356} 4357 4358- (NSAttributedString *) pageFooter 4359{ 4360 return [[[NSAttributedString alloc] initWithString: 4361 [NSString stringWithFormat:@"Page %d", 4362 [[NSPrintOperation currentOperation] currentPage]]] 4363 autorelease]; 4364} 4365 4366- (NSAttributedString *) pageHeader 4367{ 4368 return [[[NSAttributedString alloc] initWithString: 4369 [NSString stringWithFormat:@"%@ %@", [self printJobTitle], 4370 [[NSCalendarDate calendarDate] description]]] autorelease]; 4371} 4372 4373 4374/** 4375 Writes header and job information for the PostScript document. This 4376 includes at a minimum, PostScript header information. It may also 4377 include job setup information if the output is intended for a printer 4378 (i.e. not an EPS file). Most of the information for writing the 4379 header comes from the NSPrintOperation and NSPrintInfo objects 4380 associated with the current print operation. 4381 4382 There isn't normally anything that the program needs to override 4383 at the beginning of a document, although if there is additional 4384 setup that needs to be done, you can override the NSView's methods 4385 endHeaderComments, endPrologue, beginSetup, and/or endSetup. 4386 4387 This method calls the above methods in the listed order before 4388 or after writing the required information. For an EPS operation, the 4389 beginSetup and endSetup methods aren't used. */ 4390- (void)beginDocument 4391{ 4392 int first, last, pages, nup; 4393 NSRect bbox; 4394 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4395 NSGraphicsContext *ctxt = [printOp context]; 4396 NSDictionary *dict = [[printOp printInfo] dictionary]; 4397 4398 if (printOp == nil) 4399 { 4400 [NSException raise: NSInternalInconsistencyException 4401 format: @"beginDocument called without a current print op"]; 4402 } 4403 /* Inform ourselves and subviews that we're printing so we adjust 4404 the PostScript accordingly. Perhaps this could be in the thread 4405 dictionary, but that's probably overkill and slow */ 4406 viewIsPrinting = self; 4407 4408 /* Get pagination information */ 4409 nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue]; 4410 bbox = NSZeroRect; 4411 if ([dict objectForKey: @"NSPrintSheetBounds"]) 4412 bbox = [[dict objectForKey: @"NSPrintSheetBounds"] rectValue]; 4413 first = [[dict objectForKey: NSPrintFirstPage] intValue]; 4414 last = [[dict objectForKey: NSPrintLastPage] intValue]; 4415 pages = last - first + 1; 4416 if (nup > 1) 4417 pages = ceil((float)pages / nup); 4418 4419 /* Begin document structure */ 4420 [self beginPrologueBBox: bbox 4421 creationDate: [[NSCalendarDate calendarDate] description] 4422 createdBy: [[NSProcessInfo processInfo] processName] 4423 fonts: nil 4424 forWhom: NSUserName() 4425 pages: pages 4426 title: [self printJobTitle]]; 4427 [self endHeaderComments]; 4428 4429 [ctxt printerProlog]; 4430 [self endPrologue]; 4431 if ([printOp isEPSOperation] == NO) 4432 { 4433 [self beginSetup]; 4434 // Setup goes here ! 4435 [self endSetup]; 4436 } 4437 4438 [ctxt resetUsedFonts]; 4439 /* Make sure we set the visible rect so everything is printed. */ 4440 [self _invalidateCoordinates]; 4441 _visibleRect = _bounds; 4442} 4443 4444- (void) beginPageInRect: (NSRect)aRect 4445 atPlacement: (NSPoint)location 4446{ 4447 int nup; 4448 NSRect bounds; 4449 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4450 NSGraphicsContext *ctxt = [printOp context]; 4451 NSDictionary *dict = [[printOp printInfo] dictionary]; 4452 4453 if (NSIsEmptyRect(aRect)) 4454 { 4455 if ([dict objectForKey: @"NSPrintPaperBounds"]) 4456 { 4457 bounds = [[dict objectForKey: @"NSPrintPaperBounds"] rectValue]; 4458 } 4459 else 4460 { 4461 // FIXME: What should we use here? 4462 bounds = aRect; 4463 } 4464 } 4465 else 4466 { 4467 bounds = aRect; 4468 } 4469 4470 nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue]; 4471 if (nup > 1) 4472 { 4473 int page; 4474 float xoff, yoff; 4475 float scale; 4476 4477 DPSPrintf(ctxt, "/__GSpagesaveobject save def\n"); 4478 4479 scale = [[dict objectForKey: @"NSNupScale"] floatValue]; 4480 page = [printOp currentPage] 4481 - [[dict objectForKey: NSPrintFirstPage] intValue]; 4482 page = page % nup; 4483 if (nup == 2) 4484 xoff = page; 4485 else 4486 xoff = (page % (nup/2)); 4487 xoff *= NSWidth(bounds) * scale; 4488 if (nup == 2) 4489 yoff = 0; 4490 else 4491 yoff = (int)((nup-page-1) / (nup/2)); 4492 yoff *= NSHeight(bounds) * scale; 4493 DPStranslate(ctxt, xoff, yoff); 4494 DPSgsave(ctxt); 4495 DPSscale(ctxt, scale, scale); 4496 } 4497 else 4498 { 4499 DPSgsave(ctxt); 4500 } 4501 4502 /* Translate to placement */ 4503 if (location.x != 0 || location.y != 0) 4504 { 4505 DPStranslate(ctxt, location.x, location.y); 4506 } 4507} 4508 4509- (void) _endSheet 4510{ 4511 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4512 NSGraphicsContext *ctxt = [printOp context]; 4513 4514 [ctxt endSheet]; 4515} 4516 4517- (void) endDocument 4518{ 4519 int first, last, current, pages; 4520 NSPrintOperation *printOp = [NSPrintOperation currentOperation]; 4521 NSGraphicsContext *ctxt = [printOp context]; 4522 NSDictionary *dict = [[printOp printInfo] dictionary]; 4523 4524 first = [[dict objectForKey: NSPrintFirstPage] intValue]; 4525 last = [[dict objectForKey: NSPrintLastPage] intValue]; 4526 pages = last - first + 1; 4527 [self beginTrailer]; 4528 4529 if (pages == 0) 4530 { 4531 int nup = [[dict objectForKey: NSPrintPagesPerSheet] intValue]; 4532 current = [printOp currentPage]; 4533 pages = current - first; // Current is 1 more than the last page 4534 if (nup > 1) 4535 pages = ceil((float)pages / nup); 4536 } 4537 else 4538 { 4539 // Already reported at start of document 4540 pages = 0; 4541 } 4542 [ctxt endDocumentPages: pages documentFonts: [ctxt usedFonts]]; 4543 4544 [self endTrailer]; 4545 [self _invalidateCoordinates]; 4546 viewIsPrinting = nil; 4547} 4548 4549/* An exception occurred while printing. Clean up */ 4550- (void) _cleanupPrinting 4551{ 4552 [self _invalidateCoordinates]; 4553 viewIsPrinting = nil; 4554} 4555 4556/* 4557 * NSCoding protocol 4558 */ 4559- (void) encodeWithCoder: (NSCoder*)aCoder 4560{ 4561 if ([aCoder allowsKeyedCoding]) 4562 { 4563 NSUInteger vFlags = 0; 4564 4565 // encoding 4566 [aCoder encodeConditionalObject: [self nextKeyView] 4567 forKey: @"NSNextKeyView"]; 4568 [aCoder encodeConditionalObject: [self previousKeyView] 4569 forKey: @"NSPreviousKeyView"]; 4570 [aCoder encodeObject: _sub_views 4571 forKey: @"NSSubviews"]; 4572 [aCoder encodeRect: _frame 4573 forKey: @"NSFrame"]; 4574 4575 // autosizing masks. 4576 vFlags = _autoresizingMask; 4577 4578 // add the autoresize flag. 4579 if (_autoresizes_subviews) 4580 { 4581 vFlags |= 0x100; 4582 } 4583 4584 // add the hidden flag 4585 if (_is_hidden) 4586 { 4587 vFlags |= 0x80000000; 4588 } 4589 4590 [aCoder encodeInt: vFlags 4591 forKey: @"NSvFlags"]; 4592 4593 // 4594 // Don't attempt to archive the superview of a view which is the 4595 // content view for a window. 4596 // 4597 if (([[self window] contentView] != self) && _super_view != nil) 4598 { 4599 [aCoder encodeConditionalObject: _super_view forKey: @"NSSuperview"]; 4600 } 4601 } 4602 else 4603 { 4604 NSDebugLLog(@"NSView", @"NSView: start encoding\n"); 4605 [super encodeWithCoder: aCoder]; 4606 4607 [aCoder encodeRect: _frame]; 4608 [aCoder encodeRect: _bounds]; 4609 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_is_rotated_from_base]; 4610 [aCoder encodeValueOfObjCType: @encode(BOOL) 4611 at: &_is_rotated_or_scaled_from_base]; 4612 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_post_frame_changes]; 4613 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autoresizes_subviews]; 4614 [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_autoresizingMask]; 4615 [aCoder encodeConditionalObject: [self nextKeyView]]; 4616 [aCoder encodeConditionalObject: [self previousKeyView]]; 4617 [aCoder encodeObject: _sub_views]; 4618 NSDebugLLog(@"NSView", @"NSView: finish encoding\n"); 4619 } 4620} 4621 4622- (id) initWithCoder: (NSCoder*)aDecoder 4623{ 4624 NSEnumerator *e; 4625 NSView *sub; 4626 NSArray *subs; 4627 4628 // decode the superclass... 4629 self = [super initWithCoder: aDecoder]; 4630 if (!self) 4631 return nil; 4632 4633 // initialize these here, since they're needed in either case. 4634 // _frameMatrix = [NSAffineTransform new]; // Map fromsuperview to frame 4635 // _boundsMatrix = [NSAffineTransform new]; // Map from superview to bounds 4636 _matrixToWindow = [NSAffineTransform new]; // Map to window coordinates 4637 _matrixFromWindow = [NSAffineTransform new];// Map from window coordinates 4638 4639 if ([aDecoder allowsKeyedCoding]) 4640 { 4641 NSView *prevKeyView = nil; 4642 NSView *nextKeyView = nil; 4643 4644 if ([aDecoder containsValueForKey: @"NSFrame"]) 4645 { 4646 _frame = [aDecoder decodeRectForKey: @"NSFrame"]; 4647 } 4648 else 4649 { 4650 _frame = NSZeroRect; 4651 if ([aDecoder containsValueForKey: @"NSFrameSize"]) 4652 { 4653 _frame.size = [aDecoder decodeSizeForKey: @"NSFrameSize"]; 4654 } 4655 } 4656 4657 // Set bounds rectangle 4658 _bounds.origin = NSZeroPoint; 4659 _bounds.size = _frame.size; 4660 if ([aDecoder containsValueForKey: @"NSBounds"]) 4661 { 4662 [self setBounds: [aDecoder decodeRectForKey: @"NSBounds"]]; 4663 } 4664 4665 _sub_views = [NSMutableArray new]; 4666 _tracking_rects = [NSMutableArray new]; 4667 _cursor_rects = [NSMutableArray new]; 4668 4669 _is_rotated_from_base = NO; 4670 _is_rotated_or_scaled_from_base = NO; 4671 _rFlags.needs_display = YES; 4672 _post_bounds_changes = YES; 4673 _post_frame_changes = YES; 4674 _autoresizes_subviews = YES; 4675 _autoresizingMask = NSViewNotSizable; 4676 _coordinates_valid = NO; 4677 /* 4678 * Note: don't zero _nextKeyView and _previousKeyView, as the key view 4679 * chain may already have been established by super's initWithCoder: 4680 * 4681 * _nextKeyView = 0; 4682 * _previousKeyView = 0; 4683 */ 4684 4685 // previous and next key views... 4686 prevKeyView = [aDecoder decodeObjectForKey: @"NSPreviousKeyView"]; 4687 nextKeyView = [aDecoder decodeObjectForKey: @"NSNextKeyView"]; 4688 if (nextKeyView != nil) 4689 { 4690 [self setNextKeyView: nextKeyView]; 4691 } 4692 if (prevKeyView != nil) 4693 { 4694 [self setPreviousKeyView: prevKeyView]; 4695 } 4696 if ([aDecoder containsValueForKey: @"NSvFlags"]) 4697 { 4698 NSUInteger vFlags = [aDecoder decodeIntForKey: @"NSvFlags"]; 4699 4700 // We are lucky here, Apple use the same constants 4701 // in the lower bits of the flags 4702 [self setAutoresizingMask: vFlags & 0x3F]; 4703 [self setAutoresizesSubviews: ((vFlags & 0x100) == 0x100)]; 4704 [self setHidden: ((vFlags & 0x80000000) == 0x80000000)]; 4705 } 4706 4707 // iterate over subviews and put them into the view... 4708 subs = [aDecoder decodeObjectForKey: @"NSSubviews"]; 4709 e = [subs objectEnumerator]; 4710 while ((sub = [e nextObject]) != nil) 4711 { 4712 NSAssert([sub class] != [NSCustomView class], 4713 NSInternalInconsistencyException); 4714 NSAssert([sub window] == nil, 4715 NSInternalInconsistencyException); 4716 NSAssert([sub superview] == nil, 4717 NSInternalInconsistencyException); 4718 [sub _viewWillMoveToWindow: _window]; 4719 [sub _viewWillMoveToSuperview: self]; 4720 [sub setNextResponder: self]; 4721 [_sub_views addObject: sub]; 4722 _rFlags.has_subviews = 1; 4723 [sub resetCursorRects]; 4724 [sub setNeedsDisplay: YES]; 4725 [sub _viewDidMoveToWindow]; 4726 [sub viewDidMoveToSuperview]; 4727 [self didAddSubview: sub]; 4728 } 4729 4730 // the superview... 4731 //[aDecoder decodeObjectForKey: @"NSSuperview"]; 4732 } 4733 else 4734 { 4735 NSRect rect; 4736 4737 NSDebugLLog(@"NSView", @"NSView: start decoding\n"); 4738 4739 _frame = [aDecoder decodeRect]; 4740 4741 _bounds.origin = NSZeroPoint; 4742 _bounds.size = _frame.size; 4743 4744 rect = [aDecoder decodeRect]; 4745 [self setBounds: rect]; 4746 4747 _sub_views = [NSMutableArray new]; 4748 _tracking_rects = [NSMutableArray new]; 4749 _cursor_rects = [NSMutableArray new]; 4750 4751 _super_view = nil; 4752 _window = nil; 4753 _rFlags.needs_display = YES; 4754 [aDecoder decodeValueOfObjCType: @encode(BOOL) 4755 at: &_is_rotated_from_base]; 4756 [aDecoder decodeValueOfObjCType: @encode(BOOL) 4757 at: &_is_rotated_or_scaled_from_base]; 4758 _post_bounds_changes = YES; 4759 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_post_frame_changes]; 4760 [aDecoder decodeValueOfObjCType: @encode(BOOL) 4761 at: &_autoresizes_subviews]; 4762 [aDecoder decodeValueOfObjCType: @encode(NSUInteger) 4763 at: &_autoresizingMask]; 4764 _coordinates_valid = NO; 4765 [self setNextKeyView: [aDecoder decodeObject]]; 4766 [[aDecoder decodeObject] setNextKeyView: self]; 4767 4768 [aDecoder decodeValueOfObjCType: @encode(id) at: &subs]; 4769 NSDebugLLog(@"NSView", @"NSView: finish decoding\n"); 4770 4771 // iterate over subviews and put them into the view... 4772 e = [subs objectEnumerator]; 4773 while ((sub = [e nextObject]) != nil) 4774 { 4775 NSAssert([sub window] == nil, 4776 NSInternalInconsistencyException); 4777 NSAssert([sub superview] == nil, 4778 NSInternalInconsistencyException); 4779 [sub _viewWillMoveToWindow: _window]; 4780 [sub _viewWillMoveToSuperview: self]; 4781 [sub setNextResponder: self]; 4782 [_sub_views addObject: sub]; 4783 _rFlags.has_subviews = 1; 4784 [sub resetCursorRects]; 4785 [sub setNeedsDisplay: YES]; 4786 [sub _viewDidMoveToWindow]; 4787 [sub viewDidMoveToSuperview]; 4788 [self didAddSubview: sub]; 4789 } 4790 RELEASE(subs); 4791 } 4792 4793 return self; 4794} 4795 4796/* 4797 * Accessor methods 4798 */ 4799- (void) setAutoresizesSubviews: (BOOL)flag 4800{ 4801 _autoresizes_subviews = flag; 4802} 4803 4804- (void) setAutoresizingMask: (NSUInteger)mask 4805{ 4806 _autoresizingMask = mask; 4807} 4808 4809/** Returns the window in which the receiver resides. */ 4810- (NSWindow*) window 4811{ 4812 return _window; 4813} 4814 4815- (BOOL) autoresizesSubviews 4816{ 4817 return _autoresizes_subviews; 4818} 4819 4820- (NSUInteger) autoresizingMask 4821{ 4822 return _autoresizingMask; 4823} 4824 4825- (NSArray*) subviews 4826{ 4827 /* 4828 * Return a mutable copy 'cos we know that a mutable copy of an array or 4829 * a mutable array does a shallow copy - which is what we want to give 4830 * away - we don't want people to mess with our actual subviews array. 4831 */ 4832 return AUTORELEASE([_sub_views mutableCopyWithZone: NSDefaultMallocZone()]); 4833} 4834 4835- (NSView*) superview 4836{ 4837 return _super_view; 4838} 4839 4840- (BOOL) shouldDrawColor 4841{ 4842 return YES; 4843} 4844 4845- (BOOL) isOpaque 4846{ 4847 return NO; 4848} 4849 4850- (BOOL) needsDisplay 4851{ 4852 return _rFlags.needs_display; 4853} 4854 4855- (NSInteger) tag 4856{ 4857 return -1; 4858} 4859 4860- (BOOL) isFlipped 4861{ 4862 return NO; 4863} 4864 4865- (NSRect) bounds 4866{ 4867 return _bounds; 4868} 4869 4870- (NSRect) frame 4871{ 4872 return _frame; 4873} 4874 4875- (CGFloat) boundsRotation 4876{ 4877 if (_boundsMatrix != nil) 4878 { 4879 return [_boundsMatrix rotationAngle]; 4880 } 4881 4882 return 0.0; 4883} 4884 4885- (CGFloat) frameRotation 4886{ 4887 if (_frameMatrix != nil) 4888 { 4889 return [_frameMatrix rotationAngle]; 4890 } 4891 4892 return 0.0; 4893} 4894 4895/** 4896 * Returns whether the receiver posts NSViewFrameDidChangeNotification when 4897 * its frame changed. 4898 * 4899 * Returns YES by default (as documented in Cocoa View Programming Guide). 4900 */ 4901- (BOOL) postsFrameChangedNotifications 4902{ 4903 return _post_frame_changes; 4904} 4905 4906/** 4907 * Returns whether the receiver posts NSViewBoundsDidChangeNotification when 4908 * its bound changed. 4909 * 4910 * Returns YES by default (as documented in Cocoa View Programming Guide). 4911 */ 4912- (BOOL) postsBoundsChangedNotifications 4913{ 4914 return _post_bounds_changes; 4915} 4916 4917 4918/** 4919 * <p>Returns the default menu to be used for instances of the 4920 * current class; if no menu has been set through setMenu: 4921 * this default menu will be used. 4922 * </p> 4923 * <p>NSView's implementation returns nil. You should override 4924 * this method if you want all instances of your custom view 4925 * to use the same menu. 4926 * </p> 4927 */ 4928+ (NSMenu *)defaultMenu 4929{ 4930 return nil; 4931} 4932 4933/** 4934 * <p>NSResponder's method, overriden by NSView.</p> 4935 * <p>If no menu has been set through the use of setMenu:, or 4936 * if a nil value has been set through setMenu:, then the 4937 * value returned by defaultMenu is used. Otherwise this 4938 * method returns the menu set through NSResponder. 4939 * <p> 4940 * <p> see [NSResponder -menu], [NSResponder -setMenu:], 4941 * [NSView +defaultMenu] and [NSView -menuForEvent:]. 4942 * </p> 4943 */ 4944- (NSMenu *)menu 4945{ 4946 NSMenu *m = [super menu]; 4947 if (m) 4948 { 4949 return m; 4950 } 4951 else 4952 { 4953 return [[self class] defaultMenu]; 4954 } 4955} 4956 4957/** 4958 * <p>Returns the menu that it appropriates for the given 4959 * event. NSView's implementation returns the default menu of 4960 * the view.</p> 4961 * <p>This methods is intended to be overriden so that it can 4962 * return a context-sensitive for appropriate mouse's events. ( 4963 * (although it seems it can be used for any kind of event)</p> 4964 * <p>This method is used by NSView's rightMouseDown: method, 4965 * and the returned NSMenu is displayed as a context menu</p> 4966 * <p>Use of this method is discouraged in GNUstep as it breaks many 4967 * user interface guidelines. At the very least, menu items that appear 4968 * in a context sensitive menu should also always appear in a normal 4969 * menu. Otherwise, users are faced with an inconsistant interface where 4970 * the menu items they want are only available in certain (possibly 4971 * unknown) cases, making it difficult for the user to understand how 4972 * the application operates</p> 4973 * <p> see [NSResponder -menu], [NSResponder -setMenu:], 4974 * [NSView +defaultMenu] and [NSView -menu]. 4975 * </p> 4976 */ 4977- (NSMenu *)menuForEvent: (NSEvent *)theEvent 4978{ 4979 return [self menu]; 4980} 4981 4982/* 4983 * Tool Tips 4984 */ 4985 4986- (NSToolTipTag) addToolTipRect: (NSRect)aRect 4987 owner: (id)anObject 4988 userData: (void *)data 4989{ 4990 GSToolTips *tt = [GSToolTips tipsForView: self]; 4991 4992 _rFlags.has_tooltips = 1; 4993 return [tt addToolTipRect: aRect owner: anObject userData: data]; 4994} 4995 4996- (void) removeAllToolTips 4997{ 4998 if (_rFlags.has_tooltips == 1) 4999 { 5000 GSToolTips *tt = [GSToolTips tipsForView: self]; 5001 5002 [tt removeAllToolTips]; 5003 } 5004} 5005 5006- (void) removeToolTip: (NSToolTipTag)tag 5007{ 5008 if (_rFlags.has_tooltips == 1) 5009 { 5010 GSToolTips *tt = [GSToolTips tipsForView: self]; 5011 5012 [tt removeToolTip: tag]; 5013 } 5014} 5015 5016- (void) setToolTip: (NSString *)string 5017{ 5018 if (_rFlags.has_tooltips == 1 || [string length] > 0) 5019 { 5020 GSToolTips *tt = [GSToolTips tipsForView: self]; 5021 5022 _rFlags.has_tooltips = 1; 5023 [tt setToolTip: string]; 5024 } 5025} 5026 5027- (NSString *) toolTip 5028{ 5029 if (_rFlags.has_tooltips == 1) 5030 { 5031 GSToolTips *tt = [GSToolTips tipsForView: self]; 5032 5033 return [tt toolTip]; 5034 } 5035 return nil; 5036} 5037 5038- (void) rightMouseDown: (NSEvent *) theEvent 5039{ 5040 NSMenu *m; 5041 m = [self menuForEvent: theEvent]; 5042 if (m) 5043 { 5044 [NSMenu popUpContextMenu: m 5045 withEvent: theEvent 5046 forView: self]; 5047 } 5048 else 5049 { 5050 [super rightMouseDown: theEvent]; 5051 } 5052} 5053 5054- (BOOL) shouldBeTreatedAsInkEvent: (NSEvent *)theEvent 5055{ 5056 return YES; 5057} 5058 5059- (void) bind: (NSString *)binding 5060 toObject: (id)anObject 5061 withKeyPath: (NSString *)keyPath 5062 options: (NSDictionary *)options 5063{ 5064 if ([binding hasPrefix: NSHiddenBinding]) 5065 { 5066 GSKeyValueBinding *kvb; 5067 5068 [self unbind: binding]; 5069 kvb = [[GSKeyValueOrBinding alloc] initWithBinding: NSHiddenBinding 5070 withName: binding 5071 toObject: anObject 5072 withKeyPath: keyPath 5073 options: options 5074 fromObject: self]; 5075 // The binding will be retained in the binding table 5076 RELEASE(kvb); 5077 } 5078 else 5079 { 5080 [super bind: binding 5081 toObject: anObject 5082 withKeyPath: keyPath 5083 options: options]; 5084 } 5085} 5086 5087- (NSViewLayerContentsPlacement) layerContentsPlacement 5088{ 5089 // FIXME (when views have CALayer support) 5090 return NSViewLayerContentsPlacementScaleAxesIndependently; 5091} 5092 5093- (void) setLayerContentsPlacement: (NSViewLayerContentsPlacement)placement 5094{ 5095 // FIXME (when views have CALayer support) 5096 static BOOL logged = NO; 5097 if (!logged) 5098 { 5099 NSLog(@"warning: stub no-op implementation of -[NSView setLayerContentsPlacement:]"); 5100 logged = YES; 5101 } 5102} 5103 5104- (NSViewLayerContentsRedrawPolicy) layerContentsRedrawPolicy 5105{ 5106 // FIXME (when views have CALayer support) 5107 return NSViewLayerContentsRedrawNever; 5108} 5109 5110- (void) setLayerContentsRedrawPolicy: (NSViewLayerContentsRedrawPolicy) pol 5111{ 5112 // FIXME (when views have CALayer support) 5113 static BOOL logged = NO; 5114 if (!logged) 5115 { 5116 NSLog(@"warning: stub no-op implementation of -[NSView setLayerContentsRedrawPolicy:]"); 5117 logged = YES; 5118 } 5119} 5120 5121- (NSUserInterfaceLayoutDirection) userInterfaceLayoutDirection 5122{ 5123 // FIXME 5124 return NSUserInterfaceLayoutDirectionLeftToRight; 5125} 5126 5127- (void) setUserInterfaceLayoutDirection: (NSUserInterfaceLayoutDirection)dir 5128{ 5129 // FIXME: implement this 5130 return; 5131} 5132 5133@end 5134 5135@implementation NSView(KeyViewLoop) 5136 5137static NSComparisonResult 5138cmpFrame(id view1, id view2, void *context) 5139{ 5140 BOOL flippedSuperView = [(NSView *)context isFlipped]; 5141 NSRect frame1 = [view1 frame]; 5142 NSRect frame2 = [view2 frame]; 5143 5144 if (NSMinY(frame1) < NSMinY(frame2)) 5145 return flippedSuperView ? NSOrderedAscending : NSOrderedDescending; 5146 if (NSMaxY(frame1) > NSMaxY(frame2)) 5147 return flippedSuperView ? NSOrderedDescending : NSOrderedAscending; 5148 5149 // FIXME Should use NSMaxX in a Hebrew or Arabic locale 5150 if (NSMinX(frame1) < NSMinX(frame2)) 5151 return NSOrderedAscending; 5152 if (NSMinX(frame1) > NSMinX(frame2)) 5153 return NSOrderedDescending; 5154 return NSOrderedSame; 5155} 5156 5157- (void) _setUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView 5158{ 5159 if (_rFlags.has_subviews) 5160 { 5161 [self _recursiveSetUpKeyViewLoopWithNextKeyView: nextKeyView]; 5162 } 5163 else 5164 { 5165 [self setNextKeyView: nextKeyView]; 5166 } 5167} 5168 5169- (void) _recursiveSetUpKeyViewLoopWithNextKeyView: (NSView *)nextKeyView 5170{ 5171 NSArray *sortedViews; 5172 NSView *aView; 5173 NSEnumerator *e; 5174 5175 sortedViews = [_sub_views sortedArrayUsingFunction: cmpFrame context: self]; 5176 e = [sortedViews reverseObjectEnumerator]; 5177 while ((aView = [e nextObject]) != nil) 5178 { 5179 [aView _setUpKeyViewLoopWithNextKeyView: nextKeyView]; 5180 nextKeyView = aView; 5181 } 5182 [self setNextKeyView: nextKeyView]; 5183} 5184 5185@end 5186