Nicolas Cellier uploaded a new version of ST80 to project The Trunk:
http://source.squeak.org/trunk/ST80-nice.152.mcz
==================== Summary ====================
Name: ST80-nice.152
Author: nice
Time: 2 October 2013, 2:25:01.483 pm
UUID: 77107925-1fc2-44a4-9f5b-bc4f787612ef
Ancestors: ST80-nice.151
Restore the CharacterBlockScanner hack required for MVC in a specialized CharacterBlockScannerForMVC class.
Methods used exclusively by Paragraph are moved there too.
=============== Diff against ST80-nice.151 ===============
Item was added:
+ CharacterBlockScanner subclass: #CharacterBlockScannerForMVC
+ instanceVariableNames: ''
+ classVariableNames: ''
+ poolDictionaries: ''
+ category: 'ST80-Support'!
Item was added:
+ ----- Method: CharacterBlockScannerForMVC>>buildCharacterBlockIn: (in category 'private') -----
+ buildCharacterBlockIn: para
+ "This method is used by the MVC version only."
+
+ | lineIndex runLength lineStop stopCondition |
+ "handle nullText"
+ (para numberOfLines = 0 or: [text size = 0])
+ ifTrue: [^ CharacterBlock new stringIndex: 1 "like being off end of string"
+ text: para text
+ topLeft: (para leftMarginForDisplayForLine: 1 alignment: (alignment ifNil:[textStyle alignment]))
+ @ para compositionRectangle top
+ extent: 0 @ textStyle lineGrid].
+ "find the line"
+ lineIndex := para lineIndexOfTop: characterPoint y.
+ destY := para topAtLineIndex: lineIndex.
+ line := para lines at: lineIndex.
+ lastIndex := line first.
+ rightMargin := para rightMarginForDisplay.
+ self setStopConditions. " also loads the font, alignment and all emphasis attributes "
+
+ (lineIndex = para numberOfLines and:
+ [(destY + line lineHeight) < characterPoint y])
+ ifTrue: ["if beyond lastLine, force search to last character"
+ self characterPointSetX: rightMargin]
+ ifFalse: [characterPoint y < (para compositionRectangle) top
+ ifTrue: ["force search to first line"
+ characterPoint := (para compositionRectangle) topLeft].
+ characterPoint x > rightMargin
+ ifTrue: [self characterPointSetX: rightMargin]].
+ destX := leftMargin := para leftMarginForDisplayForLine: lineIndex alignment: alignment.
+ nextLeftMargin:= para leftMarginForDisplayForLine: lineIndex+1 alignment: alignment.
+ runLength := text runLengthFor: line first.
+ lineStop := characterIndex "scanning for index"
+ ifNil: [ line last ]. "scanning for point"
+ runStopIndex := lastIndex + (runLength - 1) min: lineStop.
+ lastCharacterWidth := 0.
+ spaceCount := 0.
+ self handleIndentation.
+
+ [stopCondition := self scanCharactersFrom: lastIndex to: runStopIndex
+ in: text string rightX: characterPoint x
+ stopConditions: stopConditions kern: kern.
+ "see setStopConditions for stopping conditions for character block operations."
+ self perform: stopCondition] whileFalse.
+
+ ^characterIndex == nil
+ ifTrue: ["characterBlockAtPoint"
+ ^ CharacterBlock new stringIndex: lastIndex text: text
+ topLeft: characterPoint + (font descentKern @ 0)
+ extent: lastCharacterWidth @ line lineHeight]
+ ifFalse: ["characterBlockForIndex"
+ ^ CharacterBlock new stringIndex: lastIndex text: text
+ topLeft: characterPoint + ((font descentKern) - kern @ 0)
+ extent: lastCharacterWidth @ line lineHeight]!
Item was added:
+ ----- Method: CharacterBlockScannerForMVC>>characterBlockAtPoint:in: (in category 'scanning') -----
+ characterBlockAtPoint: aPoint in: aParagraph
+ "Answer a CharacterBlock for character in aParagraph at point aPoint. It
+ is assumed that aPoint has been transformed into coordinates appropriate
+ to the text's destination form rectangle and the composition rectangle."
+
+ self initializeFromParagraph: aParagraph clippedBy: aParagraph clippingRectangle.
+ characterPoint := aPoint.
+ ^self buildCharacterBlockIn: aParagraph!
Item was added:
+ ----- Method: CharacterBlockScannerForMVC>>characterBlockForIndex:in: (in category 'scanning') -----
+ characterBlockForIndex: targetIndex in: aParagraph
+ "Answer a CharacterBlock for character in aParagraph at targetIndex. The
+ coordinates in the CharacterBlock will be appropriate to the intersection
+ of the destination form rectangle and the composition rectangle."
+
+ self
+ initializeFromParagraph: aParagraph
+ clippedBy: aParagraph clippingRectangle.
+ characterIndex := targetIndex.
+ characterPoint :=
+ aParagraph rightMarginForDisplay @
+ (aParagraph topAtLineIndex:
+ (aParagraph lineIndexOfCharacterIndex: characterIndex)).
+ ^self buildCharacterBlockIn: aParagraph!
Item was added:
+ ----- Method: CharacterBlockScannerForMVC>>crossedX (in category 'stop conditions') -----
+ crossedX
+ 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: [Transcript cr; show:'here'.
+ lastIndex := characterIndex.
+ characterPoint := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).
+ ^true]].
+ ^super crossedX!
Item was changed:
----- Method: Paragraph>>characterBlockAtPoint: (in category 'character location') -----
characterBlockAtPoint: aPoint
"Answer a CharacterBlock for characters in the text at point aPoint. It is
assumed that aPoint has been transformed into coordinates appropriate to
the receiver's destinationForm rectangle and the compositionRectangle."
+ ^CharacterBlockScannerForMVC new characterBlockAtPoint: aPoint in: self!
- ^CharacterBlockScanner new characterBlockAtPoint: aPoint in: self!
Item was changed:
----- Method: Paragraph>>characterBlockForIndex: (in category 'character location') -----
characterBlockForIndex: targetIndex
"Answer a CharacterBlock for character in the text at targetIndex. The
coordinates in the CharacterBlock will be appropriate to the intersection
of the destinationForm rectangle and the compositionRectangle."
+ ^CharacterBlockScannerForMVC new characterBlockForIndex: targetIndex in: self!
- ^CharacterBlockScanner new characterBlockForIndex: targetIndex in: self!
Nicolas Cellier uploaded a new version of Graphics to project The Trunk:
http://source.squeak.org/trunk/Graphics-nice.243.mcz
==================== Summary ====================
Name: Graphics-nice.243
Author: nice
Time: 2 October 2013, 2:47:50.925 am
UUID: 220dcb9f-1fed-4f7d-b6de-170fe2b2a3ce
Ancestors: Graphics-nice.242
Fix a composition glitch when the last character of a text is a space that crosses the right margin boundary.
In such case, a virtual empty line must be added to the composition in order to correctly materialize text selection and cursor position, and so as to continue typing on next line.
The case when last character is a carriage return is in all point similar.
Indeed, a space that crossedX is visually turned into a new line.
TextComposer previously tried to reverse engineer scanner's work to recognize the CR case, which is a smell.
This change unifies handling for the two cases by rather asking to the scanner doesTheLineBreaksAfterLastChar?
Remove fixupLastLineIfCR which is tainted with half case only.
Remove the workaround in CharacterBlockScanner that did not work around anything.
Fix the breaking at non space for eastern asia:
1) registerBreakableIndex records that the line can wrap before the current character, and spaceIndex was pointing at this character that will potentially wrap on next line.
2) It is still possible to apply Justified alignment based on space adjustment if some spaces are used in the text, so correctly set the line spaceCount and paddingWidth.
=============== Diff against Graphics-nice.242 ===============
Item was changed:
----- Method: CharacterBlockScanner>>crossedX (in category 'stop conditions') -----
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."
self retrieveLastCharacterWidth.
- 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 := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).
- ^true]].
characterPoint x <= (destX + (lastCharacterWidth // 2))
ifTrue: [characterPoint := destX @ destY.
^true].
lastIndex >= line last
ifTrue: [characterPoint := destX @ destY.
^true].
"Pointing past middle of a character, return the next character."
lastIndex := lastIndex + 1.
characterPoint := destX + lastCharacterWidth + kern @ destY.
^ true!
Item was changed:
CharacterScanner subclass: #CompositionScanner
+ instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
- instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace'
classVariableNames: ''
poolDictionaries: ''
category: 'Graphics-Text'!
!CompositionScanner commentStamp: '<historical>' prior: 0!
CompositionScanners are used to measure text and determine where line breaks and space padding should occur.!
Item was changed:
----- Method: CompositionScanner>>composeFrom:inRectangle:firstLine:leftSide:rightSide: (in category 'scanning') -----
composeFrom: startIndex inRectangle: lineRectangle
firstLine: firstLine leftSide: leftSide rightSide: rightSide
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
"Set up margins"
leftMargin := lineRectangle left.
leftSide ifTrue: [leftMargin := leftMargin +
(firstLine ifTrue: [textStyle firstIndent]
ifFalse: [textStyle restIndent])].
destX := spaceX := leftMargin.
rightMargin := lineRectangle right.
rightSide ifTrue: [rightMargin := rightMargin - textStyle rightIndent].
lastIndex := startIndex. "scanning sets last index"
destY := lineRectangle top.
lineHeight := baseline := 0. "Will be increased by setFont"
line := (TextLine start: lastIndex stop: 0 internalSpaces: 0 paddingWidth: 0)
rectangle: lineRectangle.
self setStopConditions. "also sets font"
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
self handleIndentation.
leftMargin := destX.
line leftMargin: leftMargin.
[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] whileFalse.
^ line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>composeLine:fromCharacterIndex:inParagraph: (in category 'scanning') -----
composeLine: lineIndex fromCharacterIndex: startIndex inParagraph: aParagraph
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
destX := spaceX := leftMargin := aParagraph leftMarginForCompositionForLine: lineIndex.
destY := 0.
rightMargin := aParagraph rightMarginForComposition.
leftMargin >= rightMargin ifTrue: [self error: 'No room between margins to compose'].
lastIndex := startIndex. "scanning sets last index"
lineHeight := textStyle lineGrid. "may be increased by setFont:..."
baseline := textStyle baseline.
self setStopConditions. "also sets font"
self handleIndentation.
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
line := TextLineInterval
start: lastIndex
stop: 0
internalSpaces: 0
paddingWidth: 0.
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
[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] whileFalse.
^line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>cr (in category 'stop conditions') -----
cr
"Answer true. Set up values for the text line interval currently being
composed."
pendingKernX := 0.
(lastIndex < text size and: [(text at: lastIndex) = CR and: [(text at: lastIndex+1) = Character lf]]) ifTrue: [lastIndex := lastIndex + 1].
line stop: lastIndex.
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
lastBreakIsNotASpace := false.
line paddingWidth: rightMargin - spaceX.
^true!
Item was changed:
----- Method: CompositionScanner>>crossedX (in category 'stop conditions') -----
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,
or any other breakable character if the language permits so."
pendingKernX := 0.
lastBreakIsNotASpace ifTrue:
+ ["In some languages break is possible before a non space."
+ nextIndexAfterLineBreak := spaceIndex.
+ line stop: spaceIndex - 1.
- ["In some languages break is possible on non space."
- line stop: spaceIndex.
lineHeight := lineHeightAtSpace.
baseline := baselineAtSpace.
+ line paddingWidth: rightMargin - spaceX.
- spaceCount := spaceCount - 1.
- spaceIndex := spaceIndex - 1.
- line paddingWidth: rightMargin.
line internalSpaces: spaceCount.
^true].
spaceCount >= 1 ifTrue:
["The common case. First back off to the space at which we wrap."
line stop: spaceIndex.
+ nextIndexAfterLineBreak := spaceIndex + 1.
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) = Space])]
whileTrue:
[spaceCount := spaceCount - 1.
"Account for backing over a run which might
change width of space."
font := text fontAt: spaceIndex withStyle: textStyle.
spaceIndex := spaceIndex - 1.
spaceX := spaceX - (font widthOf: 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].
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
line paddingWidth: rightMargin - destX.
line stop: (lastIndex max: line first)].
^true!
Item was added:
+ ----- Method: CompositionScanner>>doesTheLineBreaksAfterLastChar (in category 'accessing') -----
+ doesTheLineBreaksAfterLastChar
+ ^nextIndexAfterLineBreak > text size!
Item was changed:
----- Method: TextComposer>>composeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
composeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
wantsColumnBreaks := argWantsColumnBreaks.
lines := argLinesCollection.
theTextStyle := argTextStyle.
theText := argText.
theContainer := argContainer.
deltaCharIndex := argDelta.
currCharIndex := startCharIndex := argStart.
stopCharIndex := argStop.
prevLines := argPriorLines.
currentY := argStartY.
maxRightX := theContainer left.
possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
nowSliding := false.
prevIndex := 1.
"choose an appropriate scanner - should go away soon, when they can be unified"
scanner := CompositionScanner new.
scanner text: theText textStyle: theTextStyle.
scanner wantsColumnBreaks: wantsColumnBreaks.
defaultLineHeight := scanner computeDefaultLineHeight.
isFirstLine := true.
self composeAllLines.
isFirstLine ifTrue: ["No space in container or empty text"
self
addNullLineWithIndex: startCharIndex
andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
] ifFalse: [
+ (lines last last = theText size and: [scanner doesTheLineBreaksAfterLastChar])
+ ifTrue: [self addNullLineForIndex: theText size + 1]
- self fixupLastLineIfCR
].
^{lines asArray. maxRightX}
-
!
Item was removed:
- ----- Method: TextComposer>>fixupLastLineIfCR (in category 'as yet unclassified') -----
- fixupLastLineIfCR
- "This awful bit is to ensure that if we have scanned all the text and the last character is a CR that there is a null line at the end of lines. Sometimes this was not happening which caused anomalous selections when selecting all the text. This is implemented as a post-composition fixup because I couldn't figure out where to put it in the main logic."
-
- (theText size > 0 and: [CharacterSet crlf includes: theText last]) ifFalse: [^self].
- self addNullLineForIndex: theText size + 1.
- !
Item was removed:
- ----- Method: TextComposer>>multiComposeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
- multiComposeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
-
- "temporarily add this here to support move to drop MultiTextComposer"
- "now redundant and ready to remove later"
- wantsColumnBreaks := argWantsColumnBreaks.
- lines := argLinesCollection.
- theTextStyle := argTextStyle.
- theText := argText.
- theContainer := argContainer.
- deltaCharIndex := argDelta.
- currCharIndex := startCharIndex := argStart.
- stopCharIndex := argStop.
- prevLines := argPriorLines.
- currentY := argStartY.
- maxRightX := theContainer left.
- possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
- nowSliding := false.
- prevIndex := 1.
- scanner := CompositionScanner new text: theText textStyle: theTextStyle.
- scanner wantsColumnBreaks: wantsColumnBreaks.
- defaultLineHeight := scanner computeDefaultLineHeight.
- isFirstLine := true.
- self composeAllLines.
- isFirstLine ifTrue: ["No space in container or empty text"
- self
- addNullLineWithIndex: startCharIndex
- andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
- ] ifFalse: [
- self fixupLastLineIfCR
- ].
- ^{lines asArray. maxRightX}
-
- !
Nicolas Cellier uploaded a new version of Graphics to project The Trunk:
http://source.squeak.org/trunk/Graphics-nice.243.mcz
==================== Summary ====================
Name: Graphics-nice.243
Author: nice
Time: 2 October 2013, 2:47:50.925 am
UUID: 220dcb9f-1fed-4f7d-b6de-170fe2b2a3ce
Ancestors: Graphics-nice.242
Fix a composition glitch when the last character of a text is a space that crosses the right margin boundary.
In such case, a virtual empty line must be added to the composition in order to correctly materialize text selection and cursor position, and so as to continue typing on next line.
The case when last character is a carriage return is in all point similar.
Indeed, a space that crossedX is visually turned into a new line.
TextComposer previously tried to reverse engineer scanner's work to recognize the CR case, which is a smell.
This change unifies handling for the two cases by rather asking to the scanner doesTheLineBreaksAfterLastChar?
Remove fixupLastLineIfCR which is tainted with half case only.
Remove the workaround in CharacterBlockScanner that did not work around anything.
Fix the breaking at non space for eastern asia:
1) registerBreakableIndex records that the line can wrap before the current character, and spaceIndex was pointing at this character that will potentially wrap on next line.
2) It is still possible to apply Justified alignment based on space adjustment if some spaces are used in the text, so correctly set the line spaceCount and paddingWidth.
=============== Diff against Graphics-nice.242 ===============
Item was changed:
----- Method: CharacterBlockScanner>>crossedX (in category 'stop conditions') -----
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."
self retrieveLastCharacterWidth.
- 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 := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).
- ^true]].
characterPoint x <= (destX + (lastCharacterWidth // 2))
ifTrue: [characterPoint := destX @ destY.
^true].
lastIndex >= line last
ifTrue: [characterPoint := destX @ destY.
^true].
"Pointing past middle of a character, return the next character."
lastIndex := lastIndex + 1.
characterPoint := destX + lastCharacterWidth + kern @ destY.
^ true!
Item was changed:
CharacterScanner subclass: #CompositionScanner
+ instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
- instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace'
classVariableNames: ''
poolDictionaries: ''
category: 'Graphics-Text'!
!CompositionScanner commentStamp: '<historical>' prior: 0!
CompositionScanners are used to measure text and determine where line breaks and space padding should occur.!
Item was changed:
----- Method: CompositionScanner>>composeFrom:inRectangle:firstLine:leftSide:rightSide: (in category 'scanning') -----
composeFrom: startIndex inRectangle: lineRectangle
firstLine: firstLine leftSide: leftSide rightSide: rightSide
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
"Set up margins"
leftMargin := lineRectangle left.
leftSide ifTrue: [leftMargin := leftMargin +
(firstLine ifTrue: [textStyle firstIndent]
ifFalse: [textStyle restIndent])].
destX := spaceX := leftMargin.
rightMargin := lineRectangle right.
rightSide ifTrue: [rightMargin := rightMargin - textStyle rightIndent].
lastIndex := startIndex. "scanning sets last index"
destY := lineRectangle top.
lineHeight := baseline := 0. "Will be increased by setFont"
line := (TextLine start: lastIndex stop: 0 internalSpaces: 0 paddingWidth: 0)
rectangle: lineRectangle.
self setStopConditions. "also sets font"
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
self handleIndentation.
leftMargin := destX.
line leftMargin: leftMargin.
[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] whileFalse.
^ line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>composeLine:fromCharacterIndex:inParagraph: (in category 'scanning') -----
composeLine: lineIndex fromCharacterIndex: startIndex inParagraph: aParagraph
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
destX := spaceX := leftMargin := aParagraph leftMarginForCompositionForLine: lineIndex.
destY := 0.
rightMargin := aParagraph rightMarginForComposition.
leftMargin >= rightMargin ifTrue: [self error: 'No room between margins to compose'].
lastIndex := startIndex. "scanning sets last index"
lineHeight := textStyle lineGrid. "may be increased by setFont:..."
baseline := textStyle baseline.
self setStopConditions. "also sets font"
self handleIndentation.
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
line := TextLineInterval
start: lastIndex
stop: 0
internalSpaces: 0
paddingWidth: 0.
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
[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] whileFalse.
^line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>cr (in category 'stop conditions') -----
cr
"Answer true. Set up values for the text line interval currently being
composed."
pendingKernX := 0.
(lastIndex < text size and: [(text at: lastIndex) = CR and: [(text at: lastIndex+1) = Character lf]]) ifTrue: [lastIndex := lastIndex + 1].
line stop: lastIndex.
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
lastBreakIsNotASpace := false.
line paddingWidth: rightMargin - spaceX.
^true!
Item was changed:
----- Method: CompositionScanner>>crossedX (in category 'stop conditions') -----
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,
or any other breakable character if the language permits so."
pendingKernX := 0.
lastBreakIsNotASpace ifTrue:
+ ["In some languages break is possible before a non space."
+ nextIndexAfterLineBreak := spaceIndex.
+ line stop: spaceIndex - 1.
- ["In some languages break is possible on non space."
- line stop: spaceIndex.
lineHeight := lineHeightAtSpace.
baseline := baselineAtSpace.
+ line paddingWidth: rightMargin - spaceX.
- spaceCount := spaceCount - 1.
- spaceIndex := spaceIndex - 1.
- line paddingWidth: rightMargin.
line internalSpaces: spaceCount.
^true].
spaceCount >= 1 ifTrue:
["The common case. First back off to the space at which we wrap."
line stop: spaceIndex.
+ nextIndexAfterLineBreak := spaceIndex + 1.
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) = Space])]
whileTrue:
[spaceCount := spaceCount - 1.
"Account for backing over a run which might
change width of space."
font := text fontAt: spaceIndex withStyle: textStyle.
spaceIndex := spaceIndex - 1.
spaceX := spaceX - (font widthOf: 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].
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
line paddingWidth: rightMargin - destX.
line stop: (lastIndex max: line first)].
^true!
Item was added:
+ ----- Method: CompositionScanner>>doesTheLineBreaksAfterLastChar (in category 'accessing') -----
+ doesTheLineBreaksAfterLastChar
+ ^nextIndexAfterLineBreak > text size!
Item was changed:
----- Method: TextComposer>>composeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
composeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
wantsColumnBreaks := argWantsColumnBreaks.
lines := argLinesCollection.
theTextStyle := argTextStyle.
theText := argText.
theContainer := argContainer.
deltaCharIndex := argDelta.
currCharIndex := startCharIndex := argStart.
stopCharIndex := argStop.
prevLines := argPriorLines.
currentY := argStartY.
maxRightX := theContainer left.
possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
nowSliding := false.
prevIndex := 1.
"choose an appropriate scanner - should go away soon, when they can be unified"
scanner := CompositionScanner new.
scanner text: theText textStyle: theTextStyle.
scanner wantsColumnBreaks: wantsColumnBreaks.
defaultLineHeight := scanner computeDefaultLineHeight.
isFirstLine := true.
self composeAllLines.
isFirstLine ifTrue: ["No space in container or empty text"
self
addNullLineWithIndex: startCharIndex
andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
] ifFalse: [
+ (lines last last = theText size and: [scanner doesTheLineBreaksAfterLastChar])
+ ifTrue: [self addNullLineForIndex: theText size + 1]
- self fixupLastLineIfCR
].
^{lines asArray. maxRightX}
-
!
Item was removed:
- ----- Method: TextComposer>>fixupLastLineIfCR (in category 'as yet unclassified') -----
- fixupLastLineIfCR
- "This awful bit is to ensure that if we have scanned all the text and the last character is a CR that there is a null line at the end of lines. Sometimes this was not happening which caused anomalous selections when selecting all the text. This is implemented as a post-composition fixup because I couldn't figure out where to put it in the main logic."
-
- (theText size > 0 and: [CharacterSet crlf includes: theText last]) ifFalse: [^self].
- self addNullLineForIndex: theText size + 1.
- !
Item was removed:
- ----- Method: TextComposer>>multiComposeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
- multiComposeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
-
- "temporarily add this here to support move to drop MultiTextComposer"
- "now redundant and ready to remove later"
- wantsColumnBreaks := argWantsColumnBreaks.
- lines := argLinesCollection.
- theTextStyle := argTextStyle.
- theText := argText.
- theContainer := argContainer.
- deltaCharIndex := argDelta.
- currCharIndex := startCharIndex := argStart.
- stopCharIndex := argStop.
- prevLines := argPriorLines.
- currentY := argStartY.
- maxRightX := theContainer left.
- possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
- nowSliding := false.
- prevIndex := 1.
- scanner := CompositionScanner new text: theText textStyle: theTextStyle.
- scanner wantsColumnBreaks: wantsColumnBreaks.
- defaultLineHeight := scanner computeDefaultLineHeight.
- isFirstLine := true.
- self composeAllLines.
- isFirstLine ifTrue: ["No space in container or empty text"
- self
- addNullLineWithIndex: startCharIndex
- andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
- ] ifFalse: [
- self fixupLastLineIfCR
- ].
- ^{lines asArray. maxRightX}
-
- !
Nicolas Cellier uploaded a new version of Graphics to project The Trunk:
http://source.squeak.org/trunk/Graphics-nice.243.mcz
==================== Summary ====================
Name: Graphics-nice.243
Author: nice
Time: 2 October 2013, 2:47:50.925 am
UUID: 220dcb9f-1fed-4f7d-b6de-170fe2b2a3ce
Ancestors: Graphics-nice.242
Fix a composition glitch when the last character of a text is a space that crosses the right margin boundary.
In such case, a virtual empty line must be added to the composition in order to correctly materialize text selection and cursor position, and so as to continue typing on next line.
The case when last character is a carriage return is in all point similar.
Indeed, a space that crossedX is visually turned into a new line.
TextComposer previously tried to reverse engineer scanner's work to recognize the CR case, which is a smell.
This change unifies handling for the two cases by rather asking to the scanner doesTheLineBreaksAfterLastChar?
Remove fixupLastLineIfCR which is tainted with half case only.
Remove the workaround in CharacterBlockScanner that did not work around anything.
Fix the breaking at non space for eastern asia:
1) registerBreakableIndex records that the line can wrap before the current character, and spaceIndex was pointing at this character that will potentially wrap on next line.
2) It is still possible to apply Justified alignment based on space adjustment if some spaces are used in the text, so correctly set the line spaceCount and paddingWidth.
=============== Diff against Graphics-nice.242 ===============
Item was changed:
----- Method: CharacterBlockScanner>>crossedX (in category 'stop conditions') -----
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."
self retrieveLastCharacterWidth.
- 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 := (nextLeftMargin ifNil: [leftMargin]) @ (destY + line lineHeight).
- ^true]].
characterPoint x <= (destX + (lastCharacterWidth // 2))
ifTrue: [characterPoint := destX @ destY.
^true].
lastIndex >= line last
ifTrue: [characterPoint := destX @ destY.
^true].
"Pointing past middle of a character, return the next character."
lastIndex := lastIndex + 1.
characterPoint := destX + lastCharacterWidth + kern @ destY.
^ true!
Item was changed:
CharacterScanner subclass: #CompositionScanner
+ instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace nextIndexAfterLineBreak'
- instanceVariableNames: 'spaceX spaceIndex lineHeight baseline lineHeightAtSpace baselineAtSpace lastBreakIsNotASpace'
classVariableNames: ''
poolDictionaries: ''
category: 'Graphics-Text'!
!CompositionScanner commentStamp: '<historical>' prior: 0!
CompositionScanners are used to measure text and determine where line breaks and space padding should occur.!
Item was changed:
----- Method: CompositionScanner>>composeFrom:inRectangle:firstLine:leftSide:rightSide: (in category 'scanning') -----
composeFrom: startIndex inRectangle: lineRectangle
firstLine: firstLine leftSide: leftSide rightSide: rightSide
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
"Set up margins"
leftMargin := lineRectangle left.
leftSide ifTrue: [leftMargin := leftMargin +
(firstLine ifTrue: [textStyle firstIndent]
ifFalse: [textStyle restIndent])].
destX := spaceX := leftMargin.
rightMargin := lineRectangle right.
rightSide ifTrue: [rightMargin := rightMargin - textStyle rightIndent].
lastIndex := startIndex. "scanning sets last index"
destY := lineRectangle top.
lineHeight := baseline := 0. "Will be increased by setFont"
line := (TextLine start: lastIndex stop: 0 internalSpaces: 0 paddingWidth: 0)
rectangle: lineRectangle.
self setStopConditions. "also sets font"
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
self handleIndentation.
leftMargin := destX.
line leftMargin: leftMargin.
[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] whileFalse.
^ line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>composeLine:fromCharacterIndex:inParagraph: (in category 'scanning') -----
composeLine: lineIndex fromCharacterIndex: startIndex inParagraph: aParagraph
"Answer an instance of TextLineInterval that represents the next line in the paragraph."
| runLength stopCondition |
destX := spaceX := leftMargin := aParagraph leftMarginForCompositionForLine: lineIndex.
destY := 0.
rightMargin := aParagraph rightMarginForComposition.
leftMargin >= rightMargin ifTrue: [self error: 'No room between margins to compose'].
lastIndex := startIndex. "scanning sets last index"
lineHeight := textStyle lineGrid. "may be increased by setFont:..."
baseline := textStyle baseline.
self setStopConditions. "also sets font"
self handleIndentation.
runLength := text runLengthFor: startIndex.
runStopIndex := (lastIndex := startIndex) + (runLength - 1).
line := TextLineInterval
start: lastIndex
stop: 0
internalSpaces: 0
paddingWidth: 0.
+ nextIndexAfterLineBreak := spaceCount := 0.
- spaceCount := 0.
lastBreakIsNotASpace := false.
[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] whileFalse.
^line
lineHeight: lineHeight + textStyle leading
baseline: baseline + textStyle leading!
Item was changed:
----- Method: CompositionScanner>>cr (in category 'stop conditions') -----
cr
"Answer true. Set up values for the text line interval currently being
composed."
pendingKernX := 0.
(lastIndex < text size and: [(text at: lastIndex) = CR and: [(text at: lastIndex+1) = Character lf]]) ifTrue: [lastIndex := lastIndex + 1].
line stop: lastIndex.
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
lastBreakIsNotASpace := false.
line paddingWidth: rightMargin - spaceX.
^true!
Item was changed:
----- Method: CompositionScanner>>crossedX (in category 'stop conditions') -----
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,
or any other breakable character if the language permits so."
pendingKernX := 0.
lastBreakIsNotASpace ifTrue:
+ ["In some languages break is possible before a non space."
+ nextIndexAfterLineBreak := spaceIndex.
+ line stop: spaceIndex - 1.
- ["In some languages break is possible on non space."
- line stop: spaceIndex.
lineHeight := lineHeightAtSpace.
baseline := baselineAtSpace.
+ line paddingWidth: rightMargin - spaceX.
- spaceCount := spaceCount - 1.
- spaceIndex := spaceIndex - 1.
- line paddingWidth: rightMargin.
line internalSpaces: spaceCount.
^true].
spaceCount >= 1 ifTrue:
["The common case. First back off to the space at which we wrap."
line stop: spaceIndex.
+ nextIndexAfterLineBreak := spaceIndex + 1.
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) = Space])]
whileTrue:
[spaceCount := spaceCount - 1.
"Account for backing over a run which might
change width of space."
font := text fontAt: spaceIndex withStyle: textStyle.
spaceIndex := spaceIndex - 1.
spaceX := spaceX - (font widthOf: 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].
+ nextIndexAfterLineBreak := lastIndex + 1.
spaceX := destX.
line paddingWidth: rightMargin - destX.
line stop: (lastIndex max: line first)].
^true!
Item was added:
+ ----- Method: CompositionScanner>>doesTheLineBreaksAfterLastChar (in category 'accessing') -----
+ doesTheLineBreaksAfterLastChar
+ ^nextIndexAfterLineBreak > text size!
Item was changed:
----- Method: TextComposer>>composeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
composeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
wantsColumnBreaks := argWantsColumnBreaks.
lines := argLinesCollection.
theTextStyle := argTextStyle.
theText := argText.
theContainer := argContainer.
deltaCharIndex := argDelta.
currCharIndex := startCharIndex := argStart.
stopCharIndex := argStop.
prevLines := argPriorLines.
currentY := argStartY.
maxRightX := theContainer left.
possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
nowSliding := false.
prevIndex := 1.
"choose an appropriate scanner - should go away soon, when they can be unified"
scanner := CompositionScanner new.
scanner text: theText textStyle: theTextStyle.
scanner wantsColumnBreaks: wantsColumnBreaks.
defaultLineHeight := scanner computeDefaultLineHeight.
isFirstLine := true.
self composeAllLines.
isFirstLine ifTrue: ["No space in container or empty text"
self
addNullLineWithIndex: startCharIndex
andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
] ifFalse: [
+ (lines last last = theText size and: [scanner doesTheLineBreaksAfterLastChar])
+ ifTrue: [self addNullLineForIndex: theText size + 1]
- self fixupLastLineIfCR
].
^{lines asArray. maxRightX}
-
!
Item was removed:
- ----- Method: TextComposer>>fixupLastLineIfCR (in category 'as yet unclassified') -----
- fixupLastLineIfCR
- "This awful bit is to ensure that if we have scanned all the text and the last character is a CR that there is a null line at the end of lines. Sometimes this was not happening which caused anomalous selections when selecting all the text. This is implemented as a post-composition fixup because I couldn't figure out where to put it in the main logic."
-
- (theText size > 0 and: [CharacterSet crlf includes: theText last]) ifFalse: [^self].
- self addNullLineForIndex: theText size + 1.
- !
Item was removed:
- ----- Method: TextComposer>>multiComposeLinesFrom:to:delta:into:priorLines:atY:textStyle:text:container:wantsColumnBreaks: (in category 'as yet unclassified') -----
- multiComposeLinesFrom: argStart to: argStop delta: argDelta into: argLinesCollection priorLines: argPriorLines atY: argStartY textStyle: argTextStyle text: argText container: argContainer wantsColumnBreaks: argWantsColumnBreaks
-
- "temporarily add this here to support move to drop MultiTextComposer"
- "now redundant and ready to remove later"
- wantsColumnBreaks := argWantsColumnBreaks.
- lines := argLinesCollection.
- theTextStyle := argTextStyle.
- theText := argText.
- theContainer := argContainer.
- deltaCharIndex := argDelta.
- currCharIndex := startCharIndex := argStart.
- stopCharIndex := argStop.
- prevLines := argPriorLines.
- currentY := argStartY.
- maxRightX := theContainer left.
- possibleSlide := stopCharIndex < theText size and: [theContainer isMemberOf: Rectangle].
- nowSliding := false.
- prevIndex := 1.
- scanner := CompositionScanner new text: theText textStyle: theTextStyle.
- scanner wantsColumnBreaks: wantsColumnBreaks.
- defaultLineHeight := scanner computeDefaultLineHeight.
- isFirstLine := true.
- self composeAllLines.
- isFirstLine ifTrue: ["No space in container or empty text"
- self
- addNullLineWithIndex: startCharIndex
- andRectangle: (theContainer topLeft extent: 0@defaultLineHeight)
- ] ifFalse: [
- self fixupLastLineIfCR
- ].
- ^{lines asArray. maxRightX}
-
- !
Nicolas Cellier uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-nice.694.mcz
==================== Summary ====================
Name: Morphic-nice.694
Author: nice
Time: 2 October 2013, 2:13:52.8 am
UUID: 1d8bc696-d20f-496d-9668-d253a08556ae
Ancestors: Morphic-nice.693
multiComposeLinesFrom:to:delta:into:priorLines:atY: is no more used, farewell...
=============== Diff against Morphic-nice.693 ===============
Item was removed:
- ----- Method: NewParagraph>>multiComposeLinesFrom:to:delta:into:priorLines:atY: (in category 'composition') -----
- multiComposeLinesFrom: start to: stop delta: delta into: lineColl priorLines: priorLines
- atY: startingY
- "While the section from start to stop has changed, composition may ripple all the way to the end of the text. However in a rectangular container, if we ever find a line beginning with the same character as before (ie corresponding to delta in the old lines), then we can just copy the old lines from there to the end of the container, with adjusted indices and y-values"
-
- | newResult |
-
- newResult := TextComposer new
- multiComposeLinesFrom: start
- to: stop
- delta: delta
- into: lineColl
- priorLines: priorLines
- atY: startingY
- textStyle: textStyle
- text: text
- container: container
- wantsColumnBreaks: wantsColumnBreaks == true.
- lines := newResult first asArray.
- maxRightX := newResult second.
- "maxRightX printString displayAt: 0@0."
- ^maxRightX
- !
Nicolas Cellier uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-nice.694.mcz
==================== Summary ====================
Name: Morphic-nice.694
Author: nice
Time: 2 October 2013, 2:13:52.8 am
UUID: 1d8bc696-d20f-496d-9668-d253a08556ae
Ancestors: Morphic-nice.693
multiComposeLinesFrom:to:delta:into:priorLines:atY: is no more used, farewell...
=============== Diff against Morphic-nice.693 ===============
Item was removed:
- ----- Method: NewParagraph>>multiComposeLinesFrom:to:delta:into:priorLines:atY: (in category 'composition') -----
- multiComposeLinesFrom: start to: stop delta: delta into: lineColl priorLines: priorLines
- atY: startingY
- "While the section from start to stop has changed, composition may ripple all the way to the end of the text. However in a rectangular container, if we ever find a line beginning with the same character as before (ie corresponding to delta in the old lines), then we can just copy the old lines from there to the end of the container, with adjusted indices and y-values"
-
- | newResult |
-
- newResult := TextComposer new
- multiComposeLinesFrom: start
- to: stop
- delta: delta
- into: lineColl
- priorLines: priorLines
- atY: startingY
- textStyle: textStyle
- text: text
- container: container
- wantsColumnBreaks: wantsColumnBreaks == true.
- lines := newResult first asArray.
- maxRightX := newResult second.
- "maxRightX printString displayAt: 0@0."
- ^maxRightX
- !
Nicolas Cellier uploaded a new version of Morphic to project The Trunk:
http://source.squeak.org/trunk/Morphic-nice.694.mcz
==================== Summary ====================
Name: Morphic-nice.694
Author: nice
Time: 2 October 2013, 2:13:52.8 am
UUID: 1d8bc696-d20f-496d-9668-d253a08556ae
Ancestors: Morphic-nice.693
multiComposeLinesFrom:to:delta:into:priorLines:atY: is no more used, farewell...
=============== Diff against Morphic-nice.693 ===============
Item was removed:
- ----- Method: NewParagraph>>multiComposeLinesFrom:to:delta:into:priorLines:atY: (in category 'composition') -----
- multiComposeLinesFrom: start to: stop delta: delta into: lineColl priorLines: priorLines
- atY: startingY
- "While the section from start to stop has changed, composition may ripple all the way to the end of the text. However in a rectangular container, if we ever find a line beginning with the same character as before (ie corresponding to delta in the old lines), then we can just copy the old lines from there to the end of the container, with adjusted indices and y-values"
-
- | newResult |
-
- newResult := TextComposer new
- multiComposeLinesFrom: start
- to: stop
- delta: delta
- into: lineColl
- priorLines: priorLines
- atY: startingY
- textStyle: textStyle
- text: text
- container: container
- wantsColumnBreaks: wantsColumnBreaks == true.
- lines := newResult first asArray.
- maxRightX := newResult second.
- "maxRightX printString displayAt: 0@0."
- ^maxRightX
- !
Frank Shearar uploaded a new version of System to project The Trunk:
http://source.squeak.org/trunk/System-fbs.599.mcz
==================== Summary ====================
Name: System-fbs.599
Author: fbs
Time: 1 October 2013, 11:19:18.501 pm
UUID: ec7dbcc2-93e6-4143-bfee-4d52d9368951
Ancestors: System-fbs.598
Minor spelling corrections.
=============== Diff against System-fbs.598 ===============
Item was changed:
TextDiffBuilder subclass: #ClassDiffBuilder
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'System-FilePackage'!
+ !ClassDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
+ I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably meant to create diffs of class definitions.!
- !ClassDiffBuilder commentStamp: 'klub 12/28/2009 05:14' prior: 0!
- I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably ment to create diffs of class definitions.!
Item was changed:
Object subclass: #TextDiffBuilder
instanceVariableNames: 'xLines yLines ignoreLineEndings'
classVariableNames: 'IgnoreLineEndings InsertTextAttributes NormalTextAttributes RemoveTextAttributes'
poolDictionaries: ''
category: 'System-FilePackage'!
+ !TextDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
- !TextDiffBuilder commentStamp: 'ul 9/11/2013 01:05' prior: 0!
I implement the diff algorithm. I can show the differences between two texts. See my method comments for further information.
Instance Variables
xLines: <Array>
yLines: <Array>
ignoreLineEndings: <Boolean>
xLines
- an Array of DiffElements which is created from the first input text
yLines
- an Array of DiffElements which is created from the second input text
ignoreLineEndings
+ - a Boolean describing whether lines only differing in the line endings should be reported as a difference, or not!
- - a Boolean describing wether lines only differing in the line endings should be reported as a difference, or not!
Item was changed:
----- Method: TextDiffBuilder>>lcsFor:and: (in category 'private') -----
lcsFor: xFilteredLines and: yFilteredLines
+ "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list contains the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers."
- "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list containts the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers"
| n m v lcss max index lcs x y |
n := xFilteredLines size.
m := yFilteredLines size.
max := m + n.
v := Array new: 2 * max + 1.
lcss := Array new: 2 * max + 1.
"Unrolled first iteration (d = 0, k = 0)"
index := max + 2.
y := x := v at: index put: 0.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + 1 put: x.
lcss at: max + 1 put: lcs.
1 to: max do: [ :d |
"Unrolled lowest diagonal checks (k = -d)."
index := max - d + 2.
x := v at: index.
y := x + d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max - d + 1 put: x.
lcss at: max - d + 1 put: lcs.
"Inner diagonals. (k in [2-d..d-2])"
2 - d to: d - 2 by: 2 do: [ :k |
index := max + k.
(v at: index) < (v at: index + 2)
ifTrue: [ x := v at: (index := index + 2) ]
ifFalse: [ x := (v at: index) + 1 ].
y := x - k.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + k + 1 put: x.
lcss at: max + k + 1 put: lcs ].
"Unrolled highest diagonal checks (k = d)."
index := max + d.
x := (v at: index) + 1.
y := x - d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + d + 1 put: x.
lcss at: max + d + 1 put: lcs ].
self error "We should never reach this point."!
Frank Shearar uploaded a new version of System to project The Trunk:
http://source.squeak.org/trunk/System-fbs.599.mcz
==================== Summary ====================
Name: System-fbs.599
Author: fbs
Time: 1 October 2013, 11:19:18.501 pm
UUID: ec7dbcc2-93e6-4143-bfee-4d52d9368951
Ancestors: System-fbs.598
Minor spelling corrections.
=============== Diff against System-fbs.598 ===============
Item was changed:
TextDiffBuilder subclass: #ClassDiffBuilder
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'System-FilePackage'!
+ !ClassDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
+ I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably meant to create diffs of class definitions.!
- !ClassDiffBuilder commentStamp: 'klub 12/28/2009 05:14' prior: 0!
- I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably ment to create diffs of class definitions.!
Item was changed:
Object subclass: #TextDiffBuilder
instanceVariableNames: 'xLines yLines ignoreLineEndings'
classVariableNames: 'IgnoreLineEndings InsertTextAttributes NormalTextAttributes RemoveTextAttributes'
poolDictionaries: ''
category: 'System-FilePackage'!
+ !TextDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
- !TextDiffBuilder commentStamp: 'ul 9/11/2013 01:05' prior: 0!
I implement the diff algorithm. I can show the differences between two texts. See my method comments for further information.
Instance Variables
xLines: <Array>
yLines: <Array>
ignoreLineEndings: <Boolean>
xLines
- an Array of DiffElements which is created from the first input text
yLines
- an Array of DiffElements which is created from the second input text
ignoreLineEndings
+ - a Boolean describing whether lines only differing in the line endings should be reported as a difference, or not!
- - a Boolean describing wether lines only differing in the line endings should be reported as a difference, or not!
Item was changed:
----- Method: TextDiffBuilder>>lcsFor:and: (in category 'private') -----
lcsFor: xFilteredLines and: yFilteredLines
+ "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list contains the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers."
- "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list containts the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers"
| n m v lcss max index lcs x y |
n := xFilteredLines size.
m := yFilteredLines size.
max := m + n.
v := Array new: 2 * max + 1.
lcss := Array new: 2 * max + 1.
"Unrolled first iteration (d = 0, k = 0)"
index := max + 2.
y := x := v at: index put: 0.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + 1 put: x.
lcss at: max + 1 put: lcs.
1 to: max do: [ :d |
"Unrolled lowest diagonal checks (k = -d)."
index := max - d + 2.
x := v at: index.
y := x + d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max - d + 1 put: x.
lcss at: max - d + 1 put: lcs.
"Inner diagonals. (k in [2-d..d-2])"
2 - d to: d - 2 by: 2 do: [ :k |
index := max + k.
(v at: index) < (v at: index + 2)
ifTrue: [ x := v at: (index := index + 2) ]
ifFalse: [ x := (v at: index) + 1 ].
y := x - k.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + k + 1 put: x.
lcss at: max + k + 1 put: lcs ].
"Unrolled highest diagonal checks (k = d)."
index := max + d.
x := (v at: index) + 1.
y := x - d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + d + 1 put: x.
lcss at: max + d + 1 put: lcs ].
self error "We should never reach this point."!
Frank Shearar uploaded a new version of System to project The Trunk:
http://source.squeak.org/trunk/System-fbs.599.mcz
==================== Summary ====================
Name: System-fbs.599
Author: fbs
Time: 1 October 2013, 11:19:18.501 pm
UUID: ec7dbcc2-93e6-4143-bfee-4d52d9368951
Ancestors: System-fbs.598
Minor spelling corrections.
=============== Diff against System-fbs.598 ===============
Item was changed:
TextDiffBuilder subclass: #ClassDiffBuilder
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'System-FilePackage'!
+ !ClassDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
+ I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably meant to create diffs of class definitions.!
- !ClassDiffBuilder commentStamp: 'klub 12/28/2009 05:14' prior: 0!
- I'm like TextDiffBuilder, but I split the input text by Character >> #separators, instead of new lines. I'm probably ment to create diffs of class definitions.!
Item was changed:
Object subclass: #TextDiffBuilder
instanceVariableNames: 'xLines yLines ignoreLineEndings'
classVariableNames: 'IgnoreLineEndings InsertTextAttributes NormalTextAttributes RemoveTextAttributes'
poolDictionaries: ''
category: 'System-FilePackage'!
+ !TextDiffBuilder commentStamp: 'fbs 9/23/2013 08:58' prior: 0!
- !TextDiffBuilder commentStamp: 'ul 9/11/2013 01:05' prior: 0!
I implement the diff algorithm. I can show the differences between two texts. See my method comments for further information.
Instance Variables
xLines: <Array>
yLines: <Array>
ignoreLineEndings: <Boolean>
xLines
- an Array of DiffElements which is created from the first input text
yLines
- an Array of DiffElements which is created from the second input text
ignoreLineEndings
+ - a Boolean describing whether lines only differing in the line endings should be reported as a difference, or not!
- - a Boolean describing wether lines only differing in the line endings should be reported as a difference, or not!
Item was changed:
----- Method: TextDiffBuilder>>lcsFor:and: (in category 'private') -----
lcsFor: xFilteredLines and: yFilteredLines
+ "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list contains the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers."
- "I find one of the longest common subsequences of my arguments. I assume that none of my arguments are empty. I return nil, or an Array which represents a list. The first two elements are the matching ''line'' numbers, the third (and last) is the next node in the list, or nil, if there are no more elements. The list containts the longest common subsequence. I'm a modified version of the Greedy LCS/SES Algorithm from the 6th page of 'An O(ND) Difference Algorithm and Its Variations (1986)' by Eugene W. Myers"
| n m v lcss max index lcs x y |
n := xFilteredLines size.
m := yFilteredLines size.
max := m + n.
v := Array new: 2 * max + 1.
lcss := Array new: 2 * max + 1.
"Unrolled first iteration (d = 0, k = 0)"
index := max + 2.
y := x := v at: index put: 0.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + 1 put: x.
lcss at: max + 1 put: lcs.
1 to: max do: [ :d |
"Unrolled lowest diagonal checks (k = -d)."
index := max - d + 2.
x := v at: index.
y := x + d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max - d + 1 put: x.
lcss at: max - d + 1 put: lcs.
"Inner diagonals. (k in [2-d..d-2])"
2 - d to: d - 2 by: 2 do: [ :k |
index := max + k.
(v at: index) < (v at: index + 2)
ifTrue: [ x := v at: (index := index + 2) ]
ifFalse: [ x := (v at: index) + 1 ].
y := x - k.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + k + 1 put: x.
lcss at: max + k + 1 put: lcs ].
"Unrolled highest diagonal checks (k = d)."
index := max + d.
x := (v at: index) + 1.
y := x - d.
lcs := lcss at: index.
[ x < n and: [ y < m and: [ (xFilteredLines at: x + 1) = (yFilteredLines at: y + 1) ] ] ]
whileTrue: [ lcs := { x := x + 1. y := y + 1. lcs } ].
x >= n ifTrue: [ y >= m ifTrue: [ ^lcs ] ].
v at: max + d + 1 put: x.
lcss at: max + d + 1 put: lcs ].
self error "We should never reach this point."!