1/** <title>NSScrollView</title>
2
3   Copyright (C) 1996 Free Software Foundation, Inc.
4
5   Author: Ovidiu Predescu <ovidiu@net-community.com>
6   Date: July 1997
7   Author: Felipe A. Rodriguez <far@ix.netcom.com>
8   Date: October 1998
9   Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
10   Date: February 1999
11   Table View Support: Nicola Pero <n.pero@mi.flashnet.it>
12   Date: March 2000
13
14   This file is part of the GNUstep GUI Library.
15
16   This library is free software; you can redistribute it and/or
17   modify it under the terms of the GNU Lesser General Public
18   License as published by the Free Software Foundation; either
19   version 2 of the License, or (at your option) any later version.
20
21   This library is distributed in the hope that it will be useful,
22   but WITHOUT ANY WARRANTY; without even the implied warranty of
23   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   Lesser General Public License for more details.
25
26   You should have received a copy of the GNU Lesser General Public
27   License along with this library; see the file COPYING.LIB.
28   If not, see <http://www.gnu.org/licenses/> or write to the
29   Free Software Foundation, 51 Franklin Street, Fifth Floor,
30   Boston, MA 02110-1301, USA.
31*/
32
33#import <Foundation/NSDebug.h>
34#import <Foundation/NSException.h>
35#import <Foundation/NSNotification.h>
36#import <Foundation/NSUserDefaults.h>
37
38#import "AppKit/NSColor.h"
39#import "AppKit/NSColorList.h"
40#import "AppKit/NSCell.h"
41#import "AppKit/NSClipView.h"
42#import "AppKit/NSEvent.h"
43#import "AppKit/NSGraphics.h"
44#import "AppKit/NSInterfaceStyle.h"
45#import "AppKit/NSRulerView.h"
46#import "AppKit/NSScroller.h"
47#import "AppKit/NSScrollView.h"
48#import "AppKit/NSTableHeaderView.h"
49#import "AppKit/NSTableView.h"
50#import "AppKit/NSWindow.h"
51#import "AppKit/PSOperators.h"
52#import "GNUstepGUI/GSTheme.h"
53
54@interface NSClipView (Private)
55- (void) _scrollToPoint: (NSPoint)aPoint;
56@end
57
58//
59// For nib compatibility, this is used to properly
60// initialize the object from a OS X nib file in initWithCoder:.
61//
62typedef struct _scrollViewFlags
63{
64#if GS_WORDS_BIGENDIAN == 1
65  unsigned int __unused6:14;
66  unsigned int __unused5:1;
67  unsigned int autohidesScrollers:1;
68  unsigned int __unused4:1;
69  unsigned int __unused3:1;
70  unsigned int __unused2:1;
71  unsigned int doesNotDrawBackground:1;
72  unsigned int __unused1:1;
73  unsigned int hasVRuler:1;
74  unsigned int hasHRuler:1;
75  unsigned int showRulers:1;
76  unsigned int oldRulerInstalled:1;
77  unsigned int nonDynamic:1;
78  unsigned int hasHScroller:1;
79  unsigned int hasVScroller:1;
80  unsigned int hScrollerRequired:1;
81  unsigned int vScrollerRequired:1;
82  NSBorderType border:2;
83#else
84  NSBorderType border:2;
85  unsigned int vScrollerRequired:1;
86  unsigned int hScrollerRequired:1;
87  unsigned int hasVScroller:1;
88  unsigned int hasHScroller:1;
89  unsigned int nonDynamic:1;
90  unsigned int oldRulerInstalled:1;
91  unsigned int showRulers:1;
92  unsigned int hasHRuler:1;
93  unsigned int hasVRuler:1;
94  unsigned int __unused1:1;
95  unsigned int doesNotDrawBackground:1;
96  unsigned int __unused2:1;
97  unsigned int __unused3:1;
98  unsigned int __unused4:1;
99  unsigned int autohidesScrollers:1;
100  unsigned int __unused5:1;
101  unsigned int __unused6:14;
102#endif
103} GSScrollViewFlags;
104
105@interface NSScrollView (GSPrivate)
106/* GNUstep private methods */
107- (void) _synchronizeHeaderAndCornerView;
108- (void) _themeDidActivate: (NSNotification*)notification;
109@end
110
111@implementation NSScrollView
112
113/*
114 * Class variables
115 */
116static Class rulerViewClass = nil;
117static CGFloat scrollerWidth;
118
119/*
120 * Class methods
121 */
122+ (void) initialize
123{
124  if (self == [NSScrollView class])
125    {
126      [self setRulerViewClass: [NSRulerView class]];
127      scrollerWidth = [NSScroller scrollerWidth];
128      [self setVersion: 2];
129    }
130}
131
132+ (void) setRulerViewClass: (Class)aClass
133{
134  rulerViewClass = aClass;
135}
136
137+ (Class) rulerViewClass
138{
139  return rulerViewClass;
140}
141
142+ (NSSize) contentSizeForFrameSize: (NSSize)frameSize
143             hasHorizontalScroller: (BOOL)hFlag
144               hasVerticalScroller: (BOOL)vFlag
145                        borderType: (NSBorderType)borderType
146{
147  NSSize size = frameSize;
148  NSSize border = [[GSTheme theme] sizeForBorderType: borderType];
149  CGFloat innerBorderWidth = [[NSUserDefaults standardUserDefaults]
150			      boolForKey: @"GSScrollViewNoInnerBorder"] ? 0.0 : 1.0;
151
152  /*
153   * Substract 1 from the width and height of
154   * the line that separates the horizontal
155   * and vertical scroller from the clip view
156   */
157  if (hFlag)
158    {
159      size.height -= scrollerWidth + innerBorderWidth;
160    }
161  if (vFlag)
162    {
163      size.width -= scrollerWidth + innerBorderWidth;
164    }
165
166  size.width -= 2 * border.width;
167  size.height -= 2 * border.height;
168
169  return size;
170}
171
172+ (NSSize) frameSizeForContentSize: (NSSize)contentSize
173             hasHorizontalScroller: (BOOL)hFlag
174               hasVerticalScroller: (BOOL)vFlag
175                        borderType: (NSBorderType)borderType
176{
177  NSSize size = contentSize;
178  NSSize border = [[GSTheme theme] sizeForBorderType: borderType];
179  CGFloat innerBorderWidth = [[NSUserDefaults standardUserDefaults]
180			      boolForKey: @"GSScrollViewNoInnerBorder"] ? 0.0 : 1.0;
181
182  /*
183   * Add 1 to the width and height for the line that separates the
184   * horizontal and vertical scroller from the clip view.
185   */
186  if (hFlag)
187    {
188      size.height += scrollerWidth + innerBorderWidth;
189    }
190  if (vFlag)
191    {
192      size.width += scrollerWidth + innerBorderWidth;
193    }
194
195  size.width += 2 * border.width;
196  size.height += 2 * border.height;
197
198  return size;
199}
200
201/*
202 * Instance methods
203 */
204- (id) initWithFrame: (NSRect)rect
205{
206  NSClipView *clipView;
207
208  self = [super initWithFrame: rect];
209  if (!self)
210    return nil;
211
212  clipView = [NSClipView new];
213  [self setContentView: clipView];
214  RELEASE(clipView);
215
216  _hLineScroll = 10;
217  _hPageScroll = 10;
218  _vLineScroll = 10;
219  _vPageScroll = 10;
220  _borderType = NSNoBorder;
221  _scrollsDynamically = YES;
222  //_autohidesScrollers = NO;
223  // FIXME: Not sure here Apple says by default all scrollers are off.
224  // For compatibility the ruler should be present but not visible.
225  [self setHasHorizontalRuler: YES];
226  [self tile];
227  _horizScrollElasticity = NSScrollElasticityAutomatic;
228  _vertScrollElasticity = NSScrollElasticityAutomatic;
229
230  [[NSNotificationCenter defaultCenter]
231    addObserver: self
232    selector: @selector(_themeDidActivate:)
233    name: GSThemeDidActivateNotification
234    object: nil];
235  return self;
236}
237
238- (void) dealloc
239{
240  [[NSNotificationCenter defaultCenter] removeObserver: self];
241
242  DESTROY(_horizScroller);
243  DESTROY(_vertScroller);
244  DESTROY(_horizRuler);
245  DESTROY(_vertRuler);
246
247  [super dealloc];
248}
249
250- (BOOL) isFlipped
251{
252  return YES;
253}
254
255- (void) setContentView: (NSClipView *)aView
256{
257  if (aView == nil)
258    [NSException raise: NSInvalidArgumentException
259                format: @"Attempt to set nil content view"];
260  if ([aView isKindOfClass: [NSView class]] == NO)
261    [NSException raise: NSInvalidArgumentException
262                format: @"Attempt to set non-view object as content view"];
263
264  if (aView != _contentView)
265    {
266      NSView *docView = [aView documentView];
267
268      [_contentView removeFromSuperview];
269      [self addSubview: aView];
270      // This must be done after adding it as a subview,
271      // otherwise it will get unset again.
272      _contentView = aView;
273
274      if (docView != nil)
275        {
276          [self setDocumentView: docView];
277        }
278    }
279  [_contentView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
280  [self tile];
281}
282
283- (void) willRemoveSubview: (NSView *)aView
284{
285  if (aView == _contentView)
286    {
287      _contentView = nil;
288    }
289  if (aView == _headerClipView)
290    {
291      _headerClipView = nil;
292    }
293  if (aView == _cornerView)
294    {
295      _cornerView = nil;
296    }
297  [super willRemoveSubview: aView];
298}
299
300- (void) setHorizontalScroller: (NSScroller*)aScroller
301{
302  [_horizScroller removeFromSuperview];
303
304  /*
305   * Do not add the scroller view to the subviews array yet;
306   * -setHasHorizontalScroller must be invoked first
307   */
308  ASSIGN(_horizScroller, aScroller);
309  if (_horizScroller)
310    {
311      [_horizScroller setAutoresizingMask: NSViewWidthSizable];
312      [_horizScroller setTarget: self];
313      [_horizScroller setAction: @selector(_doScroll:)];
314    }
315}
316
317- (void) setHasHorizontalScroller: (BOOL)flag
318{
319  if (_hasHorizScroller == flag)
320    return;
321
322  _hasHorizScroller = flag;
323
324  if (_hasHorizScroller)
325    {
326      if (!_horizScroller)
327        {
328          NSScroller *scroller = [NSScroller new];
329
330          [self setHorizontalScroller: scroller];
331          RELEASE(scroller);
332        }
333      [self addSubview: _horizScroller];
334    }
335  else
336    [_horizScroller removeFromSuperview];
337
338  [self tile];
339}
340
341- (void) setVerticalScroller: (NSScroller*)aScroller
342{
343  [_vertScroller removeFromSuperview];
344
345  /*
346   * Do not add the scroller view to the subviews array yet;
347   * -setHasVerticalScroller must be invoked first
348   */
349  ASSIGN(_vertScroller, aScroller);
350  if (_vertScroller)
351    {
352      [_vertScroller setAutoresizingMask: NSViewHeightSizable];
353      [_vertScroller setTarget: self];
354      [_vertScroller setAction: @selector(_doScroll:)];
355    }
356}
357
358- (void) setHasVerticalScroller: (BOOL)flag
359{
360  if (_hasVertScroller == flag)
361    return;
362
363  _hasVertScroller = flag;
364
365  if (_hasVertScroller)
366    {
367      if (!_vertScroller)
368        {
369          NSScroller *scroller = [NSScroller new];
370
371          [self setVerticalScroller: scroller];
372          RELEASE(scroller);
373          if (_contentView && ![_contentView isFlipped])
374            [_vertScroller setFloatValue: 1];
375        }
376      [self addSubview: _vertScroller];
377    }
378  else
379    [_vertScroller removeFromSuperview];
380
381  [self tile];
382}
383
384/**
385 <p> Return wether scroller autohiding is set or not. </p>
386 <p>See Also: -setAutohidesScrollers:</p>
387*/
388- (BOOL) autohidesScrollers
389{
390  return _autohidesScrollers;
391}
392
393/**
394 <p>Sets whether the view hides the scrollers (horizontal and/or vertical independendently) if they are not needed.</p>
395 <p>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.</p>
396 <p>See Also: -autohidesScrollers</p>
397 */
398- (void) setAutohidesScrollers: (BOOL)flag
399{
400  _autohidesScrollers = flag;
401}
402
403- (NSScrollElasticity)horizontalScrollElasticity
404{
405  return _horizScrollElasticity;
406}
407
408- (void)setHorizontalScrollElasticity:(NSScrollElasticity)value
409{
410  _horizScrollElasticity = value;
411}
412
413- (NSScrollElasticity)verticalScrollElasticity
414{
415  return _vertScrollElasticity;
416}
417
418
419- (void)setVerticalScrollElasticity:(NSScrollElasticity)value
420{
421  _vertScrollElasticity = value;
422}
423
424- (void) scrollWheel: (NSEvent *)theEvent
425{
426  NSRect clipViewBounds;
427  CGFloat deltaY = [theEvent deltaY];
428  CGFloat deltaX = [theEvent deltaX];
429  CGFloat amount;
430  NSPoint point;
431
432  if (_contentView == nil)
433    {
434      clipViewBounds = NSZeroRect;
435    }
436  else
437    {
438      clipViewBounds = [_contentView bounds];
439    }
440  point = clipViewBounds.origin;
441
442  // Holding shift converts vertical scrolling to horizontal
443  if (([theEvent modifierFlags] & NSShiftKeyMask) == NSShiftKeyMask)
444    {
445      deltaX = -deltaY;
446      deltaY = 0;
447    }
448
449  // Scroll horizontally
450  if (([theEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask)
451    {
452      amount = (clipViewBounds.size.width - _hPageScroll) * deltaX;
453    }
454  else
455    {
456      amount = _hLineScroll * deltaX;
457    }
458
459  NSDebugLLog (@"NSScrollView",
460    @"increment/decrement: amount = %f, horizontal", amount);
461
462  point.x = clipViewBounds.origin.x + amount;
463
464  // Scroll vertically
465  if (([theEvent modifierFlags] & NSAlternateKeyMask) == NSAlternateKeyMask)
466    {
467      amount = - (clipViewBounds.size.height - _vPageScroll) * deltaY;
468    }
469  else
470    {
471      amount = - _vLineScroll * deltaY;
472    }
473
474  if (_contentView != nil && ![_contentView isFlipped])
475    {
476      /* If view is flipped reverse the scroll direction */
477      amount = -amount;
478    }
479  NSDebugLLog (@"NSScrollView",
480    @"increment/decrement: amount = %f, flipped = %d",
481    amount, _contentView ? [_contentView isFlipped] : 0);
482
483  point.y = clipViewBounds.origin.y + amount;
484
485  /* scrollToPoint: will call reflectScrolledClipView:, which will
486   * update rules, headers, and scrollers.  */
487  [_contentView _scrollToPoint: point];
488}
489
490- (void) keyDown: (NSEvent *)theEvent
491{
492  NSString *chars = [theEvent characters];
493  unichar c = [chars length] == 1 ? [chars characterAtIndex: 0] : '\0';
494
495  switch (c)
496    {
497      case NSUpArrowFunctionKey:
498        [self scrollLineUp: self];
499        break;
500
501      case NSDownArrowFunctionKey:
502        [self scrollLineDown: self];
503        break;
504
505      case NSPageUpFunctionKey:
506        [self scrollPageUp: self];
507        break;
508
509      case NSPageDownFunctionKey:
510        [self scrollPageDown: self];
511        break;
512
513      default:
514        [super keyDown: theEvent];
515        break;
516    }
517}
518
519/*
520 * This code is based on _doScroll: and still may need some tuning.
521 */
522- (void) scrollLineUp: (id)sender
523{
524  NSRect  clipViewBounds;
525  NSPoint point;
526  CGFloat amount;
527
528  if (_contentView == nil)
529    {
530      clipViewBounds = NSZeroRect;
531    }
532  else
533    {
534      clipViewBounds = [_contentView bounds];
535    }
536  point = clipViewBounds.origin;
537  amount = _vLineScroll;
538  if (_contentView != nil && ![_contentView isFlipped])
539    {
540      amount = -amount;
541    }
542  point.y = clipViewBounds.origin.y - amount;
543  [_contentView _scrollToPoint: point];
544}
545
546
547/*
548 * This code is based on _doScroll: and still may need some tuning.
549 */
550- (void) scrollLineDown: (id)sender
551{
552  NSRect  clipViewBounds;
553  NSPoint point;
554  CGFloat amount;
555
556  if (_contentView == nil)
557    {
558      clipViewBounds = NSZeroRect;
559    }
560  else
561    {
562      clipViewBounds = [_contentView bounds];
563    }
564  point = clipViewBounds.origin;
565  amount = _vLineScroll;
566  if (_contentView != nil && ![_contentView isFlipped])
567    {
568      amount = -amount;
569    }
570  point.y = clipViewBounds.origin.y + amount;
571  [_contentView _scrollToPoint: point];
572}
573
574/**
575 * Scrolls the receiver by simply invoking scrollPageUp:
576 */
577- (void) pageUp: (id)sender
578{
579  [self scrollPageUp: sender];
580}
581
582/*
583 * This code is based on _doScroll: and still may need some tuning.
584 */
585- (void) scrollPageUp: (id)sender
586{
587  NSRect  clipViewBounds;
588  NSPoint point;
589  CGFloat amount;
590
591  if (_contentView == nil)
592    {
593      clipViewBounds = NSZeroRect;
594    }
595  else
596    {
597      clipViewBounds = [_contentView bounds];
598    }
599  point = clipViewBounds.origin;
600  /*
601   * Take verticalPageScroll into accout, but try to make sure
602   * that amount is never negative (ie do not scroll backwards.)
603   *
604   * FIXME: It seems _doScroll and scrollWheel: should also take
605   * care not to do negative scrolling.
606   */
607  amount = clipViewBounds.size.height - _vPageScroll;
608  amount = (amount < 0) ? 0 : amount;
609
610  if (_contentView != nil && ![_contentView isFlipped])
611    {
612      amount = -amount;
613    }
614  point.y = clipViewBounds.origin.y - amount;
615  [_contentView _scrollToPoint: point];
616}
617
618/**
619 * Scrolls the receiver by simply invoking scrollPageUp:
620 */
621- (void) pageDown: (id)sender
622{
623  [self scrollPageDown: sender];
624}
625
626/*
627 * This code is based on _doScroll:. and still may need some tuning.
628 */
629- (void) scrollPageDown: (id)sender
630{
631  NSRect  clipViewBounds;
632  NSPoint point;
633  CGFloat amount;
634
635  if (_contentView == nil)
636    {
637      clipViewBounds = NSZeroRect;
638    }
639  else
640    {
641      clipViewBounds = [_contentView bounds];
642    }
643  point = clipViewBounds.origin;
644  /*
645   * Take verticalPageScroll into accout, but try to make sure
646   * that amount is never negativ (ie do not scroll backwards.)
647   *
648   * FIXME: It seems _doScroll and scrollWheel: should also take
649   * care not to do negative scrolling.
650   */
651  amount = clipViewBounds.size.height - _vPageScroll;
652  amount = (amount < 0) ? 0 : amount;
653  if (_contentView != nil && ![_contentView isFlipped])
654    {
655      amount = -amount;
656    }
657  point.y = clipViewBounds.origin.y + amount;
658  [_contentView _scrollToPoint: point];
659}
660
661- (void) _doScroll: (NSScroller*)scroller
662{
663  float floatValue = [scroller floatValue];
664  NSScrollerPart hitPart = [scroller hitPart];
665  NSRect clipViewBounds;
666  NSRect documentRect;
667  CGFloat amount = 0;
668  NSPoint point;
669
670  if (_contentView == nil)
671    {
672      clipViewBounds = NSZeroRect;
673      documentRect = NSZeroRect;
674    }
675  else
676    {
677      clipViewBounds = [_contentView bounds];
678      documentRect = [_contentView documentRect];
679    }
680  point = clipViewBounds.origin;
681
682  NSDebugLLog (@"NSScrollView", @"_doScroll: float value = %f", floatValue);
683
684  /* do nothing if scroller is unknown */
685  if (scroller != _horizScroller && scroller != _vertScroller)
686    return;
687
688  _knobMoved = NO;
689
690  if (hitPart == NSScrollerKnob || hitPart == NSScrollerKnobSlot)
691    _knobMoved = YES;
692  else
693    {
694      if (hitPart == NSScrollerIncrementLine)
695        {
696          if (scroller == _horizScroller)
697            amount = _hLineScroll;
698          else
699            amount = _vLineScroll;
700        }
701      else if (hitPart == NSScrollerDecrementLine)
702        {
703          if (scroller == _horizScroller)
704            amount = -_hLineScroll;
705          else
706            amount = -_vLineScroll;
707        }
708      else if (hitPart == NSScrollerIncrementPage)
709        {
710          if (scroller == _horizScroller)
711            amount = clipViewBounds.size.width - _hPageScroll;
712          else
713            amount = clipViewBounds.size.height - _vPageScroll;
714        }
715      else if (hitPart == NSScrollerDecrementPage)
716        {
717          if (scroller == _horizScroller)
718            amount = _hPageScroll - clipViewBounds.size.width;
719          else
720            amount = _vPageScroll - clipViewBounds.size.height;
721        }
722      else
723        {
724          return;
725        }
726    }
727
728  if (!_knobMoved)        /* button scrolling */
729    {
730      if (scroller == _horizScroller)
731        {
732          point.x = clipViewBounds.origin.x + amount;
733        }
734      else
735        {
736          if (_contentView != nil && ![_contentView isFlipped])
737            {
738              /* If view is flipped reverse the scroll direction */
739              amount = -amount;
740            }
741          NSDebugLLog (@"NSScrollView",
742                       @"increment/decrement: amount = %f, flipped = %d",
743            amount, _contentView ? [_contentView isFlipped] : 0);
744          point.y = clipViewBounds.origin.y + amount;
745        }
746    }
747  else         /* knob scolling */
748    {
749      if (scroller == _horizScroller)
750        {
751          point.x = floatValue * (documentRect.size.width
752                                              - clipViewBounds.size.width);
753          point.x += documentRect.origin.x;
754        }
755      else
756        {
757          if (_contentView != nil && ![_contentView isFlipped])
758            floatValue = 1 - floatValue;
759          point.y = floatValue * (documentRect.size.height
760            - clipViewBounds.size.height);
761          point.y += documentRect.origin.y;
762        }
763    }
764
765  /* scrollToPoint will call reflectScrollerClipView, and that will
766   * update scrollers, rulers and headers */
767  [_contentView _scrollToPoint: point];
768}
769
770- (void) scrollToBeginningOfDocument: (id)sender
771{
772  NSRect  clipViewBounds, documentRect;
773  NSPoint point;
774
775  if (_contentView == nil)
776    {
777      clipViewBounds = NSZeroRect;
778      documentRect = NSZeroRect;
779    }
780  else
781    {
782      clipViewBounds = [_contentView bounds];
783      documentRect = [_contentView documentRect];
784    }
785  point = documentRect.origin;
786  if (_contentView != nil && ![_contentView isFlipped])
787    {
788      point.y = NSMaxY(documentRect) - NSHeight(clipViewBounds);
789      if (point.y < 0)
790        point.y = 0;
791    }
792  [_contentView _scrollToPoint: point];
793}
794
795- (void) scrollToEndOfDocument: (id)sender
796{
797  NSRect  clipViewBounds, documentRect;
798  NSPoint point;
799
800  if (_contentView == nil)
801    {
802      clipViewBounds = NSZeroRect;
803      documentRect = NSZeroRect;
804    }
805  else
806    {
807      clipViewBounds = [_contentView bounds];
808      documentRect = [_contentView documentRect];
809    }
810  point = documentRect.origin;
811  if (_contentView == nil || [_contentView isFlipped])
812    {
813      point.y = NSMaxY(documentRect) - NSHeight(clipViewBounds);
814      if (point.y < 0)
815        point.y = 0;
816    }
817  [_contentView _scrollToPoint: point];
818}
819
820//
821// This method is here purely for nib compatibility.  This is the action
822// connected to by NSScrollers in IB when building a scrollview.
823//
824- (void) _doScroller: (NSScroller *)scroller
825{
826  [self _doScroll: scroller];
827}
828
829- (void) reflectScrolledClipView: (NSClipView *)aClipView
830{
831  NSRect documentFrame = NSZeroRect;
832  NSRect clipViewBounds = NSZeroRect;
833  float floatValue;
834  CGFloat knobProportion;
835  id documentView;
836
837  if (aClipView != _contentView)
838    {
839      return;
840    }
841
842  NSDebugLLog (@"NSScrollView", @"reflectScrolledClipView:");
843
844  if (_contentView)
845    {
846      clipViewBounds = [_contentView bounds];
847    }
848  if ((documentView = [_contentView documentView]))
849    {
850      documentFrame = [documentView frame];
851    }
852
853  // FIXME: Should we just hide the scroll bar or remove it?
854  if ((_autohidesScrollers)
855    && (documentFrame.size.height > clipViewBounds.size.height))
856    {
857      [self setHasVerticalScroller: YES];
858    }
859
860  if (_hasVertScroller)
861    {
862      if (documentFrame.size.height <= clipViewBounds.size.height)
863        {
864          if (_autohidesScrollers)
865            {
866              [self setHasVerticalScroller: NO];
867            }
868          else
869            {
870              [_vertScroller setEnabled: NO];
871            }
872        }
873      else
874        {
875          [_vertScroller setEnabled: YES];
876
877          knobProportion = clipViewBounds.size.height
878            / documentFrame.size.height;
879
880          floatValue = (clipViewBounds.origin.y - documentFrame.origin.y)
881            / (documentFrame.size.height - clipViewBounds.size.height);
882
883          if (![_contentView isFlipped])
884            {
885              floatValue = 1 - floatValue;
886            }
887          [_vertScroller setFloatValue: floatValue
888                         knobProportion: knobProportion];
889        }
890    }
891
892  if ((_autohidesScrollers)
893    && (documentFrame.size.width > clipViewBounds.size.width))
894    {
895      [self setHasHorizontalScroller: YES];
896    }
897
898  if (_hasHorizScroller)
899    {
900      if (documentFrame.size.width <= clipViewBounds.size.width)
901        {
902          if (_autohidesScrollers)
903            {
904              [self setHasHorizontalScroller: NO];
905            }
906          else
907            {
908              [_horizScroller setEnabled: NO];
909            }
910        }
911      else
912        {
913          [_horizScroller setEnabled: YES];
914
915          knobProportion = clipViewBounds.size.width
916            / documentFrame.size.width;
917
918          floatValue = (clipViewBounds.origin.x - documentFrame.origin.x)
919            / (documentFrame.size.width - clipViewBounds.size.width);
920
921          [_horizScroller setFloatValue: floatValue
922                         knobProportion: knobProportion];
923        }
924    }
925
926  if (_hasHeaderView)
927    {
928      NSPoint headerClipViewOrigin;
929
930      headerClipViewOrigin = [_headerClipView bounds].origin;
931
932      /* If needed, scroll the headerview too.  */
933      if (headerClipViewOrigin.x != clipViewBounds.origin.x)
934        {
935          headerClipViewOrigin.x = clipViewBounds.origin.x;
936          [_headerClipView scrollToPoint: headerClipViewOrigin];
937        }
938    }
939
940  if (_rulersVisible == YES)
941    {
942      if (_hasHorizRuler)
943        {
944          [_horizRuler setNeedsDisplay: YES];
945        }
946      if (_hasVertRuler)
947        {
948          [_vertRuler setNeedsDisplay: YES];
949       }
950    }
951}
952
953- (void) setHorizontalRulerView: (NSRulerView *)aRulerView
954{
955  if (_rulersVisible && _horizRuler != nil)
956    {
957      [_horizRuler removeFromSuperview];
958    }
959
960  ASSIGN(_horizRuler, aRulerView);
961
962  if (_horizRuler == nil)
963    {
964      _hasHorizRuler = NO;
965    }
966  else if (_rulersVisible)
967    {
968      [self addSubview:_horizRuler];
969    }
970
971  if (_rulersVisible)
972    {
973      [self tile];
974    }
975}
976
977- (void) setHasHorizontalRuler: (BOOL)flag
978{
979  if (_hasHorizRuler == flag)
980    return;
981
982  _hasHorizRuler = flag;
983  if (_hasHorizRuler && _horizRuler == nil)
984    {
985      _horizRuler = [[object_getClass(self) rulerViewClass] alloc];
986      _horizRuler = [_horizRuler initWithScrollView: self
987                                 orientation: NSHorizontalRuler];
988    }
989
990  if (_rulersVisible)
991    {
992      if (_hasHorizRuler)
993        {
994          [self addSubview: _horizRuler];
995        }
996      else
997        {
998          [_horizRuler removeFromSuperview];
999        }
1000      [self tile];
1001    }
1002}
1003
1004- (void) setVerticalRulerView: (NSRulerView *)aRulerView
1005{
1006  if (_rulersVisible && _vertRuler != nil)
1007    {
1008      [_vertRuler removeFromSuperview];
1009    }
1010
1011  ASSIGN(_vertRuler, aRulerView);
1012
1013  if (_vertRuler == nil)
1014    {
1015      _hasVertRuler = NO;
1016    }
1017  else if (_rulersVisible)
1018    {
1019      [self addSubview:_vertRuler];
1020    }
1021
1022  if (_rulersVisible)
1023    {
1024      [self tile];
1025    }
1026}
1027
1028- (void) setHasVerticalRuler: (BOOL)flag
1029{
1030  if (_hasVertRuler == flag)
1031    return;
1032
1033  _hasVertRuler = flag;
1034  if (_hasVertRuler && _vertRuler == nil)
1035    {
1036      _vertRuler = [[object_getClass(self) rulerViewClass] alloc];
1037      _vertRuler = [_vertRuler initWithScrollView: self
1038                               orientation: NSVerticalRuler];
1039    }
1040
1041  if (_rulersVisible)
1042    {
1043      if (_hasVertRuler)
1044        {
1045          [self addSubview: _vertRuler];
1046        }
1047      else
1048        {
1049          [_vertRuler removeFromSuperview];
1050        }
1051      [self tile];
1052    }
1053}
1054
1055- (void) setRulersVisible: (BOOL)flag
1056{
1057  if (_rulersVisible == flag)
1058    return;
1059
1060  _rulersVisible = flag;
1061  if (flag)
1062    {
1063      if (_hasVertRuler)
1064        [self addSubview: _vertRuler];
1065      if (_hasHorizRuler)
1066        [self addSubview: _horizRuler];
1067    }
1068  else
1069    {
1070      if (_hasVertRuler)
1071        [_vertRuler removeFromSuperview];
1072      if (_hasHorizRuler)
1073        [_horizRuler removeFromSuperview];
1074    }
1075  [self tile];
1076}
1077
1078- (void) setFrame: (NSRect)rect
1079{
1080  [super setFrame: rect];
1081  [self tile];
1082}
1083
1084- (void) setFrameSize: (NSSize)size
1085{
1086  [super setFrameSize: size];
1087  [self tile];
1088}
1089
1090static NSRectEdge
1091GSOppositeEdge(NSRectEdge edge)
1092{
1093  return (edge == NSMinXEdge) ? NSMaxXEdge : NSMinXEdge;
1094}
1095
1096- (void) tile
1097{
1098  NSRect headerRect, contentRect;
1099  NSSize border = [[GSTheme theme] sizeForBorderType: _borderType];
1100  NSRectEdge bottomEdge, topEdge;
1101  CGFloat headerViewHeight = 0;
1102  NSRectEdge verticalScrollerEdge = NSMinXEdge;
1103  NSInterfaceStyle style;
1104  CGFloat innerBorderWidth = [[NSUserDefaults standardUserDefaults]
1105			      boolForKey: @"GSScrollViewNoInnerBorder"] ? 0.0 : 1.0;
1106
1107  const BOOL useBottomCorner = [[GSTheme theme] scrollViewUseBottomCorner];
1108  const BOOL overlapBorders = [[GSTheme theme] scrollViewScrollersOverlapBorders];
1109
1110  style = NSInterfaceStyleForKey(@"NSScrollViewInterfaceStyle", nil);
1111
1112  if (style == NSMacintoshInterfaceStyle
1113    || style == NSWindows95InterfaceStyle)
1114    {
1115      verticalScrollerEdge = NSMaxXEdge;
1116    }
1117
1118  /* Determine edge positions.  */
1119  if ([self isFlipped])
1120    {
1121      topEdge = NSMinYEdge;
1122      bottomEdge = NSMaxYEdge;
1123    }
1124  else
1125    {
1126      topEdge = NSMaxYEdge;
1127      bottomEdge = NSMinYEdge;
1128    }
1129
1130  /* Prepare the contentRect by insetting the borders.  */
1131  contentRect = _bounds;
1132
1133  if (!overlapBorders)
1134    contentRect = NSInsetRect(contentRect, border.width, border.height);
1135
1136  if (contentRect.size.width < 0 || contentRect.size.height < 0)
1137    {
1138      /* FIXME ... should we do something else when given
1139       * too small a size to tile? */
1140      return;
1141    }
1142
1143  [self _synchronizeHeaderAndCornerView];
1144
1145  if (overlapBorders)
1146    {
1147      if (_borderType != NSNoBorder)
1148	{
1149	  if (!(_hasHeaderView || _hasCornerView))
1150	    {
1151	      // Inset 1px on the top
1152	      NSDivideRect(contentRect, NULL, &contentRect, 1, topEdge);
1153	    }
1154	  if (!_hasVertScroller)
1155	    {
1156	      // Inset 1px on the edge where the vertical scroller would be
1157	      NSDivideRect(contentRect, NULL, &contentRect, 1, verticalScrollerEdge);
1158	    }
1159	  if (!_hasHorizScroller)
1160	    {
1161	      NSDivideRect(contentRect, NULL, &contentRect, 1, bottomEdge);
1162	    }
1163	  // The vertical edge without a scroller
1164	  {
1165	    NSDivideRect(contentRect, NULL, &contentRect, 1,
1166			 GSOppositeEdge(verticalScrollerEdge));
1167	  }
1168	}
1169    }
1170
1171  /* First, allocate vertical space for the headerView / cornerView
1172     (but - NB - the headerView needs to be placed above the clipview
1173     later on, we can't place it now).  */
1174
1175  if (_hasHeaderView == YES)
1176    {
1177      headerViewHeight = [[_headerClipView documentView] frame].size.height;
1178    }
1179
1180  if (_hasCornerView == YES)
1181    {
1182      if (headerViewHeight == 0)
1183        {
1184          headerViewHeight = [_cornerView frame].size.height;
1185        }
1186    }
1187
1188  /* Remove the vertical slice used by the header/corner view.  Save
1189     the height and y position of headerRect for later reuse.  */
1190  NSDivideRect (contentRect, &headerRect, &contentRect, headerViewHeight,
1191                topEdge);
1192
1193  /* Ok - now go on with drawing the actual scrollview in the
1194     remaining space.  Just consider contentRect to be the area in
1195     which we draw, ignoring header/corner view.  */
1196
1197  /* Prepare the vertical scroller.  */
1198  if (_hasVertScroller)
1199    {
1200      NSRect vertScrollerRect;
1201
1202      NSDivideRect (contentRect, &vertScrollerRect, &contentRect,
1203        scrollerWidth, verticalScrollerEdge);
1204
1205      /* If the theme requests it, leave a square gap in the bottom-
1206       * left (or bottom-right) corner where the horizontal and vertical
1207       * scrollers meet. */
1208      if (_hasHorizScroller && !useBottomCorner)
1209	{
1210	  NSDivideRect (vertScrollerRect, NULL, &vertScrollerRect,
1211			scrollerWidth, bottomEdge);
1212	}
1213
1214      /** Vertically expand the scroller by 1pt on each end */
1215      if (overlapBorders)
1216        {
1217	  vertScrollerRect.origin.y -= 1;
1218	  vertScrollerRect.size.height += 2;
1219	}
1220      else if (_hasHeaderView || _hasCornerView)
1221        {
1222	  vertScrollerRect.origin.y -= 1;
1223	  vertScrollerRect.size.height += 1;
1224        }
1225
1226      [_vertScroller setFrame: vertScrollerRect];
1227
1228      /* Substract 1 for the line that separates the vertical scroller
1229       * from the clip view (and eventually the horizontal scroller),
1230       * unless the GSScrollViewNoInnerBorder default is set. */
1231      NSDivideRect (contentRect, NULL, &contentRect, innerBorderWidth, verticalScrollerEdge);
1232    }
1233
1234  /* Prepare the horizontal scroller.  */
1235  if (_hasHorizScroller)
1236    {
1237      NSRect horizScrollerRect;
1238
1239      NSDivideRect (contentRect, &horizScrollerRect, &contentRect,
1240        scrollerWidth, bottomEdge);
1241
1242      /** Horizontall expand the scroller by 1pt on each end */
1243      if (overlapBorders)
1244	{
1245	  horizScrollerRect.origin.x -= 1;
1246	  horizScrollerRect.size.width += 2;
1247	}
1248
1249      [_horizScroller setFrame: horizScrollerRect];
1250
1251      /* Substract 1 for the width for the line that separates the
1252       * horizontal scroller from the clip view,
1253       * unless the GSScrollViewNoInnerBorder default is set. */
1254      NSDivideRect (contentRect, NULL, &contentRect, innerBorderWidth, bottomEdge);
1255    }
1256
1257  /* Now place and size the header view to be exactly above the
1258     resulting clipview.  */
1259  if (_hasHeaderView)
1260    {
1261      NSRect rect = headerRect;
1262
1263      rect.origin.x = contentRect.origin.x;
1264      rect.size.width = contentRect.size.width;
1265
1266      [_headerClipView setFrame: rect];
1267    }
1268
1269  /* Now place the corner view.  */
1270  if (_hasCornerView)
1271    {
1272      NSPoint p = headerRect.origin;
1273
1274      if (verticalScrollerEdge == NSMaxXEdge)
1275        {
1276          p.x += contentRect.size.width;
1277        }
1278      [_cornerView setFrameOrigin: p];
1279    }
1280
1281  /* Now place the rulers.  */
1282  if (_rulersVisible)
1283    {
1284      if (_hasHorizRuler)
1285        {
1286          NSRect horizRulerRect;
1287
1288          NSDivideRect (contentRect, &horizRulerRect, &contentRect,
1289            [_horizRuler requiredThickness], topEdge);
1290          [_horizRuler setFrame: horizRulerRect];
1291        }
1292
1293      if (_hasVertRuler)
1294        {
1295          NSRect vertRulerRect;
1296
1297          NSDivideRect (contentRect, &vertRulerRect, &contentRect,
1298            [_vertRuler requiredThickness], NSMinXEdge);
1299          [_vertRuler setFrame: vertRulerRect];
1300        }
1301    }
1302
1303  [_contentView setFrame: contentRect];
1304  [self setNeedsDisplay: YES];
1305}
1306
1307- (void) drawRect: (NSRect)rect
1308{
1309   [[GSTheme theme] drawScrollViewRect: rect
1310		    inView: self];
1311}
1312
1313- (NSRect) documentVisibleRect
1314{
1315  return [_contentView documentVisibleRect];
1316}
1317
1318- (void) setBackgroundColor: (NSColor*)aColor
1319{
1320  [_contentView setBackgroundColor: aColor];
1321}
1322
1323- (NSColor*) backgroundColor
1324{
1325  return [_contentView backgroundColor];
1326}
1327
1328- (void) setDrawsBackground: (BOOL)flag
1329{
1330  [_contentView setDrawsBackground: flag];
1331  if ((flag == NO) &&
1332      [_contentView respondsToSelector: @selector(setCopiesOnScroll:)])
1333    [_contentView setCopiesOnScroll: NO];
1334}
1335
1336- (BOOL) drawsBackground
1337{
1338  return [_contentView drawsBackground];
1339}
1340
1341- (void) setBorderType: (NSBorderType)borderType
1342{
1343  _borderType = borderType;
1344  [self tile];
1345}
1346
1347- (void) setDocumentView: (NSView *)aView
1348{
1349  [_contentView setDocumentView: aView];
1350
1351  if (_contentView && ![_contentView isFlipped])
1352    {
1353      [_vertScroller setFloatValue: 1];
1354    }
1355  [self tile];
1356}
1357
1358- (void) resizeSubviewsWithOldSize: (NSSize)oldSize
1359{
1360  [super resizeSubviewsWithOldSize: oldSize];
1361  [self tile];
1362}
1363
1364- (id) documentView
1365{
1366  return [_contentView documentView];
1367}
1368
1369- (NSCursor*) documentCursor
1370{
1371  return [_contentView documentCursor];
1372}
1373
1374- (void) setDocumentCursor: (NSCursor*)aCursor
1375{
1376  [_contentView setDocumentCursor: aCursor];
1377}
1378
1379- (BOOL) isOpaque
1380{
1381  // FIXME: Only needs to be NO in a corner case,
1382  // when [[GSTheme theme] scrollViewUseBottomCorner] is NO
1383  // and the theme tile for the bottom corner is transparent.
1384  // So maybe cache the value of
1385  // [[GSTheme theme] scrollViewUseBottomCorner] and check it here.
1386  return NO;
1387}
1388
1389- (NSBorderType) borderType
1390{
1391  return _borderType;
1392}
1393
1394/** <p>Returns whether the NSScrollView has a horizontal ruler</p>
1395    <p>See Also: -setHasHorizontalRuler:</p>
1396 */
1397- (BOOL) hasHorizontalRuler
1398{
1399  return _hasHorizRuler;
1400}
1401
1402/** <p>Returns whether the NSScrollView has a horizontal scroller</p>
1403    <p>See Also: -setHasHorizontalScroller:</p>
1404 */
1405- (BOOL) hasHorizontalScroller
1406{
1407  return _hasHorizScroller;
1408}
1409
1410/** <p>Returns whether the NSScrollView has a vertical ruler</p>
1411    <p>See Also: -setHasVerticalRuler:</p>
1412 */
1413- (BOOL) hasVerticalRuler
1414{
1415  return _hasVertRuler;
1416}
1417
1418/** <p>Returns whether the NSScrollView has a vertical scroller</p>
1419    <p>See Also: -setHasVerticalScroller:</p>
1420 */
1421- (BOOL) hasVerticalScroller
1422{
1423  return _hasVertScroller;
1424}
1425
1426/**<p>Returns the size of the NSScrollView's content view</p>
1427 */
1428- (NSSize) contentSize
1429{
1430  return [_contentView bounds].size;
1431}
1432
1433- (NSClipView *) contentView
1434{
1435  return _contentView;
1436}
1437
1438- (NSRulerView *) horizontalRulerView
1439{
1440  return _horizRuler;
1441}
1442
1443- (NSRulerView *) verticalRulerView
1444{
1445  return _vertRuler;
1446}
1447
1448- (BOOL) rulersVisible
1449{
1450  return _rulersVisible;
1451}
1452
1453- (void) setLineScroll: (CGFloat)aFloat
1454{
1455  _hLineScroll = aFloat;
1456  _vLineScroll = aFloat;
1457}
1458
1459- (void) setHorizontalLineScroll: (CGFloat)aFloat
1460{
1461  _hLineScroll = aFloat;
1462}
1463
1464- (void) setVerticalLineScroll: (CGFloat)aFloat
1465{
1466  _vLineScroll = aFloat;
1467}
1468
1469- (CGFloat) lineScroll
1470{
1471  if (_hLineScroll != _vLineScroll)
1472    [NSException raise: NSInternalInconsistencyException
1473                format: @"horizontal and vertical values not same"];
1474  return _vLineScroll;
1475}
1476
1477- (CGFloat) horizontalLineScroll
1478{
1479  return _hLineScroll;
1480}
1481
1482- (CGFloat) verticalLineScroll
1483{
1484  return _vLineScroll;
1485}
1486
1487- (void) setPageScroll: (CGFloat)aFloat
1488{
1489  _hPageScroll = aFloat;
1490  _vPageScroll = aFloat;
1491}
1492
1493- (void) setHorizontalPageScroll: (CGFloat)aFloat
1494{
1495  _hPageScroll = aFloat;
1496}
1497
1498- (void) setVerticalPageScroll: (CGFloat)aFloat
1499{
1500  _vPageScroll = aFloat;
1501}
1502
1503- (CGFloat) pageScroll
1504{
1505  if (_hPageScroll != _vPageScroll)
1506    [NSException raise: NSInternalInconsistencyException
1507                format: @"horizontal and vertical values not same"];
1508  return _vPageScroll;
1509}
1510
1511- (CGFloat) horizontalPageScroll
1512{
1513  return _hPageScroll;
1514}
1515
1516- (CGFloat) verticalPageScroll
1517{
1518  return _vPageScroll;
1519}
1520
1521- (void) setScrollsDynamically: (BOOL)flag
1522{
1523  // FIXME: This should change the behaviour of the scrollers
1524  _scrollsDynamically = flag;
1525}
1526
1527- (BOOL) scrollsDynamically
1528{
1529  return _scrollsDynamically;
1530}
1531
1532- (NSScroller*) horizontalScroller
1533{
1534  return _horizScroller;
1535}
1536
1537- (NSScroller*) verticalScroller
1538{
1539  return _vertScroller;
1540}
1541
1542- (BOOL)allowsMagnification
1543{
1544  //we need an ivar for this
1545  return NO;
1546}
1547
1548- (void)setAllowsMagnification:(BOOL)m
1549{
1550  //we need an ivar for this
1551}
1552
1553/*
1554 * NSCoding protocol
1555 */
1556- (void) encodeWithCoder: (NSCoder*)aCoder
1557{
1558  [super encodeWithCoder: aCoder];
1559
1560  if ([aCoder allowsKeyedCoding])
1561    {
1562      unsigned long flags = 0;
1563
1564      [aCoder encodeObject: _horizScroller forKey: @"NSHScroller"];
1565      [aCoder encodeObject: _vertScroller forKey: @"NSVScroller"];
1566      [aCoder encodeObject: _contentView forKey: @"NSContentView"];
1567
1568      // only encode this, if it's not null...
1569      if (_headerClipView != nil)
1570        {
1571          [aCoder encodeObject: _headerClipView forKey: @"NSHeaderClipView"];
1572        }
1573
1574      flags = _borderType;
1575      if (_hasVertScroller)
1576        flags |= 16;
1577      if (_hasHorizScroller)
1578        flags |= 32;
1579      if (_autohidesScrollers)
1580        flags |= 512;
1581
1582      [aCoder encodeInt: flags forKey: @"NSsFlags"];
1583    }
1584  else
1585    {
1586      [aCoder encodeObject: _contentView];
1587      // Was int, we need to stay compatible
1588      [aCoder encodeValueOfObjCType: @encode(NSInteger) at: &_borderType];
1589      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_scrollsDynamically];
1590      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_rulersVisible];
1591      [aCoder encodeValueOfObjCType: @encode(float) at: &_hLineScroll];
1592      [aCoder encodeValueOfObjCType: @encode(float) at: &_hPageScroll];
1593      [aCoder encodeValueOfObjCType: @encode(float) at: &_vLineScroll];
1594      [aCoder encodeValueOfObjCType: @encode(float) at: &_vPageScroll];
1595
1596      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHorizScroller];
1597      if (_hasHorizScroller)
1598        [aCoder encodeObject: _horizScroller];
1599
1600      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasVertScroller];
1601      if (_hasVertScroller)
1602        [aCoder encodeObject: _vertScroller];
1603
1604      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHorizRuler];
1605      if (_hasHorizRuler)
1606        [aCoder encodeObject: _horizRuler];
1607
1608      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasVertRuler];
1609      if (_hasVertRuler)
1610        [aCoder encodeObject: _vertRuler];
1611
1612      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasHeaderView];
1613      if (_hasHeaderView)
1614        [aCoder encodeObject: _headerClipView];
1615
1616      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_hasCornerView];
1617
1618      /* We do not need to encode headerview, cornerview stuff */
1619    }
1620}
1621
1622- (id) initWithCoder: (NSCoder*)aDecoder
1623{
1624  self = [super initWithCoder: aDecoder];
1625  if (!self)
1626    return nil;
1627
1628  if ([aDecoder allowsKeyedCoding])
1629    {
1630      NSScroller *hScroller = [aDecoder decodeObjectForKey: @"NSHScroller"];
1631      NSScroller *vScroller = [aDecoder decodeObjectForKey: @"NSVScroller"];
1632      NSClipView *content = [aDecoder decodeObjectForKey: @"NSContentView"];
1633      NSView *docView = [content documentView];
1634      BOOL post_frame = [docView postsFrameChangedNotifications];
1635      BOOL post_bound = [docView postsBoundsChangedNotifications];
1636
1637      [docView setPostsFrameChangedNotifications: NO];
1638      [docView setPostsBoundsChangedNotifications: NO];
1639      _hLineScroll = 10;
1640      _hPageScroll = 10;
1641      _vLineScroll = 10;
1642      _vPageScroll = 10;
1643      _scrollsDynamically = YES;
1644      /* _autohidesScroller, _rulersVisible, _hasHorizRuler and _hasVertRuler
1645         implicitly set to NO */
1646
1647      if ([aDecoder containsValueForKey: @"NSsFlags"])
1648        {
1649          int flags = [aDecoder decodeInt32ForKey: @"NSsFlags"];
1650
1651          _borderType = flags & 3;
1652          _hasVertScroller = (flags & 16) == 16;
1653          _hasHorizScroller = (flags & 32) == 32;
1654          _autohidesScrollers = (flags & 512) == 512;
1655        }
1656
1657      /* FIXME: This should only happen when we load a Mac NIB file.
1658         And as far as I can tell tile is handling this correctly.
1659      if (vScroller != nil && _hasVertScroller && content != nil)
1660        {
1661          // Move the content view since it is not moved when we retile.
1662          NSRect frame = [content frame];
1663          float w = [vScroller frame].size.width;
1664
1665          //
1666          // Slide the content view over, since on Mac OS X the scroller is on the
1667          // right, the content view is not properly positioned since our scroller
1668          // is on the left.
1669          //
1670          frame.origin.x += w;
1671          [content setFrame: frame];
1672        }
1673      */
1674
1675      if (hScroller != nil && _hasHorizScroller)
1676        {
1677          [self setHorizontalScroller: hScroller];
1678	  [hScroller setHidden: NO];
1679        }
1680
1681      if (vScroller != nil && _hasVertScroller)
1682        {
1683          [self setVerticalScroller: vScroller];
1684	  [vScroller setHidden: NO];
1685        }
1686
1687      if ([aDecoder containsValueForKey: @"NSHeaderClipView"])
1688        {
1689          _hasHeaderView = YES;
1690          _headerClipView = [aDecoder decodeObjectForKey: @"NSHeaderClipView"];
1691        }
1692
1693      // set the document view into the content.
1694      [self setContentView: content];
1695      [self tile];
1696      // Reenable notification sending.
1697      [docView setPostsFrameChangedNotifications: post_frame];
1698      [docView setPostsBoundsChangedNotifications: post_bound];
1699    }
1700  else
1701    {
1702      int version = [aDecoder versionForClassName: @"NSScrollView"];
1703      NSDebugLLog(@"NSScrollView", @"NSScrollView: start decoding\n");
1704      _contentView = [aDecoder decodeObject];
1705      // Was int, we need to stay compatible
1706      [aDecoder decodeValueOfObjCType: @encode(NSInteger) at: &_borderType];
1707      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_scrollsDynamically];
1708      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_rulersVisible];
1709      [aDecoder decodeValueOfObjCType: @encode(float) at: &_hLineScroll];
1710      [aDecoder decodeValueOfObjCType: @encode(float) at: &_hPageScroll];
1711      [aDecoder decodeValueOfObjCType: @encode(float) at: &_vLineScroll];
1712      [aDecoder decodeValueOfObjCType: @encode(float) at: &_vPageScroll];
1713
1714      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHorizScroller];
1715      if (_hasHorizScroller)
1716        [aDecoder decodeValueOfObjCType: @encode(id) at: &_horizScroller];
1717
1718      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasVertScroller];
1719      if (_hasVertScroller)
1720        [aDecoder decodeValueOfObjCType: @encode(id) at: &_vertScroller];
1721
1722      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHorizRuler];
1723      if (_hasHorizRuler)
1724        [aDecoder decodeValueOfObjCType: @encode(id) at: &_horizRuler];
1725
1726      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasVertRuler];
1727      if (_hasVertRuler)
1728        [aDecoder decodeValueOfObjCType: @encode(id) at: &_vertRuler];
1729
1730      if (version == 2)
1731        {
1732          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasHeaderView];
1733          if (_hasHeaderView)
1734            _headerClipView = [aDecoder decodeObject];
1735
1736          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_hasCornerView];
1737        }
1738      else if (version == 1)
1739        {
1740          /* This recreates all the info about headerView, cornerView, etc */
1741          [self setDocumentView: [_contentView documentView]];
1742        }
1743      else
1744        {
1745          NSLog(@"unknown NSScrollView version (%d)", version);
1746          DESTROY(self);
1747          return nil;
1748        }
1749      [self tile];
1750
1751      NSDebugLLog(@"NSScrollView", @"NSScrollView: finish decoding\n");
1752    }
1753
1754  [[NSNotificationCenter defaultCenter]
1755    addObserver: self
1756    selector: @selector(_themeDidActivate:)
1757    name: GSThemeDidActivateNotification
1758    object: nil];
1759
1760  return self;
1761}
1762
1763- (BOOL)automaticallyAdjustsContentInsets
1764{
1765  // FIXME
1766  return NO;
1767}
1768
1769- (void)setAutomaticallyAdjustsContentInsets: (BOOL)adjusts
1770{
1771  static BOOL logged = NO;
1772  if (!logged)
1773    {
1774      NSLog(@"warning: stub no-op implementation of"
1775            "-[NSScrollView setAutomaticallyAdjustsContentInsets:]");
1776      logged = YES;
1777    }
1778}
1779
1780- (NSEdgeInsets)contentInsets
1781{
1782  // FIXME
1783  return NSEdgeInsetsZero;
1784}
1785- (void)setContentInsets: (NSEdgeInsets)edgeInsets
1786{
1787  static BOOL logged = NO;
1788  if (!logged)
1789    {
1790      NSLog(@"warning: stub no-op implementation of"
1791            "-[NSScrollView setContentInsets:]");
1792      logged = YES;
1793    }
1794}
1795
1796- (NSEdgeInsets)scrollerInsets
1797{
1798  // FIXME
1799  return NSEdgeInsetsZero;
1800}
1801- (void)setScrollerInsets: (NSEdgeInsets)insets
1802{
1803  static BOOL logged = NO;
1804  if (!logged)
1805    {
1806      NSLog(@"warning: stub no-op implementation of"
1807            "-[NSScrollView setScrollerInsets:]");
1808      logged = YES;
1809    }
1810}
1811
1812@end
1813
1814@implementation NSScrollView (GSPrivate)
1815
1816/* GNUstep private method */
1817
1818/* we update both of these at the same time during -tile
1819   so there is no reason in seperating them that'd just add
1820   message passing */
1821- (void) _synchronizeHeaderAndCornerView
1822{
1823  BOOL hadHeaderView = _hasHeaderView;
1824  BOOL hadCornerView = _hasCornerView;
1825  NSView *aView = nil;
1826
1827  _hasHeaderView = ([[self documentView]
1828                        respondsToSelector: @selector(headerView)]
1829                    && (aView=[(NSTableView *)[self documentView] headerView]));
1830  if (_hasHeaderView == YES)
1831    {
1832      if (hadHeaderView == NO)
1833        {
1834          _headerClipView = [NSClipView new];
1835          [self addSubview: _headerClipView];
1836          RELEASE(_headerClipView);
1837        }
1838      [_headerClipView setDocumentView: aView];
1839    }
1840  else if (hadHeaderView == YES)
1841    {
1842      [self removeSubview: _headerClipView];
1843    }
1844  if (_hasHeaderView == YES &&
1845      _hasVertScroller == YES)
1846    {
1847      aView = nil;
1848      _hasCornerView =
1849        ([[self documentView] respondsToSelector: @selector(cornerView)]
1850         && (aView=[(NSTableView *)[self documentView] cornerView]));
1851
1852      if (aView == _cornerView)
1853        return;
1854      if (_hasCornerView == YES)
1855        {
1856          if (hadCornerView == NO)
1857            {
1858               [self addSubview: aView];
1859            }
1860          else
1861            {
1862              [self replaceSubview: _cornerView with: aView];
1863            }
1864        }
1865      else if (hadCornerView == YES)
1866        {
1867          [self removeSubview: _cornerView];
1868        }
1869      _cornerView = aView;
1870    }
1871  else if (_cornerView != nil)
1872    {
1873      [self removeSubview: _cornerView];
1874      _cornerView = nil;
1875      _hasCornerView = NO;
1876    }
1877}
1878
1879- (void) _themeDidActivate: (NSNotification*)notification
1880{
1881  // N.B. Reload cached [NSScroller scrollerWidth] since the
1882  // new theme may have a different scroller width.
1883  //
1884  // Since scrollerWidth is a static, it will get overwritten
1885  // several times; doesn't matter though.
1886  scrollerWidth = [NSScroller scrollerWidth];
1887
1888  [self tile];
1889}
1890
1891@end
1892
1893