/* * CPTextContainer.j * AppKit * * Created by Emmanuel Maillard on 27/02/2010. * Copyright Emmanuel Maillard 2010. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @import @import "CPLayoutManager.j" @class CPTextView @class CPLayoutManager /* @global @group CPLineSweepDirection */ CPLineSweepLeft = 0; /* @global @group CPLineSweepDirection */ CPLineSweepRight = 1; /* @global @group CPLineSweepDirection */ CPLineSweepDown = 2; /* @global @group CPLineSweepDirection */ CPLineSweepUp = 3; /* @global @group CPLineMovementDirection */ CPLineDoesntMoves = 0; /* @global @group CPLineMovementDirection */ CPLineMovesLeft = 1; /* @global @group CPLineMovementDirection */ CPLineMovesRight = 2; /* @global @group CPLineMovementDirection */ CPLineMovesDown = 3; /* @global @group CPLineMovementDirection */ CPLineMovesUp = 4; /*! @ingroup appkit @class CPTextContainer */ @implementation CPTextContainer : CPObject { float _lineFragmentPadding @accessors(property=lineFragmentPadding); CGSize _size @accessors(property=containerSize); CPLayoutManager _layoutManager @accessors(property=layoutManager); CPTextView _textView @accessors(property=textView); BOOL _inResizing; BOOL _widthTracksTextView; BOOL _heightTracksTextView; } // MARK: - // MARK: Init methods - (id)initWithContainerSize:(CGSize)aSize { self = [super init]; if (self) { _size = aSize; [self _init]; } return self; } - (id)init { return [self initWithContainerSize:CPMakeSize(1e7, 1e7)]; } - (void)_init { _lineFragmentPadding = 0.0; _layoutManager = [[CPLayoutManager alloc] init]; [_layoutManager addTextContainer:self]; } // MARK: - // MARK: Setter methods - (void)setContainerSize:(CGSize)someSize { var oldSize = _size; _size = CGSizeMakeCopy(someSize); if (oldSize.width != _size.width) { _inResizing = YES; [_layoutManager invalidateLayoutForCharacterRange:CPMakeRange(0, [[_layoutManager textStorage] length]) isSoft:NO actualCharacterRange:NULL]; [_layoutManager _validateLayoutAndGlyphs]; [_textView sizeToFit]; // this is necessary to adopt the height of CPTextView in case of rewrapping _inResizing = NO; } } // Controls whether the receiver adjusts the width of its bounding rectangle when its text view is resized. - (BOOL)widthTracksTextView { return _widthTracksTextView; } - (void)setWidthTracksTextView:(BOOL)flag { if (_widthTracksTextView === flag) return; _widthTracksTextView = flag; [self _updateFrameObserver]; } // Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized. - (BOOL)heightTracksTextView { return _heightTracksTextView; } - (void)setHeightTracksTextView:(BOOL)flag { if (_heightTracksTextView === flag) return; _heightTracksTextView = flag; [self _updateFrameObserver]; } - (void)_updateFrameObserver { if (_textView) { [[CPNotificationCenter defaultCenter] removeObserver:self name:CPViewFrameDidChangeNotification object:_textView]; var flag = _widthTracksTextView || _heightTracksTextView; [_textView setPostsFrameChangedNotifications:flag]; if (flag) { [[CPNotificationCenter defaultCenter] addObserver:self selector:@selector(textViewFrameChanged:) name:CPViewFrameDidChangeNotification object:_textView]; } } } - (void)textViewFrameChanged:(CPNotification)aNotification { var newSize = CGSizeMake(_size.width, _size.height); if (_widthTracksTextView) newSize.width = [_textView frame].size.width; if (_heightTracksTextView) newSize.height = [_textView frame].size.height; [self setContainerSize:newSize]; } - (void)setTextView:(CPTextView)aTextView { if (_textView) { [[CPNotificationCenter defaultCenter] removeObserver:self name:CPViewFrameDidChangeNotification object:_textView]; [_textView setTextContainer:nil]; } _textView = aTextView; if (_textView) { [self _updateFrameObserver]; [_textView setTextContainer:self]; } [_layoutManager textContainerChangedTextView:self]; } - (BOOL)containsPoint:(CGPoint)aPoint { return CGRectContainsPoint(CGRectMake(0, 0, _size.width, _size.height), aPoint); } - (BOOL)isSimpleRectangularTextContainer { return YES; } - (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect sweepDirection:(CPLineSweepDirection)sweep movementDirection:(CPLineMovementDirection)movement remainingRect:(CGRectPointer)remainingRect { var resultRect = CGRectCreateCopy(proposedRect); if (sweep != CPLineSweepRight || movement != CPLineMovesDown) { CPLog.trace(@"FIXME: unsupported sweep (" + sweep + ") or movement (" + movement + ")"); return CGRectMakeZero(); } if (resultRect.origin.x + resultRect.size.width > _size.width) resultRect.size.width = _size.width - resultRect.origin.x; if (resultRect.size.width < 0) resultRect = CGRectMakeZero(); if (remainingRect) { remainingRect.origin.x = resultRect.origin.x + resultRect.size.width; remainingRect.origin.y = resultRect.origin.y; remainingRect.size.height = resultRect.size.height; remainingRect.size.width = _size.width - (resultRect.origin.x + resultRect.size.width); } return resultRect; } @end var CPTextContainerSizeKey = @"CPTextContainerSizeKey", CPTextContainerLayoutManagerKey = @"CPTextContainerLayoutManagerKey", CPTextContainerWidthTracksTextViewKey = @"CPTextContainerWidthTracksTextViewKey", CPTextContainerHeightTracksTextViewKey = @"CPTextContainerHeightTracksTextViewKey"; @implementation CPTextContainer (CPCoding) - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; if (self) { [self _init]; _size = [aCoder decodeSizeForKey:CPTextContainerSizeKey]; _layoutManager = [aCoder decodeObjectForKey:CPTextContainerLayoutManagerKey]; [_layoutManager addTextContainer:self]; _widthTracksTextView = [aCoder decodeBoolForKey:CPTextContainerWidthTracksTextViewKey]; _heightTracksTextView = [aCoder decodeBoolForKey:CPTextContainerHeightTracksTextViewKey]; } return self; } - (void)encodeWithCoder:(CPCoder)aCoder { [aCoder encodeSize:_size forKey:CPTextContainerSizeKey]; [aCoder encodeObject:_layoutManager forKey:CPTextContainerLayoutManagerKey]; [aCoder encodeBool:_widthTracksTextView forKey:CPTextContainerWidthTracksTextViewKey]; [aCoder encodeBool:_heightTracksTextView forKey:CPTextContainerHeightTracksTextViewKey]; } @end