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