/**
Return wether scroller autohiding is set or not.
See Also: -setAutohidesScrollers:
*/ - (BOOL) autohidesScrollers { return _autohidesScrollers; } /**Sets whether the view hides the scrollers (horizontal and/or vertical independendently) if they are not needed.
If the content fits inside the clip view on the X or Y axis or both, the respective scroller is removed and additional space is gained.
See Also: -autohidesScrollers
*/ - (void) setAutohidesScrollers: (BOOL)flag { _autohidesScrollers = flag; } - (NSScrollElasticity)horizontalScrollElasticity { return _horizScrollElasticity; } - (void)setHorizontalScrollElasticity:(NSScrollElasticity)value { _horizScrollElasticity = value; } - (NSScrollElasticity)verticalScrollElasticity { return _vertScrollElasticity; } - (void)setVerticalScrollElasticity:(NSScrollElasticity)value { _vertScrollElasticity = value; } - (void) scrollWheel: (NSEvent *)theEvent { NSRect clipViewBounds; CGFloat deltaY = [theEvent deltaY]; CGFloat deltaX = [theEvent deltaX]; CGFloat amount; NSPoint point; if (_contentView == nil) { clipViewBounds = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; } point = clipViewBounds.origin; // Holding shift converts vertical scrolling to horizontal if (([theEvent modifierFlags] & NSShiftKeyMask) == NSShiftKeyMask) { deltaX = -deltaY; deltaY = 0; } // Scroll horizontally if (([theEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) { amount = (clipViewBounds.size.width - _hPageScroll) * deltaX; } else { amount = _hLineScroll * deltaX; } NSDebugLLog (@"NSScrollView", @"increment/decrement: amount = %f, horizontal", amount); point.x = clipViewBounds.origin.x + amount; // Scroll vertically if (([theEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask) { amount = - (clipViewBounds.size.height - _vPageScroll) * deltaY; } else { amount = - _vLineScroll * deltaY; } if (_contentView != nil && ![_contentView isFlipped]) { /* If view is flipped reverse the scroll direction */ amount = -amount; } NSDebugLLog (@"NSScrollView", @"increment/decrement: amount = %f, flipped = %d", amount, _contentView ? [_contentView isFlipped] : 0); point.y = clipViewBounds.origin.y + amount; /* scrollToPoint: will call reflectScrolledClipView:, which will * update rules, headers, and scrollers. */ [_contentView _scrollToPoint: point]; } - (void) keyDown: (NSEvent *)theEvent { NSString *chars = [theEvent characters]; unichar c = [chars length] == 1 ? [chars characterAtIndex: 0] : '\0'; switch (c) { case NSUpArrowFunctionKey: [self scrollLineUp: self]; break; case NSDownArrowFunctionKey: [self scrollLineDown: self]; break; case NSPageUpFunctionKey: [self scrollPageUp: self]; break; case NSPageDownFunctionKey: [self scrollPageDown: self]; break; default: [super keyDown: theEvent]; break; } } /* * This code is based on _doScroll: and still may need some tuning. */ - (void) scrollLineUp: (id)sender { NSRect clipViewBounds; NSPoint point; CGFloat amount; if (_contentView == nil) { clipViewBounds = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; } point = clipViewBounds.origin; amount = _vLineScroll; if (_contentView != nil && ![_contentView isFlipped]) { amount = -amount; } point.y = clipViewBounds.origin.y - amount; [_contentView _scrollToPoint: point]; } /* * This code is based on _doScroll: and still may need some tuning. */ - (void) scrollLineDown: (id)sender { NSRect clipViewBounds; NSPoint point; CGFloat amount; if (_contentView == nil) { clipViewBounds = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; } point = clipViewBounds.origin; amount = _vLineScroll; if (_contentView != nil && ![_contentView isFlipped]) { amount = -amount; } point.y = clipViewBounds.origin.y + amount; [_contentView _scrollToPoint: point]; } /** * Scrolls the receiver by simply invoking scrollPageUp: */ - (void) pageUp: (id)sender { [self scrollPageUp: sender]; } /* * This code is based on _doScroll: and still may need some tuning. */ - (void) scrollPageUp: (id)sender { NSRect clipViewBounds; NSPoint point; CGFloat amount; if (_contentView == nil) { clipViewBounds = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; } point = clipViewBounds.origin; /* * Take verticalPageScroll into accout, but try to make sure * that amount is never negative (ie do not scroll backwards.) * * FIXME: It seems _doScroll and scrollWheel: should also take * care not to do negative scrolling. */ amount = clipViewBounds.size.height - _vPageScroll; amount = (amount < 0) ? 0 : amount; if (_contentView != nil && ![_contentView isFlipped]) { amount = -amount; } point.y = clipViewBounds.origin.y - amount; [_contentView _scrollToPoint: point]; } /** * Scrolls the receiver by simply invoking scrollPageUp: */ - (void) pageDown: (id)sender { [self scrollPageDown: sender]; } /* * This code is based on _doScroll:. and still may need some tuning. */ - (void) scrollPageDown: (id)sender { NSRect clipViewBounds; NSPoint point; CGFloat amount; if (_contentView == nil) { clipViewBounds = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; } point = clipViewBounds.origin; /* * Take verticalPageScroll into accout, but try to make sure * that amount is never negativ (ie do not scroll backwards.) * * FIXME: It seems _doScroll and scrollWheel: should also take * care not to do negative scrolling. */ amount = clipViewBounds.size.height - _vPageScroll; amount = (amount < 0) ? 0 : amount; if (_contentView != nil && ![_contentView isFlipped]) { amount = -amount; } point.y = clipViewBounds.origin.y + amount; [_contentView _scrollToPoint: point]; } - (void) _doScroll: (NSScroller*)scroller { float floatValue = [scroller floatValue]; NSScrollerPart hitPart = [scroller hitPart]; NSRect clipViewBounds; NSRect documentRect; CGFloat amount = 0; NSPoint point; if (_contentView == nil) { clipViewBounds = NSZeroRect; documentRect = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; documentRect = [_contentView documentRect]; } point = clipViewBounds.origin; NSDebugLLog (@"NSScrollView", @"_doScroll: float value = %f", floatValue); /* do nothing if scroller is unknown */ if (scroller != _horizScroller && scroller != _vertScroller) return; _knobMoved = NO; if (hitPart == NSScrollerKnob || hitPart == NSScrollerKnobSlot) _knobMoved = YES; else { if (hitPart == NSScrollerIncrementLine) { if (scroller == _horizScroller) amount = _hLineScroll; else amount = _vLineScroll; } else if (hitPart == NSScrollerDecrementLine) { if (scroller == _horizScroller) amount = -_hLineScroll; else amount = -_vLineScroll; } else if (hitPart == NSScrollerIncrementPage) { if (scroller == _horizScroller) amount = clipViewBounds.size.width - _hPageScroll; else amount = clipViewBounds.size.height - _vPageScroll; } else if (hitPart == NSScrollerDecrementPage) { if (scroller == _horizScroller) amount = _hPageScroll - clipViewBounds.size.width; else amount = _vPageScroll - clipViewBounds.size.height; } else { return; } } if (!_knobMoved) /* button scrolling */ { if (scroller == _horizScroller) { point.x = clipViewBounds.origin.x + amount; } else { if (_contentView != nil && ![_contentView isFlipped]) { /* If view is flipped reverse the scroll direction */ amount = -amount; } NSDebugLLog (@"NSScrollView", @"increment/decrement: amount = %f, flipped = %d", amount, _contentView ? [_contentView isFlipped] : 0); point.y = clipViewBounds.origin.y + amount; } } else /* knob scolling */ { if (scroller == _horizScroller) { point.x = floatValue * (documentRect.size.width - clipViewBounds.size.width); point.x += documentRect.origin.x; } else { if (_contentView != nil && ![_contentView isFlipped]) floatValue = 1 - floatValue; point.y = floatValue * (documentRect.size.height - clipViewBounds.size.height); point.y += documentRect.origin.y; } } /* scrollToPoint will call reflectScrollerClipView, and that will * update scrollers, rulers and headers */ [_contentView _scrollToPoint: point]; } - (void) scrollToBeginningOfDocument: (id)sender { NSRect clipViewBounds, documentRect; NSPoint point; if (_contentView == nil) { clipViewBounds = NSZeroRect; documentRect = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; documentRect = [_contentView documentRect]; } point = documentRect.origin; if (_contentView != nil && ![_contentView isFlipped]) { point.y = NSMaxY(documentRect) - NSHeight(clipViewBounds); if (point.y < 0) point.y = 0; } [_contentView _scrollToPoint: point]; } - (void) scrollToEndOfDocument: (id)sender { NSRect clipViewBounds, documentRect; NSPoint point; if (_contentView == nil) { clipViewBounds = NSZeroRect; documentRect = NSZeroRect; } else { clipViewBounds = [_contentView bounds]; documentRect = [_contentView documentRect]; } point = documentRect.origin; if (_contentView == nil || [_contentView isFlipped]) { point.y = NSMaxY(documentRect) - NSHeight(clipViewBounds); if (point.y < 0) point.y = 0; } [_contentView _scrollToPoint: point]; } // // This method is here purely for nib compatibility. This is the action // connected to by NSScrollers in IB when building a scrollview. // - (void) _doScroller: (NSScroller *)scroller { [self _doScroll: scroller]; } - (void) reflectScrolledClipView: (NSClipView *)aClipView { NSRect documentFrame = NSZeroRect; NSRect clipViewBounds = NSZeroRect; float floatValue; CGFloat knobProportion; id documentView; if (aClipView != _contentView) { return; } NSDebugLLog (@"NSScrollView", @"reflectScrolledClipView:"); if (_contentView) { clipViewBounds = [_contentView bounds]; } if ((documentView = [_contentView documentView])) { documentFrame = [documentView frame]; } // FIXME: Should we just hide the scroll bar or remove it? if ((_autohidesScrollers) && (documentFrame.size.height > clipViewBounds.size.height)) { [self setHasVerticalScroller: YES]; } if (_hasVertScroller) { if (documentFrame.size.height <= clipViewBounds.size.height) { if (_autohidesScrollers) { [self setHasVerticalScroller: NO]; } else { [_vertScroller setEnabled: NO]; } } else { [_vertScroller setEnabled: YES]; knobProportion = clipViewBounds.size.height / documentFrame.size.height; floatValue = (clipViewBounds.origin.y - documentFrame.origin.y) / (documentFrame.size.height - clipViewBounds.size.height); if (![_contentView isFlipped]) { floatValue = 1 - floatValue; } [_vertScroller setFloatValue: floatValue knobProportion: knobProportion]; } } if ((_autohidesScrollers) && (documentFrame.size.width > clipViewBounds.size.width)) { [self setHasHorizontalScroller: YES]; } if (_hasHorizScroller) { if (documentFrame.size.width <= clipViewBounds.size.width) { if (_autohidesScrollers) { [self setHasHorizontalScroller: NO]; } else { [_horizScroller setEnabled: NO]; } } else { [_horizScroller setEnabled: YES]; knobProportion = clipViewBounds.size.width / documentFrame.size.width; floatValue = (clipViewBounds.origin.x - documentFrame.origin.x) / (documentFrame.size.width - clipViewBounds.size.width); [_horizScroller setFloatValue: floatValue knobProportion: knobProportion]; } } if (_hasHeaderView) { NSPoint headerClipViewOrigin; headerClipViewOrigin = [_headerClipView bounds].origin; /* If needed, scroll the headerview too. */ if (headerClipViewOrigin.x != clipViewBounds.origin.x) { headerClipViewOrigin.x = clipViewBounds.origin.x; [_headerClipView scrollToPoint: headerClipViewOrigin]; } } if (_rulersVisible == YES) { if (_hasHorizRuler) { [_horizRuler setNeedsDisplay: YES]; } if (_hasVertRuler) { [_vertRuler setNeedsDisplay: YES]; } } } - (void) setHorizontalRulerView: (NSRulerView *)aRulerView { if (_rulersVisible && _horizRuler != nil) { [_horizRuler removeFromSuperview]; } ASSIGN(_horizRuler, aRulerView); if (_horizRuler == nil) { _hasHorizRuler = NO; } else if (_rulersVisible) { [self addSubview:_horizRuler]; } if (_rulersVisible) { [self tile]; } } - (void) setHasHorizontalRuler: (BOOL)flag { if (_hasHorizRuler == flag) return; _hasHorizRuler = flag; if (_hasHorizRuler && _horizRuler == nil) { _horizRuler = [[object_getClass(self) rulerViewClass] alloc]; _horizRuler = [_horizRuler initWithScrollView: self orientation: NSHorizontalRuler]; } if (_rulersVisible) { if (_hasHorizRuler) { [self addSubview: _horizRuler]; } else { [_horizRuler removeFromSuperview]; } [self tile]; } } - (void) setVerticalRulerView: (NSRulerView *)aRulerView { if (_rulersVisible && _vertRuler != nil) { [_vertRuler removeFromSuperview]; } ASSIGN(_vertRuler, aRulerView); if (_vertRuler == nil) { _hasVertRuler = NO; } else if (_rulersVisible) { [self addSubview:_vertRuler]; } if (_rulersVisible) { [self tile]; } } - (void) setHasVerticalRuler: (BOOL)flag { if (_hasVertRuler == flag) return; _hasVertRuler = flag; if (_hasVertRuler && _vertRuler == nil) { _vertRuler = [[object_getClass(self) rulerViewClass] alloc]; _vertRuler = [_vertRuler initWithScrollView: self orientation: NSVerticalRuler]; } if (_rulersVisible) { if (_hasVertRuler) { [self addSubview: _vertRuler]; } else { [_vertRuler removeFromSuperview]; } [self tile]; } } - (void) setRulersVisible: (BOOL)flag { if (_rulersVisible == flag) return; _rulersVisible = flag; if (flag) { if (_hasVertRuler) [self addSubview: _vertRuler]; if (_hasHorizRuler) [self addSubview: _horizRuler]; } else { if (_hasVertRuler) [_vertRuler removeFromSuperview]; if (_hasHorizRuler) [_horizRuler removeFromSuperview]; } [self tile]; } - (void) setFrame: (NSRect)rect { [super setFrame: rect]; [self tile]; } - (void) setFrameSize: (NSSize)size { [super setFrameSize: size]; [self tile]; } static NSRectEdge GSOppositeEdge(NSRectEdge edge) { return (edge == NSMinXEdge) ? NSMaxXEdge : NSMinXEdge; } - (void) tile { NSRect headerRect, contentRect; NSSize border = [[GSTheme theme] sizeForBorderType: _borderType]; NSRectEdge bottomEdge, topEdge; CGFloat headerViewHeight = 0; NSRectEdge verticalScrollerEdge = NSMinXEdge; NSInterfaceStyle style; CGFloat innerBorderWidth = [[NSUserDefaults standardUserDefaults] boolForKey: @"GSScrollViewNoInnerBorder"] ? 0.0 : 1.0; const BOOL useBottomCorner = [[GSTheme theme] scrollViewUseBottomCorner]; const BOOL overlapBorders = [[GSTheme theme] scrollViewScrollersOverlapBorders]; style = NSInterfaceStyleForKey(@"NSScrollViewInterfaceStyle", nil); if (style == NSMacintoshInterfaceStyle || style == NSWindows95InterfaceStyle) { verticalScrollerEdge = NSMaxXEdge; } /* Determine edge positions. */ if ([self isFlipped]) { topEdge = NSMinYEdge; bottomEdge = NSMaxYEdge; } else { topEdge = NSMaxYEdge; bottomEdge = NSMinYEdge; } /* Prepare the contentRect by insetting the borders. */ contentRect = _bounds; if (!overlapBorders) contentRect = NSInsetRect(contentRect, border.width, border.height); if (contentRect.size.width < 0 || contentRect.size.height < 0) { /* FIXME ... should we do something else when given * too small a size to tile? */ return; } [self _synchronizeHeaderAndCornerView]; if (overlapBorders) { if (_borderType != NSNoBorder) { if (!(_hasHeaderView || _hasCornerView)) { // Inset 1px on the top NSDivideRect(contentRect, NULL, &contentRect, 1, topEdge); } if (!_hasVertScroller) { // Inset 1px on the edge where the vertical scroller would be NSDivideRect(contentRect, NULL, &contentRect, 1, verticalScrollerEdge); } if (!_hasHorizScroller) { NSDivideRect(contentRect, NULL, &contentRect, 1, bottomEdge); } // The vertical edge without a scroller { NSDivideRect(contentRect, NULL, &contentRect, 1, GSOppositeEdge(verticalScrollerEdge)); } } } /* First, allocate vertical space for the headerView / cornerView (but - NB - the headerView needs to be placed above the clipview later on, we can't place it now). */ if (_hasHeaderView == YES) { headerViewHeight = [[_headerClipView documentView] frame].size.height; } if (_hasCornerView == YES) { if (headerViewHeight == 0) { headerViewHeight = [_cornerView frame].size.height; } } /* Remove the vertical slice used by the header/corner view. Save the height and y position of headerRect for later reuse. */ NSDivideRect (contentRect, &headerRect, &contentRect, headerViewHeight, topEdge); /* Ok - now go on with drawing the actual scrollview in the remaining space. Just consider contentRect to be the area in which we draw, ignoring header/corner view. */ /* Prepare the vertical scroller. */ if (_hasVertScroller) { NSRect vertScrollerRect; NSDivideRect (contentRect, &vertScrollerRect, &contentRect, scrollerWidth, verticalScrollerEdge); /* If the theme requests it, leave a square gap in the bottom- * left (or bottom-right) corner where the horizontal and vertical * scrollers meet. */ if (_hasHorizScroller && !useBottomCorner) { NSDivideRect (vertScrollerRect, NULL, &vertScrollerRect, scrollerWidth, bottomEdge); } /** Vertically expand the scroller by 1pt on each end */ if (overlapBorders) { vertScrollerRect.origin.y -= 1; vertScrollerRect.size.height += 2; } [_vertScroller setFrame: vertScrollerRect]; /* Substract 1 for the line that separates the vertical scroller * from the clip view (and eventually the horizontal scroller), * unless the GSScrollViewNoInnerBorder default is set. */ NSDivideRect (contentRect, NULL, &contentRect, innerBorderWidth, verticalScrollerEdge); } /* Prepare the horizontal scroller. */ if (_hasHorizScroller) { NSRect horizScrollerRect; NSDivideRect (contentRect, &horizScrollerRect, &contentRect, scrollerWidth, bottomEdge); /** Horizontall expand the scroller by 1pt on each end */ if (overlapBorders) { horizScrollerRect.origin.x -= 1; horizScrollerRect.size.width += 2; } [_horizScroller setFrame: horizScrollerRect]; /* Substract 1 for the width for the line that separates the * horizontal scroller from the clip view, * unless the GSScrollViewNoInnerBorder default is set. */ NSDivideRect (contentRect, NULL, &contentRect, innerBorderWidth, bottomEdge); } /* Now place and size the header view to be exactly above the resulting clipview. */ if (_hasHeaderView) { NSRect rect = headerRect; rect.origin.x = contentRect.origin.x; rect.size.width = contentRect.size.width; [_headerClipView setFrame: rect]; } /* Now place the corner view. */ if (_hasCornerView) { NSPoint p = headerRect.origin; if (verticalScrollerEdge == NSMaxXEdge) { p.x += contentRect.size.width; } [_cornerView setFrameOrigin: p]; } /* Now place the rulers. */ if (_rulersVisible) { if (_hasHorizRuler) { NSRect horizRulerRect; NSDivideRect (contentRect, &horizRulerRect, &contentRect, [_horizRuler requiredThickness], topEdge); [_horizRuler setFrame: horizRulerRect]; } if (_hasVertRuler) { NSRect vertRulerRect; NSDivideRect (contentRect, &vertRulerRect, &contentRect, [_vertRuler requiredThickness], NSMinXEdge); [_vertRuler setFrame: vertRulerRect]; } } [_contentView setFrame: contentRect]; [self setNeedsDisplay: YES]; } - (void) drawRect: (NSRect)rect { [[GSTheme theme] drawScrollViewRect: rect inView: self]; } - (NSRect) documentVisibleRect { return [_contentView documentVisibleRect]; } - (void) setBackgroundColor: (NSColor*)aColor { [_contentView setBackgroundColor: aColor]; } - (NSColor*) backgroundColor { return [_contentView backgroundColor]; } - (void) setDrawsBackground: (BOOL)flag { [_contentView setDrawsBackground: flag]; if ((flag == NO) && [_contentView respondsToSelector: @selector(setCopiesOnScroll:)]) [_contentView setCopiesOnScroll: NO]; } - (BOOL) drawsBackground { return [_contentView drawsBackground]; } - (void) setBorderType: (NSBorderType)borderType { _borderType = borderType; [self tile]; } - (void) setDocumentView: (NSView *)aView { [_contentView setDocumentView: aView]; if (_contentView && ![_contentView isFlipped]) { [_vertScroller setFloatValue: 1]; } [self tile]; } - (void) resizeSubviewsWithOldSize: (NSSize)oldSize { [super resizeSubviewsWithOldSize: oldSize]; [self tile]; } - (id) documentView { return [_contentView documentView]; } - (NSCursor*) documentCursor { return [_contentView documentCursor]; } - (void) setDocumentCursor: (NSCursor*)aCursor { [_contentView setDocumentCursor: aCursor]; } - (BOOL) isOpaque { // FIXME: Only needs to be NO in a corner case, // when [[GSTheme theme] scrollViewUseBottomCorner] is NO // and the theme tile for the bottom corner is transparent. // So maybe cache the value of // [[GSTheme theme] scrollViewUseBottomCorner] and check it here. return NO; } - (NSBorderType) borderType { return _borderType; } /**Returns whether the NSScrollView has a horizontal ruler
See Also: -setHasHorizontalRuler:
*/ - (BOOL) hasHorizontalRuler { return _hasHorizRuler; } /**Returns whether the NSScrollView has a horizontal scroller
See Also: -setHasHorizontalScroller:
*/ - (BOOL) hasHorizontalScroller { return _hasHorizScroller; } /**Returns whether the NSScrollView has a vertical ruler
See Also: -setHasVerticalRuler:
*/ - (BOOL) hasVerticalRuler { return _hasVertRuler; } /**Returns whether the NSScrollView has a vertical scroller
See Also: -setHasVerticalScroller:
*/ - (BOOL) hasVerticalScroller { return _hasVertScroller; } /**Returns the size of the NSScrollView's content view
*/ - (NSSize) contentSize { return [_contentView bounds].size; } - (NSClipView *) contentView { return _contentView; } - (NSRulerView *) horizontalRulerView { return _horizRuler; } - (NSRulerView *) verticalRulerView { return _vertRuler; } - (BOOL) rulersVisible { return _rulersVisible; } - (void) setLineScroll: (CGFloat)aFloat { _hLineScroll = aFloat; _vLineScroll = aFloat; } - (void) setHorizontalLineScroll: (CGFloat)aFloat { _hLineScroll = aFloat; } - (void) setVerticalLineScroll: (CGFloat)aFloat { _vLineScroll = aFloat; } - (CGFloat) lineScroll { if (_hLineScroll != _vLineScroll) [NSException raise: NSInternalInconsistencyException format: @"horizontal and vertical values not same"]; return _vLineScroll; } - (CGFloat) horizontalLineScroll { return _hLineScroll; } - (CGFloat) verticalLineScroll { return _vLineScroll; } - (void) setPageScroll: (CGFloat)aFloat { _hPageScroll = aFloat; _vPageScroll = aFloat; } - (void) setHorizontalPageScroll: (CGFloat)aFloat { _hPageScroll = aFloat; } - (void) setVerticalPageScroll: (CGFloat)aFloat { _vPageScroll = aFloat; } - (CGFloat) pageScroll { if (_hPageScroll != _vPageScroll) [NSException raise: NSInternalInconsistencyException format: @"horizontal and vertical values not same"]; return _vPageScroll; } - (CGFloat) horizontalPageScroll { return _hPageScroll; } - (CGFloat) verticalPageScroll { return _vPageScroll; } - (void) setScrollsDynamically: (BOOL)flag { // FIXME: This should change the behaviour of the scrollers _scrollsDynamically = flag; } - (BOOL) scrollsDynamically { return _scrollsDynamically; } - (NSScroller*) horizontalScroller { return _horizScroller; } - (NSScroller*) verticalScroller { return _vertScroller; } - (BOOL)allowsMagnification { //we need an ivar for this return NO; } - (void)setAllowsMagnification:(BOOL)m { //we need an ivar for this } /* * NSCoding protocol */ - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder]; if ([aCoder allowsKeyedCoding]) { unsigned long flags = 0; [aCoder encodeObject: _horizScroller forKey: @"NSHScroller"]; [aCoder encodeObject: _vertScroller forKey: @"NSVScroller"]; [aCoder encodeObject: _contentView forKey: @"NSContentView"]; // only encode this, if it's not null... if (_headerClipView != nil) { [aCoder encodeObject: _headerClipView forKey: @"NSHeaderClipView"]; } flags = _borderType; if (_hasVertScroller) flags |= 16; if (_hasHorizScroller) flags |= 32; if (_autohidesScrollers) flags |= 512; [aCoder encodeInt: flags forKey: @"NSsFlags"]; } else { [aCoder encodeObject: _contentView]; // Was int, we need to stay compatible [aCoder encodeValueOfObjCType: @encode(NSInteger) at: &_borderType]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_scrollsDynamically]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_rulersVisible]; [aCoder encodeValueOfObjCType: @encode(float) at: &_hLineScroll]; [aCoder encodeValueOfObjCType: @encode(float) at: &_hPageScroll]; [aCoder encodeValueOfObjCType: @encode(float) at: &_vLineScroll]; [aCoder encodeValueOfObjCType: @encode(float) at: &_vPageScroll]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHorizScroller]; if (_hasHorizScroller) [aCoder encodeObject: _horizScroller]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasVertScroller]; if (_hasVertScroller) [aCoder encodeObject: _vertScroller]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHorizRuler]; if (_hasHorizRuler) [aCoder encodeObject: _horizRuler]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasVertRuler]; if (_hasVertRuler) [aCoder encodeObject: _vertRuler]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHeaderView]; if (_hasHeaderView) [aCoder encodeObject: _headerClipView]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasCornerView]; /* We do not need to encode headerview, cornerview stuff */ } } - (id) initWithCoder: (NSCoder*)aDecoder { self = [super initWithCoder: aDecoder]; if (!self) return nil; if ([aDecoder allowsKeyedCoding]) { NSScroller *hScroller = [aDecoder decodeObjectForKey: @"NSHScroller"]; NSScroller *vScroller = [aDecoder decodeObjectForKey: @"NSVScroller"]; NSClipView *content = [aDecoder decodeObjectForKey: @"NSContentView"]; NSView *docView = [content documentView]; BOOL post_frame = [docView postsFrameChangedNotifications]; BOOL post_bound = [docView postsBoundsChangedNotifications]; [docView setPostsFrameChangedNotifications: NO]; [docView setPostsBoundsChangedNotifications: NO]; _hLineScroll = 10; _hPageScroll = 10; _vLineScroll = 10; _vPageScroll = 10; _scrollsDynamically = YES; /* _autohidesScroller, _rulersVisible, _hasHorizRuler and _hasVertRuler implicitly set to NO */ if ([aDecoder containsValueForKey: @"NSsFlags"]) { int flags = [aDecoder decodeInt32ForKey: @"NSsFlags"]; _borderType = flags & 3; _hasVertScroller = (flags & 16) == 16; _hasHorizScroller = (flags & 32) == 32; _autohidesScrollers = (flags & 512) == 512; } /* FIXME: This should only happen when we load a Mac NIB file. And as far as I can tell tile is handling this correctly. if (vScroller != nil && _hasVertScroller && content != nil) { // Move the content view since it is not moved when we retile. NSRect frame = [content frame]; float w = [vScroller frame].size.width; // // Slide the content view over, since on Mac OS X the scroller is on the // right, the content view is not properly positioned since our scroller // is on the left. // frame.origin.x += w; [content setFrame: frame]; } */ if (hScroller != nil && _hasHorizScroller) { [self setHorizontalScroller: hScroller]; [hScroller setHidden: NO]; } if (vScroller != nil && _hasVertScroller) { [self setVerticalScroller: vScroller]; [vScroller setHidden: NO]; } if ([aDecoder containsValueForKey: @"NSHeaderClipView"]) { _hasHeaderView = YES; _headerClipView = [aDecoder decodeObjectForKey: @"NSHeaderClipView"]; } // set the document view into the content. [self setContentView: content]; [self tile]; // Reenable notification sending. [docView setPostsFrameChangedNotifications: post_frame]; [docView setPostsBoundsChangedNotifications: post_bound]; } else { int version = [aDecoder versionForClassName: @"NSScrollView"]; NSDebugLLog(@"NSScrollView", @"NSScrollView: start decoding\n"); _contentView = [aDecoder decodeObject]; // Was int, we need to stay compatible [aDecoder decodeValueOfObjCType: @encode(NSInteger) at: &_borderType]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_scrollsDynamically]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_rulersVisible]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_hLineScroll]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_hPageScroll]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_vLineScroll]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_vPageScroll]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHorizScroller]; if (_hasHorizScroller) [aDecoder decodeValueOfObjCType: @encode(id) at: &_horizScroller]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasVertScroller]; if (_hasVertScroller) [aDecoder decodeValueOfObjCType: @encode(id) at: &_vertScroller]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHorizRuler]; if (_hasHorizRuler) [aDecoder decodeValueOfObjCType: @encode(id) at: &_horizRuler]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasVertRuler]; if (_hasVertRuler) [aDecoder decodeValueOfObjCType: @encode(id) at: &_vertRuler]; if (version == 2) { [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHeaderView]; if (_hasHeaderView) _headerClipView = [aDecoder decodeObject]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasCornerView]; } else if (version == 1) { /* This recreates all the info about headerView, cornerView, etc */ [self setDocumentView: [_contentView documentView]]; } else { NSLog(@"unknown NSScrollView version (%d)", version); DESTROY(self); return nil; } [self tile]; NSDebugLLog(@"NSScrollView", @"NSScrollView: finish decoding\n"); } [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_themeDidActivate:) name: GSThemeDidActivateNotification object: nil]; return self; } - (BOOL)automaticallyAdjustsContentInsets { // FIXME return NO; } - (void)setAutomaticallyAdjustsContentInsets: (BOOL)adjusts { static BOOL logged = NO; if (!logged) { NSLog(@"warning: stub no-op implementation of" "-[NSScrollView setAutomaticallyAdjustsContentInsets:]"); logged = YES; } } - (NSEdgeInsets)contentInsets { // FIXME return NSEdgeInsetsZero; } - (void)setContentInsets: (NSEdgeInsets)edgeInsets { static BOOL logged = NO; if (!logged) { NSLog(@"warning: stub no-op implementation of" "-[NSScrollView setContentInsets:]"); logged = YES; } } - (NSEdgeInsets)scrollerInsets { // FIXME return NSEdgeInsetsZero; } - (void)setScrollerInsets: (NSEdgeInsets)insets { static BOOL logged = NO; if (!logged) { NSLog(@"warning: stub no-op implementation of" "-[NSScrollView setScrollerInsets:]"); logged = YES; } } @end @implementation NSScrollView (GSPrivate) /* GNUstep private method */ /* we update both of these at the same time during -tile so there is no reason in seperating them that'd just add message passing */ - (void) _synchronizeHeaderAndCornerView { BOOL hadHeaderView = _hasHeaderView; BOOL hadCornerView = _hasCornerView; NSView *aView = nil; _hasHeaderView = ([[self documentView] respondsToSelector: @selector(headerView)] && (aView=[(NSTableView *)[self documentView] headerView])); if (_hasHeaderView == YES) { if (hadHeaderView == NO) { _headerClipView = [NSClipView new]; [self addSubview: _headerClipView]; RELEASE(_headerClipView); } [_headerClipView setDocumentView: aView]; } else if (hadHeaderView == YES) { [self removeSubview: _headerClipView]; } if (_hasHeaderView == YES && _hasVertScroller == YES) { aView = nil; _hasCornerView = ([[self documentView] respondsToSelector: @selector(cornerView)] && (aView=[(NSTableView *)[self documentView] cornerView])); if (aView == _cornerView) return; if (_hasCornerView == YES) { if (hadCornerView == NO) { [self addSubview: aView]; } else { [self replaceSubview: _cornerView with: aView]; } } else if (hadCornerView == YES) { [self removeSubview: _cornerView]; } _cornerView = aView; } else if (_cornerView != nil) { [self removeSubview: _cornerView]; _cornerView = nil; _hasCornerView = NO; } } - (void) _themeDidActivate: (NSNotification*)notification { // N.B. Reload cached [NSScroller scrollerWidth] since the // new theme may have a different scroller width. // // Since scrollerWidth is a static, it will get overwritten // several times; doesn't matter though. scrollerWidth = [NSScroller scrollerWidth]; [self tile]; } @end