'From Cuis 1.0 of 21 August 2009 [latest update: #268] on 26 August 2009 at 10:03:10 am'!
!classDefinition: #TextStyle category: #'Graphics-Text'!
Object subclass: #TextStyle
	instanceVariableNames: 'name font alignment tabsArray color restIndent firstIndent rightIndent '
	classVariableNames: 'AvailableTextStyles DefaultTextStyle '
	poolDictionaries: ''
	category: 'Graphics-Text'!

!CharacterBlockScanner methodsFor: 'stop conditions' stamp: 'jmv 8/26/2009 09:54'!
crossedX
	"Text display has wrapping. The scanner just found a character past the x 
	location of the cursor. We know that the cursor is pointing at a character 
	or before one."

	| currentX |
	characterIndex == nil ifFalse: [
		"If the last character of the last line is a space,
		and it crosses the right margin, then locating
		the character block after it is impossible without this hack."
		characterIndex > text size ifTrue: [
			lastIndex _ characterIndex.
			characterPoint _ leftMargin @ (destY + line lineHeight).
			^true]].
	characterPoint x <= (destX + (lastCharacterExtent x // 2))
		ifTrue:	[lastCharacter _ (text at: lastIndex).
				characterPoint _ destX @ destY.
				^true].
	lastIndex >= line last 
		ifTrue:	[lastCharacter _ (text at: line last).
				characterPoint _ destX @ destY.
				^true].

	"Pointing past middle of a character, return the next character."
	lastIndex _ lastIndex + 1.
	lastCharacter _ text at: lastIndex.
	currentX _ destX + lastCharacterExtent x + kern.
	self lastCharacterExtentSetX: (font widthOf: lastCharacter).
	characterPoint _ currentX @ destY.
	lastCharacter = Character space ifFalse: [^ true].

	"Yukky if next character is space or tab."
	alignment = CharacterScanner justifiedCode ifTrue:
		[self lastCharacterExtentSetX:
			(lastCharacterExtent x + 	(line justifiedPadFor: (spaceCount + 1))).
		^ true].

	^ true! !


!CompositionScanner methodsFor: 'scanning' stamp: 'jmv 8/26/2009 09:40'!
composeFrom: startIndex inRectangle: lineRectangle firstLine: firstLine leftSide: leftSide rightSide: rightSide

	"Answer an instance of TextLineInterval that represents the next line in the paragraph."
	| runLength done stopCondition |
	
	"Set up margins"
	leftMargin _ lineRectangle left.
	leftSide ifTrue: [
		leftMargin _ leftMargin +
			(firstLine ifTrue: [actualTextStyle firstIndent]
					ifFalse: [actualTextStyle restIndent])].
	destX _ spaceX _ leftMargin.
	rightMargin _ lineRectangle right.
	rightSide ifTrue: [
		rightMargin _ rightMargin - actualTextStyle rightIndent].
	
	lastIndex _ startIndex.	"scanning sets last index"
	destY _ lineRectangle top.
	lineHeight _ baseline _ 0.  "Will be increased by setFont"
	self setStopConditions.	"also sets font"
	runLength _ text runLengthFor: startIndex.
	runStopIndex _ (lastIndex _ startIndex) + (runLength - 1).
	line _ (TextLine start: lastIndex stop: 0 internalSpaces: 0 paddingWidth: 0)
				rectangle: lineRectangle.
	spaceCount _ 0.
	leftMargin _ destX.
	line leftMargin: leftMargin.

	done _ false.
	[done]
		whileFalse: 
			[stopCondition _ self scanCharactersFrom: lastIndex to: runStopIndex
				in: text string rightX: rightMargin stopConditions: stopConditions
				kern: kern.
			"See setStopConditions for stopping conditions for composing."
			(self perform: stopCondition)
				ifTrue: [^ line lineHeight: lineHeight baseline: baseline]]! !

!CompositionScanner methodsFor: 'stop conditions' stamp: 'jmv 8/26/2009 09:55'!
crossedX
	"There is a word that has fallen across the right edge of the composition 
	rectangle. This signals the need for wrapping which is done to the last 
	space that was encountered, as recorded by the space stop condition."

	spaceCount >= 1 ifTrue: [
		"The common case. First back off to the space at which we wrap."
		line stop: spaceIndex.
		lineHeight _ lineHeightAtSpace.
		baseline _ baselineAtSpace.
		spaceCount _ spaceCount - 1.
		spaceIndex _ spaceIndex - 1.

		"Check to see if any spaces preceding the one at which we wrap.
			Double space after punctuation, most likely."
		[(spaceCount > 1 and: [(text at: spaceIndex) = Character space])]
			whileTrue: [
				spaceCount _ spaceCount - 1.
				"Account for backing over a run which might
					change width of space."
				font _ text fontAt: spaceIndex.
				spaceIndex _ spaceIndex - 1.
				spaceX _ spaceX - (font widthOf: Character space)].
		line paddingWidth: rightMargin - spaceX.
		line internalSpaces: spaceCount]
	ifFalse: [
		"Neither internal nor trailing spaces -- almost never happens."
		lastIndex _ lastIndex - 1.
		[ destX <= rightMargin or: [ lastIndex = 0]]
			whileFalse: [
				destX _ destX - (font widthOf: (text at: lastIndex)).
				lastIndex _ lastIndex - 1].
		spaceX _ destX.
		line paddingWidth: rightMargin - destX.
		line stop: (lastIndex max: line first)].
	^true! !


!TextComposer methodsFor: 'as yet unclassified' stamp: 'th 11/18/2002 19:13'!
composeAllRectangles: rectangles

	| charIndexBeforeLine numberOfLinesBefore reasonForStopping |

	actualHeight _ defaultLineHeight.
	charIndexBeforeLine _ currCharIndex.
	numberOfLinesBefore _ lines size.
	reasonForStopping _ self composeEachRectangleIn: rectangles.

	currentY _ currentY + actualHeight.
	currentY > theContainer bottom ifTrue: [
		"Oops -- the line is really too high to fit -- back out"
		currCharIndex _ charIndexBeforeLine.
		lines size - numberOfLinesBefore timesRepeat: [lines removeLast].
		^self
	].
	
	"It's OK -- the line still fits."
	maxRightX _ maxRightX max: scanner rightX.
	1 to: rectangles size - 1 do: [ :i |
		"Adjust heights across rectangles if necessary"
		(lines at: lines size - rectangles size + i)
			lineHeight: lines last lineHeight
			baseline: lines last baseline
	].
	isFirstLine _ false.
	reasonForStopping == #columnBreak ifTrue: [^nil].
	currCharIndex > theText size ifTrue: [
		^nil		"we are finished composing"
	].
	! !


!TextStyle methodsFor: 'accessing'!
firstIndent
	"Answer the horizontal indenting of the first line of a paragraph in the 
	style of the receiver."

	^firstIndent! !

!TextStyle methodsFor: 'accessing'!
restIndent
	"Answer the indent for all but the first line of a paragraph in the style 
	of the receiver."

	^restIndent! !

!TextStyle methodsFor: 'accessing'!
rightIndent
	"Answer the right margin indent for the lines of a paragraph in the style 
	of the receiver."

	^rightIndent! !

!TextStyle methodsFor: 'tabs and margins'!
nextTabXFrom: anX leftMargin: leftMargin rightMargin: rightMargin 
	"Tab stops are distances from the left margin. Set the distance into the 
	argument, anX, normalized for the paragraph's left margin."

	| normalizedX tabX |
	normalizedX _ anX - leftMargin.
	1 to: tabsArray size do: 
		[:i | (tabX _ tabsArray at: i) > normalizedX 
				ifTrue: [^leftMargin + tabX min: rightMargin]].
	^rightMargin! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:30'!
privateColor: aColor
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."
	
	color _ aColor! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:32'!
privateFirstIndent: anInteger 
	"Set the horizontal indenting of the first line of a paragraph in the style 
	of the receiver to be the argument, anInteger."
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."

	firstIndent _ anInteger! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:30'!
privateName: aString
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."
	
	name _ aString! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:30'!
privateName: aString font: aFont
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."
	
	name _ aString.
	font _ aFont.
	alignment _ 0.
	tabsArray _ TextStyle defaultTabsArray! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:30'!
privateName: aString font: aFont alignment: code
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."
	
	name _ aString.
	font _ aFont.
	alignment _ code.
	tabsArray _ TextStyle defaultTabsArray! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:32'!
privateRestIndent: anInteger 
	"Set the indent for all but the first line of a paragraph in the style of the 
	receiver to be the argument, anInteger."
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."

	restIndent _ anInteger! !

!TextStyle methodsFor: 'initialization' stamp: 'jmv 8/26/2009 09:32'!
privateRightIndent: anInteger 
	"Set the right margin indent for the lines of a paragraph in the style 
	of the receiver to be the argument, anInteger."
	"To be used from TextStyle instance creation methods.
	If used on existing instances, existing text will be modified."

	rightIndent _ anInteger! !

TextStyle removeSelector: #firstIndent:!
TextStyle removeSelector: #restIndent:!
TextStyle removeSelector: #rightIndent:!
!classDefinition: #TextStyle category: #'Graphics-Text'!
Object subclass: #TextStyle
	instanceVariableNames: 'name font alignment tabsArray color firstIndent restIndent rightIndent'
	classVariableNames: 'AvailableTextStyles DefaultTextStyle'
	poolDictionaries: ''
	category: 'Graphics-Text'!
"Postscript:
Leave the line above, and replace the rest of this comment by a useful one.
Executable statements should follow this comment, and should
be separated by periods, with no exclamation points (!!).
Be sure to put any further comments in double-quotes, like this one."
TextStyle allInstancesDo: [ :s |
	s
		privateFirstIndent: 0;
		privateRestIndent: 0;
		privateRightIndent: 0 ]!

