1/* 2 NSRulerView.m 3 4 Copyright (C) 2002 Free Software Foundation, Inc. 5 6 Author: Diego Kreutz (kreutz@inf.ufsm.br) 7 Date: January 2002 8 9 This file is part of the GNUstep GUI Library. 10 11 This library is free software; you can redistribute it and/or 12 modify it under the terms of the GNU Lesser General Public 13 License as published by the Free Software Foundation; either 14 version 2 of the License, or (at your option) any later version. 15 16 This library is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 Lesser General Public License for more details. 20 21 You should have received a copy of the GNU Lesser General Public 22 License along with this library; see the file COPYING.LIB. 23 If not, see <http://www.gnu.org/licenses/> or write to the 24 Free Software Foundation, 51 Franklin Street, Fifth Floor, 25 Boston, MA 02110-1301, USA. 26*/ 27 28#include <math.h> 29#import "config.h" 30 31#import <Foundation/NSArray.h> 32#import <Foundation/NSDebug.h> 33#import <Foundation/NSException.h> 34#import <Foundation/NSValue.h> 35#import "AppKit/NSAttributedString.h" 36#import "AppKit/NSBezierPath.h" 37#import "AppKit/NSColor.h" 38#import "AppKit/NSEvent.h" 39#import "AppKit/NSFont.h" 40#import "AppKit/NSGraphics.h" 41#import "AppKit/NSRulerMarker.h" 42#import "AppKit/NSRulerView.h" 43#import "AppKit/NSScrollView.h" 44#import "AppKit/NSStringDrawing.h" 45#import "GSGuiPrivate.h" 46 47#define MIN_LABEL_DISTANCE 40 48#define MIN_MARK_DISTANCE 5 49 50#define MARK_SIZE 2 51#define MID_MARK_SIZE 4 52#define BIG_MARK_SIZE 6 53#define LABEL_MARK_SIZE 11 54 55#define RULER_THICKNESS 16 56#define MARKER_THICKNESS 15 57 58#define DRAW_HASH_MARK(path, size) \ 59 do { \ 60 if (_orientation == NSHorizontalRuler)\ 61 { \ 62 [path relativeLineToPoint: NSMakePoint(0, size)]; \ 63 } \ 64 else \ 65 { \ 66 [path relativeLineToPoint: NSMakePoint(size, 0)]; \ 67 } \ 68 } while (0) 69 70@interface GSRulerUnit : NSObject 71{ 72 NSString *_unitName; 73 NSString *_abbreviation; 74 CGFloat _conversionFactor; 75 NSArray *_stepUpCycle; 76 NSArray *_stepDownCycle; 77} 78 79+ (GSRulerUnit *) unitWithName: (NSString *)uName 80 abbreviation: (NSString *)abbrev 81 unitToPointsConversionFactor: (CGFloat)factor 82 stepUpCycle: (NSArray *)upCycle 83 stepDownCycle: (NSArray *)downCycle; 84- (id) initWithUnitName: (NSString *)uName 85 abbreviation: (NSString *)abbrev 86unitToPointsConversionFactor: (CGFloat)factor 87 stepUpCycle: (NSArray *)upCycle 88 stepDownCycle: (NSArray *)downCycle; 89- (NSString *) unitName; 90- (NSString *) abbreviation; 91- (CGFloat) conversionFactor; 92- (NSArray *) stepUpCycle; 93- (NSArray *) stepDownCycle; 94 95@end 96 97@implementation GSRulerUnit 98 99+ (GSRulerUnit *) unitWithName: (NSString *)uName 100 abbreviation: (NSString *)abbrev 101 unitToPointsConversionFactor: (CGFloat)factor 102 stepUpCycle: (NSArray *)upCycle 103 stepDownCycle: (NSArray *)downCycle 104{ 105 return [[[self alloc] initWithUnitName: uName 106 abbreviation: abbrev 107 unitToPointsConversionFactor: factor 108 stepUpCycle: upCycle 109 stepDownCycle: downCycle] autorelease]; 110} 111 112- (id) initWithUnitName: (NSString *)uName 113 abbreviation: (NSString *)abbrev 114unitToPointsConversionFactor: (CGFloat)factor 115 stepUpCycle: (NSArray *)upCycle 116 stepDownCycle: (NSArray *)downCycle 117{ 118 self = [super init]; 119 if (self != nil) 120 { 121 ASSIGN(_unitName, uName); 122 ASSIGN(_abbreviation, abbrev); 123 _conversionFactor = factor; 124 ASSIGN(_stepUpCycle, upCycle); 125 ASSIGN(_stepDownCycle, downCycle); 126 } 127 128 return self; 129} 130 131- (NSString *) unitName 132{ 133 return _unitName; 134} 135 136- (NSString *) abbreviation 137{ 138 return _abbreviation; 139} 140 141- (CGFloat) conversionFactor 142{ 143 return _conversionFactor; 144} 145 146- (NSArray *) stepUpCycle 147{ 148 return _stepUpCycle; 149} 150 151- (NSArray *) stepDownCycle 152{ 153 return _stepDownCycle; 154} 155 156- (void) dealloc 157{ 158 [_unitName release]; 159 [_abbreviation release]; 160 [_stepUpCycle release]; 161 [_stepDownCycle release]; 162 [super dealloc]; 163} 164 165@end 166 167 168@implementation NSRulerView 169 170/* 171 * Class variables 172 */ 173static NSMutableDictionary *units = nil; 174 175/* 176 * Class methods 177 */ 178+ (void) initialize 179{ 180 if (self == [NSRulerView class]) 181 { 182 NSArray *array05; 183 NSArray *array052; 184 NSArray *array2; 185 NSArray *array10; 186 187 [self setVersion: 1]; 188 189 units = [[NSMutableDictionary alloc] init]; 190 array05 = [NSArray arrayWithObject: [NSNumber numberWithFloat: 0.5]]; 191 array052 = [NSArray arrayWithObjects: [NSNumber numberWithFloat: 0.5], 192 [NSNumber numberWithFloat: 0.2], nil]; 193 array2 = [NSArray arrayWithObject: [NSNumber numberWithFloat: 2.0]]; 194 array10 = [NSArray arrayWithObject: [NSNumber numberWithFloat: 10.0]]; 195 [self registerUnitWithName: @"Inches" 196 abbreviation: @"in" 197 unitToPointsConversionFactor: 72.0 198 stepUpCycle: array2 199 stepDownCycle: array05]; 200 [self registerUnitWithName: @"Centimeters" 201 abbreviation: @"cm" 202 unitToPointsConversionFactor: 28.35 203 stepUpCycle: array2 204 stepDownCycle: array052]; 205 [self registerUnitWithName: @"Points" 206 abbreviation: @"pt" 207 unitToPointsConversionFactor: 1.0 208 stepUpCycle: array10 209 stepDownCycle: array05]; 210 [self registerUnitWithName: @"Picas" 211 abbreviation: @"pc" 212 unitToPointsConversionFactor: 12.0 213 stepUpCycle: array2 214 stepDownCycle: array05]; 215 } 216} 217 218- (id) initWithScrollView: (NSScrollView *)aScrollView 219 orientation: (NSRulerOrientation)o 220{ 221 self = [super initWithFrame: NSZeroRect]; 222 if (self != nil) 223 { 224 [self setScrollView: aScrollView]; 225 [self setOrientation: o]; 226 [self setMeasurementUnits: @"Points"]; /* FIXME: should be user's pref */ 227 [self setRuleThickness: RULER_THICKNESS]; 228 [self setOriginOffset: 0.0]; 229 [self setReservedThicknessForAccessoryView: 0.0]; 230 if (o == NSHorizontalRuler) 231 { 232 [self setReservedThicknessForMarkers: MARKER_THICKNESS]; 233 } 234 else 235 { 236 [self setReservedThicknessForMarkers: 0.0]; 237 } 238 [self invalidateHashMarks]; 239 } 240 return self; 241} 242 243+ (void) registerUnitWithName: (NSString *)uName 244 abbreviation: (NSString *)abbreviation 245 unitToPointsConversionFactor: (CGFloat)conversionFactor 246 stepUpCycle: (NSArray *)stepUpCycle 247 stepDownCycle: (NSArray *)stepDownCycle 248{ 249 GSRulerUnit *u = [GSRulerUnit unitWithName: uName 250 abbreviation: abbreviation 251 unitToPointsConversionFactor: conversionFactor 252 stepUpCycle: stepUpCycle 253 stepDownCycle: stepDownCycle]; 254 [units setObject: u forKey: uName]; 255} 256 257- (void) setMeasurementUnits: (NSString *)uName 258{ 259 GSRulerUnit *newUnit; 260 261 newUnit = [units objectForKey: uName]; 262 if (newUnit == nil) 263 { 264 [NSException raise: NSInvalidArgumentException 265 format: @"Unknown measurement unit %@", uName]; 266 } 267 ASSIGN(_unit, newUnit); 268 [self invalidateHashMarks]; 269} 270 271- (NSString *) measurementUnits 272{ 273 return [_unit unitName]; 274} 275 276- (void) setClientView: (NSView *)aView 277{ 278 if (_clientView == aView) 279 return; 280 281 if (_clientView != nil 282 && [_clientView respondsToSelector: 283 @selector(rulerView:willSetClientView:)]) 284 { 285 [_clientView rulerView: self willSetClientView: aView]; 286 } 287 /* NB: We should not RETAIN the clientView. */ 288 _clientView = aView; 289 [self setMarkers: nil]; 290 [self invalidateHashMarks]; 291} 292 293- (BOOL) isOpaque 294{ 295 return YES; 296} 297 298- (NSView *) clientView 299{ 300 return _clientView; 301} 302 303- (void) setAccessoryView: (NSView *)aView 304{ 305 /* FIXME/TODO: support for accessory views is not implemented */ 306 ASSIGN(_accessoryView, aView); 307 [self setNeedsDisplay: YES]; 308} 309 310- (NSView *) accessoryView 311{ 312 return _accessoryView; 313} 314 315- (void) setOriginOffset: (CGFloat)offset 316{ 317 _originOffset = offset; 318 [self invalidateHashMarks]; 319} 320 321- (CGFloat) originOffset 322{ 323 return _originOffset; 324} 325 326- (void) _verifyReservedThicknessForMarkers 327{ 328 NSEnumerator *en; 329 NSRulerMarker *marker; 330 CGFloat maxThickness = _reservedThicknessForMarkers; 331 332 if (_markers == nil) 333 { 334 return; 335 } 336 en = [_markers objectEnumerator]; 337 while ((marker = [en nextObject]) != nil) 338 { 339 CGFloat markerThickness; 340 markerThickness = [marker thicknessRequiredInRuler]; 341 if (markerThickness > maxThickness) 342 { 343 maxThickness = markerThickness; 344 } 345 } 346 if (maxThickness > _reservedThicknessForMarkers) 347 { 348 [self setReservedThicknessForMarkers: maxThickness]; 349 } 350} 351 352- (void) setMarkers: (NSArray *)newMarkers 353{ 354 if (newMarkers != nil && _clientView == nil) 355 { 356 [NSException raise: NSInternalInconsistencyException 357 format: @"Cannot set markers without a client view"]; 358 } 359 if (newMarkers != nil) 360 { 361 ASSIGN(_markers, [NSMutableArray arrayWithArray: newMarkers]); 362 [self _verifyReservedThicknessForMarkers]; 363 } 364 else 365 { 366 ASSIGN(_markers, nil); 367 } 368 [self setNeedsDisplay: YES]; 369} 370 371- (NSArray *) markers 372{ 373 return _markers; 374} 375 376- (void) addMarker: (NSRulerMarker *)aMarker 377{ 378 CGFloat markerThickness = [aMarker thicknessRequiredInRuler]; 379 380 if (_clientView == nil) 381 { 382 [NSException raise: NSInternalInconsistencyException 383 format: @"Cannot add a marker without a client view"]; 384 } 385 386 if (markerThickness > [self reservedThicknessForMarkers]) 387 { 388 [self setReservedThicknessForMarkers: markerThickness]; 389 } 390 if (_markers == nil) 391 { 392 _markers = [[NSMutableArray alloc] initWithObjects: aMarker, nil]; 393 } 394 else 395 { 396 [_markers addObject: aMarker]; 397 } 398 399 [self setNeedsDisplay: YES]; 400} 401 402- (void) removeMarker: (NSRulerMarker *)aMarker 403{ 404 [_markers removeObject: aMarker]; 405 [self setNeedsDisplay: YES]; 406} 407 408- (BOOL) trackMarker: (NSRulerMarker *)aMarker 409 withMouseEvent: (NSEvent *)theEvent 410{ 411 NSParameterAssert(aMarker != nil); 412 413 return [aMarker trackMouse: theEvent adding: YES]; 414} 415 416- (NSRect) _rulerRect 417{ 418 NSRect rect = [self bounds]; 419 if (_orientation == NSHorizontalRuler) 420 { 421 rect.size.height = _ruleThickness; 422 if ([self isFlipped]) 423 { 424 rect.origin.y = [self baselineLocation]; 425 } 426 else 427 { 428 rect.origin.y = [self baselineLocation] - _ruleThickness; 429 } 430 } 431 else 432 { 433 rect.size.width = _ruleThickness; 434 rect.origin.x = [self baselineLocation]; 435 } 436 return rect; 437} 438 439- (NSRect) _markersRect 440{ 441 NSRect rect = [self bounds]; 442 if (_orientation == NSHorizontalRuler) 443 { 444 rect.size.height = _reservedThicknessForMarkers; 445 if ([self isFlipped]) 446 { 447 rect.origin.y = _reservedThicknessForAccessoryView; 448 } 449 else 450 { 451 rect.origin.y = _ruleThickness; 452 } 453 } 454 else 455 { 456 rect.size.width = _reservedThicknessForMarkers; 457 rect.origin.x = _reservedThicknessForAccessoryView; 458 } 459 return rect; 460} 461 462- (NSRulerMarker *) _markerAtPoint: (NSPoint)point 463{ 464 NSEnumerator *markerEnum; 465 NSRulerMarker *marker; 466 BOOL flipped = [self isFlipped]; 467 468 /* test markers in reverse order so that markers drawn on top 469 are tested before those underneath */ 470 markerEnum = [_markers reverseObjectEnumerator]; 471 while ((marker = [markerEnum nextObject]) != nil) 472 { 473 if (NSMouseInRect (point, [marker imageRectInRuler], flipped)) 474 { 475 return marker; 476 } 477 } 478 return nil; 479} 480 481- (void) mouseDown: (NSEvent*)theEvent 482{ 483 NSPoint clickPoint; 484 BOOL flipped = [self isFlipped]; 485 486 clickPoint = [self convertPoint: [theEvent locationInWindow] 487 fromView: nil]; 488 if (NSMouseInRect (clickPoint, [self _rulerRect], flipped)) 489 { 490 if (_clientView != nil 491 && [_clientView respondsToSelector: 492 @selector(rulerView:handleMouseDown:)]) 493 { 494 [_clientView rulerView: self handleMouseDown: theEvent]; 495 } 496 } 497 else if (NSMouseInRect (clickPoint, [self _markersRect], flipped)) 498 { 499 NSRulerMarker *clickedMarker; 500 501 clickedMarker = [self _markerAtPoint: clickPoint]; 502 if (clickedMarker != nil) 503 { 504 [clickedMarker trackMouse: theEvent adding: NO]; 505 } 506 } 507} 508 509- (void) moveRulerlineFromLocation: (CGFloat)oldLoc 510 toLocation: (CGFloat)newLoc 511{ 512 /* FIXME/TODO: not implemented */ 513} 514 515- (void)drawRect: (NSRect)aRect 516{ 517 [[NSColor controlColor] set]; 518 NSRectFill(aRect); 519 [self drawHashMarksAndLabelsInRect: aRect]; 520 [self drawMarkersInRect: aRect]; 521} 522 523- (float) _stepForIndex: (int)index 524{ 525 int newindex; 526 NSArray *stepCycle; 527 528 if (index > 0) 529 { 530 stepCycle = [_unit stepUpCycle]; 531 newindex = (index - 1) % [stepCycle count]; 532 return [[stepCycle objectAtIndex: newindex] floatValue]; 533 } 534 else 535 { 536 stepCycle = [_unit stepDownCycle]; 537 newindex = (-index) % [stepCycle count]; 538 return 1 / [[stepCycle objectAtIndex: newindex] floatValue]; 539 } 540} 541 542- (void) _verifyCachedValues 543{ 544 if (! _cacheIsValid) 545 { 546 NSSize unitSize; 547 CGFloat cf; 548 int convIndex; 549 550 /* calculate the size one unit in document view has in the ruler */ 551 cf = [_unit conversionFactor]; 552 unitSize = [self convertSize: NSMakeSize(cf, cf) 553 fromView: [_scrollView documentView]]; 554 555 if (_orientation == NSHorizontalRuler) 556 { 557 _unitToRuler = unitSize.width; 558 } 559 else 560 { 561 _unitToRuler = unitSize.height; 562 } 563 564 /* Calculate distance between marks. */ 565 /* It must not be less than MIN_MARK_DISTANCE in ruler units 566 * and must obey the current unit's step cycles. */ 567 _markDistance = _unitToRuler; 568 convIndex = 0; 569 /* make sure it's smaller than MIN_MARK_DISTANCE */ 570 while ((_markDistance) > MIN_MARK_DISTANCE) 571 { 572 _markDistance /= [self _stepForIndex: convIndex]; 573 convIndex--; 574 } 575 /* find the first size that's not < MIN_MARK_DISTANCE */ 576 while ((_markDistance) < MIN_MARK_DISTANCE) 577 { 578 convIndex++; 579 _markDistance *= [self _stepForIndex: convIndex]; 580 } 581 582 /* calculate number of small marks in each bigger mark */ 583 _marksToMidMark = GSRoundTowardsInfinity([self _stepForIndex: convIndex + 1]); 584 _marksToBigMark = _marksToMidMark 585 * GSRoundTowardsInfinity([self _stepForIndex: convIndex + 2]); 586 587 /* Calculate distance between labels. 588 It must not be less than MIN_LABEL_DISTANCE. */ 589 _labelDistance = _markDistance; 590 while (_labelDistance < MIN_LABEL_DISTANCE) 591 { 592 convIndex++; 593 _labelDistance *= [self _stepForIndex: convIndex]; 594 } 595 596 /* number of small marks between two labels */ 597 _marksToLabel = GSRoundTowardsInfinity(_labelDistance / _markDistance); 598 599 /* format of labels */ 600 if (_labelDistance / _unitToRuler >= 1) 601 { 602 ASSIGN(_labelFormat, @"%1.f"); 603 } 604 else 605 { 606 /* smallest integral value not less than log10(1/labelDistInUnits) */ 607 int log = ceil(log10(1 / (_labelDistance / _unitToRuler))); 608 NSString *string = [NSString stringWithFormat: @"%%.%df", (int)log]; 609 ASSIGN(_labelFormat, string); 610 } 611 612 _cacheIsValid = YES; 613 } 614} 615 616- (void) drawHashMarksAndLabelsInRect: (NSRect)aRect 617{ 618 NSView *docView; 619 NSRect docBounds; 620 NSRect baselineRect; 621 NSRect visibleBaselineRect; 622 CGFloat firstBaselineLocation; 623 CGFloat firstVisibleLocation; 624 CGFloat lastVisibleLocation; 625 int firstVisibleMark; 626 int lastVisibleMark; 627 int mark; 628 int firstVisibleLabel; 629 int lastVisibleLabel; 630 int label; 631 CGFloat baselineLocation = [self baselineLocation]; 632 NSPoint zeroPoint; 633 CGFloat zeroLocation; 634 NSBezierPath *path; 635 NSFont *font = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]]; 636 NSDictionary *attr = [[NSDictionary alloc] 637 initWithObjectsAndKeys: 638 font, NSFontAttributeName, 639 [NSColor blackColor], NSForegroundColorAttributeName, 640 nil]; 641 642 docView = [_scrollView documentView]; 643 docBounds = [docView bounds]; 644 645 /* Calculate the location of 'zero' hash mark */ 646 // _originOffset is an offset from document bounds origin, in doc coords 647 zeroPoint.x = docBounds.origin.x + _originOffset; 648 zeroPoint.y = docBounds.origin.y + _originOffset; 649 zeroPoint = [self convertPoint: zeroPoint fromView: docView]; 650 if (_orientation == NSHorizontalRuler) 651 { 652 zeroLocation = zeroPoint.x; 653 } 654 else 655 { 656 zeroLocation = zeroPoint.y; 657 } 658 659 [self _verifyCachedValues]; 660 661 /* Calculate the base line (corresponds to the document bounds) */ 662 baselineRect = [self convertRect: docBounds fromView: docView]; 663 if (_orientation == NSHorizontalRuler) 664 { 665 baselineRect.origin.y = baselineLocation; 666 baselineRect.size.height = 1; 667 firstBaselineLocation = NSMinX(baselineRect); 668 visibleBaselineRect = NSIntersectionRect(baselineRect, aRect); 669 firstVisibleLocation = NSMinX(visibleBaselineRect); 670 lastVisibleLocation = NSMaxX(visibleBaselineRect); 671 } 672 else 673 { 674 baselineRect.origin.x = baselineLocation; 675 baselineRect.size.width = 1; 676 firstBaselineLocation = NSMinY(baselineRect); 677 visibleBaselineRect = NSIntersectionRect(baselineRect, aRect); 678 firstVisibleLocation = NSMinY(visibleBaselineRect); 679 lastVisibleLocation = NSMaxY(visibleBaselineRect); 680 } 681 682 /* draw the base line */ 683 [[NSColor blackColor] set]; 684 NSRectFill(visibleBaselineRect); 685 686 /* draw hash marks */ 687 firstVisibleMark = ceil((firstVisibleLocation - zeroLocation) 688 / _markDistance); 689 lastVisibleMark = floor((lastVisibleLocation - zeroLocation) 690 / _markDistance); 691 path = [NSBezierPath new]; 692 693 for (mark = firstVisibleMark; mark <= lastVisibleMark; mark++) 694 { 695 CGFloat markLocation; 696 697 markLocation = zeroLocation + mark * _markDistance; 698 if (_orientation == NSHorizontalRuler) 699 { 700 [path moveToPoint: NSMakePoint(markLocation, baselineLocation)]; 701 } 702 else 703 { 704 [path moveToPoint: NSMakePoint(baselineLocation, markLocation)]; 705 } 706 707 if ((mark % _marksToLabel) == 0) 708 { 709 DRAW_HASH_MARK(path, LABEL_MARK_SIZE); 710 } 711 else if ((mark % _marksToBigMark) == 0) 712 { 713 DRAW_HASH_MARK(path, BIG_MARK_SIZE); 714 } 715 else if ((mark % _marksToMidMark) == 0) 716 { 717 DRAW_HASH_MARK(path, MID_MARK_SIZE); 718 } 719 else 720 { 721 DRAW_HASH_MARK(path, MARK_SIZE); 722 } 723 } 724 [path stroke]; 725 RELEASE(path); 726 727 /* draw labels */ 728 /* FIXME: shouldn't be using NSCell to draw labels? */ 729 firstVisibleLabel = floor((firstVisibleLocation - zeroLocation) 730 / (_marksToLabel * _markDistance)); 731 lastVisibleLabel = floor((lastVisibleLocation - zeroLocation) 732 / (_marksToLabel * _markDistance)); 733 /* firstVisibleLabel can be to the left of the visible ruler area. 734 This is OK because just part of the label can be visible to the left 735 when scrolling. However, it should not be drawn if outside of the 736 baseline. */ 737 if (zeroLocation + firstVisibleLabel * _marksToLabel * _markDistance 738 < firstBaselineLocation) 739 { 740 firstVisibleLabel++; 741 } 742 743 for (label = firstVisibleLabel; label <= lastVisibleLabel; label++) 744 { 745 CGFloat labelLocation = zeroLocation + label * _marksToLabel * _markDistance; 746 // This has to be a float or we need to change the label format 747 float labelValue = (labelLocation - zeroLocation) / _unitToRuler; 748 NSString *labelString = [NSString stringWithFormat: _labelFormat, labelValue]; 749 NSSize size = [labelString sizeWithAttributes: attr]; 750 NSPoint labelPosition; 751 752 if (_orientation == NSHorizontalRuler) 753 { 754 labelPosition.x = labelLocation + 1; 755 labelPosition.y = baselineLocation + LABEL_MARK_SIZE + 4 - size.height; 756 } 757 else 758 { 759 labelPosition.x = baselineLocation + _ruleThickness - size.width; 760 labelPosition.y = labelLocation + 1; 761 } 762 [labelString drawAtPoint: labelPosition withAttributes: attr]; 763 } 764 765 RELEASE(attr); 766} 767 768- (void) drawMarkersInRect: (NSRect)aRect 769{ 770 NSRulerMarker *marker; 771 NSEnumerator *en; 772 773 en = [_markers objectEnumerator]; 774 while ((marker = [en nextObject]) != nil) 775 { 776 [marker drawRect: aRect]; 777 } 778} 779 780- (void) invalidateHashMarks 781{ 782 _cacheIsValid = NO; 783 [self setNeedsDisplay:YES]; 784} 785 786- (void) setScrollView: (NSScrollView *)scrollView 787{ 788 /* We do NOT retain the scrollView; the scrollView is retaining us. */ 789 _scrollView = scrollView; 790} 791 792- (NSScrollView *) scrollView 793{ 794 return _scrollView; 795} 796 797- (void) setOrientation: (NSRulerOrientation)o 798{ 799 _orientation = o; 800} 801 802- (NSRulerOrientation)orientation 803{ 804 return _orientation; 805} 806 807- (void) setReservedThicknessForAccessoryView: (CGFloat)thickness 808{ 809 _reservedThicknessForAccessoryView = thickness; 810 [_scrollView tile]; 811} 812 813- (CGFloat) reservedThicknessForAccessoryView 814{ 815 return _reservedThicknessForAccessoryView; 816} 817 818- (void) setReservedThicknessForMarkers: (CGFloat)thickness 819{ 820 _reservedThicknessForMarkers = thickness; 821 [_scrollView tile]; 822} 823 824- (CGFloat) reservedThicknessForMarkers 825{ 826 return _reservedThicknessForMarkers; 827} 828 829- (void) setRuleThickness: (CGFloat)thickness 830{ 831 _ruleThickness = thickness; 832 [_scrollView tile]; 833} 834 835- (CGFloat) ruleThickness 836{ 837 return _ruleThickness; 838} 839 840- (CGFloat) requiredThickness 841{ 842 return [self ruleThickness] 843 + [self reservedThicknessForAccessoryView] 844 + [self reservedThicknessForMarkers]; 845} 846 847- (CGFloat) baselineLocation 848{ 849 return [self reservedThicknessForAccessoryView] 850 + [self reservedThicknessForMarkers]; 851} 852 853- (BOOL) isFlipped 854{ 855 if (_orientation == NSVerticalRuler) 856 { 857 return [[_scrollView documentView] isFlipped]; 858 } 859 return YES; 860} 861 862- (void) encodeWithCoder: (NSCoder *)encoder 863{ 864 [super encodeWithCoder: encoder]; 865 /* FIXME/TODO: not implemented */ 866 return; 867} 868 869- (id) initWithCoder: (NSCoder *)decoder 870{ 871 self = [super initWithCoder: decoder]; 872 if (self == nil) 873 return nil; 874 875 /* FIXME/TODO: not implemented */ 876 return self; 877} 878 879- (void) dealloc 880{ 881 RELEASE(_unit); 882 RELEASE(_accessoryView); 883 RELEASE(_markers); 884 RELEASE(_labelFormat); 885 [super dealloc]; 886} 887 888@end 889 890