API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPClipView.j
Go to the documentation of this file.
1 /*
2  * CPClipView.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 @class CPScrollView
25 
33 @implementation CPClipView : CPView
34 {
35  CPView _documentView;
36 }
37 
42 - (void)setDocumentView:(CPView)aView
43 {
44  if (_documentView == aView)
45  return;
46 
47  if (_documentView)
48  {
49  var defaultCenter = [CPNotificationCenter defaultCenter];
50 
51  [defaultCenter
52  removeObserver:self
53  name:CPViewFrameDidChangeNotification
54  object:_documentView];
55 
56  [defaultCenter
57  removeObserver:self
58  name:CPViewBoundsDidChangeNotification
59  object:_documentView];
60 
61  [_documentView removeFromSuperview];
62  }
63 
64  _documentView = aView;
65 
66  if (_documentView)
67  {
68  [self addSubview:_documentView];
69  [self _observeDocumentView];
70  }
71 }
72 
73 - (void)_observeDocumentView
74 {
75  var defaultCenter = [CPNotificationCenter defaultCenter];
76 
77  [_documentView setPostsFrameChangedNotifications:YES];
78  [_documentView setPostsBoundsChangedNotifications:YES];
79 
80  [defaultCenter
81  addObserver:self
82  selector:@selector(viewFrameChanged:)
83  name:CPViewFrameDidChangeNotification
84  object:_documentView];
85 
86  [defaultCenter
87  addObserver:self
88  selector:@selector(viewBoundsChanged:)
89  name:CPViewBoundsDidChangeNotification
90  object:_documentView];
91 }
92 
96 - (id)documentView
97 {
98  return _documentView;
99 }
100 
107 - (CGPoint)constrainScrollPoint:(CGPoint)aPoint
108 {
109  if (!_documentView)
110  return CGPointMakeZero();
111 
112  var documentFrame = [_documentView frame];
113 
114  aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(CGRectGetWidth(documentFrame) - CGRectGetWidth(_bounds), 0.0)));
115  aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(CGRectGetHeight(documentFrame) - CGRectGetHeight(_bounds), 0.0)));
116 
117  return aPoint;
118 }
119 
120 - (void)setBoundsOrigin:(CGPoint)aPoint
121 {
122  if (CGPointEqualToPoint(_bounds.origin, aPoint))
123  return;
124 
125  [super setBoundsOrigin:aPoint];
126 
127  var superview = [self superview],
128 
129  // This is hack to avoid having to import CPScrollView.
130  // FIXME: Should CPScrollView be finding out about this on its own somehow?
131  scrollViewClass = objj_getClass("CPScrollView");
132 
133  if ([superview isKindOfClass:scrollViewClass])
134  [superview reflectScrolledClipView:self];
135 }
136 
141 - (void)scrollToPoint:(CGPoint)aPoint
142 {
143  [self setBoundsOrigin:[self constrainScrollPoint:aPoint]];
144 }
145 
150 - (void)viewBoundsChanged:(CPNotification)aNotification
151 {
152  [self _constrainScrollPoint];
153 }
154 
159 - (void)viewFrameChanged:(CPNotification)aNotification
160 {
161  [self _constrainScrollPoint];
162 }
163 
164 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
165 {
166  [super resizeSubviewsWithOldSize:aSize];
167  [self _constrainScrollPoint];
168 }
169 
170 - (void)_constrainScrollPoint
171 {
172  var oldScrollPoint = [self bounds].origin;
173 
174  // Call scrollToPoint: because the current scroll point may no longer make
175  // sense given the new frame of the document view.
176  [self scrollToPoint:oldScrollPoint];
177 
178  // scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
179  // the scroll points are not equal (meaning scrollToPoint: didn't early bail).
180  if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
181  return;
182 
183  // ... and we're in a scroll view of course.
184  var superview = [self superview],
185 
186  // This is hack to avoid having to import CPScrollView.
187  // FIXME: Should CPScrollView be finding out about this on its own somehow?
188  scrollViewClass = objj_getClass("CPScrollView");
189 
190  if ([superview isKindOfClass:scrollViewClass])
191  [superview reflectScrolledClipView:self];
192 }
193 
194 - (BOOL)autoscroll:(CPEvent)anEvent
195 {
196  var bounds = [self bounds],
197  eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
198  superview = [self superview],
199  deltaX = 0,
200  deltaY = 0;
201 
202  if (CGRectContainsPoint(bounds, eventLocation))
203  return NO;
204 
205  if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
206  {
207  if (eventLocation.y < CGRectGetMinY(bounds))
208  deltaY = CGRectGetMinY(bounds) - eventLocation.y;
209  else if (eventLocation.y > CGRectGetMaxY(bounds))
210  deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
211  if (deltaY < -bounds.size.height)
212  deltaY = -bounds.size.height;
213  if (deltaY > bounds.size.height)
214  deltaY = bounds.size.height;
215  }
216 
217  if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
218  {
219  if (eventLocation.x < CGRectGetMinX(bounds))
220  deltaX = CGRectGetMinX(bounds) - eventLocation.x;
221  else if (eventLocation.x > CGRectGetMaxX(bounds))
222  deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
223  if (deltaX < -bounds.size.width)
224  deltaX = -bounds.size.width;
225  if (deltaX > bounds.size.width)
226  deltaX = bounds.size.width;
227  }
228 
229  return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
230 }
231 
232 @end
233 
234 
235 var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
236 
237 @implementation CPClipView (CPCoding)
238 
239 - (id)initWithCoder:(CPCoder)aCoder
240 {
241  if (self = [super initWithCoder:aCoder])
242  {
243  // Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the
244  // view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded.
245  _documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
246  [self _observeDocumentView];
247  }
248 
249  return self;
250 }
251 
252 - (void)encodeWithCoder:(CPCoder)aCoder
253 {
254  [super encodeWithCoder:aCoder];
255 
256  [aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
257 }
258 
259 @end