// // NSTextTable.m // mySTEP // // Created by Dr. H. Nikolaus Schaller on Sat Jan 07 2006. // Copyright (c) 2005 DSITRI. // // This file is part of the mySTEP Library and is provided // under the terms of the GNU Library General Public License. // #import "AppKit/AppKit.h" @implementation NSTextBlock - (void) dealloc; { int i; [_backgroundColor release]; for(i=0; i<=NSMaxYEdge; i++) [_borderColorForEdge[i] release]; [super dealloc]; } /* this function is optimized so that the compiler can highly optimize if layer and edge are constants */ static inline float getWidth(NSTextBlock *self, NSTextBlockLayer layer, NSRectEdge edge, NSSize size) { if(self->_widthType[layer-NSTextBlockPadding][edge] == NSTextBlockPercentageValueType) { // relative to size if(edge == NSMinXEdge || edge == NSMaxXEdge) return self->_width[layer-NSTextBlockPadding][edge]*size.width; else return self->_width[layer-NSTextBlockPadding][edge]*size.height; } return self->_width[layer-NSTextBlockPadding][edge]; // absolute } - (NSColor *) backgroundColor; { return _backgroundColor; } - (NSColor *) borderColorForEdge:(NSRectEdge) edge; { NSAssert(edge <= NSMaxYEdge, @"invalid edge"); return _borderColorForEdge[edge]; } - (NSRect) boundsRectForContentRect:(NSRect) cont /* content rectangle returned by rectForLayoutAtPoint: */ inRect:(NSRect) rect /* text container rectangle */ textContainer:(NSTextContainer *) container characterRange:(NSRange) range; { // called once per layout action (after rectForLayoutAtPoint:) by -[NSTypeSetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] float d; // delta d = getWidth(self, NSTextBlockPadding, NSMinXEdge, rect.size) // defines space between border and content + getWidth(self, NSTextBlockBorder, NSMinXEdge, rect.size) // defines border width + getWidth(self, NSTextBlockMargin, NSMinXEdge, rect.size); // inset where border starts cont.origin.x -= d; cont.size.width += d + getWidth(self, NSTextBlockPadding, NSMaxXEdge, rect.size) + getWidth(self, NSTextBlockBorder, NSMaxXEdge, rect.size) + getWidth(self, NSTextBlockMargin, NSMaxXEdge, rect.size); d = getWidth(self, NSTextBlockPadding, NSMinYEdge, rect.size) // defines space between border and content + getWidth(self, NSTextBlockBorder, NSMinYEdge, rect.size) // defines border width + getWidth(self, NSTextBlockMargin, NSMinYEdge, rect.size); // inset where border starts cont.origin.y -= d; cont.size.height += d + getWidth(self, NSTextBlockPadding, NSMaxYEdge, rect.size) + getWidth(self, NSTextBlockBorder, NSMaxYEdge, rect.size) + getWidth(self, NSTextBlockMargin, NSMaxYEdge, rect.size); return cont; } - (float) contentWidth; { return _contentWidth; } - (NSTextBlockValueType) contentWidthValueType; { return _contentWidthValueType; } - (void) drawBackgroundWithFrame:(NSRect) rect inView:(NSView *) view characterRange:(NSRange) range layoutManager:(NSLayoutManager *) lm; { // called from -[NSLayoutManager drawBackgroundForGlyphRange:atPoint:] NSBezierPath *p; NSColor *color; float width; NSRect outer, inner; // outer box and inner box of border outer=rect; width=getWidth(self, NSTextBlockPadding, NSMinXEdge, rect.size); outer.origin.x+=width; outer.size.width-=width + getWidth(self, NSTextBlockPadding, NSMaxXEdge, rect.size); width=getWidth(self, NSTextBlockPadding, NSMinYEdge, rect.size); outer.origin.y+=width; outer.size.height-=width + getWidth(self, NSTextBlockPadding, NSMaxYEdge, rect.size); inner=outer; width=getWidth(self, NSTextBlockBorder, NSMinXEdge, rect.size); inner.origin.x+=width; inner.size.width-=width + getWidth(self, NSTextBlockBorder, NSMaxXEdge, rect.size); width=getWidth(self, NSTextBlockBorder, NSMinYEdge, rect.size); inner.origin.y+=width; inner.size.height-=width + getWidth(self, NSTextBlockBorder, NSMaxYEdge, rect.size); color=[self borderColorForEdge:NSMinXEdge]; if(color) { // left p=[NSBezierPath new]; [p moveToPoint:(NSPoint) { NSMinX(outer), NSMinY(outer) }]; [p lineToPoint:(NSPoint) { NSMinX(inner), NSMinY(inner) }]; [p lineToPoint:(NSPoint) { NSMinX(inner), NSMaxY(inner) }]; [p lineToPoint:(NSPoint) { NSMinX(outer), NSMaxY(outer) }]; [p closePath]; [color set]; [p fill]; // fill left border trapezoid [p release]; } [[self borderColorForEdge:NSMaxXEdge] set]; // right color=[self borderColorForEdge:NSMaxXEdge]; width=getWidth(self, NSTextBlockBorder, NSMaxXEdge, rect.size); // border width if(color && width > 0) { // right p=[NSBezierPath new]; [p moveToPoint:(NSPoint) { NSMaxX(outer), NSMinY(outer) }]; [p lineToPoint:(NSPoint) { NSMaxX(inner), NSMinY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(inner), NSMaxY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(outer), NSMaxY(outer) }]; [p closePath]; [color set]; [p fill]; // fill right border trapezoid [p release]; } color=[self borderColorForEdge:NSMinYEdge]; width=getWidth(self, NSTextBlockBorder, NSMinYEdge, rect.size); // border width if(color && width > 0) { // bottom p=[NSBezierPath new]; [p moveToPoint:(NSPoint) { NSMinX(outer), NSMinY(outer) }]; [p lineToPoint:(NSPoint) { NSMinX(inner), NSMinY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(inner), NSMinY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(outer), NSMinY(outer) }]; [p closePath]; [color set]; [p fill]; // fill left border trapezoid [p release]; } [[self borderColorForEdge:NSMaxYEdge] set]; // right color=[self borderColorForEdge:NSMaxYEdge]; width=getWidth(self, NSTextBlockBorder, NSMaxYEdge, rect.size); // border width if(color && width > 0) { // top p=[NSBezierPath new]; [p moveToPoint:(NSPoint) { NSMinX(outer), NSMaxY(outer) }]; [p lineToPoint:(NSPoint) { NSMinX(inner), NSMaxY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(inner), NSMaxY(inner) }]; [p lineToPoint:(NSPoint) { NSMaxX(outer), NSMaxY(outer) }]; [p closePath]; [color set]; [p fill]; // fill right border trapezoid [p release]; } [[self backgroundColor] set]; NSRectFill(inner); // fill background behind text } - (NSRect) rectForLayoutAtPoint:(NSPoint) point /* relative position within text container rect */ inRect:(NSRect) rect /* text container rect */ textContainer:(NSTextContainer *) cont characterRange:(NSRange) range; { // called by -[NSTypeSetter getLineFragmentRect:(NSRectPointer)lineFragmentRect usedRect:(NSRectPointer)lineFragmentUsedRect remainingRect:(NSRectPointer)remainingRect forStartingGlyphAtIndex:(NSUInteger)startingGlyphIndex proposedRect:(NSRect)proposedRect lineSpacing:(CGFloat)lineSpacing paragraphSpacingBefore:(CGFloat)paragraphSpacingBefore paragraphSpacingAfter:(CGFloat)paragraphSpacingAfter] // we probably should determine recursively how much space we need // constrain rect.width by _contentWidth; // handle alignment if rect is larger than what we need // take the character range for the contents // rect.size is a little smaller than [cont containerSize] - maybe 2x rect.origin.y (was 5.0 in one example) NSLayoutManager *lm=[cont layoutManager]; NSTextStorage *ts=[lm textStorage]; NSAttributedString *contents = [ts attributedSubstringFromRange:range]; NSRect r; float li, ri, ti, bi; // inset from bounds to content li = getWidth(self, NSTextBlockPadding, NSMinXEdge, rect.size) // space between cells + getWidth(self, NSTextBlockBorder, NSMinXEdge, rect.size) // border width + getWidth(self, NSTextBlockMargin, NSMinXEdge, rect.size); // space (inset) between border and text ri = getWidth(self, NSTextBlockPadding, NSMaxXEdge, rect.size) + getWidth(self, NSTextBlockBorder, NSMaxXEdge, rect.size) + getWidth(self, NSTextBlockMargin, NSMaxXEdge, rect.size); bi = getWidth(self, NSTextBlockPadding, NSMinYEdge, rect.size) + getWidth(self, NSTextBlockBorder, NSMinYEdge, rect.size) + getWidth(self, NSTextBlockMargin, NSMinYEdge, rect.size); ti = getWidth(self, NSTextBlockPadding, NSMaxYEdge, rect.size) + getWidth(self, NSTextBlockBorder, NSMaxYEdge, rect.size) + getWidth(self, NSTextBlockMargin, NSMaxYEdge, rect.size); // FIXME: do we do column breakdown and alignment here? // we should ask the layout manager!; // or how else does it depend on contents dimensions? // we may simply split the content rect by columns into cells r=[contents boundingRectWithSize:(NSSize) { rect.size.width-li-ri, rect.size.height-ti-bi } options:0]; /// TEST: r.size.width=20; /// FIXME: contentWidth is 0.0 !?! float wmax=_contentWidthValueType == NSTextBlockPercentageValueType?_contentWidth*rect.size.width:_contentWidth; // if(r.size.width > wmax) // r.size.width=wmax; // limit to content width r.origin.x+=rect.origin.x+point.x+li; r.origin.y+=rect.origin.y+point.y+ti; return r; } - (void) setBackgroundColor:(NSColor *) color; { ASSIGN(_backgroundColor, color); } - (void) setBorderColor:(NSColor *) color; { int i; for(i=0; i<=NSMaxYEdge; i++) ASSIGN(_borderColorForEdge[i], color); } - (void) setBorderColor:(NSColor *) color forEdge:(NSRectEdge) edge; { NSAssert(edge <= NSMaxYEdge, @"invalid edge"); ASSIGN(_borderColorForEdge[edge], color); } - (void) setContentWidth:(float) val type:(NSTextBlockValueType) type; { _contentWidth=val; _contentWidthValueType=type; } - (void) setValue:(float) val type:(NSTextBlockValueType) type forDimension:(NSTextBlockDimension) dimension; { NSAssert(dimension <= NSTextBlockMaximumHeight, @"invalid dimension"); _value[dimension]=val; _valueType[dimension]=type; } - (void) setVerticalAlignment:(NSTextBlockVerticalAlignment) alignment; { _verticalAlignment=alignment; } - (void) setWidth:(float) val type:(NSTextBlockValueType) type forLayer:(NSTextBlockLayer) layer; { // set width for all edges int i; // FIXME: don't use NSAssert but raise NSException! NSAssert(layer >= NSTextBlockPadding && layer <= NSTextBlockMargin, @"invalid layer"); for(i=0; i<=NSMaxYEdge; i++) { _width[layer-NSTextBlockPadding][i]=val; _widthType[layer-NSTextBlockPadding][i]=type; } } - (void) setWidth:(float) val type:(NSTextBlockValueType) type forLayer:(NSTextBlockLayer) layer edge:(NSRectEdge) edge; { NSAssert(layer >= NSTextBlockPadding && layer <= NSTextBlockMargin, @"invalid layer"); NSAssert(edge <= NSMaxYEdge, @"invalid edge"); _width[layer-NSTextBlockPadding][edge]=val; _widthType[layer-NSTextBlockPadding][edge]=type; } - (float) valueForDimension:(NSTextBlockDimension) dimension; { NSAssert(dimension <= NSTextBlockMaximumHeight, @"invalid dimension"); return _value[dimension]; } - (NSTextBlockValueType) valueTypeForDimension:(NSTextBlockDimension) dimension; { NSAssert(dimension <= NSTextBlockMaximumHeight, @"invalid dimension"); return _valueType[dimension]; } - (NSTextBlockVerticalAlignment) verticalAlignment; { return _verticalAlignment; } - (float) widthForLayer:(NSTextBlockLayer) layer edge:(NSRectEdge) edge; { NSAssert(layer >= NSTextBlockPadding && layer <= NSTextBlockMargin, @"invalid layer"); NSAssert(edge <= NSMaxYEdge, @"invalid edge"); return _width[layer-NSTextBlockPadding][edge]; } - (NSTextBlockValueType) widthValueTypeForLayer:(NSTextBlockLayer) layer edge:(NSRectEdge) edge; { NSAssert(layer >= NSTextBlockPadding && layer <= NSTextBlockMargin, @"invalid layer"); NSAssert(edge <= NSMaxYEdge, @"invalid edge"); return _widthType[layer-NSTextBlockPadding][edge]; } - (void) encodeWithCoder:(NSCoder *)aCoder // NSCoding protocol { // [super encodeWithCoder:aCoder]; if([aCoder allowsKeyedCoding]) { } else { } NIMP; } - (id) initWithCoder:(NSCoder *)aDecoder { if([aDecoder allowsKeyedCoding]) { } else { } return NIMP; } - (id) copyWithZone:(NSZone *) z { return NIMP; } @end @implementation NSTextTable - (NSRect) boundsRectForBlock:(NSTextTableBlock *) block contentRect:(NSRect) content inRect:(NSRect)rect textContainer:(NSTextContainer *) container characterRange:(NSRange) range; { NIMP; return NSZeroRect; } - (BOOL) collapsesBorders; { return _collapsesBorders; } - (void) drawBackgroundForBlock:(NSTextTableBlock *) block withFrame:(NSRect) frame inView:(NSView *) controlView characterRange:(NSRange) range layoutManager:(NSLayoutManager *) manager; { NIMP } - (BOOL) hidesEmptyCells; { return _hidesEmptyCells; } - (NSTextTableLayoutAlgorithm) layoutAlgorithm; { return _layoutAlgorithm; } - (unsigned) numberOfColumns; { return _numberOfColumns; } - (NSRect) rectForBlock:(NSTextTableBlock *) block layoutAtPoint:(NSPoint) start inRect:(NSRect) rect textContainer:(NSTextContainer *) container characterRange:(NSRange) range; { NIMP; return NSZeroRect; } - (void) setCollapsesBorders:(BOOL) flag; { _collapsesBorders=flag; } - (void) setHidesEmptyCells:(BOOL) flag; { _hidesEmptyCells=flag; } - (void) setLayoutAlgorithm:(NSTextTableLayoutAlgorithm) algorithm; { _layoutAlgorithm=algorithm; } - (void) setNumberOfColumns:(unsigned) n; { _numberOfColumns=n; } @end @implementation NSTextTableBlock - (void) dealloc; { [_table release]; [super dealloc]; } - (id) initWithTable:(NSTextTable *) table startingRow:(int) row rowSpan:(int) rspan startingColumn:(int) col columnSpan:(int) cspan; { if((self=[super init])) { _table=[table retain]; _row=row; _rspan=rspan; _col=col; _cspan=cspan; } return self; } /* FIXME: we may have to override - (NSRect) rectForLayoutAtPoint:(NSPoint) point inRect:(NSRect) rect // text container rect textContainer:(NSTextContainer *) cont characterRange:(NSRange) range; and consult [table numberOfColumns] to determine the right position of the cells */ - (int) columnSpan; { return _cspan; } - (int) rowSpan; { return _rspan; } - (int) startingColumn; { return _col; } - (int) startingRow; { return _row; } - (NSTextTable *) table; { return _table; } @end