--- a/ListView.st Thu Sep 05 15:38:16 2013 +0200
+++ b/ListView.st Fri Sep 06 14:24:18 2013 +0200
@@ -18,7 +18,10 @@
fontIsFixedWidth fontWidth autoScroll autoScrollBlock
autoScrollDeltaT wordCheck includesNonStrings widthOfWidestLine
listMsg viewOrigin listChannel backgroundAlreadyClearedColor
- scrollWhenUpdating scrollLocked lineEndCRLF highlightAreas'
+ scrollWhenUpdating scrollLocked lineEndCRLF highlightAreas
+ compareModelOnUpdate expandTabsWhenUpdating
+ checkLineEndConventionWhenUpdating
+ numberOfLinesForWidthOfContentsComputation'
classVariableNames:'DefaultForegroundColor DefaultBackgroundColor DefaultTabPositions
UserDefaultTabPositions DefaultLeftMargin DefaultTopMargin'
poolDictionaries:''
@@ -459,6 +462,98 @@
!ListView methodsFor:'accessing-behavior'!
+checkLineEndConventionWhenUpdating
+ "return the line-end convention check when updating behavior.
+ If true (the default), the first line of the list given is checked for having a
+ cr-lf line end (which is a DOS convention), and the lineEndCRLF flag is set dynamically.
+ If false, the lineEndCRLF remains as specified by the user.
+ You may want to disable this flag if it is very expensive to generate a line
+ (although, only the very first line is checked, anyway)"
+
+ ^ checkLineEndConventionWhenUpdating
+!
+
+checkLineEndConventionWhenUpdating:aBoolean
+ "define the line-end convention check when updating behavior.
+ If true (the default), the first line of the list given is checked for having a
+ cr-lf line end (which is a DOS convention), and the lineEndCRLF flag is set dynamically.
+ If false, the lineEndCRLF remains as specified by the user.
+ You may want to disable this flag if it is very expensive to generate a line
+ (although, only the very first line is checked, anyway)"
+
+ checkLineEndConventionWhenUpdating := aBoolean
+!
+
+compareModelWhenUpdating
+ "return the compare when updating behavior.
+ If true (the default), the list of lines as given due to a model update
+ is processed and compared against the currently shown text.
+ If they are the same, no action is taken.
+ This behavior is ok in 99.99% of all applications.
+ However, you may turn this off iff:
+ - it is very expensive to process the list (for example, because the list
+ is defined by a virtual array, which computes the lines dynamically, on
+ the fly).
+ One use where this flag should be turned off is in the hex-memory display,
+ which is able to simulate texts with millions of lines, but they are actually
+ simulated by generating the presented lines dynamically, as they are displayed."
+
+ ^ compareModelOnUpdate
+!
+
+compareModelWhenUpdating:aBoolean
+ "define the compare when updating behavior.
+ If true (the default), the list of lines as given due to a model update
+ is processed and compared against the currently shown text.
+ If they are the same, no action is taken.
+ This behavior is ok in 99.99% of all applications.
+ However, you may turn this off iff:
+ - it is very expensive to process the list (for example, because the list
+ is defined by a virtual array, which computes the lines dynamically, on
+ the fly).
+ One use where this flag should be turned off is in the hex-memory display,
+ which is able to simulate texts with millions of lines, but they are actually
+ simulated by generating the presented lines dynamically, as they are displayed."
+
+ compareModelOnUpdate := aBoolean
+!
+
+expandTabsWhenUpdating
+ "return the tab expansion behavior.
+ If true (the default), the list of lines as given via #list: or
+ due to a model update is processed and lines are replaced by lines with
+ tabs expanded.
+ This behavior is ok in 99.99% of all applications.
+ However, you may turn this off iff:
+ - you are certain, that no tabs are in the passed in list
+ - it is very expensive to process the list (for example, because the list
+ is defined by a virtual array, which computes the lines dynamically, on
+ the fly).
+ One use where this flag should be turned off is in the hex-memory display,
+ which is able to simulate texts with millions of lines, but they are actually
+ simulated by generating the presented lines dynamically, as they are displayed."
+
+ ^ expandTabsWhenUpdating
+!
+
+expandTabsWhenUpdating:aBoolean
+ "define the tab expansion behavior.
+ If true (the default), the list of lines as given via #list: or
+ due to a model update is processed and lines are replaced by lines with
+ tabs expanded.
+ This behavior is ok in 99.99% of all applications.
+ However, you may turn this off iff:
+ - you are certain, that no tabs are in the passed in list
+ - it is very expensive to process the list (for example, because the list
+ is defined by a virtual array, which computes the lines dynamically, on
+ the fly).
+ One use where this flag should be turned off is in the hex-memory display,
+ which is able to simulate texts with millions of lines, but they are actually
+ simulated by generating the presented lines dynamically, as they are displayed."
+
+ expandTabsWhenUpdating := aBoolean
+!
+
lineEndCRLF
"answer true, if CRLF is used for the line end.
This is true for DOS/Windows files.
@@ -469,6 +564,26 @@
"Created: / 04-07-2006 / 19:05:01 / fm"
!
+numberOfLinesForWidthOfContentsComputation
+ "return the number of lines to consider in the widthOfContents computation,
+ which is needed by the scrollBar interface.
+ If nil (the default), all lines are processed and the width of the longest line is taken.
+ You may want to change this to 1 if it is guaranteed that all linesa are of the same width
+ (for example, when it is very expensive to generate all lines"
+
+ ^ numberOfLinesForWidthOfContentsComputation
+!
+
+numberOfLinesForWidthOfContentsComputation:anIntegerOrNil
+ "set the number of lines to consider in the widthOfContents computation,
+ which is needed by the scrollBar interface.
+ If nil (the default), all lines are processed and the width of the longest line is taken.
+ You may want to change this to 1 if it is guaranteed that all linesa are of the same width
+ (for example, when it is very expensive to generate all lines"
+
+ numberOfLinesForWidthOfContentsComputation := anIntegerOrNil
+!
+
scrollWhenUpdating
"return the scroll behavior, when I get a new text
(via the model or the #contents/#list)
@@ -828,12 +943,28 @@
(remembered to optimize later redraws)."
self
- list:aCollection expandTabs:expand scanForNonStrings:scan includesNonStrings:true
+ list:aCollection expandTabs:expand scanForNonStrings:scan includesNonStrings:nil
"Modified: 5.6.1997 / 12:40:35 / cg"
!
-list:aCollection expandTabs:expand scanForNonStrings:scan includesNonStrings:nonStrings
+list:aCollection expandTabs:expand scanForNonStrings:scan includesNonStrings:nonStringsIfNoScan
+ "set the contents (a collection of strings)
+ and scroll as specified in scrollWhenUpdating (default:top-left).
+ If expand is true, tabs are expanded (to spaces).
+ If scan is true, scan the passed list for nonStrings;
+ otherwise, take the information from the nonStrings arg.
+ (the nonStrings information is remembered to optimize later redraws & height computations)."
+
+ self
+ list:aCollection
+ expandTabs:expand
+ scanForNonStrings:scan
+ includesNonStrings:nonStringsIfNoScan
+ redraw:true
+!
+
+list:aCollection expandTabs:expand scanForNonStrings:scan includesNonStrings:nonStringsIfNoScan redraw:doRedraw
"set the contents (a collection of strings)
and scroll as specified in scrollWhenUpdating (default:top-left).
If expand is true, tabs are expanded (to spaces).
@@ -844,47 +975,57 @@
|oldFirst oldLeft nonStringsBefore fontHeightBefore
scrollToEnd scrollToTop newLeftOffset wText same firstLine|
- "/ see if there is a change at all.
- "/ use to compare using =, but thats not enough in case of emphasis change.
- aCollection size == list size ifTrue:[
- same := true.
- aCollection size > 0 ifTrue:[
- aCollection with:list do:[:eachNewLine :eachOldLine |
- (eachNewLine == eachOldLine)
-"/ ((eachNewLine species == eachOldLine species)
-"/ and:[eachNewLine = eachOldLine])
- ifFalse:[
- same := false.
+ "/ cg: what is the point in comparing here?
+ "/ I think, if there is something to optimize,
+ "/ the caller should do so (moved to getListFromModel).
+ "/ notice, that it may be very expensive to ask aCollection for each line
+ "/ for example, iff the lines are generated on the fly by an algorithm
+ false ifTrue:[
+ "/ see if there is a change at all.
+ "/ use to compare using =, but that's not enough in case of emphasis change.
+ aCollection size == list size ifTrue:[
+ same := true.
+ aCollection size > 0 ifTrue:[
+ aCollection with:list do:[:eachNewLine :eachOldLine |
+ (eachNewLine == eachOldLine)
+ ifFalse:[
+ same := false.
+ ]
]
- ]
+ ].
+ same ifTrue:[^ self].
].
- same ifTrue:[^ self].
].
scrollToTop := scrollWhenUpdating == #begin or:[scrollWhenUpdating == #beginOfText].
scrollToEnd := scrollWhenUpdating == #end or:[scrollWhenUpdating == #endOfText].
- (aCollection isNil and:[list isNil]) ifTrue:[
+ (aCollection isEmptyOrNil and:[list isEmptyOrNil]) ifTrue:[
"no contents change"
- scrollToTop ifTrue:[
- self scrollToTop.
- ] ifFalse:[
- scrollToEnd ifTrue:[
- self scrollToBottom.
- ]
+ list := aCollection.
+ scrollLocked ifFalse:[
+ scrollToTop ifTrue:[
+ self scrollToTop.
+ ] ifFalse:[
+ scrollToEnd ifTrue:[
+ self scrollToBottom.
+ ]
+ ].
+ self scrollToLeft.
].
- self scrollToLeft.
^ self
].
- "Check if the we use DOS/Windows line end conventin with CR LF.
- The LF has already been consumed by the conversion to a StringCollection,
- now check for and remove the trailing left over CRs"
-
- lineEndCRLF := (aCollection size > 0
- and:[(firstLine := aCollection at:1) isString
- and:[firstLine notEmpty
- and:[firstLine string endsWith:Character return]]]).
+ checkLineEndConventionWhenUpdating ifTrue:[
+ "Check if the we use DOS/Windows line end convention with CR LF.
+ The LF has already been consumed by the conversion to a StringCollection,
+ now check for and remove the trailing left over CRs"
+
+ lineEndCRLF := (aCollection size > 0
+ and:[(firstLine := aCollection at:1) isString
+ and:[firstLine notEmpty
+ and:[firstLine string endsWith:Character return]]]).
+ ].
lineEndCRLF ifTrue:[
list := aCollection
collect:[:eachLineWithCROrNil |
@@ -908,7 +1049,7 @@
scan ifTrue:[
includesNonStrings := list contains:[:e | e isString not].
] ifFalse:[
- includesNonStrings := nonStrings
+ includesNonStrings := nonStringsIfNoScan ? nonStringsBefore
]
].
].
@@ -924,47 +1065,45 @@
self computeNumberOfLinesShown.
].
- newLeftOffset := viewOrigin x.
- scrollToTop ifTrue:[
- firstLineShown := 1.
- newLeftOffset := 0.
- ] ifFalse:[
- scrollToEnd ifTrue:[
- firstLineShown := (list size - nFullLinesShown + 1) max:1.
+ scrollLocked ifFalse:[
+ newLeftOffset := viewOrigin x.
+ scrollToTop ifTrue:[
+ firstLineShown := 1.
newLeftOffset := 0.
- ]
- ].
- newLeftOffset > 0 ifTrue:[
- wText := self widthOfContents.
- (viewOrigin x + self innerWidth) > wText ifTrue:[
- newLeftOffset := (wText - self innerWidth) max:0.
+ ] ifFalse:[
+ scrollToEnd ifTrue:[
+ firstLineShown := (list size - nFullLinesShown + 1) max:1.
+ newLeftOffset := 0.
+ ]
].
- ].
- newLeftOffset ~= oldLeft ifTrue:[
- viewOrigin := newLeftOffset @ viewOrigin y.
+ newLeftOffset > 0 ifTrue:[
+ wText := self widthOfContents.
+ (viewOrigin x + self innerWidth) > wText ifTrue:[
+ newLeftOffset := (wText - self innerWidth) max:0.
+ ].
+ ].
+ newLeftOffset ~= oldLeft ifTrue:[
+ viewOrigin := newLeftOffset @ viewOrigin y.
+ ].
].
realized ifTrue:[
self contentsChanged.
- "
- don't use scroll here to avoid double redraw
- "
- viewOrigin := viewOrigin isNil ifTrue:[0@0] ifFalse:[(viewOrigin x) @ 0].
- transformation := nil.
-
- oldFirst ~~ firstLineShown ifTrue:[
- self originChanged:0 @ ((oldFirst - 1) * fontHeight negated).
+ scrollLocked ifFalse:[
+ "
+ don't use scroll here to avoid double redraw
+ "
+ viewOrigin := viewOrigin isNil ifTrue:[0@0] ifFalse:[(viewOrigin x) @ 0].
+ transformation := nil.
+
+ oldFirst ~~ firstLineShown ifTrue:[
+ self originChanged:0 @ ((oldFirst - 1) * fontHeight negated).
+ ].
].
- shown ifTrue:[
-self invalidate.
-"/ self redrawFromVisibleLine:1 to:nLinesShown.
-
-"/ fontHeightBefore > fontHeight ifTrue:[
-"/ (self listLineIsVisible:(self size)) ifTrue:[
-"/"/ self clearRectangle:((margin @ (self yOfVisibleLine:nLinesShown+1))
-"/"/ corner:(width-margin) @ (height-margin)).
-"/ ].
-"/ ]
+ doRedraw ifTrue:[
+ shown ifTrue:[
+ self invalidate.
+ ]
]
]
@@ -1152,58 +1291,98 @@
This can be used to update a self-changing list
(for example: a file list being shown, without disturbing the user too much)"
- |oldFirst nonStringsBefore|
-
- (aCollection isNil and:[list isNil]) ifTrue:[
- "no change"
- ^ self
- ].
-
- list := aCollection.
-
- nonStringsBefore := includesNonStrings.
- includesNonStrings := false.
-
- list notNil ifTrue:[
- expandTabs ifTrue:[
- self expandTabs
- ] ifFalse:[
- includesNonStrings := (list findFirst:[:e | e isString not]) ~~ 0.
- ].
- ].
- (includesNonStrings ~~ nonStringsBefore) ifTrue:[
- self getFontParameters.
- self computeNumberOfLinesShown.
+ self
+ setList:aCollection expandTabs:expandTabs scanForNonStrings:true includesNonStrings:nil
+ redraw:doRedraw
+!
+
+setList:aCollection expandTabs:expandTabs scanForNonStrings:scan includesNonStrings:nonStringsIfNoScan redraw:doRedraw
+ "set the contents (a collection of strings);
+ do not change position (i.e. do not scroll).
+ This can be used to update a self-changing list
+ (for example: a file list being shown, without disturbing the user too much).
+ TODO: this stinks: most of the code is the same as in #list:expandTabs:...
+ needs a refactoring"
+
+ |prev|
+
+ prev := scrollLocked.
+ [
+ scrollLocked := false.
+ self
+ list:aCollection
+ expandTabs:expandTabs
+ scanForNonStrings:scan
+ includesNonStrings:nonStringsIfNoScan
+ redraw:doRedraw
+ ] ensure:[
+ scrollLocked := prev
].
-
- "/ new: reposition horizontally if too big
- widthOfWidestLine := nil. "/ i.e. unknown
- innerWidth >= self widthOfContents ifTrue:[
- viewOrigin := 0 @ viewOrigin y.
- ].
- self contentsChanged.
-
- "/ new: reposition vertically if too big
- (firstLineShown + nFullLinesShown) > self size ifTrue:[
- oldFirst := firstLineShown.
- firstLineShown := self size - nFullLinesShown + 1.
- firstLineShown < 1 ifTrue:[firstLineShown := 1].
- oldFirst ~= firstLineShown ifTrue:[
- viewOrigin y:(firstLineShown - 1 * fontHeight).
- self originChanged:0 @ ((oldFirst - 1) negated * fontHeight).
- shown ifTrue:[
- self clearView.
- ]
- ]
- ].
-
- (shown and:[doRedraw]) ifTrue:[
- self redrawFromVisibleLine:1 to:nLinesShown
- ]
-
- "Modified: / 18.12.1995 / 23:27:54 / stefan"
- "Created: / 22.4.1998 / 11:11:51 / cg"
- "Modified: / 26.7.1998 / 13:46:49 / cg"
+"/
+"/
+"/"/ scrollLocked ifTrue:[
+"/"/ self setList:newText expandTabs:expandTabsWhenUpdating
+"/"/ ] ifFalse:[
+"/ self list:newText expandTabs:expandTabsWhenUpdating scanForNonStrings:expandTabsWhenUpdating
+"/"/ ]
+"/
+"/ |oldFirst nonStringsBefore|
+"/
+"/ (aCollection isNil and:[list isNil]) ifTrue:[
+"/ "no change"
+"/ ^ self
+"/ ].
+"/
+"/ list := aCollection.
+"/
+"/ nonStringsBefore := includesNonStrings.
+"/ includesNonStrings := false.
+"/
+"/ list notNil ifTrue:[
+"/ expandTabs ifTrue:[
+"/ self expandTabs
+"/ ] ifFalse:[
+"/ scan ifTrue:[
+"/ includesNonStrings := (list findFirst:[:e | e isString not]) ~~ 0.
+"/ ] ifFalse:[
+"/ includesNonStrings := nonStringsIfNoScan ? nonStringsBefore
+"/ ]
+"/ ].
+"/ ].
+"/ (includesNonStrings ~~ nonStringsBefore) ifTrue:[
+"/ self getFontParameters.
+"/ self computeNumberOfLinesShown.
+"/ ].
+"/
+"/ "/ new: reposition horizontally if too big
+"/ widthOfWidestLine := nil. "/ i.e. unknown
+"/ innerWidth >= self widthOfContents ifTrue:[
+"/ viewOrigin := 0 @ viewOrigin y.
+"/ ].
+"/ self contentsChanged.
+"/
+"/ "/ new: reposition vertically if too big
+"/ (firstLineShown + nFullLinesShown) > self size ifTrue:[
+"/ oldFirst := firstLineShown.
+"/ firstLineShown := self size - nFullLinesShown + 1.
+"/ firstLineShown < 1 ifTrue:[firstLineShown := 1].
+"/ oldFirst ~= firstLineShown ifTrue:[
+"/ viewOrigin y:((firstLineShown - 1) * fontHeight).
+"/ self originChanged:0 @ ((oldFirst - 1) negated * fontHeight).
+"/ shown ifTrue:[
+"/ self clearView.
+"/ ]
+"/ ]
+"/ ].
+"/
+"/ (shown and:[doRedraw]) ifTrue:[
+"/ self invalidate
+"/ "/ self redrawFromVisibleLine:1 to:nLinesShown
+"/ ]
+"/
+"/ "Modified: / 18.12.1995 / 23:27:54 / stefan"
+"/ "Created: / 22.4.1998 / 11:11:51 / cg"
+"/ "Modified: / 26.7.1998 / 13:46:49 / cg"
!
size
@@ -2273,10 +2452,15 @@
partialLines := true.
tabPositions := UserDefaultTabPositions ? DefaultTabPositions.
includesNonStrings := false.
+ numberOfLinesForWidthOfContentsComputation := nil."/ i.e. all
self getFontParameters.
self initializeWordCheckAction.
+
scrollWhenUpdating := #keep. "/ #beginOfText.
-
+ expandTabsWhenUpdating := true.
+ compareModelOnUpdate := true.
+ checkLineEndConventionWhenUpdating := true.
+ scrollLocked := false.
autoScroll := true.
"Modified: / 03-07-2006 / 17:03:59 / cg"
@@ -2623,29 +2807,48 @@
"ask my model (if any) for the text via the listMsg.
If there is no listMessage, try aspect for backward compatibility."
- |text msg|
+ |newText msg|
model notNil ifTrue:[
msg := listMsg ? aspectMsg.
msg notNil ifTrue:[
- text := model perform:msg.
+ newText := model perform:msg.
"/ cg: this makes many optimizations (virtualArray) useless;
- "/ I do not think that this is a good idea!!
+ "/ I do not think that this is a good idea:
"/ text notNil ifTrue:[
"/ so I changed it to:
- (text notNil and:[text isString]) ifTrue:[
- text := text asStringCollection.
+ (newText notNil and:[newText isString]) ifTrue:[
+ newText := newText asStringCollection.
+ ].
+
+ compareModelOnUpdate ifTrue:[
+ "/ see if there is a change at all.
+ "/ use to compare using =, but that's not enough in case of emphasis change.
+ newText size == list size ifTrue:[
+ |same|
+
+ same := true.
+ newText size > 0 ifTrue:[
+ newText with:list do:[:eachNewLine :eachOldLine |
+ (eachNewLine == eachOldLine) ifFalse:[
+ same := false.
+ ]
+ ]
+ ].
+ same ifTrue:[^ self].
+ ].
].
"/ SV: this compare does not work, if model uses (i.e. updates)
"/ the same stringCollection as the view!!
true "text ~= list" ifTrue:[
- scrollLocked == true ifTrue:[
- self setList:text
- ] ifFalse:[
- self list:text
- ]
+ "/ changed #list to care for scrollLocked
+"/ scrollLocked ifTrue:[
+"/ self setList:newText expandTabs:expandTabsWhenUpdating
+"/ ] ifFalse:[
+ self list:newText expandTabs:expandTabsWhenUpdating scanForNonStrings:expandTabsWhenUpdating
+"/ ]
].
].
].
@@ -2657,16 +2860,16 @@
getListFromModelScroll:aBoolean
"ask my model (if any) for the text via the listMsg.
If there is no listMessage, try aspect for backward compatibility.
- Scrolling is suppressed here"
+ If aBoolean is false, scrolling is suppressed here"
|prev|
prev := scrollLocked.
- scrollLocked := true.
+ scrollLocked := aBoolean not.
[
- self getListFromModel
+ self getListFromModel
] ensure:[
- scrollLocked := prev.
+ scrollLocked := prev.
].
!
@@ -4931,10 +5134,10 @@
!ListView class methodsFor:'documentation'!
version
- ^ '$Header: /cvs/stx/stx/libwidg/ListView.st,v 1.378 2013-09-03 20:06:14 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libwidg/ListView.st,v 1.379 2013-09-06 12:24:18 cg Exp $'
!
version_CVS
- ^ '$Header: /cvs/stx/stx/libwidg/ListView.st,v 1.378 2013-09-03 20:06:14 cg Exp $'
+ ^ '$Header: /cvs/stx/stx/libwidg/ListView.st,v 1.379 2013-09-06 12:24:18 cg Exp $'
! !