API  1.0.0
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
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 
31 @protocol CPScrollViewDelegate <CPObject>
32 
33 @optional
34 - (void)scrollViewWillScroll:(CPScrollView)aScrollView;
35 - (void)scrollViewDidScroll:(CPScrollView)aScrollView;
36 
37 @end
38 
39 
41 var _isBrowserUsingOverlayScrollers = function()
42 {
43 #if PLATFORM(DOM)
44  /*
45  Even if the system supports overlay (Lion) scrollers,
46  the browser (e.g. FireFox *cough*) may not.
47 
48  To determine if the browser is using overlay scrollbars,
49  we put a <p> element inside a shorter <div> and set its
50  overflow to scroll. If the browser is using visible scrollers,
51  the outer div's clientWidth will less than the offsetWidth, because
52  clientWidth does not include scrollbars, whereas offsetWidth does.
53  So if clientWidth === offsetWidth, the scrollers must be overlay.
54  Even IE gets this right.
55  */
56  var outer = document.createElement('div'),
57  inner = document.createElement('p');
58 
59  // position it absolute so it doesn't affect existing DOM elements
60  outer.style.position = "absolute";
61  outer.style.top = "0px";
62  outer.style.left = "0px";
63  outer.style.visibility = "hidden";
64  outer.style.width = "200px";
65  outer.style.height = "150px";
66  outer.style.overflow = "scroll";
67 
68  inner.style.width = "100%";
69  inner.style.height = "200px";
70  outer.appendChild(inner);
71 
72  document.body.appendChild(outer);
73 
74  var usingOverlayScrollers = outer.clientWidth === outer.offsetWidth;
75 
76  document.body.removeChild(outer);
77 
78  return usingOverlayScrollers;
79 #else
80  return NO;
81 #endif
82 };
83 
84 var TIMER_INTERVAL = 0.2,
87 
89 
91  CPScrollerStyleGlobalChangeNotification = @"CPScrollerStyleGlobalChangeNotification";
92 
100 @implementation CPScrollView : CPView
101 {
102  CPClipView _contentView;
103  CPClipView _headerClipView;
104  CPView _cornerView;
105  CPView _bottomCornerView;
106 
107  id <CPScrollViewDelegate> _delegate;
108  CPTimer _scrollTimer;
109 
110  BOOL _hasVerticalScroller;
111  BOOL _hasHorizontalScroller;
112  BOOL _autohidesScrollers;
113 
114  CPScroller _verticalScroller;
115  CPScroller _horizontalScroller;
116 
117  CPInteger _recursionCount;
118  CPInteger _implementedDelegateMethods;
119 
120  float _verticalLineScroll;
121  float _verticalPageScroll;
122  float _horizontalLineScroll;
123  float _horizontalPageScroll;
124 
125  CPBorderType _borderType;
126 
127  CPTimer _timerScrollersHide;
128 
129  int _scrollerStyle;
130  int _scrollerKnobStyle;
131 }
132 
133 
134 #pragma mark -
135 #pragma mark Class methods
136 
137 + (void)initialize
138 {
139  if (self !== [CPScrollView class])
140  return;
141 
142  var globalValue = [[CPBundle mainBundle] objectForInfoDictionaryKey:@"CPScrollersGlobalStyle"];
143 
144  if (globalValue === nil || globalValue === -1)
145  CPScrollerStyleGlobal = _isBrowserUsingOverlayScrollers() ? CPScrollerStyleOverlay : CPScrollerStyleLegacy
146  else
147  CPScrollerStyleGlobal = globalValue;
148 }
149 
150 + (CPString)defaultThemeClass
151 {
152  return @"scrollview"
153 }
154 
155 + (CPDictionary)themeAttributes
156 {
157  return @{
158  @"bottom-corner-color": [CPColor whiteColor],
159  @"border-color": [CPColor blackColor]
160  };
161 }
162 
163 + (CGSize)contentSizeForFrameSize:(CGSize)frameSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
164 {
165  var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, frameSize.width, frameSize.height) borderType:borderType];
166 
167  if (hFlag)
168  bounds.size.height -= [_horizontalScroller scrollerWidth];
169 
170  if (vFlag)
171  bounds.size.width -= [_verticalScroller scrollerWidth];
172 
173  return bounds.size;
174 }
175 
176 + (CGSize)frameSizeForContentSize:(CGSize)contentSize hasHorizontalScroller:(BOOL)hFlag hasVerticalScroller:(BOOL)vFlag borderType:(CPBorderType)borderType
177 {
178  var bounds = [self _insetBounds:CGRectMake(0.0, 0.0, contentSize.width, contentSize.height) borderType:borderType],
179  widthInset = contentSize.width - bounds.size.width,
180  heightInset = contentSize.height - bounds.size.height,
181  frameSize = CGSizeMake(contentSize.width + widthInset, contentSize.height + heightInset);
182 
183  if (hFlag)
184  frameSize.height += [_horizontalScroller scrollerWidth];
185 
186  if (vFlag)
187  frameSize.width += [_verticalScroller scrollerWidth];
188 
189  return frameSize;
190 }
191 
192 + (CGRect)_insetBounds:(CGRect)bounds borderType:(CPBorderType)borderType
193 {
194  switch (borderType)
195  {
196  case CPLineBorder:
197  case CPBezelBorder:
198  return CGRectInset(bounds, 1.0, 1.0);
199 
200  case CPGrooveBorder:
201  bounds = CGRectInset(bounds, 2.0, 2.0);
202  ++bounds.origin.y;
203  --bounds.size.height;
204  return bounds;
205 
206  case CPNoBorder:
207  default:
208  return bounds;
209  }
210 }
211 
215 + (int)globalScrollerStyle
216 {
217  return CPScrollerStyleGlobal;
218 }
219 
225 + (void)setGlobalScrollerStyle:(int)aStyle
226 {
227  CPScrollerStyleGlobal = aStyle;
228  [[CPNotificationCenter defaultCenter] postNotificationName:CPScrollerStyleGlobalChangeNotification object:nil];
229 }
230 
231 
232 #pragma mark -
233 #pragma mark Initialization
234 
235 - (id)initWithFrame:(CGRect)aFrame
236 {
237  self = [super initWithFrame:aFrame];
238 
239  if (self)
240  {
241  _verticalLineScroll = 10.0;
242  _verticalPageScroll = 10.0;
243 
244  _horizontalLineScroll = 10.0;
245  _horizontalPageScroll = 10.0;
246 
247  _borderType = CPNoBorder;
248 
249  _contentView = [[CPClipView alloc] initWithFrame:[self _insetBounds]];
250  [self addSubview:_contentView];
251 
252  _headerClipView = [[CPClipView alloc] init];
253  [self addSubview:_headerClipView];
254 
255  _bottomCornerView = [[CPView alloc] init];
256  [self addSubview:_bottomCornerView];
257 
258  [self setHasVerticalScroller:YES];
259  [self setHasHorizontalScroller:YES];
260  _scrollerKnobStyle = CPScrollerKnobStyleDefault;
261  [self setScrollerStyle:CPScrollerStyleGlobal];
262 
263  _delegate = nil;
264  _scrollTimer = nil;
265  _implementedDelegateMethods = 0;
266  }
267 
268  return self;
269 }
270 
271 
272 #pragma mark -
273 #pragma mark Getters / Setters
274 
278 - (id)delegate
279 {
280  return _delegate;
281 }
282 
298 - (void)setDelegate:(id <CPScrollViewDelegate>)aDelegate
299 {
300  if (aDelegate === _delegate)
301  return;
302 
303  _delegate = aDelegate;
304  _implementedDelegateMethods = 0;
305 
306  if (_delegate === nil)
307  return;
308 
309  if ([_delegate respondsToSelector:@selector(scrollViewWillScroll:)])
310  _implementedDelegateMethods |= CPScrollViewDelegate_scrollViewWillScroll_;
311 
312  if ([_delegate respondsToSelector:@selector(scrollViewDidScroll:)])
313  _implementedDelegateMethods |= CPScrollViewDelegate_scrollViewDidScroll_;
314 }
315 
316 - (int)scrollerStyle
317 {
318  return _scrollerStyle;
319 }
320 
327 - (void)setScrollerStyle:(int)aStyle
328 {
329  if (_scrollerStyle === aStyle)
330  return;
331 
332  _scrollerStyle = aStyle;
333 
334  [self _updateScrollerStyle];
335 }
336 
347 - (int)scrollerKnobStyle
348 {
349  return _scrollerKnobStyle;
350 }
351 
362 - (void)setScrollerKnobStyle:(int)newScrollerKnobStyle
363 {
364  if (_scrollerKnobStyle === newScrollerKnobStyle)
365  return;
366 
367  _scrollerKnobStyle = newScrollerKnobStyle;
368 
369  [self _updateScrollerStyle];
370 }
371 
375 - (CPClipView)contentView
376 {
377  return _contentView;
378 }
379 
385 - (void)setContentView:(CPClipView)aContentView
386 {
387  if (_contentView === aContentView || !aContentView)
388  return;
389 
390  var documentView = [aContentView documentView];
391 
392  if (documentView)
393  [documentView removeFromSuperview];
394 
395  [_contentView removeFromSuperview];
396 
397  _contentView = aContentView;
398 
399  [_contentView setDocumentView:documentView];
400 
401  [self addSubview:_contentView];
402 
403  // This will size the content view appropriately, so no need to size it in this method.
404  [self reflectScrolledClipView:_contentView];
405 }
406 
410 - (CGSize)contentSize
411 {
412  return [_contentView frame].size;
413 }
414 
418 - (id)documentView
419 {
420  return [_contentView documentView];
421 }
422 
428 - (void)setDocumentView:(CPView)aView
429 {
430  [_contentView setDocumentView:aView];
431 
432  // FIXME: This should be observed.
433  [self _updateCornerAndHeaderView];
434  [self reflectScrolledClipView:_contentView];
435 }
436 
440 - (CPBorderType)borderType
441 {
442  return _borderType;
443 }
444 
455 - (void)setBorderType:(CPBorderType)borderType
456 {
457  if (_borderType == borderType)
458  return;
459 
460  _borderType = borderType;
461 
462  [self reflectScrolledClipView:_contentView];
463  [self setNeedsDisplay:YES];
464 }
465 
466 
470 - (CPScroller)horizontalScroller
471 {
472  return _horizontalScroller;
473 }
474 
480 - (void)setHorizontalScroller:(CPScroller)aScroller
481 {
482  if (_horizontalScroller === aScroller)
483  return;
484 
485  [_horizontalScroller removeFromSuperview];
486  [_horizontalScroller setTarget:nil];
487  [_horizontalScroller setAction:nil];
488 
489  _horizontalScroller = aScroller;
490 
491  [_horizontalScroller setTarget:self];
492  [_horizontalScroller setAction:@selector(_horizontalScrollerDidScroll:)];
493 
494  [self addSubview:_horizontalScroller];
495 
496  [self _updateScrollerStyle];
497 }
498 
502 - (BOOL)hasHorizontalScroller
503 {
504  return _hasHorizontalScroller;
505 }
506 
513 - (void)setHasHorizontalScroller:(BOOL)shouldHaveHorizontalScroller
514 {
515  if (_hasHorizontalScroller === shouldHaveHorizontalScroller)
516  return;
517 
518  _hasHorizontalScroller = shouldHaveHorizontalScroller;
519 
520  if (_hasHorizontalScroller && !_horizontalScroller)
521  {
522  var bounds = [self _insetBounds];
523 
524  [self setHorizontalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, MAX(CGRectGetWidth(bounds), [_horizontalScroller scrollerWidth] + 1), [_horizontalScroller scrollerWidth])]];
525  [[self horizontalScroller] setFrameSize:CGSizeMake(CGRectGetWidth(bounds), [_horizontalScroller scrollerWidth])];
526  }
527 
528  [self reflectScrolledClipView:_contentView];
529 }
530 
534 - (CPScroller)verticalScroller
535 {
536  return _verticalScroller;
537 }
538 
544 - (void)setVerticalScroller:(CPScroller)aScroller
545 {
546  if (_verticalScroller === aScroller)
547  return;
548 
549  [_verticalScroller removeFromSuperview];
550  [_verticalScroller setTarget:nil];
551  [_verticalScroller setAction:nil];
552 
553  _verticalScroller = aScroller;
554 
555  [_verticalScroller setTarget:self];
556  [_verticalScroller setAction:@selector(_verticalScrollerDidScroll:)];
557 
558  [self addSubview:_verticalScroller];
559 
560  [self _updateScrollerStyle];
561 }
562 
566 - (BOOL)hasVerticalScroller
567 {
568  return _hasVerticalScroller;
569 }
570 
578 - (void)setHasVerticalScroller:(BOOL)shouldHaveVerticalScroller
579 {
580  if (_hasVerticalScroller === shouldHaveVerticalScroller)
581  return;
582 
583  _hasVerticalScroller = shouldHaveVerticalScroller;
584 
585  if (_hasVerticalScroller && !_verticalScroller)
586  {
587  var bounds = [self _insetBounds];
588 
589  [self setVerticalScroller:[[CPScroller alloc] initWithFrame:CGRectMake(0.0, 0.0, [_verticalScroller scrollerWidth], MAX(CGRectGetHeight(bounds), [_verticalScroller scrollerWidth] + 1))]];
590  [[self verticalScroller] setFrameSize:CGSizeMake([_verticalScroller scrollerWidth], CGRectGetHeight(bounds))];
591  }
592 
593  [self reflectScrolledClipView:_contentView];
594 }
595 
599 - (BOOL)autohidesScrollers
600 {
601  return _autohidesScrollers;
602 }
603 
610 - (void)setAutohidesScrollers:(BOOL)autohidesScrollers
611 {
612  if (_autohidesScrollers == autohidesScrollers)
613  return;
614 
615  _autohidesScrollers = autohidesScrollers;
616 
617  [self reflectScrolledClipView:_contentView];
618 }
619 
620 - (CPView)bottomCornerView
621 {
622  return _bottomCornerView;
623 }
624 
625 - (void)setBottomCornerView:(CPView)aBottomCornerView
626 {
627  if (_bottomCornerView === aBottomCornerView)
628  return;
629 
630  [_bottomCornerView removeFromSuperview];
631 
632  [aBottomCornerView setFrame:[self _bottomCornerViewFrame]];
633  [self addSubview:aBottomCornerView];
634 
635  _bottomCornerView = aBottomCornerView;
636 
637  [self _updateCornerAndHeaderView];
638 }
639 
643 - (float)lineScroll
644 {
645  return [self horizontalLineScroll];
646 }
647 
653 - (void)setLineScroll:(float)aLineScroll
654 {
655  [self setHorizontalLineScroll:aLineScroll];
656  [self setVerticalLineScroll:aLineScroll];
657 }
658 
662 - (float)horizontalLineScroll
663 {
664  return _horizontalLineScroll;
665 }
666 
672 - (void)setHorizontalLineScroll:(float)aLineScroll
673 {
674  _horizontalLineScroll = aLineScroll;
675 }
676 
680 - (float)verticalLineScroll
681 {
682  return _verticalLineScroll;
683 }
684 
690 - (void)setVerticalLineScroll:(float)aLineScroll
691 {
692  _verticalLineScroll = aLineScroll;
693 }
694 
698 - (float)pageScroll
699 {
700  return [self horizontalPageScroll];
701 }
702 
708 - (void)setPageScroll:(float)aPageScroll
709 {
710  [self setHorizontalPageScroll:aPageScroll];
711  [self setVerticalPageScroll:aPageScroll];
712 }
713 
717 - (float)horizontalPageScroll
718 {
719  return _horizontalPageScroll;
720 }
721 
727 - (void)setHorizontalPageScroll:(float)aPageScroll
728 {
729  _horizontalPageScroll = aPageScroll;
730 }
731 
735 - (float)verticalPageScroll
736 {
737  return _verticalPageScroll;
738 }
739 
745 - (void)setVerticalPageScroll:(float)aPageScroll
746 {
747  _verticalPageScroll = aPageScroll;
748 }
749 
750 
751 #pragma mark -
752 #pragma mark Privates
753 
754 /* @ignore */
755 - (void)_updateScrollerStyle
756 {
757  if (_hasHorizontalScroller)
758  {
759  [_horizontalScroller setStyle:_scrollerStyle];
760  [_horizontalScroller unsetThemeState:CPThemeStateSelected];
761 
762  switch (_scrollerKnobStyle)
763  {
765  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
766  [_horizontalScroller setThemeState:CPThemeStateScrollerKnobLight];
767  break;
768 
770  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
771  [_horizontalScroller setThemeState:CPThemeStateScrollerKnobDark];
772  break;
773 
774  default:
775  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
776  [_horizontalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
777  }
778  }
779 
780  if (_hasVerticalScroller)
781  {
782  [_verticalScroller setStyle:_scrollerStyle];
783  [_verticalScroller unsetThemeState:CPThemeStateSelected];
784 
785  switch (_scrollerKnobStyle)
786  {
788  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
789  [_verticalScroller setThemeState:CPThemeStateScrollerKnobLight];
790  break;
791 
793  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
794  [_verticalScroller setThemeState:CPThemeStateScrollerKnobDark];
795  break;
796 
797  default:
798  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobLight];
799  [_verticalScroller unsetThemeState:CPThemeStateScrollerKnobDark];
800  }
801  }
802 
803  if (_scrollerStyle == CPScrollerStyleOverlay)
804  {
805  if (_timerScrollersHide)
806  [_timerScrollersHide invalidate];
807 
808  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
809  [[self bottomCornerView] setHidden:YES];
810  }
811  else
812  [[self bottomCornerView] setHidden:NO];
813 
814  [self reflectScrolledClipView:_contentView];
815 }
816 
817 /* @ignore */
818 - (CGRect)_insetBounds
819 {
820  return [[self class] _insetBounds:[self bounds] borderType:_borderType];
821 }
822 
823 /* @ignore */
824 - (void)_updateCornerAndHeaderView
825 {
826  var documentView = [self documentView],
827  currentHeaderView = [self _headerView],
828  documentHeaderView = [documentView respondsToSelector:@selector(headerView)] ? [documentView headerView] : nil;
829 
830  if (currentHeaderView !== documentHeaderView)
831  {
832  [currentHeaderView removeFromSuperview];
833  [_headerClipView setDocumentView:documentHeaderView];
834  }
835 
836  var documentCornerView = [documentView respondsToSelector:@selector(cornerView)] ? [documentView cornerView] : nil;
837 
838  if (_cornerView !== documentCornerView)
839  {
840  [_cornerView removeFromSuperview];
841 
842  _cornerView = documentCornerView;
843 
844  if (_cornerView)
845  {
846  [_cornerView setHidden:!SHOULD_SHOW_CORNER_VIEW()];
847  [self addSubview:_cornerView];
848  }
849  }
850 
851  [self reflectScrolledClipView:_contentView];
852  [documentHeaderView setNeedsLayout];
853  [documentHeaderView setNeedsDisplay:YES];
854 }
855 
856 /* @ignore */
857 - (CPView)_headerView
858 {
859  return [_headerClipView documentView];
860 }
861 
862 /* @ignore */
863 - (CGRect)_cornerViewFrame
864 {
865  if (!_cornerView)
866  return CGRectMakeZero();
867 
868  var bounds = [self _insetBounds],
869  frame = [_cornerView frame];
870 
871  frame.origin.x = CGRectGetMaxX(bounds) - CGRectGetWidth(frame);
872  frame.origin.y = CGRectGetMinY(bounds);
873 
874  return frame;
875 }
876 
877 /* @ignore */
878 - (CGRect)_headerClipViewFrame
879 {
880  var headerView = [self _headerView];
881 
882  if (!headerView)
883  return CGRectMakeZero();
884 
885  var frame = [self _insetBounds];
886 
887  frame.size.height = CGRectGetHeight([headerView frame]);
888 
890  frame.size.width -= CGRectGetWidth([self _cornerViewFrame]);
891 
892  return frame;
893 }
894 
895 /* @ignore */
896 - (CGRect)_bottomCornerViewFrame
897 {
898  if ([[self horizontalScroller] isHidden] || [[self verticalScroller] isHidden])
899  return CGRectMakeZero();
900 
901  var verticalFrame = [[self verticalScroller] frame],
902  bottomCornerFrame = CGRectMakeZero();
903 
904  bottomCornerFrame.origin.x = CGRectGetMinX(verticalFrame);
905  bottomCornerFrame.origin.y = CGRectGetMaxY(verticalFrame);
906  bottomCornerFrame.size.width = [_verticalScroller scrollerWidth];
907  bottomCornerFrame.size.height = [_horizontalScroller scrollerWidth];
908 
909  return bottomCornerFrame;
910 }
911 
912 /* @ignore */
913 - (void)_verticalScrollerDidScroll:(CPScroller)aScroller
914 {
915  var value = [aScroller floatValue],
916  documentFrame = [[_contentView documentView] frame],
917  contentBounds = [_contentView bounds];
918 
919 
920  switch ([_verticalScroller hitPart])
921  {
923  contentBounds.origin.y -= _verticalLineScroll;
924  break;
925 
927  contentBounds.origin.y += _verticalLineScroll;
928  break;
929 
931  contentBounds.origin.y -= CGRectGetHeight(contentBounds) - _verticalPageScroll;
932  break;
933 
935  contentBounds.origin.y += CGRectGetHeight(contentBounds) - _verticalPageScroll;
936  break;
937 
938  // We want integral bounds!
939  case CPScrollerKnobSlot:
940  case CPScrollerKnob:
941  default:
942  contentBounds.origin.y = ROUND(value * (CGRectGetHeight(documentFrame) - CGRectGetHeight(contentBounds)));
943  }
944 
945  [self _sendDelegateMessages];
946 
947  [_contentView scrollToPoint:contentBounds.origin];
948 }
949 
950 /* @ignore */
951 - (void)_horizontalScrollerDidScroll:(CPScroller)aScroller
952 {
953  var value = [aScroller floatValue],
954  documentFrame = [[self documentView] frame],
955  contentBounds = [_contentView bounds];
956 
957  switch ([_horizontalScroller hitPart])
958  {
960  contentBounds.origin.x -= _horizontalLineScroll;
961  break;
962 
964  contentBounds.origin.x += _horizontalLineScroll;
965  break;
966 
968  contentBounds.origin.x -= CGRectGetWidth(contentBounds) - _horizontalPageScroll;
969  break;
970 
972  contentBounds.origin.x += CGRectGetWidth(contentBounds) - _horizontalPageScroll;
973  break;
974 
975  // We want integral bounds!
976  case CPScrollerKnobSlot:
977  case CPScrollerKnob:
978  default:
979  contentBounds.origin.x = ROUND(value * (CGRectGetWidth(documentFrame) - CGRectGetWidth(contentBounds)));
980  }
981 
982  [self _sendDelegateMessages];
983 
984  [_contentView scrollToPoint:contentBounds.origin];
985  [_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
986 }
987 
988 /* @ignore */
989 - (void)_sendDelegateMessages
990 {
991  if (_implementedDelegateMethods == 0)
992  return;
993 
994  if (!_scrollTimer)
995  {
996  [self _scrollViewWillScroll];
997  _scrollTimer = [CPTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self selector:@selector(_scrollViewDidScroll) userInfo:nil repeats:YES];
998  }
999  else
1000  [_scrollTimer setFireDate:[CPDate dateWithTimeIntervalSinceNow:TIMER_INTERVAL]];
1001 }
1002 
1003 /* @ignore */
1004 - (void)_hideScrollers:(CPTimer)theTimer
1005 {
1006  if ([_verticalScroller allowFadingOut])
1007  [_verticalScroller fadeOut];
1008  if ([_horizontalScroller allowFadingOut])
1009  [_horizontalScroller fadeOut];
1010  _timerScrollersHide = nil;
1011 }
1012 
1013 /* @ignore */
1014 - (void)_respondToScrollWheelEventWithDeltaX:(float)deltaX deltaY:(float)deltaY
1015 {
1016  var documentFrame = [[self documentView] frame],
1017  contentBounds = [_contentView bounds],
1018  contentFrame = [_contentView frame],
1019  enclosingScrollView = [self enclosingScrollView];
1020 
1021  // We want integral bounds!
1022  contentBounds.origin.x = ROUND(contentBounds.origin.x + deltaX);
1023  contentBounds.origin.y = ROUND(contentBounds.origin.y + deltaY);
1024 
1025  var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)],
1026  extraX = contentBounds.origin.x - constrainedOrigin.x,
1027  extraY = contentBounds.origin.y - constrainedOrigin.y;
1028 
1029  [self _sendDelegateMessages];
1030 
1031  [_contentView scrollToPoint:constrainedOrigin];
1032  [_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
1033 
1034  if (extraX || extraY)
1035  [enclosingScrollView _respondToScrollWheelEventWithDeltaX:extraX deltaY:extraY];
1036 }
1037 
1038 /* @ignore */
1039 - (void)_scrollViewWillScroll
1040 {
1041  if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewWillScroll_)
1042  [_delegate scrollViewWillScroll:self];
1043 }
1044 
1045 /* @ignore */
1046 - (void)_scrollViewDidScroll
1047 {
1048  [_scrollTimer invalidate];
1049  _scrollTimer = nil;
1050 
1051  if (_implementedDelegateMethods & CPScrollViewDelegate_scrollViewDidScroll_)
1052  [_delegate scrollViewDidScroll:self];
1053 }
1054 
1056 - (void)_didReceiveDefaultStyleChange:(CPNotification)aNotification
1057 {
1058  [self setScrollerStyle:CPScrollerStyleGlobal];
1059 }
1060 
1061 
1062 
1063 #pragma mark -
1064 #pragma mark Utilities
1065 
1069 - (void)tile
1070 {
1071  // yuck.
1072  // RESIZE: tile->setHidden AND refl
1073  // Outside Change: refl->tile->setHidden AND refl
1074  // scroll: refl.
1075 }
1076 
1082 - (void)reflectScrolledClipView:(CPClipView)aClipView
1083 {
1084  if (_contentView !== aClipView)
1085  return;
1086 
1087  if (_recursionCount > 5)
1088  return;
1089 
1090  ++_recursionCount;
1091 
1092  var documentView = [self documentView];
1093 
1094  if (!documentView)
1095  {
1096  if (_autohidesScrollers)
1097  {
1098  [_verticalScroller setHidden:YES];
1099  [_horizontalScroller setHidden:YES];
1100  }
1101 
1102  [_contentView setFrame:[self _insetBounds]];
1103  [_headerClipView setFrame:CGRectMakeZero()];
1104 
1105  --_recursionCount;
1106 
1107  return;
1108  }
1109 
1110  var documentFrame = [documentView frame], // the size of the whole document
1111  contentFrame = [self _insetBounds], // assume it takes up the entire size of the scrollview (no scrollers)
1112  headerClipViewFrame = [self _headerClipViewFrame],
1113  headerClipViewHeight = CGRectGetHeight(headerClipViewFrame);
1114 
1115  contentFrame.origin.y += headerClipViewHeight;
1116  contentFrame.size.height -= headerClipViewHeight;
1117 
1118  var difference = CGSizeMake(CGRectGetWidth(documentFrame) - CGRectGetWidth(contentFrame), CGRectGetHeight(documentFrame) - CGRectGetHeight(contentFrame)),
1119  verticalScrollerWidth = [_verticalScroller scrollerWidth],
1120  horizontalScrollerHeight = [_horizontalScroller scrollerWidth],
1121  hasVerticalScroll = difference.height > 0.0,
1122  hasHorizontalScroll = difference.width > 0.0,
1123  shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll),
1124  shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
1125 
1126  // Now we have to account for the shown scrollers affecting the deltas.
1127  if (shouldShowVerticalScroller)
1128  {
1129  if (_scrollerStyle === CPScrollerStyleLegacy)
1130  difference.width += verticalScrollerWidth;
1131  hasHorizontalScroll = difference.width > 0.0;
1132  shouldShowHorizontalScroller = _hasHorizontalScroller && (!_autohidesScrollers || hasHorizontalScroll);
1133  }
1134 
1135  if (shouldShowHorizontalScroller)
1136  {
1137  if (_scrollerStyle === CPScrollerStyleLegacy)
1138  difference.height += horizontalScrollerHeight;
1139  hasVerticalScroll = difference.height > 0.0;
1140  shouldShowVerticalScroller = _hasVerticalScroller && (!_autohidesScrollers || hasVerticalScroll);
1141  }
1142 
1143  // We now definitively know which scrollers are shown or not, as well as whether they are showing scroll values.
1144  [_verticalScroller setHidden:!shouldShowVerticalScroller];
1145  [_verticalScroller setEnabled:hasVerticalScroll];
1146 
1147  [_horizontalScroller setHidden:!shouldShowHorizontalScroller];
1148  [_horizontalScroller setEnabled:hasHorizontalScroll];
1149 
1150  var overlay = [CPScroller scrollerOverlay];
1151  if (_scrollerStyle === CPScrollerStyleLegacy)
1152  {
1153  // We can thus appropriately account for them changing the content size.
1154  if (shouldShowVerticalScroller)
1155  contentFrame.size.width -= verticalScrollerWidth;
1156 
1157  if (shouldShowHorizontalScroller)
1158  contentFrame.size.height -= horizontalScrollerHeight;
1159  overlay = 0;
1160  }
1161 
1162  var scrollPoint = [_contentView bounds].origin,
1163  wasShowingVerticalScroller = ![_verticalScroller isHidden],
1164  wasShowingHorizontalScroller = ![_horizontalScroller isHidden];
1165 
1166  if (shouldShowVerticalScroller)
1167  {
1168  var verticalScrollerY =
1169  MAX(CGRectGetMinY(contentFrame), MAX(CGRectGetMaxY([self _cornerViewFrame]), CGRectGetMaxY(headerClipViewFrame)));
1170 
1171  var verticalScrollerHeight = CGRectGetMaxY(contentFrame) - verticalScrollerY;
1172 
1173  // Make a gap at the bottom of the vertical scroller so that the horizontal and vertical can't overlap.
1174  if (_scrollerStyle === CPScrollerStyleOverlay && hasHorizontalScroll)
1175  verticalScrollerHeight -= horizontalScrollerHeight;
1176 
1177  var documentHeight = CGRectGetHeight(documentFrame);
1178  [_verticalScroller setFloatValue:(difference.height <= 0.0) ? 0.0 : scrollPoint.y / difference.height];
1179  [_verticalScroller setKnobProportion:documentHeight > 0 ? CGRectGetHeight(contentFrame) / documentHeight : 1.0];
1180  [_verticalScroller setFrame:CGRectMake(CGRectGetMaxX(contentFrame) - overlay, verticalScrollerY, verticalScrollerWidth, verticalScrollerHeight)];
1181  }
1182  else if (wasShowingVerticalScroller)
1183  {
1184  [_verticalScroller setFloatValue:0.0];
1185  [_verticalScroller setKnobProportion:1.0];
1186  }
1187 
1188  if (shouldShowHorizontalScroller)
1189  {
1190  var horizontalScrollerWidth = CGRectGetWidth(contentFrame);
1191  // Make a gap at the bottom of the vertical scroller so that the horizontal and vertical can't overlap.
1192  if (_scrollerStyle === CPScrollerStyleOverlay && hasVerticalScroll)
1193  horizontalScrollerWidth -= verticalScrollerWidth;
1194 
1195  var documentWidth = CGRectGetWidth(documentFrame);
1196 
1197  [_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
1198  [_horizontalScroller setKnobProportion:documentWidth > 0 ? CGRectGetWidth(contentFrame) / documentWidth : 1.0];
1199  [_horizontalScroller setFrame:CGRectMake(CGRectGetMinX(contentFrame), CGRectGetMaxY(contentFrame) - overlay, horizontalScrollerWidth, horizontalScrollerHeight)];
1200  }
1201  else if (wasShowingHorizontalScroller)
1202  {
1203  [_horizontalScroller setFloatValue:0.0];
1204  [_horizontalScroller setKnobProportion:1.0];
1205  }
1206 
1207  [_contentView setFrame:contentFrame];
1208  [_headerClipView setFrame:[self _headerClipViewFrame]];
1209  [[_headerClipView documentView] setNeedsDisplay:YES];
1211  {
1212  [_cornerView setFrame:[self _cornerViewFrame]];
1213  [_cornerView setHidden:NO];
1214  }
1215  else
1216  [_cornerView setHidden:YES];
1217 
1218  if (_scrollerStyle === CPScrollerStyleLegacy)
1219  {
1220  [[self bottomCornerView] setFrame:[self _bottomCornerViewFrame]];
1221  [[self bottomCornerView] setBackgroundColor:[self currentValueForThemeAttribute:@"bottom-corner-color"]];
1222  }
1223 
1224  --_recursionCount;
1225 }
1226 
1230 - (void)flashScrollers
1231 {
1232  if (_scrollerStyle === CPScrollerStyleLegacy)
1233  return;
1234 
1235  if (_hasHorizontalScroller)
1236  {
1237  [_horizontalScroller setHidden:NO];
1238  [_horizontalScroller fadeIn];
1239  }
1240 
1241  if (_hasVerticalScroller)
1242  {
1243  [_verticalScroller setHidden:NO];
1244  [_verticalScroller fadeIn];
1245  }
1246 
1247  if (_timerScrollersHide)
1248  [_timerScrollersHide invalidate];
1249 
1250  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
1251 }
1252 
1253 /* @ignore */
1254 - (void)resizeSubviewsWithOldSize:(CGSize)aSize
1255 {
1256  [self reflectScrolledClipView:_contentView];
1257 }
1258 
1259 - (CGRect)documentVisibleRect
1260 {
1261  return [_contentView documentVisibleRect];
1262 }
1263 
1264 #pragma mark -
1265 #pragma mark Overrides
1266 
1267 
1268 - (void)_removeObservers
1269 {
1270  if (!_isObserving)
1271  return;
1272 
1274  name:CPScrollerStyleGlobalChangeNotification
1275  object:nil];
1276 
1277  [super _removeObservers];
1278 }
1279 
1280 - (void)_addObservers
1281 {
1282  if (_isObserving)
1283  return;
1284 
1285  //Make sure to have the last global style for the scroller
1286  [self _didReceiveDefaultStyleChange:nil];
1287 
1290  name:CPScrollerStyleGlobalChangeNotification
1291  object:nil];
1292 
1293  [super _addObservers];
1294 }
1295 
1296 
1297 
1298 - (void)drawRect:(CGRect)aRect
1299 {
1300  [super drawRect:aRect];
1301 
1302  if (_borderType == CPNoBorder)
1303  return;
1304 
1305  var strokeRect = [self bounds],
1307 
1308  CGContextSetLineWidth(context, 1);
1309 
1310  switch (_borderType)
1311  {
1312  case CPLineBorder:
1313  CGContextSetStrokeColor(context, [self currentValueForThemeAttribute:@"border-color"]);
1314  CGContextStrokeRect(context, CGRectInset(strokeRect, 0.5, 0.5));
1315  break;
1316 
1317  case CPBezelBorder:
1318  [self _drawGrayBezelInContext:context bounds:strokeRect];
1319  break;
1320 
1321  case CPGrooveBorder:
1322  [self _drawGrooveInContext:context bounds:strokeRect];
1323  break;
1324 
1325  default:
1326  break;
1327  }
1328 }
1329 
1330 - (void)_drawGrayBezelInContext:(CGContext)context bounds:(CGRect)aRect
1331 {
1332  var minX = CGRectGetMinX(aRect),
1333  maxX = CGRectGetMaxX(aRect),
1334  minY = CGRectGetMinY(aRect),
1335  maxY = CGRectGetMaxY(aRect),
1336  y = minY + 0.5;
1337 
1338  // Slightly darker line on top.
1339  CGContextSetStrokeColor(context, [CPColor colorWithWhite:142.0 / 255.0 alpha:1.0]);
1340  CGContextBeginPath(context);
1341  CGContextMoveToPoint(context, minX, y);
1342  CGContextAddLineToPoint(context, maxX, y);
1343  CGContextStrokePath(context);
1344 
1345  // The rest of the border.
1346  CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
1347 
1348  var x = maxX - 0.5;
1349 
1350  CGContextBeginPath(context);
1351  CGContextMoveToPoint(context, x, minY + 1.0);
1352  CGContextAddLineToPoint(context, x, maxY);
1353  CGContextMoveToPoint(context, x - 0.5, maxY - 0.5);
1354  CGContextAddLineToPoint(context, minX, maxY - 0.5);
1355 
1356  x = minX + 0.5;
1357 
1358  CGContextMoveToPoint(context, x, maxY);
1359  CGContextAddLineToPoint(context, x, minY + 1.0);
1360 
1361  CGContextStrokePath(context);
1362 }
1363 
1364 - (void)_drawGrooveInContext:(CGContext)context bounds:(CGRect)aRect
1365 {
1366  var minX = CGRectGetMinX(aRect),
1367  maxX = CGRectGetMaxX(aRect),
1368  minY = CGRectGetMinY(aRect),
1369  maxY = CGRectGetMaxY(aRect);
1370 
1371  CGContextBeginPath(context);
1372  CGContextSetStrokeColor(context, [CPColor colorWithWhite:159.0 / 255.0 alpha:1.0]);
1373 
1374  var y = minY + 0.5;
1375 
1376  CGContextMoveToPoint(context, minX, y);
1377  CGContextAddLineToPoint(context, maxX, y);
1378 
1379  var x = maxX - 1.5;
1380 
1381  CGContextMoveToPoint(context, x, minY + 2.0);
1382  CGContextAddLineToPoint(context, x, maxY - 1.0);
1383 
1384  y = maxY - 1.5;
1385 
1386  CGContextMoveToPoint(context, maxX - 1.0, y);
1387  CGContextAddLineToPoint(context, minX + 2.0, y);
1388 
1389  x = minX + 0.5;
1390 
1391  CGContextMoveToPoint(context, x, maxY);
1392  CGContextAddLineToPoint(context, x, minY);
1393 
1394  CGContextStrokePath(context);
1395 
1396  CGContextBeginPath(context);
1397  CGContextSetStrokeColor(context, [CPColor whiteColor]);
1398 
1399  var rect = CGRectOffset(aRect, 1.0, 1.0);
1400 
1401  rect.size.width -= 1.0;
1402  rect.size.height -= 1.0;
1403  CGContextStrokeRect(context, CGRectInset(rect, 0.5, 0.5));
1404 
1405  CGContextBeginPath(context);
1406  CGContextSetStrokeColor(context, [CPColor colorWithWhite:192.0 / 255.0 alpha:1.0]);
1407 
1408  y = minY + 2.5;
1409 
1410  CGContextMoveToPoint(context, minX + 2.0, y);
1411  CGContextAddLineToPoint(context, maxX - 2.0, y);
1412  CGContextStrokePath(context);
1413 }
1414 
1420 - (void)scrollWheel:(CPEvent)anEvent
1421 {
1422  if (_timerScrollersHide)
1423  [_timerScrollersHide invalidate];
1424  if (![_verticalScroller isHidden])
1425  [_verticalScroller fadeIn];
1426  if (![_horizontalScroller isHidden])
1427  [_horizontalScroller fadeIn];
1428  if (![_horizontalScroller isHidden] || ![_verticalScroller isHidden])
1429  _timerScrollersHide = [CPTimer scheduledTimerWithTimeInterval:CPScrollViewFadeOutTime target:self selector:@selector(_hideScrollers:) userInfo:nil repeats:NO];
1430 
1431  var deltaX = [anEvent scrollingDeltaX],
1432  deltaY = [anEvent scrollingDeltaY];
1433 
1434  if (![anEvent hasPreciseScrollingDeltas])
1435  {
1436  deltaX *= (_horizontalLineScroll || 1.0);
1437  deltaY *= (_verticalLineScroll || 1.0);
1438  }
1439 
1440  [self _respondToScrollWheelEventWithDeltaX:deltaX deltaY:deltaY];
1441 }
1442 
1443 - (void)scrollPageUp:(id)sender
1444 {
1445  var contentBounds = [_contentView bounds];
1446  [self moveByOffset:CGSizeMake(0.0, -(CGRectGetHeight(contentBounds) - _verticalPageScroll))];
1447 }
1448 
1449 - (void)scrollPageDown:(id)sender
1450 {
1451  var contentBounds = [_contentView bounds];
1452  [self moveByOffset:CGSizeMake(0.0, CGRectGetHeight(contentBounds) - _verticalPageScroll)];
1453 }
1454 
1455 - (void)scrollToBeginningOfDocument:(id)sender
1456 {
1457  [_contentView scrollToPoint:CGPointMakeZero()];
1458  [_headerClipView scrollToPoint:CGPointMakeZero()];
1459 }
1460 
1461 - (void)scrollToEndOfDocument:(id)sender
1462 {
1463  var contentBounds = [_contentView bounds],
1464  documentFrame = [[self documentView] frame],
1465  scrollPoint = CGPointMake(0.0, CGRectGetHeight(documentFrame) - CGRectGetHeight(contentBounds));
1466 
1467  [_contentView scrollToPoint:scrollPoint];
1468  [_headerClipView scrollToPoint:CGPointMakeZero()];
1469 }
1470 
1471 - (void)moveLeft:(id)sender
1472 {
1473  [self moveByOffset:CGSizeMake(-_horizontalLineScroll, 0.0)];
1474 }
1475 
1476 - (void)moveRight:(id)sender
1477 {
1478  [self moveByOffset:CGSizeMake(_horizontalLineScroll, 0.0)];
1479 }
1480 
1481 - (void)moveUp:(id)sender
1482 {
1483  [self moveByOffset:CGSizeMake(0.0, -_verticalLineScroll)];
1484 }
1485 
1486 - (void)moveDown:(id)sender
1487 {
1488  [self moveByOffset:CGSizeMake(0.0, _verticalLineScroll)];
1489 }
1490 
1491 - (void)moveByOffset:(CGSize)aSize
1492 {
1493  var documentFrame = [[self documentView] frame],
1494  contentBounds = [_contentView bounds];
1495 
1496  contentBounds.origin.x += aSize.width;
1497  contentBounds.origin.y += aSize.height;
1498 
1499  [_contentView scrollToPoint:contentBounds.origin];
1500  [_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0)];
1501 }
1502 
1503 @end
1504 
1505 #pragma mark -
1506 
1508 
1509 // Those 4 next methods are needed to (un)set CPThemeStateFirstResponder based on content view
1510 
1511 - (void)viewWillMoveToWindow:(CPWindow)aWindow
1512 {
1513  [super viewWillMoveToWindow:aWindow];
1514 
1515  [self _stopObservingFirstResponderForWindow:[self window]];
1516 
1517  if (aWindow)
1518  [self _startObservingFirstResponderForWindow:aWindow];
1519 }
1520 
1521 - (void)_startObservingFirstResponderForWindow:(CPWindow)aWindow
1522 {
1523  [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(_firstResponderDidChange:) name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
1524 }
1525 
1526 - (void)_stopObservingFirstResponderForWindow:(CPWindow)aWindow
1527 {
1528  [[CPNotificationCenter defaultCenter] removeObserver:self name:_CPWindowDidChangeFirstResponderNotification object:aWindow];
1529 }
1530 
1531 - (void)_firstResponderDidChange:(CPNotification)aNotification
1532 {
1533  var responder = [[self window] firstResponder],
1534  // FIXME: We add focus ring only on table views right now. When focus ring management will be added, this must be adapted.
1535  shouldAddFocusRing = [responder isKindOfClass:[CPTableView class]],
1536  found;
1537 
1538  while (!(found = (responder === self)) && responder)
1539  responder = [responder superview];
1540 
1541  if (found && shouldAddFocusRing)
1542  [self setThemeState:CPThemeStateFirstResponder];
1543  else
1544  [self unsetThemeState:CPThemeStateFirstResponder];
1545 }
1546 
1547 @end
1548 
1549 #pragma mark -
1550 
1551 var CPScrollViewContentViewKey = @"CPScrollViewContentView",
1552  CPScrollViewHeaderClipViewKey = @"CPScrollViewHeaderClipViewKey",
1553  CPScrollViewVLineScrollKey = @"CPScrollViewVLineScroll",
1554  CPScrollViewHLineScrollKey = @"CPScrollViewHLineScroll",
1555  CPScrollViewVPageScrollKey = @"CPScrollViewVPageScroll",
1556  CPScrollViewHPageScrollKey = @"CPScrollViewHPageScroll",
1557  CPScrollViewHasVScrollerKey = @"CPScrollViewHasVScroller",
1558  CPScrollViewHasHScrollerKey = @"CPScrollViewHasHScroller",
1559  CPScrollViewVScrollerKey = @"CPScrollViewVScroller",
1560  CPScrollViewHScrollerKey = @"CPScrollViewHScroller",
1561  CPScrollViewAutohidesScrollerKey = @"CPScrollViewAutohidesScroller",
1562  CPScrollViewCornerViewKey = @"CPScrollViewCornerViewKey",
1563  CPScrollViewBottomCornerViewKey = @"CPScrollViewBottomCornerViewKey",
1564  CPScrollViewBorderTypeKey = @"CPScrollViewBorderTypeKey",
1565  CPScrollViewScrollerStyleKey = @"CPScrollViewScrollerStyleKey",
1566  CPScrollViewScrollerKnobStyleKey = @"CPScrollViewScrollerKnobStyleKey";
1567 
1569 
1570 - (id)initWithCoder:(CPCoder)aCoder
1571 {
1572  if (self = [super initWithCoder:aCoder])
1573  {
1574  _verticalLineScroll = [aCoder decodeFloatForKey:CPScrollViewVLineScrollKey];
1575  _verticalPageScroll = [aCoder decodeFloatForKey:CPScrollViewVPageScrollKey];
1576 
1577  _horizontalLineScroll = [aCoder decodeFloatForKey:CPScrollViewHLineScrollKey];
1578  _horizontalPageScroll = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
1579 
1580  _contentView = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
1581  _headerClipView = [aCoder decodeObjectForKey:CPScrollViewHeaderClipViewKey];
1582 
1583  if (!_headerClipView)
1584  {
1585  _headerClipView = [[CPClipView alloc] init];
1586  [self addSubview:_headerClipView];
1587  }
1588 
1589  _verticalScroller = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
1590  _horizontalScroller = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
1591 
1592  _hasVerticalScroller = [aCoder decodeBoolForKey:CPScrollViewHasVScrollerKey];
1593  _hasHorizontalScroller = [aCoder decodeBoolForKey:CPScrollViewHasHScrollerKey];
1594  _autohidesScrollers = [aCoder decodeBoolForKey:CPScrollViewAutohidesScrollerKey];
1595 
1596  _borderType = [aCoder decodeIntForKey:CPScrollViewBorderTypeKey];
1597 
1598  _cornerView = [aCoder decodeObjectForKey:CPScrollViewCornerViewKey];
1599  _bottomCornerView = [aCoder decodeObjectForKey:CPScrollViewBottomCornerViewKey];
1600 
1601  _delegate = nil;
1602  _scrollTimer = nil;
1603  _implementedDelegateMethods = 0;
1604 
1605  _scrollerStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerStyleKey] || CPScrollerStyleGlobal;
1606  _scrollerKnobStyle = [aCoder decodeObjectForKey:CPScrollViewScrollerKnobStyleKey] || CPScrollerKnobStyleDefault;
1607 
1610  name:CPScrollerStyleGlobalChangeNotification
1611  object:nil];
1612  }
1613 
1614  return self;
1615 }
1616 
1620 - (void)awakeFromCib
1621 {
1622  [super awakeFromCib];
1623 
1624  [self _updateScrollerStyle];
1625  [self _updateCornerAndHeaderView];
1626 }
1627 
1628 - (void)encodeWithCoder:(CPCoder)aCoder
1629 {
1630  [super encodeWithCoder:aCoder];
1631 
1632  [aCoder encodeObject:_contentView forKey:CPScrollViewContentViewKey];
1633  [aCoder encodeObject:_headerClipView forKey:CPScrollViewHeaderClipViewKey];
1634 
1635  [aCoder encodeObject:_verticalScroller forKey:CPScrollViewVScrollerKey];
1636  [aCoder encodeObject:_horizontalScroller forKey:CPScrollViewHScrollerKey];
1637 
1638  [aCoder encodeFloat:_verticalLineScroll forKey:CPScrollViewVLineScrollKey];
1639  [aCoder encodeFloat:_verticalPageScroll forKey:CPScrollViewVPageScrollKey];
1640  [aCoder encodeFloat:_horizontalLineScroll forKey:CPScrollViewHLineScrollKey];
1641  [aCoder encodeFloat:_horizontalPageScroll forKey:CPScrollViewHPageScrollKey];
1642 
1643  [aCoder encodeBool:_hasVerticalScroller forKey:CPScrollViewHasVScrollerKey];
1644  [aCoder encodeBool:_hasHorizontalScroller forKey:CPScrollViewHasHScrollerKey];
1645  [aCoder encodeBool:_autohidesScrollers forKey:CPScrollViewAutohidesScrollerKey];
1646 
1647  [aCoder encodeObject:_cornerView forKey:CPScrollViewCornerViewKey];
1648  [aCoder encodeObject:_bottomCornerView forKey:CPScrollViewBottomCornerViewKey];
1649 
1650  [aCoder encodeInt:_borderType forKey:CPScrollViewBorderTypeKey];
1651 
1652  [aCoder encodeInt:_scrollerStyle forKey:CPScrollViewScrollerStyleKey];
1653  [aCoder encodeInt:_scrollerKnobStyle forKey:CPScrollViewScrollerKnobStyleKey];
1654 }
1655 
1656 @end
id initWithFrame:(CGRect aFrame)
Definition: CPView.j:351
void setHasHorizontalScroller:(BOOL shouldHaveHorizontalScroller)
Definition: CPScrollView.j:513
BOOL isHidden()
Definition: CALayer.j:597
var CPScrollViewDelegate_scrollViewWillScroll_
Definition: CPScrollView.j:85
void addSubview:(CPView aSubview)
Definition: CPView.j:536
void drawRect:(CGRect aRect)
Definition: CPView.j:2505
var CPScrollViewBorderTypeKey
CGRect frame
float scrollingDeltaX()
Definition: CPEvent.j:487
void setVerticalScroller:(CPScroller aScroller)
Definition: CPScrollView.j:544
var CPScrollViewHasVScrollerKey
A representation of a single point in time.
Definition: CPDate.h:2
CPGraphicsContext currentContext()
CPScroller verticalScroller()
Definition: CPScrollView.j:534
#define SHOULD_SHOW_CORNER_VIEW()
Definition: CPScrollView.j:28
void setVerticalLineScroll:(float aLineScroll)
Definition: CPScrollView.j:690
function CGContextSetStrokeColor(aContext, aColor)
Definition: CGContext.j:675
float scrollerOverlay()
Definition: CPScroller.j:146
CPBorderType CPNoBorder
Definition: CPBox.j:34
var CPScrollViewDelegate_scrollViewDidScroll_
Definition: CPScrollView.j:86
float scrollingDeltaY()
Definition: CPEvent.j:497
void addObserver:selector:name:object:(id anObserver, [selector] SEL aSelector, [name] CPString aNotificationName, [object] id anObject)
CPScrollerKnob
Definition: CPScroller.j:35
var CPScrollViewBottomCornerViewKey
void setFrame:(CGRect aFrame)
Definition: CPView.j:1020
var CPScrollViewAutohidesScrollerKey
CPColor whiteColor()
Definition: CPColor.j:361
id delegate()
Definition: CALayer.j:965
CPScrollerIncrementLine
Definition: CPScroller.j:38
var CPScrollViewFadeOutTime
Definition: CPScrollView.j:88
void setHasVerticalScroller:(BOOL shouldHaveVerticalScroller)
Definition: CPScrollView.j:578
CGRect bounds()
Definition: CPView.j:1326
CPScrollerDecrementPage
Definition: CPScroller.j:34
void postNotificationName:object:(CPString aNotificationName, [object] id anObject)
void setHorizontalScroller:(CPScroller aScroller)
Definition: CPScrollView.j:480
void viewWillMoveToWindow:(CPWindow aWindow)
Definition: CPView.j:913
function CGContextAddLineToPoint(aContext, x, y)
Definition: CGContext.j:247
function CGContextStrokePath(aContext)
Definition: CGContext.j:619
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
function CGContextSetLineWidth(aContext, aLineWidth)
Definition: CGContext.j:177
var CPScrollViewVPageScrollKey
CGRect bounds()
Definition: CALayer.j:203
var CPScrollViewScrollerKnobStyleKey
CPWindow window()
Definition: CPView.j:527
CPColor blackColor()
Definition: CPColor.j:284
void setHorizontalPageScroll:(float aPageScroll)
Definition: CPScrollView.j:727
void reflectScrolledClipView:(CPClipView aClipView)
var CPScrollViewHasHScrollerKey
void setHorizontalLineScroll:(float aLineScroll)
Definition: CPScrollView.j:672
An immutable string (collection of characters).
Definition: CPString.h:2
CPBundle mainBundle()
Definition: CPBundle.j:82
CPScrollerKnobStyleDark
Definition: CPScroller.j:65
id documentView()
Definition: CPClipView.j:57
var TIMER_INTERVAL
Definition: CPScrollView.j:84
var CPScrollViewScrollerStyleKey
void setNeedsDisplay:(BOOL aFlag)
Definition: CPView.j:2597
void encodeWithCoder:(CPCoder aCoder)
Definition: CPView.j:3806
function CGContextBeginPath(aContext)
Definition: CGContext.j:311
CPScrollerDecrementLine
Definition: CPScroller.j:37
A notification that can be posted to a CPNotificationCenter.
Definition: CPNotification.h:2
CPTimer scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:(CPTimeInterval seconds, [target] id aTarget, [selector] SEL aSelector, [userInfo] id userInfo, [repeats] BOOL shouldRepeat)
Definition: CPTimer.j:58
var CPScrollViewHPageScrollKey
CPScrollerStyleLegacy
Definition: CPScroller.j:61
void moveByOffset:(CGSize aSize)
CPLineBorder
Definition: CPBox.j:35
var CPScrollViewVLineScrollKey
var CPScrollViewVScrollerKey
CPGrooveBorder
Definition: CPBox.j:37
var CPScrollViewHLineScrollKey
CPScrollerKnobStyleLight
Definition: CPScroller.j:66
float floatValue()
Definition: CPControl.j:545
CPScrollerKnobSlot
Definition: CPScroller.j:39
A timer object that can send a message after the given time interval.
Definition: CPTimer.h:2
id objectForInfoDictionaryKey:(CPString aKey)
Definition: CPBundle.j:183
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
function CGContextStrokeRect(aContext, aRect)
Definition: CGContext.j:388
Sends messages (CPNotification) between objects.
CPScrollerIncrementPage
Definition: CPScroller.j:36
var CPScrollViewCornerViewKey
void setBackgroundColor:(CPColor aColor)
Definition: CPView.j:1947
float horizontalPageScroll()
Definition: CPScrollView.j:717
var CPScrollViewContentViewKey
void removeObserver:name:object:(id anObserver, [name] CPString aNotificationName, [object] id anObject)
Definition: CPEvent.h:2
float horizontalLineScroll()
Definition: CPScrollView.j:662
CPView bottomCornerView()
Definition: CPScrollView.j:620
id dateWithTimeIntervalSinceNow:(CPTimeInterval seconds)
Definition: CPDate.j:47
CPScroller horizontalScroller()
Definition: CPScrollView.j:470
CPScrollerStyleOverlay
Definition: CPScroller.j:62
void setScrollerStyle:(int aStyle)
Definition: CPScrollView.j:327
var CPScrollerStyleGlobalChangeNotification
Definition: CPScrollView.j:91
CPBezelBorder
Definition: CPBox.j:36
var CPScrollViewHScrollerKey
void setFrameSize:(CGSize aSize)
Definition: CPScroller.j:691
function CGContextMoveToPoint(aContext, x, y)
Definition: CGContext.j:344
var CPScrollerStyleGlobal
Definition: CPScrollView.j:90
CPScrollerKnobStyleDefault
Definition: CPScroller.j:64
Definition: CPView.j:137
var CPScrollViewHeaderClipViewKey
void setVerticalPageScroll:(float aPageScroll)
Definition: CPScrollView.j:745