1/** <title>NSClipView</title> 2 3 Copyright (C) 1996 Free Software Foundation, Inc. 4 5 Author: Ovidiu Predescu <ovidiu@net-community.com> 6 Date: July 1997 7 Author: Richard Frith-Macdonald <richard@brainstorm.co.uk> 8 Date: January 1999 9 10 This file is part of the GNUstep GUI Library. 11 12 This library is free software; you can redistribute it and/or 13 modify it under the terms of the GNU Lesser General Public 14 License as published by the Free Software Foundation; either 15 version 2 of the License, or (at your option) any later version. 16 17 This library is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public 23 License along with this library; see the file COPYING.LIB. 24 If not, see <http://www.gnu.org/licenses/> or write to the 25 Free Software Foundation, 51 Franklin Street, Fifth Floor, 26 Boston, MA 02110-1301, USA. 27*/ 28 29#import "config.h" 30#import <Foundation/NSNotification.h> 31#import <Foundation/NSException.h> 32 33#import "AppKit/NSClipView.h" 34#import "AppKit/NSCursor.h" 35#import "AppKit/NSColor.h" 36#import "AppKit/NSEvent.h" 37#import "AppKit/NSGraphics.h" 38#import "AppKit/NSTableView.h" 39#import "AppKit/NSWindow.h" 40#import "AppKit/PSOperators.h" 41 42#import <GNUstepGUI/GSNibLoading.h> 43#import "GSGuiPrivate.h" 44 45#include <math.h> 46 47@interface NSClipView (Private) 48- (void) _scrollToPoint: (NSPoint)aPoint; 49@end 50 51/* 52 * Return the biggest integral (in device space) rect contained in rect. 53 * Conversion to/from device space is done using view. 54 * 55 */ 56static inline NSRect integralRect (NSRect rect, NSView *view) 57{ 58 NSRect output; 59 int rounded; 60 61 output = [view convertRect: rect toView: nil]; 62 63 rounded = (int)(output.origin.x); 64 if ((CGFloat)rounded != output.origin.x) 65 { 66 output.origin.x = rounded + 1; 67 } 68 69 rounded = (int)(output.origin.y); 70 if ((CGFloat)rounded != output.origin.y) 71 { 72 output.origin.y = rounded + 1; 73 } 74 75 rounded = (int)(NSMaxX (output)); 76 if ((CGFloat)rounded != NSMaxX (output)) 77 { 78 output.size.width = rounded - output.origin.x; 79 } 80 81 rounded = (int)(NSMaxY (output)); 82 if ((CGFloat)rounded != NSMaxY (output)) 83 { 84 output.size.height = rounded - output.origin.y; 85 } 86 87 return [view convertRect: output fromView: nil]; 88} 89 90 91/* Note that the ivar _documentView is really just a convienience 92 variable. The actual document view is stored in NSClipView's 93 subview array. Deallocation, coding, etc of the view is then 94 handled by NSView 95*/ 96@implementation NSClipView 97 98- (id) initWithFrame: (NSRect)frameRect 99{ 100 self = [super initWithFrame:frameRect]; 101 if (self) 102 { 103 [self setAutoresizesSubviews: YES]; 104 [self setBackgroundColor: [NSColor controlBackgroundColor]]; 105 _copiesOnScroll = YES; 106 _drawsBackground = YES; 107 } 108 return self; 109} 110 111- (void) dealloc 112{ 113 [self setDocumentView: nil]; 114 RELEASE(_cursor); 115 RELEASE(_backgroundColor); 116 117 [super dealloc]; 118} 119 120 121/**<p>Sets aView the NSClipView's document view to <var>aView</var> 122 </p> 123 <p>See Also: -documentView</p> 124 */ 125- (void) setDocumentView: (NSView*)aView 126{ 127 NSNotificationCenter *nc; 128 NSView *nextKV; 129 130 if (_documentView == aView) 131 { 132 return; 133 } 134 135 nc = [NSNotificationCenter defaultCenter]; 136 if (_documentView) 137 { 138 nextKV = [_documentView nextKeyView]; 139 if ([nextKV isDescendantOf: _documentView]) 140 { 141 nextKV = nil; 142 } 143 144 [nc removeObserver: self 145 name: NSViewFrameDidChangeNotification 146 object: _documentView]; 147 [nc removeObserver: self 148 name: NSViewBoundsDidChangeNotification 149 object: _documentView]; 150 151 /* if our documentView was a tableview, unregister its 152 * observers 153 */ 154 if ([_documentView isKindOfClass: [NSTableView class]]) 155 { 156 [nc removeObserver: _documentView 157 name: NSViewFrameDidChangeNotification 158 object: self]; 159 } 160 [_documentView removeFromSuperview]; 161 } 162 else 163 { 164 nextKV = [self nextKeyView]; 165 } 166 167 /* Don't retain this since it's stored in our subviews. */ 168 _documentView = aView; 169 170 /* Update the view hierarchy coordinates if -isFlipped has changed. 171 Call this before doing anything else! */ 172 [self _invalidateCoordinates]; 173 174 if (_documentView) 175 { 176 NSRect df; 177 178 [self addSubview: _documentView]; 179 180 df = [_documentView frame]; 181 [self setBoundsOrigin: df.origin]; 182 183 /* Register for notifications sent by the document view */ 184 [_documentView setPostsFrameChangedNotifications: YES]; 185 [_documentView setPostsBoundsChangedNotifications: YES]; 186 187 [nc addObserver: self 188 selector: @selector(viewFrameChanged:) 189 name: NSViewFrameDidChangeNotification 190 object: _documentView]; 191 [nc addObserver: self 192 selector: @selector(viewBoundsChanged:) 193 name: NSViewBoundsDidChangeNotification 194 object: _documentView]; 195 196 /* 197 * if our document view is a tableview, let it know 198 * when we resize 199 */ 200 if ([_documentView isKindOfClass: [NSTableView class]]) 201 { 202 [self setPostsFrameChangedNotifications: YES]; 203 [nc addObserver: _documentView 204 selector: @selector(superviewFrameChanged:) 205 name: NSViewFrameDidChangeNotification 206 object: self]; 207 } 208 209 [self setNextKeyView: _documentView]; 210 if (![_documentView nextKeyView]) 211 [_documentView setNextKeyView: nextKV]; 212 } 213 else 214 [self setNextKeyView: nextKV]; 215 216 [_super_view reflectScrolledClipView: self]; 217} 218 219- (void) resetCursorRects 220{ 221 [self addCursorRect: _bounds cursor: _cursor]; 222} 223 224- (void) scrollToPoint: (NSPoint)aPoint 225{ 226 [self setBoundsOrigin: [self constrainScrollPoint: aPoint]]; 227 [self resetCursorRects]; 228} 229 230- (void) setBounds: (NSRect)b 231{ 232 [super setBounds: b]; 233 [self setNeedsDisplay: YES]; 234 [_super_view reflectScrolledClipView: self]; 235} 236 237- (void) setBoundsSize: (NSSize)aSize 238{ 239 [super setBoundsSize: aSize]; 240 [self setNeedsDisplay: YES]; 241 [_super_view reflectScrolledClipView: self]; 242} 243 244- (void) setBoundsOrigin: (NSPoint)aPoint 245{ 246 NSRect originalBounds = _bounds; 247 NSRect newBounds = originalBounds; 248 NSRect intersection; 249 250 newBounds.origin = aPoint; 251 252 if (NSEqualPoints(originalBounds.origin, newBounds.origin)) 253 { 254 return; 255 } 256 257 if (_documentView == nil) 258 { 259 return; 260 } 261 262 if (_copiesOnScroll && _window && [_window gState]) 263 { 264 /* Copy the portion of the view that is common before and after 265 scrolling. Then, document view needs to redraw the remaining 266 areas. */ 267 268 /* Common part - which is a first approx of what we could 269 copy... */ 270 intersection = NSIntersectionRect (originalBounds, newBounds); 271 272 /* but we must make sure we only copy from visible rect - we 273 can't copy bits which have been clipped (ie discarded) */ 274 intersection = NSIntersectionRect (intersection, [self visibleRect]); 275 276 /* Copying is done in device space so we only can copy by 277 integral rects in device space - adjust our copy rect */ 278 intersection = integralRect (intersection, self); 279 280 /* At this point, intersection is the rectangle containing the 281 image we can recycle from the old to the new situation. We 282 must not make any assumption on its position/size, because it 283 has been intersected with visible rect, which is an arbitrary 284 rectangle as far as we know. */ 285 if (NSEqualRects (intersection, NSZeroRect)) 286 { 287 // no recyclable part -- docview should redraw everything 288 // from scratch 289 [super setBoundsOrigin: newBounds.origin]; 290 [_documentView setNeedsDisplayInRect: 291 [self documentVisibleRect]]; 292 } 293 else 294 { 295 /* It is assumed these dx and dy will be integer in device 296 space because they are the difference of the bounds 297 origins, both of which should be integers in device space 298 because of the code at the end of 299 constrainScrollPoint:. */ 300 CGFloat dx = newBounds.origin.x - originalBounds.origin.x; 301 CGFloat dy = newBounds.origin.y - originalBounds.origin.y; 302 NSRect redrawRect; 303 304 /* Copy the intersection to the new position */ 305 [self scrollRect: intersection by: NSMakeSize(-dx, -dy)]; 306 307 /* Change coordinate system to the new one */ 308 [super setBoundsOrigin: newBounds.origin]; 309 310 /* Get the rectangle representing intersection in the new 311 bounds (mainly to keep code readable) */ 312 intersection.origin.x -= dx; 313 intersection.origin.y -= dy; 314 // intersection.size is the same 315 316 /* Now mark everything which is outside intersection as 317 needing to be redrawn by hand. NB: During simple usage - 318 scrolling in a single direction (left/rigth/up/down) - 319 and a normal visible rect, only one of the following 320 rects will be non-empty. */ 321 322 /* To the left of intersection */ 323 redrawRect = NSMakeRect(NSMinX(_bounds), _bounds.origin.y, 324 NSMinX(intersection) - NSMinX(_bounds), 325 _bounds.size.height); 326 if (NSIsEmptyRect(redrawRect) == NO) 327 { 328 [_documentView setNeedsDisplayInRect: 329 [self convertRect: redrawRect 330 toView: _documentView]]; 331 } 332 333 /* Right */ 334 redrawRect = NSMakeRect(NSMaxX(intersection), _bounds.origin.y, 335 NSMaxX(_bounds) - NSMaxX(intersection), 336 _bounds.size.height); 337 if (NSIsEmptyRect(redrawRect) == NO) 338 { 339 [_documentView setNeedsDisplayInRect: 340 [self convertRect: redrawRect 341 toView: _documentView]]; 342 } 343 344 /* Up (or Down according to whether it's flipped or not) */ 345 redrawRect = NSMakeRect(_bounds.origin.x, NSMinY(_bounds), 346 _bounds.size.width, 347 NSMinY(intersection) - NSMinY(_bounds)); 348 if (NSIsEmptyRect(redrawRect) == NO) 349 { 350 [_documentView setNeedsDisplayInRect: 351 [self convertRect: redrawRect 352 toView: _documentView]]; 353 } 354 355 /* Down (or Up) */ 356 redrawRect = NSMakeRect(_bounds.origin.x, NSMaxY(intersection), 357 _bounds.size.width, 358 NSMaxY(_bounds) - NSMaxY(intersection)); 359 if (NSIsEmptyRect(redrawRect) == NO) 360 { 361 [_documentView setNeedsDisplayInRect: 362 [self convertRect: redrawRect 363 toView: _documentView]]; 364 } 365 } 366 } 367 else 368 { 369 // dont copy anything -- docview draws it all 370 [super setBoundsOrigin: newBounds.origin]; 371 [_documentView setNeedsDisplayInRect: [self documentVisibleRect]]; 372 } 373 374 /* ?? TODO: Understand the following code - and add explanatory comment */ 375 /*if ([NSView focusView] == _documentView) 376 { 377 PStranslate (NSMinX (originalBounds) - aPoint.x, 378 NSMinY (originalBounds) - aPoint.y); 379 }*/ 380 381 [_super_view reflectScrolledClipView: self]; 382} 383 384/** 385 *<p></p> 386 */ 387- (NSPoint) constrainScrollPoint: (NSPoint)proposedNewOrigin 388{ 389 NSRect documentFrame; 390 NSPoint new = proposedNewOrigin; 391 392 if (_documentView == nil) 393 { 394 return _bounds.origin; 395 } 396 397 documentFrame = [_documentView frame]; 398 if (documentFrame.size.width <= _bounds.size.width) 399 { 400 new.x = documentFrame.origin.x; 401 } 402 else if (proposedNewOrigin.x <= documentFrame.origin.x) 403 { 404 new.x = documentFrame.origin.x; 405 } 406 else if (proposedNewOrigin.x + _bounds.size.width >= NSMaxX(documentFrame)) 407 { 408 new.x = NSMaxX(documentFrame) - _bounds.size.width; 409 } 410 411 if (documentFrame.size.height <= _bounds.size.height) 412 { 413 new.y = documentFrame.origin.y; 414 } 415 else if (proposedNewOrigin.y <= documentFrame.origin.y) 416 { 417 new.y = documentFrame.origin.y; 418 } 419 else if (proposedNewOrigin.y + _bounds.size.height >= NSMaxY(documentFrame)) 420 { 421 new.y = NSMaxY(documentFrame) - _bounds.size.height; 422 } 423 424 /* Make it an integer coordinate in device space - this is to make 425 sure that when the coordinates are changed and we need to copy to 426 do the scrolling, the difference is an integer and so we can copy 427 the image translating it by an integer in device space - and not 428 by a float. */ 429 430 new = [self convertPoint: new toView: nil]; 431 new.x = GSRoundTowardsInfinity(new.x); 432 new.y = GSRoundTowardsInfinity(new.y); 433 new = [self convertPoint: new fromView: nil]; 434 return new; 435} 436 437/**<p>Returns the document rectangle.</p> 438 <p>See Also: -documentVisibleRect </p> 439 */ 440- (NSRect) documentRect 441{ 442 NSRect documentFrame; 443 NSRect clipViewBounds; 444 NSRect rect; 445 446 if (_documentView == nil) 447 { 448 return _bounds; 449 } 450 451 documentFrame = [_documentView frame]; 452 clipViewBounds = _bounds; 453 rect.origin = documentFrame.origin; 454 rect.size.width = MAX(documentFrame.size.width, clipViewBounds.size.width); 455 rect.size.height = MAX(documentFrame.size.height, clipViewBounds.size.height); 456 457 return rect; 458} 459 460/**<p>Returns the document visible rectangle in the document views coordinate 461 * system. 462 * </p> 463 * <p>See Also: -documentRect [NSView-convertRect:toView:]</p> 464 */ 465- (NSRect) documentVisibleRect 466{ 467 return [self convertRect: _bounds toView:_documentView]; 468} 469 470- (void) drawRect: (NSRect)rect 471{ 472 if (_drawsBackground) 473 { 474 [_backgroundColor set]; 475 NSRectFill(rect); 476 } 477} 478 479/**<p>Scrolls in response to mouse-dragged events. </p> 480 */ 481- (BOOL) autoscroll: (NSEvent*)theEvent 482{ 483 NSPoint new; 484 NSPoint delta; 485 NSRect r; 486 487 if (_documentView == nil) 488 { 489 return NO; 490 } 491 492 new = [_documentView convertPoint: [theEvent locationInWindow] 493 fromView: nil]; 494 495 r = [self documentVisibleRect]; 496 497 if (new.x < NSMinX(r)) 498 delta.x = new.x - NSMinX(r); 499 else if (new.x > NSMaxX(r)) 500 delta.x = new.x - NSMaxX(r); 501 else 502 delta.x = 0; 503 504 if (new.y < NSMinY(r)) 505 delta.y = new.y - NSMinY(r); 506 else if (new.y > NSMaxY(r)) 507 delta.y = new.y - NSMaxY(r); 508 else 509 delta.y = 0; 510 511 new.x = _bounds.origin.x + delta.x; 512 new.y = _bounds.origin.y + delta.y; 513 514 new = [self constrainScrollPoint: new]; 515 if (NSEqualPoints(new, _bounds.origin)) 516 return NO; 517 518 [self setBoundsOrigin: new]; 519 return YES; 520} 521 522- (void) viewBoundsChanged: (NSNotification*)aNotification 523{ 524 [_super_view reflectScrolledClipView: self]; 525} 526 527/**<p>Used when the document view frame notify its change. 528 ( with NSViewFrameDidChangeNotification )</p> 529 */ 530- (void) viewFrameChanged: (NSNotification*)aNotification 531{ 532 [self _scrollToPoint: _bounds.origin]; 533 534 /* If document frame does not completely cover _bounds */ 535 if (NSContainsRect([_documentView frame], _bounds) == NO) 536 { 537 /* 538 * fill the area not covered by documentView with background color 539 */ 540 [self setNeedsDisplay: YES]; 541 } 542 543 [_super_view reflectScrolledClipView: self]; 544} 545 546- (void) scaleUnitSquareToSize: (NSSize)newUnitSize 547{ 548 [super scaleUnitSquareToSize: newUnitSize]; 549 [_super_view reflectScrolledClipView: self]; 550} 551 552- (void) setFrameSize: (NSSize)aSize 553{ 554 [super setFrameSize: aSize]; 555 [self setBoundsOrigin: [self constrainScrollPoint: _bounds.origin]]; 556 [_super_view reflectScrolledClipView: self]; 557} 558 559- (void) setFrameOrigin: (NSPoint)aPoint 560{ 561 [super setFrameOrigin: aPoint]; 562 [self setBoundsOrigin: [self constrainScrollPoint: _bounds.origin]]; 563 [_super_view reflectScrolledClipView: self]; 564} 565 566- (void) setFrame: (NSRect)rect 567{ 568 [super setFrame: rect]; 569 [self setBoundsOrigin: [self constrainScrollPoint: _bounds.origin]]; 570 [_super_view reflectScrolledClipView: self]; 571} 572 573- (void) translateOriginToPoint: (NSPoint)aPoint 574{ 575 [super translateOriginToPoint: aPoint]; 576 [_super_view reflectScrolledClipView: self]; 577} 578 579/** 580 *<p>Returns the NSClipView's document view.</p> 581 *<p>See Also: -setDocumentView: </p> 582 */ 583- (id) documentView 584{ 585 return _documentView; 586} 587 588/** 589 */ 590- (void) setCopiesOnScroll: (BOOL)flag 591{ 592 _copiesOnScroll = flag; 593} 594 595/** 596 */ 597- (BOOL) copiesOnScroll 598{ 599 return _copiesOnScroll; 600} 601 602/**<p>Sets the cursor for the document view to <var>aCursor</var></p> 603 <p>See Also: -documentCursor</p> 604 */ 605- (void) setDocumentCursor: (NSCursor*)aCursor 606{ 607 ASSIGN(_cursor, aCursor); 608} 609 610/**<p>Returns the cursor of the document view</p> 611 <p>See Also: -setDocumentCursor: </p> 612*/ 613- (NSCursor*) documentCursor 614{ 615 return _cursor; 616} 617 618/**<p>Returns the NSClipView's background color</p> 619 <p>See Also: -setBackgroundColor:</p> 620 */ 621- (NSColor*) backgroundColor 622{ 623 return _backgroundColor; 624} 625 626/**<p>Sets the NSClipView's background color to <var>aColor</var> and marks 627 self for display. Sets the opaque flag if needed ( to YES if the 628 NSClipView does not draw its background, if the background color 629 is nil or if the background color alpha component is less than 1.0 , NO 630 otherwise) </p> 631 <p>See Also: -backgroundColor [NSView-isOpaque]</p> 632 */ 633- (void) setBackgroundColor: (NSColor*)aColor 634{ 635 if (![_backgroundColor isEqual: aColor]) 636 { 637 ASSIGN (_backgroundColor, aColor); 638 639 [self setNeedsDisplay: YES]; 640 641 if (_drawsBackground == NO || _backgroundColor == nil 642 || [_backgroundColor alphaComponent] < 1.0) 643 { 644 _isOpaque = NO; 645 } 646 else 647 { 648 _isOpaque = YES; 649 } 650 } 651} 652 653- (void) setDrawsBackground: (BOOL)flag 654{ 655 if (_drawsBackground != flag) 656 { 657 _drawsBackground = flag; 658 659 [self setNeedsDisplay: YES]; 660 661 if (_drawsBackground == NO || _backgroundColor == nil 662 || [_backgroundColor alphaComponent] < 1.0) 663 { 664 _isOpaque = NO; 665 } 666 else 667 { 668 _isOpaque = YES; 669 } 670 } 671} 672 673- (BOOL) drawsBackground 674{ 675 return _drawsBackground; 676} 677 678- (BOOL) isOpaque 679{ 680 return _isOpaque; 681} 682 683- (BOOL) isFlipped 684{ 685 return (_documentView != nil) ? [_documentView isFlipped] : NO; 686} 687 688/* Disable rotation of clip view */ 689- (void) rotateByAngle: (CGFloat)angle 690{ 691} 692 693- (void) setBoundsRotation: (CGFloat)angle 694{ 695} 696 697- (void) setFrameRotation: (CGFloat)angle 698{ 699} 700 701/* Managing responder chain */ 702- (BOOL) acceptsFirstResponder 703{ 704 if (_documentView == nil) 705 { 706 return NO; 707 } 708 else 709 { 710 return [_documentView acceptsFirstResponder]; 711 } 712} 713 714- (BOOL) becomeFirstResponder 715{ 716 if (_documentView == nil) 717 { 718 return NO; 719 } 720 else 721 { 722 return [_window makeFirstResponder: _documentView]; 723 } 724} 725 726- (void) setNextKeyView: (NSView *)aView 727{ 728 if (_documentView && aView != _documentView) 729 [_documentView setNextKeyView: aView]; 730 else 731 [super setNextKeyView: aView]; 732} 733 734/* 735 * NSCoding protocol 736 */ 737- (void) encodeWithCoder: (NSCoder*)aCoder 738{ 739 [super encodeWithCoder: aCoder]; 740 if ([aCoder allowsKeyedCoding]) 741 { 742 unsigned int flags = 0; 743 [aCoder encodeObject: [self backgroundColor] forKey: @"NSBGColor"]; 744 [aCoder encodeObject: [self documentCursor] forKey: @"NSCursor"]; 745 [aCoder encodeObject: [self documentView] forKey: @"NSDocView"]; 746 747 if ([self drawsBackground]) 748 flags |= 4; 749 if ([self copiesOnScroll] == NO) 750 flags |= 2; 751 752 [aCoder encodeInt: flags forKey: @"NScvFlags"]; 753 } 754 else 755 { 756 [aCoder encodeObject: _backgroundColor]; 757 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_copiesOnScroll]; 758 [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_drawsBackground]; 759 [aCoder encodeObject: _cursor]; 760 } 761} 762 763- (id) initWithCoder: (NSCoder*)aDecoder 764{ 765 self = [super initWithCoder: aDecoder]; 766 if (self == nil) 767 { 768 return nil; 769 } 770 771 if ([aDecoder allowsKeyedCoding]) 772 { 773 [self setAutoresizesSubviews: YES]; 774 775 [self setBackgroundColor: [aDecoder decodeObjectForKey: @"NSBGColor"]]; 776 [self setDocumentCursor: [aDecoder decodeObjectForKey: @"NSCursor"]]; 777 778 if ([aDecoder containsValueForKey: @"NScvFlags"]) 779 { 780 int flags = [aDecoder decodeIntForKey: @"NScvFlags"]; 781 BOOL drawsBackground = ((4 & flags) > 0); 782 BOOL noCopyOnScroll = ((2 & flags) > 0); // ??? Not sure... 783 784 [self setCopiesOnScroll: (noCopyOnScroll == NO)]; 785 [self setDrawsBackground: drawsBackground]; 786 } 787 788 if ([[self subviews] count] > 0) 789 { 790 NSRect rect; 791 id document = [aDecoder decodeObjectForKey: @"NSDocView"]; 792 793 NSAssert([document class] != [NSCustomView class], 794 NSInvalidArgumentException); 795 rect = [document frame]; 796 rect.origin = NSZeroPoint; 797 [document setFrame: rect]; 798 RETAIN(document); // prevent it from being released. 799 [document removeFromSuperview]; 800 [self setDocumentView: document]; 801 RELEASE(document); 802 } 803 } 804 else 805 { 806 BOOL temp; 807 808 [self setAutoresizesSubviews: YES]; 809 810 [self setBackgroundColor: [aDecoder decodeObject]]; 811 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_copiesOnScroll]; 812 [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &temp]; 813 [self setDrawsBackground: temp]; 814 [aDecoder decodeValueOfObjCType: @encode(id) at: &_cursor]; 815 816 if ([[self subviews] count] > 0) 817 { 818 NSView *document = [[self subviews] objectAtIndex: 0]; 819 RETAIN(document); // prevent it from being released. 820 [document removeFromSuperview]; 821 [self setDocumentView: document]; 822 RELEASE(document); 823 } 824 } 825 return self; 826} 827@end 828 829@implementation NSClipView (Private) 830 831- (void) _scrollToPoint: (NSPoint)aPoint 832{ 833 NSRect proposedBounds; 834 NSRect proposedVisibleRect; 835 NSRect newVisibleRect; 836 NSRect newBounds; 837 838 // give documentView a chance to adjust its visible rectangle 839 proposedBounds = _bounds; 840 proposedBounds.origin = aPoint; 841 proposedVisibleRect = [self convertRect: proposedBounds 842 toView: _documentView]; 843 newVisibleRect = [_documentView adjustScroll: proposedVisibleRect]; 844 newBounds = [self convertRect: newVisibleRect fromView: _documentView]; 845 846 [self scrollToPoint: newBounds.origin]; 847} 848 849@end 850 851