API  0.9.7
 All Classes Files Functions Variables Macros Groups Pages
CPScrollView.j
Go to the documentation of this file.
1 /*
2  * CPScrollView.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * Modified to match Lion style by Antoine Mercadal 2011
9  * <antoine.mercadal@archipelproject.org>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24  */
25 
26 
27 
28 #define SHOULD_SHOW_CORNER_VIEW() (_scrollerStyle === CPScrollerStyleLegacy && _verticalScroller && ![_verticalScroller isHidden])
29 
30 
32 var _isBrowserUsingOverlayScrollers = function()
33 {
34 #if PLATFORM(DOM)
35  /*
36  Even if the system supports overlay (Lion) scrollers,
37  the browser (e.g. FireFox *cough*) may not.
38 
39  To determine if the browser is using overlay scrollbars,
40  we put a <p> element inside a shorter <div> and set its
41  overflow to scroll. If the browser is using visible scrollers,
42  the outer div's clientWidth will less than the offsetWidth, because
43  clientWidth does not include scrollbars, whereas offsetWidth does.
44  So if clientWidth === offsetWidth, the scrollers must be overlay.
45  Even IE gets this right.
46  */
47  var outer = document.createElement('div'),
48  inner = document.createElement('p');
49 
50  // position it absolute so it doesn't affect existing DOM elements
51  outer.style.position = "absolute";
52  outer.style.top = "0px";
53  outer.style.left = "0px";
54  outer.style.visibility = "hidden";
55  outer.style.width = "200px";
56  outer.style.height = "150px";
57  outer.style.overflow = "scroll";
58 
59  inner.style.width = "100%";
60  inner.style.height = "200px";
61  outer.appendChild(inner);
62 
63  document.body.appendChild(outer);
64 
65  var usingOverlayScrollers = outer.clientWidth === outer.offsetWidth;
66 
67  document.body.removeChild(outer);
68 
69  return usingOverlayScrollers;
70 #else
71  return NO;
72 #endif
73 };
74 
84 var TIMER_INTERVAL = 0.2,
87 
89 
91  CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
92 
93 
94 @implementation CPScrollView : CPView
95 {
96  CPClipView _contentView;
97  CPClipView _headerClipView;
98  CPView _cornerView;
99  CPView _bottomCornerView;
100 
101  id _delegate;
102  CPTimer _scrollTimer;
103 
104  BOOL _hasVerticalScroller;
105  BOOL _hasHorizontalScroller;
106  BOOL _autohidesScrollers;
107 
108  CPScroller _verticalScroller;
109  CPScroller _horizontalScroller;
110 
111  CPInteger _recursionCount;
112  CPInteger _implementedDelegateMethods;
113 
114  float _verticalLineScroll;
115  float _verticalPageScroll;
116  float _horizontalLineScroll;
117  float _horizontalPageScroll;
118 
119  CPBorderType _borderType;
120 
121  CPTimer _timerScrollersHide;
122 
123  int _scrollerStyle;
124  int _scrollerKnobStyle;
125 }
126 
127 
128 #pragma mark -
129 #pragma mark Class methods
130 
131 + (void)initialize
132 {
133  if (self !== [CPScrollView class])
134  return;
135 
136  var globalValue = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPScrollersGlobalStyle"];
137 
138  if (globalValue === nil || globalValue === -1)
139  CPScrollerStyleGlobal = _isBrowserUsingOverlayScrollers() ? CPScrollerStyleOverlay : CPScrollerStyleLegacy
140  else
141  CPScrollerStyleGlobal = globalValue;
142 }
143 
144 + (CPString)defaultThemeClass
145 {
146  return @"scrollview"
147 }
148 
149 + (CPDictionary)themeAttributes
150 {
151  return @{
152  @"bottom-corner-color": [CPColor whiteColor],
153  @"border-color": [CPColor blackColor]
154  };
155 }
156 
157 + (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
158 {
159  var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType],
160  scrollerWidth = [CPScroller scrollerWidth];
161 
162  if (hFlag)
163  bounds.size.height -= scrollerWidth;
164 
165  if (vFlag)
166  bounds.size.width -= scrollerWidth;
167 
168  return bounds.size;
169 }
170 
171 + (CGSize)frameSizeForContentSize:(CGSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
172 {
173  var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) borderType:borderType],
174  widthInset = contentSize.width - bounds.size.width,
175  heightInset = contentSize.height - bounds.size.height,
176  frameSize = CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset),
177  scrollerWidth = [CPScroller scrollerWidth];
178 
179  if (hFlag)
180  frameSize.height += scrollerWidth;
181 
182  if (vFlag)
183  frameSize.width += scrollerWidth;
184 
185  return frameSize;
186 }
187 
188 + (CGRect)_insetBounds:(CGRect)bounds borderType:(CPBorderType)borderType
189 {
190  switch (borderType)
191  {
192  case CPLineBorder:
193  case CPBezelBorder:
194  return CGRectInset(bounds, 1.0, 1.0);
195 
196  case CPGrooveBorder:
197  bounds = CGRectInset(bounds, 2.0, 2.0);
198  ++bounds.origin.y;
199  --bounds.size.height;
200  return bounds;
201 
202  case CPNoBorder:
203  default:
204  return bounds;
205  }
206 }
207 
211 + (int)globalScrollerStyle
212 {
213  return CPScrollerStyleGlobal;
214 }
215 
221 + (void)setGlobalScrollerStyle:(int)aStyle
222 {
223  CPScrollerStyleGlobal = aStyle;
224  [[CPNotificationCenter defaultCenter] postNotificationName:CPScrollerStyleGlobalChangeNotification object:nil];
225 }
226 
227 
228 #pragma mark -
229 #pragma mark Initialization
230 
231 - (id)initWithFrame:(CGRect)aFrame
232 {
233  self = [super initWithFrame:aFrame];
234 
235  if (self)
236  {
237  _verticalLineScroll = 10.0;
238  _verticalPageScroll = 10.0;
239 
240  _horizontalLineScroll = 10.0;
241  _horizontalPageScroll = 10.0;
242 
243  _borderType = CPNoBorder;
244 
245  _contentView = [[CPClipView alloc] initWithFrame:[self _insetBounds]];
246  [self addSubview:_contentView];
247 
248  _headerClipView = [[CPClipView alloc] init];
249  [self addSubview:_headerClipView];
250 
251  _bottomCornerView = [[CPView alloc] init];
252  [self addSubview:_bottomCornerView];
253 
254  [self setHasVerticalScroller:YES];
255  [self setHasHorizontalScroller:YES];
256  _scrollerKnobStyle = CPScrollerKnobStyleDefault;
257  [self setScrollerStyle:CPScrollerStyleGlobal];
258 
259  _delegate = nil;
260  _scrollTimer = nil;
261  _implementedDelegateMethods = 0;
262 
265  name:CPScrollerStyleGlobalChangeNotification
266  object:nil];
267  }
268 
269  return self;
270 }
271 
272 
273 #pragma mark -
274 #pragma mark Getters / Setters
275 
279 - (id)delegate
280 {
281  return _delegate;
282 }
283 
299 - (void)setDelegate:(id)aDelegate
300 {
301  if (aDelegate === _delegate)
302  return;
303 
304  _delegate = aDelegate;
305  _implementedDelegateMethods = 0;
306 
307  if (_delegate === nil)
308  return;
309 
310  if ([_delegate respondsToSelector:@selector(scrollViewWillScroll:)])
311  _implementedDelegateMethods |= CPScrollViewDelegate_scrollViewWillScroll_;
312 
313  if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)])
314  _implementedDelegateMethods |= CPScrollViewDelegate_scrollViewDidScroll_;
315 }
316 
317 - (int)scrollerStyle
318 {
319  return _scrollerStyle;
320 }
321 
328 - (void)setScrollerStyle:(int)aStyle
329 {
330  if (_scrollerStyle === aStyle)
331  return;
332 
333  _scrollerStyle = aStyle;
334 
335  [self _updateScrollerStyle];
336 }
337 
348 - (int)scrollerKnobStyle
349 {
350  return _scrollerKnobStyle;
351 }
352 
363 - (void)setScrollerKnobStyle:(int)newScrollerKnobStyle
364 {
365  if (_scrollerKnobStyle === newScrollerKnobStyle)
366  return;
367 
368  _scrollerKnobStyle = newScrollerKnobStyle;
369 
370  [self _updateScrollerStyle];
371 }
372 
376 - (CPClipView)contentView
377 {
378  return _contentView;
379 }
380 
386 - (void)setContentView:(CPClipView)aContentView
387 {
388  if (_contentView === aContentView || !aContentView)
389  return;
390 
391  var documentView = [aContentView documentView];
392 
393  if (documentView)
394  [documentView removeFromSuperview];
395 
396  [_contentView removeFromSuperview];
397 
398  _contentView = aContentView;
399 
400  [_contentView setDocumentView:documentView];
401 
402  [self addSubview:_contentView];
403 
404  // This will size the content view appropriately, so no need to size it in this method.
405  [self reflectScrolledClipView:_contentView];
406 }
407 
411 - (CGSize)contentSize
412 {
413  return [_contentView frame].size;
414 }
415 
419 - (id)documentView
420 {
421  return [_contentView documentView];
422 }
423 
429 - (void)setDocumentView:(CPView)aView
430 {
431  [_contentView setDocumentView:aView];
432 
433  // FIXME: This should be observed.
434  [self _updateCornerAndHeaderView];
435  [self reflectScrolledClipView:_contentView];
436 }
437 
441 - (CPBorderType)borderType
442 {
443  return _borderType;
444 }
445 
456 - (void)setBorderType:(CPBorderType)borderType
457 {
458  if (_borderType == borderType)
459  return;
460 
461  _borderType = borderType;
462 
463  [self reflectScrolledClipView:_contentView];
464  [self setNeedsDisplay:YES];
465 }
466 
467 
471 - (CPScroller)horizontalScroller
472 {
473  return _horizontalScroller;
474 }
475 
481 - (void)setHorizontalScroller:(CPScroller)aScroller
482 {
483  if (_horizontalScroller === aScroller)
484  return;
485 
486  [_horizontalScroller removeFromSuperview];
487  [_horizontalScroller setTarget:nil];
488  [_horizontalScroller setAction:nil];
489 
490  _horizontalScroller = aScroller;
491 
492  [_horizontalScroller setTarget:self];
493  [_horizontalScroller setAction:@selector(_horizontalScrollerDidScroll:)];
494 
495  [self addSubview:_horizontalScroller];
496 
497  [self _updateScrollerStyle];
498 }
499 
503 - (BOOL)hasHorizontalScroller
504 {
505  return _hasHorizontalScroller;
506 }
507 
514 - (void)setHasHorizontalScroller:(BOOL)shouldHaveHorizontalScroller
515 {
516  if (_hasHorizontalScroller === shouldHaveHorizontalScroller)
517  return;
518 
519  _hasHorizontalScroller = shouldHaveHorizontalScroller;
520 
521  if (_hasHorizontalScroller && !_horizontalScroller)
522  {
523  var bounds = [self _insetBounds];
524 
525  [self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(CGRectGetWidth(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle] + 1), [CPScroller scrollerWidthInStyle:_scrollerStyle])]];
526  [[self horizontalScroller] setFrameSize:CGSizeMake(CGRectGetWidth(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle])];
527  }
528 
529  [self reflectScrolledClipView:_contentView];
530 }
531 
535 - (CPScroller)verticalScroller
536 {
537  return _verticalScroller;
538 }
539 
545 - (void)setVerticalScroller:(CPScroller)aScroller
546 {
547  if (_verticalScroller === aScroller)
548  return;
549 
550  [_verticalScroller removeFromSuperview];
551  [_verticalScroller setTarget:nil];
552  [_verticalScroller setAction:nil];
553 
554  _verticalScroller = aScroller;
555 
556  [_verticalScroller setTarget:self];
557  [_verticalScroller setAction:@selector(_verticalScrollerDidScroll:)];
558 
559  [self addSubview:_verticalScroller];
560 
561  [self _updateScrollerStyle];
562 }
563 
567 - (BOOL)hasVerticalScroller
568 {
569  return _hasVerticalScroller;
570 }
571 
579 - (void)setHasVerticalScroller:(BOOL)shouldHaveVerticalScroller
580 {
581  if (_hasVerticalScroller === shouldHaveVerticalScroller)
582  return;
583 
584  _hasVerticalScroller = shouldHaveVerticalScroller;
585 
586  if (_hasVerticalScroller && !_verticalScroller)
587  {
588  var bounds = [self _insetBounds];
589 
590  [self setVerticalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, [CPScroller scrollerWidthInStyle:_scrollerStyle], MAX(CGRectGetHeight(bounds), [CPScroller scrollerWidthInStyle:_scrollerStyle] + 1))]];
591  [[self verticalScroller] setFrameSize:CGSizeMake([CPScroller scrollerWidthInStyle:_scrollerStyle], CGRectGetHeight(bounds))];
592  }
593 
594  [self reflectScrolledClipView:_contentView];
595 }
596 
600 - (BOOL)autohidesScrollers
601 {
602  return _autohidesScrollers;
603 }
604 
611 - (void)setAutohidesScrollers:(BOOL)autohidesScrollers
612 {
613  if (_autohidesScrollers == autohidesScrollers)
614  return;
615 
616  _autohidesScrollers = autohidesScrollers;
617 
618  [self reflectScrolledClipView:_contentView];
619 }
620 
621 - (CPView)bottomCornerView
622 {
623  return _bottomCornerView;
624 }
625 
626 - (void)setBottomCornerView:(CPView)aBottomCornerView
627 {
628  if (_bottomCornerView === aBottomCornerView)
629  return;
630 
631  [_bottomCornerView removeFromSuperview];
632 
633  [aBottomCornerView setFrame:[self _bottomCornerViewFrame]];
634  [self addSubview:aBottomCornerView];
635 
636  _bottomCornerView = aBottomCornerView;
637 
638  [self _updateCornerAndHeaderView];
639 }
640 
644 - (float)lineScroll
645 {
646  return [self horizontalLineScroll];
647 }
648 
654 - (void)setLineScroll:(float)aLineScroll
655 {
656  [self setHorizontalLineScroll:aLineScroll];
657  [self setVerticalLineScroll:aLineScroll];
658 }
659 
663 - (float)horizontalLineScroll
664 {
665  return _horizontalLineScroll;
666 }
667 
673 - (void)setHorizontalLineScroll:(float)aLineScroll
674 {
675  _horizontalLineScroll = aLineScroll;
676 }
677 
681 - (float)verticalLineScroll
682 {
683  return _verticalLineScroll;
684 }
685 
691 - (void)setVerticalLineScroll:(float)aLineScroll
692 {
693  _verticalLineScroll = aLineScroll;
694 }
695 
699 - (float)pageScroll
700 {
701  return [self horizontalPageScroll];
702 }
703 
709 - (void)setPageScroll:(float)aPageScroll
710 {
711  [self setHorizontalPageScroll:aPageScroll];
712  [self setVerticalPageScroll:aPageScroll];
713 }
714 
718 - (float)horizontalPageScroll
719 {
720  return _horizontalPageScroll;
721 }
722 
728 - (void)setHorizontalPageScroll:(float)aPageScroll
729 {
730  _horizontalPageScroll = aPageScroll;
731 }
732 
736 - (float)verticalPageScroll
737 {
738  return _verticalPageScroll;
739 }
740 
746 - (void)setVerticalPageScroll:(float)aPageScroll
747 {
748  _verticalPageScroll = aPageScroll;
749 }
750 
751 
752 #pragma mark -
753 #pragma mark Privates
754 
755 /* @ignore */
756 - (void)_updateScrollerStyle
757 {
758  if (_hasHorizontalScroller)
759  {
760  [_horizontalScroller setStyle:_scrollerStyle];
761  [_horizontalScroller unsetThemeState:CPThemeStateSelected];
762 
763  switch (_scrollerKnobStyle)
764  {
766  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
767  [_horizontalScroller setThemeState:CPThemeStateScrollerKnobLight];
768  break;
769 
771  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
772  [_horizontalScroller setThemeState:CPThemeStateScrollerKnobDark];
773  break;
774 
775  default:
776  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
777  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
778  }
779  }
780 
781  if (_hasVerticalScroller)
782  {
783  [_verticalScroller setStyle:_scrollerStyle];
784  [_verticalScroller unsetThemeState:CPThemeStateSelected];
785 
786  switch (_scrollerKnobStyle)
787  {
789  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
790  [_verticalScroller setThemeState:CPThemeStateScrollerKnobLight];
791  break;
792 
794  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
795  [_verticalScroller setThemeState:CPThemeStateScrollerKnobDark];
796  break;
797 
798  default:
799  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
800  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
801  }
802  }
803 
804  if (_scrollerStyle == CPScrollerStyleOverlay)
805  {
806  if (_timerScrollersHide)
807  [_timerScrollersHide invalidate];
808 
809  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
810  [[self bottomCornerView] setHidden:YES];
811  }
812  else
813  [[self bottomCornerView] setHidden:NO];
814 
815  [self reflectScrolledClipView:_contentView];
816 }
817 
818 /* @ignore */
819 - (CGRect)_insetBounds
820 {
821  return [[self class] _insetBounds:[self bounds] borderType:_borderType];
822 }
823 
824 /* @ignore */
825 - (void)_updateCornerAndHeaderView
826 {
827  var documentView = [self documentView],
828  currentHeaderView = [self _headerView],
829  documentHeaderView = [documentView respondsToSelector:@selector(headerView)] ? [documentView headerView] : nil;
830 
831  if (currentHeaderView !== documentHeaderView)
832  {
833  [currentHeaderView removeFromSuperview];
834  [_headerClipView setDocumentView:documentHeaderView];
835  }
836 
837  var documentCornerView = [documentView respondsToSelector:@selector(cornerView)] ? [documentView cornerView] : nil;
838 
839  if (_cornerView !== documentCornerView)
840  {
841  [_cornerView removeFromSuperview];
842 
843  _cornerView = documentCornerView;
844 
845  if (_cornerView)
846  {
847  [_cornerView setHidden:!SHOULD_SHOW_CORNER_VIEW()];
848  [self addSubview:_cornerView];
849  }
850  }
851 
852  [self reflectScrolledClipView:_contentView];
853  [documentHeaderView setNeedsLayout];
854  [documentHeaderView setNeedsDisplay:YES];
855 }
856 
857 /* @ignore */
858 - (CPView)_headerView
859 {
860  return [_headerClipView documentView];
861 }
862 
863 /* @ignore */
864 - (CGRect)_cornerViewFrame
865 {
866  if (!_cornerView)
867  return CGRectMakeZero();
868 
869  var bounds = [self _insetBounds],
870  frame = [_cornerView frame];
871 
872  frame.origin.x = CGRectGetMaxX(bounds) - CGRectGetWidth(frame);
873  frame.origin.y = CGRectGetMinY(bounds);
874 
875  return frame;
876 }
877 
878 /* @ignore */
879 - (CGRect)_headerClipViewFrame
880 {
881  var headerView = [self _headerView];
882 
883  if (!headerView)
884  return CGRectMakeZero();
885 
886  var frame = [self _insetBounds];
887 
888  frame.size.height = CGRectGetHeight([headerView frame]);
889 
891  frame.size.width -= CGRectGetWidth([self _cornerViewFrame]);
892 
893  return frame;
894 }
895 
896 /* @ignore */
897 - (CGRect)_bottomCornerViewFrame
898 {
899  if ([[self horizontalScroller] isHidden] || [[self verticalScroller] isHidden])
900  return CGRectMakeZero();
901 
902  var verticalFrame = [[self verticalScroller] frame],
903  bottomCornerFrame = CGRectMakeZero();
904 
905  bottomCornerFrame.origin.x = CGRectGetMinX(verticalFrame);
906  bottomCornerFrame.origin.y = CGRectGetMaxY(verticalFrame);
907  bottomCornerFrame.size.width = [CPScroller scrollerWidthInStyle:_scrollerStyle];
908  bottomCornerFrame.size.height = [CPScroller scrollerWidthInStyle:_scrollerStyle];
909 
910  return bottomCornerFrame;
911 }
912 
913 /* @ignore */
914 - (void)_verticalScrollerDidScroll:(CPScroller)aScroller
915 {
916  var value = [aScroller floatValue],
917  documentFrame = [[_contentView documentView] frame],
918  contentBounds = [_contentView bounds];
919 
920 
921  switch ([_verticalScroller hitPart])
922  {
924  contentBounds.origin.y -= _verticalLineScroll;
925  break;
926 
928  contentBounds.origin.y += _verticalLineScroll;
929  break;
930 
932  contentBounds.origin.y -= CGRectGetHeight(contentBounds) - _verticalPageScroll;
933  break;
934 
936  contentBounds.origin.y += CGRectGetHeight(contentBounds) - _verticalPageScroll;
937  break;
938 
939  // We want integral bounds!
940  case CPScrollerKnobSlot:
941  case CPScrollerKnob:
942  default:
943  contentBounds.origin.y = ROUND(value * (CGRectGetHeight(documentFrame) - CGRectGetHeight(contentBounds)));
944  }
945 
946  [self _sendDelegateMessages];
947 
948  [_contentView scrollToPoint:contentBounds.origin];
949 }
950 
951 /* @ignore */
952 - (void)_horizontalScrollerDidScroll:(CPScroller)aScroller
953 {
954  var value = [aScroller floatValue],
955  documentFrame = [[self documentView] frame],
956  contentBounds = [_contentView bounds];
957 
958  switch ([_horizontalScroller hitPart])
959  {
961  contentBounds.origin.x -= _horizontalLineScroll;
962  break;
963 
965  contentBounds.origin.x += _horizontalLineScroll;
966  break;
967 
969  contentBounds.origin.x -= CGRectGetWidth(contentBounds) - _horizontalPageScroll;
970  break;
971 
973  contentBounds.origin.x += CGRectGetWidth(contentBounds) - _horizontalPageScroll;
974  break;
975 
976  // We want integral bounds!
977  case CPScrollerKnobSlot:
978  case CPScrollerKnob:
979  default:
980  contentBounds.origin.x = ROUND(value * (CGRectGetWidth(documentFrame) - CGRectGetWidth(contentBounds)));
981  }
982 
983  [self _sendDelegateMessages];
984 
985  [_contentView scrollToPoint:contentBounds.origin];
986  [_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
987 }
988 
989 /* @ignore */
990 - (void)_sendDelegateMessages
991 {
992  if (_implementedDelegateMethods == 0)
993  return;
994 
995  if (!_scrollTimer)
996  {
997  [self _scrollViewWillScroll];
998  _scrollTimer = [CPTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(_scrollViewDidScroll) userInfo:nil repeats:YES];
999  }
1000  else
1001  [_scrollTimer setFireDate:[CPDate dateWithTimeIntervalSinceNow:TIMER_INTERVAL]];
1002 }
1003 
1004 /* @ignore */
1005 - (void)_hideScrollers:(CPTimer)theTimer
1006 {
1007  if ([_verticalScroller allowFadingOut])
1008  [_verticalScroller fadeOut];
1009  if ([_horizontalScroller allowFadingOut])
1010  [_horizontalScroller fadeOut];
1011  _timerScrollersHide = nil;
1012 }
1013 
1014 /* @ignore */
1015 - (void)_respondToScrollWheelEventWithDeltaX:(float)deltaX deltaY:(float)deltaY
1016 {
1017  var documentFrame = [[self documentView] frame],
1018  contentBounds = [_contentView bounds],
1019  contentFrame = [_contentView frame],
1020  enclosingScrollView = [self enclosingScrollView];
1021 
1022  // We want integral bounds!
1023  contentBounds.origin.x = ROUND(contentBounds.origin.x + deltaX);
1024  contentBounds.origin.y = ROUND(contentBounds.origin.y + deltaY);
1025 
1026  var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)],
1027  extraX = contentBounds.origin.x - constrainedOrigin.x,
1028  extraY = contentBounds.origin.y - constrainedOrigin.y;
1029 
1030  [self _sendDelegateMessages];
1031 
1032  [_contentView scrollToPoint:constrainedOrigin];
1033  [_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
1034 
1035  if (extraX || extraY)
1036  [enclosingScrollView _respondToScrollWheelEventWithDeltaX:extraX deltaY:extraY];
1037 }
1038 
1039 /* @ignore */
1040 - (void)_scrollViewWillScroll
1041 {
1042  if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewWillScroll_)
1043  [_delegate scrollViewWillScroll:self];
1044 }
1045 
1046 /* @ignore */
1047 - (void)_scrollViewDidScroll
1048 {
1049  [_scrollTimer invalidate];
1050  _scrollTimer = nil;
1051 
1052  if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewDidScroll_)
1053  [_delegate scrollViewDidScroll:self];
1054 }
1055 
1057 - (void)_didReceiveDefaultStyleChange:(CPNotification)aNotification
1058 {
1059  [self setScrollerStyle:CPScrollerStyleGlobal];
1060 }
1061 
1062 
1063 
1064 #pragma mark -
1065 #pragma mark Utilities
1066 
1070 - (void)tile
1071 {
1072  // yuck.
1073  // RESIZE: tile->setHidden AND refl
1074  // Outside Change: refl->tile->setHidden AND refl
1075  // scroll: refl.
1076 }
1077 
1083 - (void)reflectScrolledClipView:(CPClipView)aClipView
1084 {
1085  if (_contentView !== aClipView)
1086  return;
1087 
1088  if (_recursionCount > 5)
1089  return;
1090 
1091  ++_recursionCount;
1092 
1093  var documentView = [self documentView];
1094 
1095  if (!documentView)
1096  {
1097  if (_autohidesScrollers)
1098  {
1099  [_verticalScroller setHidden:YES];
1100  [_horizontalScroller setHidden:YES];
1101  }
1102 
1103  [_contentView setFrame:[self _insetBounds]];
1104  [_headerClipView setFrame:CGRectMakeZero()];
1105 
1106  --_recursionCount;
1107 
1108  return;
1109  }
1110 
1111  var documentFrame = [documentView frame], // the size of the whole document
1112  contentFrame = [self _insetBounds], // assume it takes up the entire size of the scrollview (no scrollers)
1113  headerClipViewFrame = [self _headerClipViewFrame],
1114  headerClipViewHeight = CGRectGetHeight(headerClipViewFrame);
1115 
1116  contentFrame.origin.y += headerClipViewHeight;
1117  contentFrame.size.height -= headerClipViewHeight;
1118 
1119  var difference = CGSizeMake(CGRectGetWidth(documentFrame) - CGRectGetWidth(contentFrame), CGRectGetHeight(documentFrame) - CGRectGetHeight(contentFrame)),
1120  verticalScrollerWidth = [CPScroller scrollerWidthInStyle:[_verticalScroller style]],
1121  horizontalScrollerHeight = [CPScroller scrollerWidthInStyle:[_horizontalScroller style]],
1122  hasVerticalScroll = difference.height > 0.0,
1123  hasHorizontalScroll = difference.width > 0.0,
1124  shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
1125  shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
1126 
1127  // Now we have to account for the shown scrollers affecting the deltas.
1128  if (shouldShowVerticalScroller)
1129  {
1130  if (_scrollerStyle === CPScrollerStyleLegacy)
1131  difference.width += verticalScrollerWidth;
1132  hasHorizontalScroll = difference.width > 0.0;
1133  shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
1134  }
1135 
1136  if (shouldShowHorizontalScroller)
1137  {
1138  if (_scrollerStyle === CPScrollerStyleLegacy)
1139  difference.height += horizontalScrollerHeight;
1140  hasVerticalScroll = difference.height > 0.0;
1141  shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll);
1142  }
1143 
1144  // We now definitively know which scrollers are shown or not, as well as whether they are showing scroll values.
1145  [_verticalScroller setHidden:!shouldShowVerticalScroller];
1146  [_verticalScroller setEnabled:hasVerticalScroll];
1147 
1148  [_horizontalScroller setHidden:!shouldShowHorizontalScroller];
1149  [_horizontalScroller setEnabled:hasHorizontalScroll];
1150 
1151  var overlay = [CPScroller scrollerOverlay];
1152  if (_scrollerStyle === CPScrollerStyleLegacy)
1153  {
1154  // We can thus appropriately account for them changing the content size.
1155  if (shouldShowVerticalScroller)
1156  contentFrame.size.width -= verticalScrollerWidth;
1157 
1158  if (shouldShowHorizontalScroller)
1159  contentFrame.size.height -= horizontalScrollerHeight;
1160  overlay = 0;
1161  }
1162 
1163  var scrollPoint = [_contentView bounds].origin,
1164  wasShowingVerticalScroller = ![_verticalScroller isHidden],
1165  wasShowingHorizontalScroller = ![_horizontalScroller isHidden];
1166 
1167  if (shouldShowVerticalScroller)
1168  {
1169  var verticalScrollerY =
1170  MAX(CGRectGetMinY(contentFrame), MAX(CGRectGetMaxY([self _cornerViewFrame]), CGRectGetMaxY(headerClipViewFrame)));
1171 
1172  var verticalScrollerHeight = CGRectGetMaxY(contentFrame) - verticalScrollerY;
1173 
1174  // Make a gap at the bottom of the vertical scroller so that the horizontal and vertical can't overlap.
1175  if (_scrollerStyle === CPScrollerStyleOverlay && hasHorizontalScroll)
1176  verticalScrollerHeight -= horizontalScrollerHeight;
1177 
1178  var documentHeight = CGRectGetHeight(documentFrame);
1179  [_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
1180  [_verticalScroller setKnobProportion:documentHeight > 0 ? CGRectGetHeight(contentFrame) / documentHeight : 1.0];
1181  [_verticalScroller setFrame:CGRectMake(CGRectGetMaxX(contentFrame) - overlay, verticalScrollerY, verticalScrollerWidth, verticalScrollerHeight)];
1182  }
1183  else if (wasShowingVerticalScroller)
1184  {
1185  [_verticalScroller setFloatValue:0.0];
1186  [_verticalScroller setKnobProportion:1.0];
1187  }
1188 
1189  if (shouldShowHorizontalScroller)
1190  {
1191  var horizontalScrollerWidth = CGRectGetWidth(contentFrame);
1192  // Make a gap at the bottom of the vertical scroller so that the horizontal and vertical can't overlap.
1193  if (_scrollerStyle === CPScrollerStyleOverlay && hasVerticalScroll)
1194  horizontalScrollerWidth -= verticalScrollerWidth;
1195 
1196  var documentWidth = CGRectGetWidth(documentFrame);
1197 
1198  [_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
1199  [_horizontalScroller setKnobProportion:documentWidth > 0 ? CGRectGetWidth(contentFrame) / documentWidth : 1.0];
1200  [_horizontalScroller setFrame:CGRectMake(CGRectGetMinX(contentFrame), CGRectGetMaxY(contentFrame) - overlay, horizontalScrollerWidth, horizontalScrollerHeight)];
1201  }
1202  else if (wasShowingHorizontalScroller)
1203  {
1204  [_horizontalScroller setFloatValue:0.0];
1205  [_horizontalScroller setKnobProportion:1.0];
1206  }
1207 
1208  [_contentView setFrame:contentFrame];
1209  [_headerClipView setFrame:[self _headerClipViewFrame]];
1210  [[_headerClipView documentView] setNeedsDisplay:YES];
1212  {
1213  [_cornerView setFrame:[self _cornerViewFrame]];
1214  [_cornerView setHidden:NO];
1215  }
1216  else
1217  [_cornerView setHidden:YES];
1218 
1219  if (_scrollerStyle === CPScrollerStyleLegacy)
1220  {
1221  [[self bottomCornerView] setFrame:[self _bottomCornerViewFrame]];
1222  [[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
1223  }
1224 
1225  --_recursionCount;
1226 }
1227 
1231 - (void)flashScrollers
1232 {
1233  if (_scrollerStyle === CPScrollerStyleLegacy)
1234  return;
1235 
1236  if (_hasHorizontalScroller)
1237  {
1238  [_horizontalScroller setHidden:NO];
1239  [_horizontalScroller fadeIn];
1240  }
1241 
1242  if (_hasVerticalScroller)
1243  {
1244  [_verticalScroller setHidden:NO];
1245  [_verticalScroller fadeIn];
1246  }
1247 
1248  if (_timerScrollersHide)
1249  [_timerScrollersHide invalidate]
1250 
1251  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
1252 }
1253 
1254 /* @ignore */
1255 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
1256 {
1257  [self reflectScrolledClipView:_contentView];
1258 }
1259 
1260 
1261 #pragma mark -
1262 #pragma mark Overrides
1263 
1264 - (void)drawRect:(CGRect)aRect
1265 {
1266  [super drawRect:aRect];
1267 
1268  if (_borderType == CPNoBorder)
1269  return;
1270 
1271  var strokeRect = [self bounds],
1273 
1274  CGContextSetLineWidth(context, 1);
1275 
1276  switch (_borderType)
1277  {
1278  case CPLineBorder:
1279  CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"border-color"]);
1280  CGContextStrokeRect(context, CGRectInset(strokeRect, 0.5, 0.5));
1281  break;
1282 
1283  case CPBezelBorder:
1284  [self _drawGrayBezelInContext:context bounds:strokeRect];
1285  break;
1286 
1287  case CPGrooveBorder:
1288  [self _drawGrooveInContext:context bounds:strokeRect];
1289  break;
1290 
1291  default:
1292  break;
1293  }
1294 }
1295 
1296 - (void)_drawGrayBezelInContext:(CGContext)context bounds:(CGRect)aRect
1297 {
1298  var minX = CGRectGetMinX(aRect),
1299  maxX = CGRectGetMaxX(aRect),
1300  minY = CGRectGetMinY(aRect),
1301  maxY = CGRectGetMaxY(aRect),
1302  y = minY + 0.5;
1303 
1304  // Slightly darker line on top.
1305  CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0 / 255.0 alpha:1.0]);
1306  CGContextBeginPath(context);
1307  CGContextMoveToPoint(context, minX, y);
1308  CGContextAddLineToPoint(context, maxX, y);
1309  CGContextStrokePath(context);
1310 
1311  // The rest of the border.
1312  CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
1313 
1314  var x = maxX - 0.5;
1315 
1316  CGContextBeginPath(context);
1317  CGContextMoveToPoint(context, x, minY + 1.0);
1318  CGContextAddLineToPoint(context, x, maxY);
1319  CGContextMoveToPoint(context, x - 0.5, maxY - 0.5);
1320  CGContextAddLineToPoint(context, minX, maxY - 0.5);
1321 
1322  x = minX + 0.5;
1323 
1324  CGContextMoveToPoint(context, x, maxY);
1325  CGContextAddLineToPoint(context, x, minY + 1.0);
1326 
1327  CGContextStrokePath(context);
1328 }
1329 
1330 - (void)_drawGrooveInContext:(CGContext)context bounds:(CGRect)aRect
1331 {
1332  var minX = CGRectGetMinX(aRect),
1333  maxX = CGRectGetMaxX(aRect),
1334  minY = CGRectGetMinY(aRect),
1335  maxY = CGRectGetMaxY(aRect);
1336 
1337  CGContextBeginPath(context);
1338  CGContextSetStrokeColor(context, [CPColor colorWithWhite:159.0 / 255.0 alpha:1.0]);
1339 
1340  var y = minY + 0.5;
1341 
1342  CGContextMoveToPoint(context, minX, y);
1343  CGContextAddLineToPoint(context, maxX, y);
1344 
1345  var x = maxX - 1.5;
1346 
1347  CGContextMoveToPoint(context, x, minY + 2.0);
1348  CGContextAddLineToPoint(context, x, maxY - 1.0);
1349 
1350  y = maxY - 1.5;
1351 
1352  CGContextMoveToPoint(context, maxX - 1.0, y);
1353  CGContextAddLineToPoint(context, minX + 2.0, y);
1354 
1355  x = minX + 0.5;
1356 
1357  CGContextMoveToPoint(context, x, maxY);
1358  CGContextAddLineToPoint(context, x, minY);
1359 
1360  CGContextStrokePath(context);
1361 
1362  CGContextBeginPath(context);
1363  CGContextSetStrokeColor(context, [CPColor whiteColor]);
1364 
1365  var rect = CGRectOffset(aRect, 1.0, 1.0);
1366 
1367  rect.size.width -= 1.0;
1368  rect.size.height -= 1.0;
1369  CGContextStrokeRect(context, CGRectInset(rect, 0.5, 0.5));
1370 
1371  CGContextBeginPath(context);
1372  CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
1373 
1374  y = minY + 2.5;
1375 
1376  CGContextMoveToPoint(context, minX + 2.0, y);
1377  CGContextAddLineToPoint(context, maxX - 2.0, y);
1378  CGContextStrokePath(context);
1379 }
1380 
1386 - (void)scrollWheel:(CPEvent)anEvent
1387 {
1388  if (_timerScrollersHide)
1389  [_timerScrollersHide invalidate];
1390  if (![_verticalScroller isHidden])
1391  [_verticalScroller fadeIn];
1392  if (![_horizontalScroller isHidden])
1393  [_horizontalScroller fadeIn];
1394  if (![_horizontalScroller isHidden] || ![_verticalScroller isHidden])
1395  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
1396 
1397  [self _respondToScrollWheelEventWithDeltaX:[anEvent deltaX] deltaY:[anEvent deltaY]];
1398 }
1399 
1400 - (void)scrollPageUp:(id)sender
1401 {
1402  var contentBounds = [_contentView bounds];
1403  [self moveByOffset:CGSizeMake(0.0, -(CGRectGetHeight(contentBounds) - _verticalPageScroll))];
1404 }
1405 
1406 - (void)scrollPageDown:(id)sender
1407 {
1408  var contentBounds = [_contentView bounds];
1409  [self moveByOffset:CGSizeMake(0.0, CGRectGetHeight(contentBounds) - _verticalPageScroll)];
1410 }
1411 
1412 - (void)scrollToBeginningOfDocument:(id)sender
1413 {
1414  [_contentView scrollToPoint:CGPointMakeZero()];
1415  [_headerClipView scrollToPoint:CGPointMakeZero()];
1416 }
1417 
1418 - (void)scrollToEndOfDocument:(id)sender
1419 {
1420  var contentBounds = [_contentView bounds],
1421  documentFrame = [[self documentView] frame],
1422  scrollPoint = CGPointMake(0.0, CGRectGetHeight(documentFrame) - CGRectGetHeight(contentBounds));
1423 
1424  [_contentView scrollToPoint:scrollPoint];
1425  [_headerClipView scrollToPoint:CGPointMakeZero()];
1426 }
1427 
1428 - (void)moveLeft:(id)sender
1429 {
1430  [self moveByOffset:CGSizeMake(-_horizontalLineScroll, 0.0)];
1431 }
1432 
1433 - (void)moveRight:(id)sender
1434 {
1435  [self moveByOffset:CGSizeMake(_horizontalLineScroll, 0.0)];
1436 }
1437 
1438 - (void)moveUp:(id)sender
1439 {
1440  [self moveByOffset:CGSizeMake(0.0, -_verticalLineScroll)];
1441 }
1442 
1443 - (void)moveDown:(id)sender
1444 {
1445  [self moveByOffset:CGSizeMake(0.0, _verticalLineScroll)];
1446 }
1447 
1448 - (void)moveByOffset:(CGSize)aSize
1449 {
1450  var documentFrame = [[self documentView] frame],
1451  contentBounds = [_contentView bounds];
1452 
1453  contentBounds.origin.x += aSize.width;
1454  contentBounds.origin.y += aSize.height;
1455 
1456  [_contentView scrollToPoint:contentBounds.origin];
1457  [_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0)];
1458 }
1459 
1460 @end
1461 
1462 
1463 var CPScrollViewContentViewKey = @"CPScrollViewContentView",
1464  CPScrollViewHeaderClipViewKey = @"CPScrollViewHeaderClipViewKey",
1465  CPScrollViewVLineScrollKey = @"CPScrollViewVLineScroll",
1466  CPScrollViewHLineScrollKey = @"CPScrollViewHLineScroll",
1467  CPScrollViewVPageScrollKey = @"CPScrollViewVPageScroll",
1468  CPScrollViewHPageScrollKey = @"CPScrollViewHPageScroll",
1469  CPScrollViewHasVScrollerKey = @"CPScrollViewHasVScroller",
1470  CPScrollViewHasHScrollerKey = @"CPScrollViewHasHScroller",
1471  CPScrollViewVScrollerKey = @"CPScrollViewVScroller",
1472  CPScrollViewHScrollerKey = @"CPScrollViewHScroller",
1473  CPScrollViewAutohidesScrollerKey = @"CPScrollViewAutohidesScroller",
1474  CPScrollViewCornerViewKey = @"CPScrollViewCornerViewKey",
1475  CPScrollViewBottomCornerViewKey = @"CPScrollViewBottomCornerViewKey",
1476  CPScrollViewBorderTypeKey = @"CPScrollViewBorderTypeKey",
1477  CPScrollViewScrollerStyleKey = @"CPScrollViewScrollerStyleKey",
1478  CPScrollViewScrollerKnobStyleKey = @"CPScrollViewScrollerKnobStyleKey";
1479 
1481 
1482 - (id)initWithCoder:(CPCoder)aCoder
1483 {
1484  if (self = [super initWithCoder:aCoder])
1485  {
1486  _verticalLineScroll = [aCoder decodeFloatForKey:CPScrollViewVLineScrollKey];
1487  _verticalPageScroll = [aCoder decodeFloatForKey:CPScrollViewVPageScrollKey];
1488 
1489  _horizontalLineScroll = [aCoder decodeFloatForKey:CPScrollViewHLineScrollKey];
1490  _horizontalPageScroll = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
1491 
1492  _contentView = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
1493  _headerClipView = [aCoder decodeObjectForKey:CPScrollViewHeaderClipViewKey];
1494 
1495  if (!_headerClipView)
1496  {
1497  _headerClipView = [[CPClipView alloc] init];
1498  [self addSubview:_headerClipView];
1499  }
1500 
1501  _verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
1502  _horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
1503 
1504  _hasVerticalScroller = [aCoder decodeBoolForKey:CPScrollViewHasVScrollerKey];
1505  _hasHorizontalScroller = [aCoder decodeBoolForKey:CPScrollViewHasHScrollerKey];
1506  _autohidesScrollers = [aCoder decodeBoolForKey:CPScrollViewAutohidesScrollerKey];
1507 
1508  _borderType = [aCoder decodeIntForKey:CPScrollViewBorderTypeKey];
1509 
1510  _cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
1511  _bottomCornerView = [aCoder decodeObjectForKey:CPScrollViewBottomCornerViewKey];
1512 
1513  _delegate = nil;
1514  _scrollTimer = nil;
1515  _implementedDelegateMethods = 0;
1516 
1517  _scrollerStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerStyleKey] || CPScrollerStyleGlobal;
1518  _scrollerKnobStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerKnobStyleKey] || CPScrollerKnobStyleDefault;
1519 
1522  name:CPScrollerStyleGlobalChangeNotification
1523  object:nil];
1524  }
1525 
1526  return self;
1527 }
1528 
1532 - (void)awakeFromCib
1533 {
1534  [self _updateScrollerStyle];
1535  [self _updateCornerAndHeaderView];
1536 }
1537 
1538 - (void)encodeWithCoder:(CPCoder)aCoder
1539 {
1540  [super encodeWithCoder:aCoder];
1541 
1542  [aCoder encodeObject:_contentView forKey:CPScrollViewContentViewKey];
1543  [aCoder encodeObject:_headerClipView forKey:CPScrollViewHeaderClipViewKey];
1544 
1545  [aCoder encodeObject:_verticalScroller forKey:CPScrollViewVScrollerKey];
1546  [aCoder encodeObject:_horizontalScroller forKey:CPScrollViewHScrollerKey];
1547 
1548  [aCoder encodeFloat:_verticalLineScroll forKey:CPScrollViewVLineScrollKey];
1549  [aCoder encodeFloat:_verticalPageScroll forKey:CPScrollViewVPageScrollKey];
1550  [aCoder encodeFloat:_horizontalLineScroll forKey:CPScrollViewHLineScrollKey];
1551  [aCoder encodeFloat:_horizontalPageScroll forKey:CPScrollViewHPageScrollKey];
1552 
1553  [aCoder encodeBool:_hasVerticalScroller forKey:CPScrollViewHasVScrollerKey];
1554  [aCoder encodeBool:_hasHorizontalScroller forKey:CPScrollViewHasHScrollerKey];
1555  [aCoder encodeBool:_autohidesScrollers forKey:CPScrollViewAutohidesScrollerKey];
1556 
1557  [aCoder encodeObject:_cornerView forKey:CPScrollViewCornerViewKey];
1558  [aCoder encodeObject:_bottomCornerView forKey:CPScrollViewBottomCornerViewKey];
1559 
1560  [aCoder encodeInt:_borderType forKey:CPScrollViewBorderTypeKey];
1561 
1562  [aCoder encodeInt:_scrollerStyle forKey:CPScrollViewScrollerStyleKey];
1563  [aCoder encodeInt:_scrollerKnobStyle forKey:CPScrollViewScrollerKnobStyleKey];
1564 }
1565 
1566 @end