'From Squeak3.1alpha of 5 February 2001 [latest update: #3501] on 5 February 2001 at 5:16:08 pm'! "Change Set: finalCR Date: 5 February 2001 Author: Bob Arning A not-too pretty fix for those cases when trying to selecting all the text in a pane appears not to select the last line. I have placed a fuller description in the class comment in NewParagraph" ! !NewParagraph commentStamp: 'RAA 2/5/2001 17:08' prior: 0! A Paragraph represents text that has been laid out, or composed, in some container. text A Text with encoded per-character emphasis. textStyle A TextStyle with font set, line height and horizontal alignment. firstCharacterIndex The starting index in text for this paragraph, allowing composition of a long text into a number of containers. container A Rectangle or TextContainer that determines where text can go. lines An Array of TextLines comprising the final layout of the text after it has been composed within its container. positionWhenComposed As its name implies. Allows display at new locations without the need to recompose the text. Lines are ordered vertically. However, for a given y, there may be several lines in left to right order. Lines must never be empty, even if text is empty. Notes on yet another hack - 5 Feb 2001 We really need to clean up #composeLinesFrom:to:delta:into:priorLines:atY:!!!!!! I added one more habdful of code to correct: This is an annoying bug that's been around for a couple of years, but I finally figured out how to duplicate the problem, so I figured I'd just report it now. (It doesn't necessarily have to be fixed for 3.0 if it looks messy, but if it's a simple fix, it would be worth it.) In Morphic, if you have the following text in a workspace: This is line 1 This is line 2 **and** you have a return character after line 2, you will normally be able to click the mouse two times below line 2 in order to select all the text. If you edit line 2 (e.g. so that it reads "line number 2"), you can still select all the text by clicking below the second line. However, if you edit line 1, you will not be able to select all the text from the bottom in the same way. Things get messed up such that the last return character seems to be gone. In this state, if you position the cursor immediately after the 2, and press the right arrow, the cursor jumps to the beginning of line 2... oof. (report by Doug Way) While I don't have a very deep understanding of the above mentioned method, I was able to determine that text ending in a CR worked better in the editor when the last entry in had a start of text size + 1 and a stop of text size. I have accordingly added code near the end to ensure this. It seems to have fixed the problem, but we do need to clean this baby up some day. - Bob ! ]style[(830 38 127 1000 388)f1,f2cblue;,f1,f1cred;,f1! !NewParagraph methodsFor: 'composition' stamp: 'RAA 2/5/2001 17:01'! composeLinesFrom: 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" | charIndex lineY lineHeight scanner line row firstLine lineHeightGuess saveCharIndex hitCR maybeSlide sliding bottom priorIndex priorLine oldLastLine | charIndex _ start. lines _ lineColl. lineY _ startingY. lineHeightGuess _ textStyle lineGrid. maxRightX _ container left. maybeSlide _ stop < text size and: [container isMemberOf: Rectangle]. sliding _ false. priorIndex _ 1. bottom _ container bottom. scanner _ CompositionScanner new text: text textStyle: textStyle. firstLine _ true. [charIndex <= text size and: [(lineY + lineHeightGuess) <= bottom]] whileTrue: [sliding ifTrue: ["Having detected the end of rippling recoposition, we are only sliding old lines" priorIndex < priorLines size ifTrue: ["Adjust and re-use previously composed line" priorIndex _ priorIndex + 1. priorLine _ (priorLines at: priorIndex) slideIndexBy: delta andMoveTopTo: lineY. lineColl addLast: priorLine. lineY _ priorLine bottom. charIndex _ priorLine last + 1] ifFalse: ["There are no more priorLines to slide." sliding _ maybeSlide _ false]] ifFalse: [lineHeight _ lineHeightGuess. saveCharIndex _ charIndex. hitCR _ false. row _ container rectanglesAt: lineY height: lineHeight. 1 to: row size do: [:i | (charIndex <= text size and: [hitCR not]) ifTrue: [line _ scanner composeFrom: charIndex inRectangle: (row at: i) firstLine: firstLine leftSide: i=1 rightSide: i=row size. lines addLast: line. (text at: line last) = Character cr ifTrue: [hitCR _ true]. lineHeight _ lineHeight max: line lineHeight. "includes font changes" charIndex _ line last + 1]]. row size >= 1 ifTrue: [lineY _ lineY + lineHeight. lineY > bottom ifTrue: ["Oops -- the line is really too high to fit -- back out" charIndex _ saveCharIndex. row do: [:r | lines removeLast]] ifFalse: ["It's OK -- the line still fits." maxRightX _ maxRightX max: scanner rightX. 1 to: row size - 1 do: "Adjust heights across row if necess" [:i | (lines at: lines size - row size + i) lineHeight: lines last lineHeight baseline: lines last baseline]. charIndex > text size ifTrue: ["end of text" hitCR ifTrue: ["If text ends with CR, add a null line at the end" ((lineY + lineHeightGuess) <= container bottom) ifTrue: [row _ container rectanglesAt: lineY height: lineHeightGuess. row size > 0 ifTrue: [line _ (TextLine start: charIndex stop: charIndex-1 internalSpaces: 0 paddingWidth: 0) rectangle: row first; lineHeight: lineHeightGuess baseline: textStyle baseline. lines addLast: line]]]. lines _ lines asArray. ^ maxRightX]. firstLine _ false]] ifFalse: [lineY _ lineY + lineHeight]. (maybeSlide and: [charIndex > stop]) ifTrue: ["Check whether we are now in sync with previously composed lines" [priorIndex < priorLines size and: [(priorLines at: priorIndex) first < (charIndex - delta)]] whileTrue: [priorIndex _ priorIndex + 1]. (priorLines at: priorIndex) first = (charIndex - delta) ifTrue: ["Yes -- next line will have same start as prior line." priorIndex _ priorIndex - 1. maybeSlide _ false. sliding _ true] ifFalse: [priorIndex = priorLines size ifTrue: ["Weve reached the end of priorLines, so no use to keep looking for lines to slide." maybeSlide _ false]]]]]. firstLine ifTrue: ["No space in container or empty text" line _ (TextLine start: start stop: start-1 internalSpaces: 0 paddingWidth: 0) rectangle: (container topLeft extent: 0@lineHeightGuess); lineHeight: lineHeightGuess baseline: textStyle baseline. lines _ Array with: line ] ifFalse: [ (text size > 1 and: [text last = Character cr]) ifTrue: [ oldLastLine _ lines last. oldLastLine last - oldLastLine first >= 0 ifTrue: [ line _ (TextLine start: text size+1 stop: text size internalSpaces: 0 paddingWidth: 0) rectangle: (oldLastLine left @ oldLastLine bottom extent: 0@(oldLastLine bottom - oldLastLine top)); lineHeight: lineHeightGuess baseline: textStyle baseline. lines _ lines, (Array with: line). ]. ]. ]. "end of container" lines _ lines asArray. ^ maxRightX! !