PageRenderTime 20ms CodeModel.GetById 10ms app.highlight 4ms RepoModel.GetById 2ms app.codeStats 0ms

/AppKit/CPClipView.j

http://github.com/cacaodev/cappuccino
Unknown | 227 lines | 184 code | 43 blank | 0 comment | 0 complexity | 97b47db1861c930bcff79edfc83515e6 MD5 | raw 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@import "CPView.j"
 24
 25@class CPScrollView
 26
 27/*!
 28    @ingroup appkit
 29    @class CPClipView
 30
 31    CPClipView allows you to define a clip rect and display only that portion of its containing view.
 32    It is used to hold the document view in a CPScrollView.
 33*/
 34@implementation CPClipView : CPView
 35{
 36    CPView  _documentView;
 37}
 38
 39/*!
 40    Sets the document view to be \c aView.
 41    @param aView the new document view. It's frame origin will be changed to \c (0,0) after calling this method.
 42*/
 43- (void)setDocumentView:(CPView)aView
 44{
 45    if (_documentView == aView)
 46        return;
 47
 48    if (_documentView)
 49        [_documentView removeFromSuperview];
 50
 51    _documentView = aView;
 52
 53    if (_documentView)
 54        [self addSubview:_documentView];
 55}
 56
 57/*!
 58    Returns the document view.
 59*/
 60- (id)documentView
 61{
 62    return _documentView;
 63}
 64
 65/*!
 66    Returns a new point that may be adjusted from \c aPoint
 67    to make sure it lies within the document view.
 68    @param aPoint
 69    @return the adjusted point
 70*/
 71- (CGPoint)constrainScrollPoint:(CGPoint)aPoint
 72{
 73    if (!_documentView)
 74        return CGPointMakeZero();
 75
 76    var documentFrame = [_documentView frame];
 77
 78    aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(CGRectGetWidth(documentFrame) - CGRectGetWidth(_bounds), 0.0)));
 79    aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(CGRectGetHeight(documentFrame) - CGRectGetHeight(_bounds), 0.0)));
 80
 81    return aPoint;
 82}
 83
 84- (void)setBoundsOrigin:(CGPoint)aPoint
 85{
 86    if (CGPointEqualToPoint(_bounds.origin, aPoint))
 87        return;
 88
 89    [super setBoundsOrigin:aPoint];
 90
 91    var superview = [self superview],
 92
 93        // This is hack to avoid having to import CPScrollView.
 94        // FIXME: Should CPScrollView be finding out about this on its own somehow?
 95        scrollViewClass = objj_getClass("CPScrollView");
 96
 97    if ([superview isKindOfClass:scrollViewClass])
 98        [superview reflectScrolledClipView:self];
 99}
100
101/*!
102    Scrolls the clip view to the specified point. The method
103    sets its bounds origin to \c aPoint.
104*/
105- (void)scrollToPoint:(CGPoint)aPoint
106{
107    [self setBoundsOrigin:[self constrainScrollPoint:aPoint]];
108}
109
110/*!
111    Handles a CPViewBoundsDidChangeNotification.
112    @param aNotification the notification event
113*/
114- (void)viewBoundsChanged:(CPNotification)aNotification
115{
116    [self _constrainScrollPoint];
117}
118
119/*!
120    Handles a CPViewFrameDidChangeNotification.
121    @param aNotification the notification event
122*/
123- (void)viewFrameChanged:(CPNotification)aNotification
124{
125    [self _constrainScrollPoint];
126}
127
128- (void)resizeSubviewsWithOldSize:(CGSize)aSize
129{
130    [super resizeSubviewsWithOldSize:aSize];
131    [self _constrainScrollPoint];
132}
133
134- (void)_constrainScrollPoint
135{
136    var oldScrollPoint = [self bounds].origin;
137
138    // Call scrollToPoint: because the current scroll point may no longer make
139    // sense given the new frame of the document view.
140    [self scrollToPoint:oldScrollPoint];
141
142    // scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
143    // the scroll points are not equal (meaning scrollToPoint: didn't early bail).
144    if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
145        return;
146
147    // ... and we're in a scroll view of course.
148    var superview = [self superview],
149
150        // This is hack to avoid having to import CPScrollView.
151        // FIXME: Should CPScrollView be finding out about this on its own somehow?
152        scrollViewClass = objj_getClass("CPScrollView");
153
154    if ([superview isKindOfClass:scrollViewClass])
155        [superview reflectScrolledClipView:self];
156}
157
158- (BOOL)autoscroll:(CPEvent)anEvent
159{
160    var bounds = [self bounds],
161        eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
162        superview = [self superview],
163        deltaX = 0,
164        deltaY = 0;
165
166    if (CGRectContainsPoint(bounds, eventLocation))
167        return NO;
168
169    if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
170    {
171        if (eventLocation.y < CGRectGetMinY(bounds))
172            deltaY = CGRectGetMinY(bounds) - eventLocation.y;
173        else if (eventLocation.y > CGRectGetMaxY(bounds))
174            deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
175        if (deltaY < -bounds.size.height)
176            deltaY = -bounds.size.height;
177        if (deltaY > bounds.size.height)
178            deltaY = bounds.size.height;
179    }
180
181    if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
182    {
183        if (eventLocation.x < CGRectGetMinX(bounds))
184            deltaX = CGRectGetMinX(bounds) - eventLocation.x;
185        else if (eventLocation.x > CGRectGetMaxX(bounds))
186            deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
187        if (deltaX < -bounds.size.width)
188            deltaX = -bounds.size.width;
189        if (deltaX > bounds.size.width)
190            deltaX = bounds.size.width;
191    }
192
193    return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
194}
195
196- (CGRect)documentVisibleRect
197{
198    return [self convertRect:[self bounds] fromView:_documentView];
199}
200
201@end
202
203
204var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
205
206@implementation CPClipView (CPCoding)
207
208- (id)initWithCoder:(CPCoder)aCoder
209{
210    if (self = [super initWithCoder:aCoder])
211    {
212        // Don't call setDocumentView: here. It calls addSubview:, but it's A) not necessary since the
213        // view hierarchy is fully encoded and B) dangerous if the subview is not fully decoded.
214        _documentView = [aCoder decodeObjectForKey:CPClipViewDocumentViewKey];
215    }
216
217    return self;
218}
219
220- (void)encodeWithCoder:(CPCoder)aCoder
221{
222    [super encodeWithCoder:aCoder];
223
224    [aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
225}
226
227@end