API 0.9.5
AppKit/CPClipView.j
Go to the documentation of this file.
00001 /*
00002  * CPClipView.j
00003  * AppKit
00004  *
00005  * Created by Francisco Tolmasky.
00006  * Copyright 2008, 280 North, Inc.
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00021  */
00022 
00023 
00024 
00032 @implementation CPClipView : CPView
00033 {
00034     CPView  _documentView;
00035 }
00036 
00041 - (void)setDocumentView:(CPView)aView
00042 {
00043     if (_documentView == aView)
00044         return;
00045 
00046     var defaultCenter = [CPNotificationCenter defaultCenter];
00047 
00048     if (_documentView)
00049     {
00050         [defaultCenter
00051             removeObserver:self
00052                       name:CPViewFrameDidChangeNotification
00053                     object:_documentView];
00054 
00055         [defaultCenter
00056             removeObserver:self
00057                       name:CPViewBoundsDidChangeNotification
00058                     object:_documentView];
00059 
00060         [_documentView removeFromSuperview];
00061     }
00062 
00063     _documentView = aView;
00064 
00065     if (_documentView)
00066     {
00067         [self addSubview:_documentView];
00068 
00069         [_documentView setPostsFrameChangedNotifications:YES];
00070         [_documentView setPostsBoundsChangedNotifications:YES];
00071 
00072         [defaultCenter
00073             addObserver:self
00074                selector:@selector(viewFrameChanged:)
00075                    name:CPViewFrameDidChangeNotification
00076                  object:_documentView];
00077 
00078         [defaultCenter
00079             addObserver:self
00080                selector:@selector(viewBoundsChanged:)
00081                    name:CPViewBoundsDidChangeNotification
00082                  object:_documentView];
00083     }
00084 }
00085 
00089 - (id)documentView
00090 {
00091     return _documentView;
00092 }
00093 
00100 - (CGPoint)constrainScrollPoint:(CGPoint)aPoint
00101 {
00102     if (!_documentView)
00103         return _CGPointMakeZero();
00104 
00105     var documentFrame = [_documentView frame];
00106 
00107     aPoint.x = MAX(0.0, MIN(aPoint.x, MAX(_CGRectGetWidth(documentFrame) - _CGRectGetWidth(_bounds), 0.0)));
00108     aPoint.y = MAX(0.0, MIN(aPoint.y, MAX(_CGRectGetHeight(documentFrame) - _CGRectGetHeight(_bounds), 0.0)));
00109 
00110     return aPoint;
00111 }
00112 
00113 - (void)setBoundsOrigin:(CGPoint)aPoint
00114 {
00115     if (_CGPointEqualToPoint(_bounds.origin, aPoint))
00116         return;
00117 
00118     [super setBoundsOrigin:aPoint];
00119 
00120     var superview = [self superview],
00121 
00122         // This is hack to avoid having to import CPScrollView.
00123         // FIXME: Should CPScrollView be finding out about this on its own somehow?
00124         scrollViewClass = objj_getClass("CPScrollView");
00125 
00126     if ([superview isKindOfClass:scrollViewClass])
00127         [superview reflectScrolledClipView:self];
00128 }
00129 
00134 - (void)scrollToPoint:(CGPoint)aPoint
00135 {
00136     [self setBoundsOrigin:[self constrainScrollPoint:aPoint]];
00137 }
00138 
00143 - (void)viewBoundsChanged:(CPNotification)aNotification
00144 {
00145     [self _constrainScrollPoint];
00146 }
00147 
00152 - (void)viewFrameChanged:(CPNotification)aNotification
00153 {
00154     [self _constrainScrollPoint];
00155 }
00156 
00157 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
00158 {
00159     [super resizeSubviewsWithOldSize:aSize];
00160     [self _constrainScrollPoint];
00161 }
00162 
00163 - (void)_constrainScrollPoint
00164 {
00165     var oldScrollPoint = [self bounds].origin;
00166 
00167     // Call scrollToPoint: because the current scroll point may no longer make
00168     // sense given the new frame of the document view.
00169     [self scrollToPoint:oldScrollPoint];
00170 
00171     // scrollToPoint: takes care of reflectScrollClipView: for us, so bail if
00172     // the scroll points are not equal (meaning scrollToPoint: didn't early bail).
00173     if (!CGPointEqualToPoint(oldScrollPoint, [self bounds].origin))
00174         return;
00175 
00176     // ... and we're in a scroll view of course.
00177     var superview = [self superview],
00178 
00179         // This is hack to avoid having to import CPScrollView.
00180         // FIXME: Should CPScrollView be finding out about this on its own somehow?
00181         scrollViewClass = objj_getClass("CPScrollView");
00182 
00183     if ([superview isKindOfClass:scrollViewClass])
00184         [superview reflectScrolledClipView:self];
00185 }
00186 
00187 - (BOOL)autoscroll:(CPEvent)anEvent
00188 {
00189     var bounds = [self bounds],
00190         eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
00191         superview = [self superview],
00192         deltaX = 0,
00193         deltaY = 0;
00194 
00195     if (CGRectContainsPoint(bounds, eventLocation))
00196         return NO;
00197 
00198     if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
00199     {
00200         if (eventLocation.y < CGRectGetMinY(bounds))
00201             deltaY = CGRectGetMinY(bounds) - eventLocation.y;
00202         else if (eventLocation.y > CGRectGetMaxY(bounds))
00203             deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
00204         if (deltaY < -bounds.size.height)
00205             deltaY = -bounds.size.height;
00206         if (deltaY > bounds.size.height)
00207             deltaY = bounds.size.height;
00208     }
00209 
00210     if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
00211     {
00212         if (eventLocation.x < CGRectGetMinX(bounds))
00213             deltaX = CGRectGetMinX(bounds) - eventLocation.x;
00214         else if (eventLocation.x > CGRectGetMaxX(bounds))
00215             deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
00216         if (deltaX < -bounds.size.width)
00217             deltaX = -bounds.size.width;
00218         if (deltaX > bounds.size.width)
00219             deltaX = bounds.size.width;
00220     }
00221 
00222     return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
00223 }
00224 
00225 @end
00226 
00227 
00228 var CPClipViewDocumentViewKey = @"CPScrollViewDocumentView";
00229 
00230 @implementation CPClipView (CPCoding)
00231 
00232 - (id)initWithCoder:(CPCoder)aCoder
00233 {
00234     if (self = [super initWithCoder:aCoder])
00235         [self setDocumentView:[aCoder decodeObjectForKey:CPClipViewDocumentViewKey]];
00236 
00237     return self;
00238 }
00239 
00240 - (void)encodeWithCoder:(CPCoder)aCoder
00241 {
00242     [super encodeWithCoder:aCoder];
00243 
00244     [aCoder encodeObject:_documentView forKey:CPClipViewDocumentViewKey];
00245 }
00246 
00247 @end
 All Classes Files Functions Variables Defines