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