handle & display timestamp records
authorClaus Gittinger <cg@exept.de>
Sat, 18 Nov 1995 18:04:08 +0100
changeset 182 0c2f9214d531
parent 181 2afd46f37adf
child 183 908f1f2c3ca0
handle & display timestamp records
CBrowser.st
ChangesBrowser.st
--- a/CBrowser.st	Fri Nov 17 18:24:05 1995 +0100
+++ b/CBrowser.st	Sat Nov 18 18:04:08 1995 +0100
@@ -17,7 +17,8 @@
 		changePositions changeClassNames changeHeaderLines anyChanges
 		changeNrShown changeNrProcessed skipSignal 
 		compareChanges compareCheckBox
-		changeFileSize changeFileTimestamp checkBlock'
+		changeFileSize changeFileTimestamp checkBlock
+		changeTimeStamps tabSpec'
 	 classVariableNames:'CompressSnapshotInfo'
 	 poolDictionaries:''
 	 category:'Interface-Browsers'
@@ -40,7 +41,7 @@
 !
 
 version
-    ^ '$Header: /cvs/stx/stx/libtool/Attic/CBrowser.st,v 1.47 1995-11-16 15:18:36 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libtool/Attic/CBrowser.st,v 1.48 1995-11-18 17:04:08 cg Exp $'
 !
 
 documentation
@@ -226,20 +227,20 @@
      and a list of chunk-positions (changePositions).
      Starting with 2.10.3, the entries are multi-col entries;
      the cols are:
-	1   class/selector
-	2   delta (only if comparing)
+	1   delta (only if comparing)
 		'+' -> new method (w.r.t. current state)
 		'-' -> removed method (w.r.t. current state)
 		'?' -> class does not exist currently
 		'=' -> change is same as current methods source
+	2   class/selector
 	3   type of change
 		doit
 		method
 		category change
+	4   timestamp
 
      since comparing slows down startup time, it is now disabled by
-     default and can be enabled via a toggle.
-     "
+     default and can be enabled via a toggle."
 
     |aStream maxLen f|
 
@@ -253,13 +254,7 @@
     changeFileTimestamp := f info at:#modified.
 
     self withCursor:(Cursor read) do:[
-	|tabSpec myProcess myPriority|
-
-	tabSpec := TabulatorSpecification new.
-	tabSpec unit:#inch.
-	tabSpec positions:#(0         4.5       4.65   5.5 ).
-	"                   cls>>sel  +/-       type   info"
-	tabSpec align:    #(#left     #left     #left  #left).
+	|myProcess myPriority|
 
 	"
 	 this is a time consuming operation (especially, if reading an
@@ -272,11 +267,12 @@
 	].
 
 	[
-	    |excla|
+	    |excla timeStampInfo|
 
 	    changeChunks := OrderedCollection new.
 	    changeHeaderLines := OrderedCollection new.
 	    changePositions := OrderedCollection new.
+	    changeTimeStamps := OrderedCollection new.
 	    excla := aStream class chunkSeparator.
 
 	    [aStream atEnd] whileFalse:[
@@ -296,200 +292,208 @@
 		chunkText notNil ifTrue:[
 		    |index headerLine|
 
-		    "
-		     only first line is saved in changeChunks ...
-		    "
-		    index := chunkText indexOf:(Character cr).
-		    (index ~~ 0) ifTrue:[
-			chunkText := chunkText copyTo:(index - 1).
-
-			"take care for comment changes - must still be a
-			 valid expression for classNameOfChange: to work"
-
-			(chunkText endsWith:'comment:''') ifTrue:[
-			    chunkText := chunkText , '...'''
-			].
-			(chunkText endsWith:'primitiveDefinitions:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-			(chunkText endsWith:'primitiveVariables:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-			(chunkText endsWith:'primitiveFunctions:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-		    ].
+		    (chunkText startsWith:'''---- timestamp ') ifTrue:[
+			timeStampInfo := (chunkText copyFrom:16 to:(chunkText size - 6)) withoutSpaces.
+		    ] ifFalse:[
 
-		    changeChunks add:chunkText.
-		    changePositions add:chunkPos.
-		    headerLine := nil.
-		    changeDelta := ' '.
-
-		    sawExcla ifFalse:[
-			(chunkText startsWith:'''---- snap') ifTrue:[
-			    changeType := ''.
-			    headerLine := chunkText.
-			    changeString := (chunkText contractTo:maxLen).
-			] ifFalse:[
-			    |p sel cls|
-
-			    headerLine := chunkText , ' (doIt)'.
+			"
+			 only first line is saved in changeChunks ...
+			"
+			index := chunkText indexOf:(Character cr).
+			(index ~~ 0) ifTrue:[
+			    chunkText := chunkText copyTo:(index - 1).
 
-			    "
-			     first, assume doIt - then lets have a more detailed look ...
-			    "
-			    (chunkText startsWith:'''---- file') ifTrue:[
-				changeType := ''.
-			    ] ifFalse:[
-				changeType := '(doIt)'.
-			    ].    
-			    changeString := (chunkText contractTo:maxLen).
+			    "take care for comment changes - must still be a
+			     valid expression for classNameOfChange: to work"
 
-			    p := Parser parseExpression:chunkText.
-			    (p notNil 
-			     and:[p ~~ #Error
-			     and:[p isMessage]]) ifTrue:[
-				sel := p selector.
+			    (chunkText endsWith:'comment:''') ifTrue:[
+				chunkText := chunkText , '...'''
 			    ].
-			    (sel == #removeSelector:) ifTrue:[
-				p receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver name.
-				    changeClass := (Smalltalk classNamed:cls)
-				].
-				sel := (p args at:1) evaluate.
-
-				compareChanges ifTrue:[
-				    (changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
-					changeDelta := '?'
-				    ] ifFalse:[
-					(changeClass implements:sel asSymbol) ifTrue:[
-					    changeDelta := '-'.
-					]
-				    ]
-				].
-				changeType := '(remove)'.
-				changeString := self contractClass:cls selector:sel to:maxLen.
+			    (chunkText endsWith:'primitiveDefinitions:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
-			    (p isMessage 
-			    and:[p receiver isMessage
-			    and:[p receiver selector == #compiledMethodAt:]]) ifTrue:[
-				p receiver receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls)
-				].
-				(sel == #category:) ifTrue:[
-				    sel := (p receiver args at:1) evaluate.
-				    changeType := '(category change)'.
-				    changeString := self contractClass:cls selector:sel to:maxLen.
-				].
-				(sel == #privacy:) ifTrue:[
-				    sel := (p receiver args at:1) evaluate.
-				    changeType := '(privacy change)'.
-				    changeString := self contractClass:cls selector:sel to:maxLen.
-				].
+			    (chunkText endsWith:'primitiveVariables:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
-			    (#(#'subclass:'
-			      #'variableSubclass:'
-			      #'variableByteSubclass:'
-			      #'variableWordSubclass:'
-			      #'variableLongSubclass:'
-			      #'variableFloatSubclass:'
-			      #'variableDoubleSubclass:'
-			     ) includes:sel) ifTrue:[
-				changeType := '(class definition)'.
+			    (chunkText endsWith:'primitiveFunctions:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
 			].
-		    ] ifTrue:[
-			|done first p sel cls text|
 
-			"
-			 method definitions actually consist of
-			 two (or more) chunks; skip next chunk(s)
-			 up to an empty one.
-			 The system only writes one chunk,
-			 and we cannot handle more in this ChangesBrowser ....
-			"
-			cls := nil.
-			p := Parser parseExpression:chunkText.
+			changeChunks add:chunkText.
+			changePositions add:chunkPos.
+			changeTimeStamps add:timeStampInfo.
 
-			(p notNil and:[p ~~ #Error]) ifTrue:[
-			    sel := p selector.
-			    (sel == #methodsFor:) ifTrue:[
-				p receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver name.
-				    changeClass := Smalltalk classNamed:cls
-				].
-				category := (p args at:1) evaluate.
-			    ].
-			].
-			done := false.
-			first := true.
-			[done] whileFalse:[
-			    text := aStream nextChunk.
-			    text isNil ifTrue:[
-				done := true
+			headerLine := nil.
+			changeDelta := ' '.
+
+			sawExcla ifFalse:[
+			    (chunkText startsWith:'''---- snap') ifTrue:[
+				changeType := ''.
+				headerLine := chunkText.
+				changeString := (chunkText contractTo:maxLen).
 			    ] ifFalse:[
-				done := text isEmpty
-			    ].
-			    done ifFalse:[
-				first ifFalse:[
-				    Transcript showCr:'only one method per ''methodsFor:'' handled'.
-				] ifTrue:[
-				    first := false.
-				    "
-				     try to find the selector
-				    "
-				    sel := nil.
-				    cls notNil ifTrue:[
-					p := Parser 
-						 parseMethodSpecification:text
-						 in:nil
-						 ignoreErrors:true
-						 ignoreWarnings:true.
-					(p notNil and:[p ~~ #Error]) ifTrue:[
-					    sel := p selector.
-					]
+
+				|p sel cls|
+
+				headerLine := chunkText , ' (doIt)'.
+
+				"
+				 first, assume doIt - then lets have a more detailed look ...
+				"
+				(chunkText startsWith:'''---- file') ifTrue:[
+				    changeType := ''.
+				] ifFalse:[
+				    changeType := '(doIt)'.
+				].    
+				changeString := (chunkText contractTo:maxLen).
+
+				p := Parser parseExpression:chunkText.
+				(p notNil 
+				 and:[p ~~ #Error
+				 and:[p isMessage]]) ifTrue:[
+				    sel := p selector.
+				].
+				(sel == #removeSelector:) ifTrue:[
+				    p receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver name.
+					changeClass := (Smalltalk classNamed:cls)
 				    ].
+				    sel := (p args at:1) evaluate.
 
-				    sel isNil ifTrue:[
-					changeString := (chunkText contractTo:maxLen).
-					changeType := '(change)'.
-				    ] ifFalse:[
-					changeString :=  self contractClass:cls selector:sel to:maxLen.
-					changeType := '(method in: ''' , category , ''')'.
-				    ].
-				    sel isNil ifTrue:[
-					headerLine := chunkText , ' (change)'.
-				    ] ifFalse:[
-					headerLine := cls , ' ' , sel , ' ' , '(change category: ''' , category , ''')'.
-				    ].
-
-				    compareChanges ifTrue:[    
+				    compareChanges ifTrue:[
 					(changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
 					    changeDelta := '?'
 					] ifFalse:[
-					    (changeClass implements:sel asSymbol) ifFalse:[
-						changeDelta := '+'.
-					    ] ifTrue:[
-						|m currentText|
+					    (changeClass implements:sel asSymbol) ifTrue:[
+						changeDelta := '-'.
+					    ]
+					]
+				    ].
+				    changeType := '(remove)'.
+				    changeString := self contractClass:cls selector:sel to:maxLen.
+				].
+				(p isMessage 
+				and:[p receiver isMessage
+				and:[p receiver selector == #compiledMethodAt:]]) ifTrue:[
+				    p receiver receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls)
+				    ].
+				    (sel == #category:) ifTrue:[
+					sel := (p receiver args at:1) evaluate.
+					changeType := '(category change)'.
+					changeString := self contractClass:cls selector:sel to:maxLen.
+				    ].
+				    (sel == #privacy:) ifTrue:[
+					sel := (p receiver args at:1) evaluate.
+					changeType := '(privacy change)'.
+					changeString := self contractClass:cls selector:sel to:maxLen.
+				    ].
+				].
+				(#(#'subclass:'
+				  #'variableSubclass:'
+				  #'variableByteSubclass:'
+				  #'variableWordSubclass:'
+				  #'variableLongSubclass:'
+				  #'variableFloatSubclass:'
+				  #'variableDoubleSubclass:'
+				 ) includes:sel) ifTrue:[
+				    changeType := '(class definition)'.
+				].
+			    ]
+			] ifTrue:[ "sawExcla"
+			    |done first p sel cls text|
+
+			    "
+			     method definitions actually consist of
+			     two (or more) chunks; skip next chunk(s)
+			     up to an empty one.
+			     The system only writes one chunk,
+			     and we cannot handle more in this ChangesBrowser ....
+			    "
+			    cls := nil.
+			    p := Parser parseExpression:chunkText.
 
-						m := changeClass compiledMethodAt:sel asSymbol.
-						currentText := m source.
-						currentText notNil ifTrue:[
-						    text asString = currentText asString ifTrue:[
-							changeDelta := '='
+			    (p notNil and:[p ~~ #Error]) ifTrue:[
+				sel := p selector.
+				(sel == #methodsFor:) ifTrue:[
+				    p receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver name.
+					changeClass := Smalltalk classNamed:cls
+				    ].
+				    category := (p args at:1) evaluate.
+				].
+			    ].
+			    done := false.
+			    first := true.
+			    [done] whileFalse:[
+				text := aStream nextChunk.
+				text isNil ifTrue:[
+				    done := true
+				] ifFalse:[
+				    done := text isEmpty
+				].
+				done ifFalse:[
+				    first ifFalse:[
+					Transcript showCr:'only one method per ''methodsFor:'' handled'.
+				    ] ifTrue:[
+					first := false.
+					"
+					 try to find the selector
+					"
+					sel := nil.
+					cls notNil ifTrue:[
+					    p := Parser 
+						     parseMethodSpecification:text
+						     in:nil
+						     ignoreErrors:true
+						     ignoreWarnings:true.
+					    (p notNil and:[p ~~ #Error]) ifTrue:[
+						sel := p selector.
+					    ]
+					].
+
+					sel isNil ifTrue:[
+					    changeString := (chunkText contractTo:maxLen).
+					    changeType := '(change)'.
+					] ifFalse:[
+					    changeString :=  self contractClass:cls selector:sel to:maxLen.
+					    changeType := '(method in: ''' , category , ''')'.
+					].
+					sel isNil ifTrue:[
+					    headerLine := chunkText , ' (change)'.
+					] ifFalse:[
+					    headerLine := cls , ' ' , sel , ' ' , '(change category: ''' , category , ''')'.
+					].
+
+					compareChanges ifTrue:[    
+					    (changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
+						changeDelta := '?'
+					    ] ifFalse:[
+						(changeClass implements:sel asSymbol) ifFalse:[
+						    changeDelta := '+'.
+						] ifTrue:[
+						    |m currentText|
+
+						    m := changeClass compiledMethodAt:sel asSymbol.
+						    currentText := m source.
+						    currentText notNil ifTrue:[
+							text asString = currentText asString ifTrue:[
+							    changeDelta := '='
+							]
 						    ]
 						]
 					    ]
@@ -497,18 +501,19 @@
 				    ]
 				]
 			    ]
-			]
-		    ].
-		    changeString notNil ifTrue:[
-			entry := MultiColListEntry new.
-			entry tabulatorSpecification:tabSpec.
-			entry colAt:1 put:changeString.
-			entry colAt:2 put:changeDelta.
-			entry colAt:3 put:changeType.
-			changeHeaderLines add:entry
-		    ] ifFalse:[
-			headerLine notNil ifTrue:[
-			    changeHeaderLines add:headerLine
+			].
+			changeString notNil ifTrue:[
+			    entry := MultiColListEntry new.
+			    entry tabulatorSpecification:tabSpec.
+			    entry colAt:1 put:changeDelta.
+			    entry colAt:2 put:changeString.
+			    entry colAt:3 put:changeType.
+			    entry colAt:4 put:timeStampInfo.
+			    changeHeaderLines add:entry
+			] ifFalse:[
+			    headerLine notNil ifTrue:[
+				changeHeaderLines add:headerLine
+			    ]
 			]
 		    ]
 		]
@@ -524,6 +529,7 @@
     self checkIfFileHasChanged
 
     "Modified: 27.8.1995 / 23:06:55 / claus"
+    "Modified: 18.11.1995 / 17:24:31 / cg"
 !
 
 contractClass:className selector:selector to:maxLen
@@ -553,7 +559,10 @@
     changeChunks removeIndex:changeNr.
     changePositions removeIndex:changeNr.
     changeClassNames removeIndex:changeNr.
-    changeHeaderLines removeIndex:changeNr
+    changeHeaderLines removeIndex:changeNr.
+    changeTimeStamps removeIndex:changeNr
+
+    "Modified: 18.11.1995 / 17:08:44 / cg"
 !
 
 classNameOfChange:changeNr
@@ -806,7 +815,7 @@
      That way, if anything happens, either the original file is left unchanged,
      or we have at least a backup of the previous change file."
 
-    |inStream outStream tempfile|
+    |inStream outStream tempfile stamp|
 
     tempfile := Filename newTemporaryIn:nil.
     tempfile exists ifTrue:[tempfile remove].
@@ -831,6 +840,10 @@
 	    inStream position:(changePositions at:index).
 	    sawExcla := inStream peekFor:excla.
 	    chunk := inStream nextChunk.
+	    (stamp := changeTimeStamps at:index) notNil ifTrue:[
+		outStream nextPutAll:'''---- timestamp ' , stamp , ' ----'''.
+		outStream nextPut:excla; cr.
+	    ].
 
 	    sawExcla ifTrue:[
 		outStream nextPut:excla.
@@ -862,6 +875,8 @@
 	dir renameFile:tempfile name newName:changeFileName.
 	anyChanges := false
     ]
+
+    "Modified: 18.11.1995 / 17:12:37 / cg"
 !
 
 deleteChange:changeNr
@@ -1517,7 +1532,7 @@
 
     upperFrame := View origin:(0.0 @ 0.0) corner:(1.0 @ 0.3) in:panel.
 
-    v := ScrollableView for:SelectionInListView in:upperFrame.
+    v := HVScrollableView for:SelectionInListView miniScrollerH:true in:upperFrame.
     v origin:(0.0 @ 0.0) corner:(0.8 @ 1.0).
     changeListView := v scrolledView.
     changeListView delegate:self.
@@ -1539,6 +1554,14 @@
 
     anyChanges := false.
     ObjectMemory addDependent:self.   "to get shutdown-update"
+
+    tabSpec := TabulatorSpecification new.
+    tabSpec unit:#inch.
+    tabSpec positions:#(-1      0      5      8 ).
+    "                   +/-    cls>>sel  type   info"
+    tabSpec align:    #(#left  #left     #left  #left).
+
+    "Modified: 18.11.1995 / 17:30:59 / cg"
 !
 
 focusSequence
@@ -1583,9 +1606,18 @@
     aBoolean ~~ compareChanges ifTrue:[
 	compareChanges := aBoolean.
 	compareChanges ifTrue:[
+	    tabSpec positions:#(0      0.15      5      8 ).
 	    self doUpdate
+	] ifFalse:[
+	    "/
+	    "/ set tabs to hide compare-column
+	    "/
+	    tabSpec positions:#(-1      0      5      8 ).
+	    changeListView clear; redraw.
 	]
     ]
+
+    "Modified: 18.11.1995 / 17:30:20 / cg"
 !
 
 changeListMenu
--- a/ChangesBrowser.st	Fri Nov 17 18:24:05 1995 +0100
+++ b/ChangesBrowser.st	Sat Nov 18 18:04:08 1995 +0100
@@ -17,7 +17,8 @@
 		changePositions changeClassNames changeHeaderLines anyChanges
 		changeNrShown changeNrProcessed skipSignal 
 		compareChanges compareCheckBox
-		changeFileSize changeFileTimestamp checkBlock'
+		changeFileSize changeFileTimestamp checkBlock
+		changeTimeStamps tabSpec'
 	 classVariableNames:'CompressSnapshotInfo'
 	 poolDictionaries:''
 	 category:'Interface-Browsers'
@@ -40,7 +41,7 @@
 !
 
 version
-    ^ '$Header: /cvs/stx/stx/libtool/ChangesBrowser.st,v 1.47 1995-11-16 15:18:36 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libtool/ChangesBrowser.st,v 1.48 1995-11-18 17:04:08 cg Exp $'
 !
 
 documentation
@@ -226,20 +227,20 @@
      and a list of chunk-positions (changePositions).
      Starting with 2.10.3, the entries are multi-col entries;
      the cols are:
-	1   class/selector
-	2   delta (only if comparing)
+	1   delta (only if comparing)
 		'+' -> new method (w.r.t. current state)
 		'-' -> removed method (w.r.t. current state)
 		'?' -> class does not exist currently
 		'=' -> change is same as current methods source
+	2   class/selector
 	3   type of change
 		doit
 		method
 		category change
+	4   timestamp
 
      since comparing slows down startup time, it is now disabled by
-     default and can be enabled via a toggle.
-     "
+     default and can be enabled via a toggle."
 
     |aStream maxLen f|
 
@@ -253,13 +254,7 @@
     changeFileTimestamp := f info at:#modified.
 
     self withCursor:(Cursor read) do:[
-	|tabSpec myProcess myPriority|
-
-	tabSpec := TabulatorSpecification new.
-	tabSpec unit:#inch.
-	tabSpec positions:#(0         4.5       4.65   5.5 ).
-	"                   cls>>sel  +/-       type   info"
-	tabSpec align:    #(#left     #left     #left  #left).
+	|myProcess myPriority|
 
 	"
 	 this is a time consuming operation (especially, if reading an
@@ -272,11 +267,12 @@
 	].
 
 	[
-	    |excla|
+	    |excla timeStampInfo|
 
 	    changeChunks := OrderedCollection new.
 	    changeHeaderLines := OrderedCollection new.
 	    changePositions := OrderedCollection new.
+	    changeTimeStamps := OrderedCollection new.
 	    excla := aStream class chunkSeparator.
 
 	    [aStream atEnd] whileFalse:[
@@ -296,200 +292,208 @@
 		chunkText notNil ifTrue:[
 		    |index headerLine|
 
-		    "
-		     only first line is saved in changeChunks ...
-		    "
-		    index := chunkText indexOf:(Character cr).
-		    (index ~~ 0) ifTrue:[
-			chunkText := chunkText copyTo:(index - 1).
-
-			"take care for comment changes - must still be a
-			 valid expression for classNameOfChange: to work"
-
-			(chunkText endsWith:'comment:''') ifTrue:[
-			    chunkText := chunkText , '...'''
-			].
-			(chunkText endsWith:'primitiveDefinitions:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-			(chunkText endsWith:'primitiveVariables:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-			(chunkText endsWith:'primitiveFunctions:''%{') ifTrue:[
-			    chunkText := chunkText , '... %}'''
-			].
-		    ].
+		    (chunkText startsWith:'''---- timestamp ') ifTrue:[
+			timeStampInfo := (chunkText copyFrom:16 to:(chunkText size - 6)) withoutSpaces.
+		    ] ifFalse:[
 
-		    changeChunks add:chunkText.
-		    changePositions add:chunkPos.
-		    headerLine := nil.
-		    changeDelta := ' '.
-
-		    sawExcla ifFalse:[
-			(chunkText startsWith:'''---- snap') ifTrue:[
-			    changeType := ''.
-			    headerLine := chunkText.
-			    changeString := (chunkText contractTo:maxLen).
-			] ifFalse:[
-			    |p sel cls|
-
-			    headerLine := chunkText , ' (doIt)'.
+			"
+			 only first line is saved in changeChunks ...
+			"
+			index := chunkText indexOf:(Character cr).
+			(index ~~ 0) ifTrue:[
+			    chunkText := chunkText copyTo:(index - 1).
 
-			    "
-			     first, assume doIt - then lets have a more detailed look ...
-			    "
-			    (chunkText startsWith:'''---- file') ifTrue:[
-				changeType := ''.
-			    ] ifFalse:[
-				changeType := '(doIt)'.
-			    ].    
-			    changeString := (chunkText contractTo:maxLen).
+			    "take care for comment changes - must still be a
+			     valid expression for classNameOfChange: to work"
 
-			    p := Parser parseExpression:chunkText.
-			    (p notNil 
-			     and:[p ~~ #Error
-			     and:[p isMessage]]) ifTrue:[
-				sel := p selector.
+			    (chunkText endsWith:'comment:''') ifTrue:[
+				chunkText := chunkText , '...'''
 			    ].
-			    (sel == #removeSelector:) ifTrue:[
-				p receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver name.
-				    changeClass := (Smalltalk classNamed:cls)
-				].
-				sel := (p args at:1) evaluate.
-
-				compareChanges ifTrue:[
-				    (changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
-					changeDelta := '?'
-				    ] ifFalse:[
-					(changeClass implements:sel asSymbol) ifTrue:[
-					    changeDelta := '-'.
-					]
-				    ]
-				].
-				changeType := '(remove)'.
-				changeString := self contractClass:cls selector:sel to:maxLen.
+			    (chunkText endsWith:'primitiveDefinitions:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
-			    (p isMessage 
-			    and:[p receiver isMessage
-			    and:[p receiver selector == #compiledMethodAt:]]) ifTrue:[
-				p receiver receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls)
-				].
-				(sel == #category:) ifTrue:[
-				    sel := (p receiver args at:1) evaluate.
-				    changeType := '(category change)'.
-				    changeString := self contractClass:cls selector:sel to:maxLen.
-				].
-				(sel == #privacy:) ifTrue:[
-				    sel := (p receiver args at:1) evaluate.
-				    changeType := '(privacy change)'.
-				    changeString := self contractClass:cls selector:sel to:maxLen.
-				].
+			    (chunkText endsWith:'primitiveVariables:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
-			    (#(#'subclass:'
-			      #'variableSubclass:'
-			      #'variableByteSubclass:'
-			      #'variableWordSubclass:'
-			      #'variableLongSubclass:'
-			      #'variableFloatSubclass:'
-			      #'variableDoubleSubclass:'
-			     ) includes:sel) ifTrue:[
-				changeType := '(class definition)'.
+			    (chunkText endsWith:'primitiveFunctions:''%{') ifTrue:[
+				chunkText := chunkText , '... %}'''
 			    ].
 			].
-		    ] ifTrue:[
-			|done first p sel cls text|
 
-			"
-			 method definitions actually consist of
-			 two (or more) chunks; skip next chunk(s)
-			 up to an empty one.
-			 The system only writes one chunk,
-			 and we cannot handle more in this ChangesBrowser ....
-			"
-			cls := nil.
-			p := Parser parseExpression:chunkText.
+			changeChunks add:chunkText.
+			changePositions add:chunkPos.
+			changeTimeStamps add:timeStampInfo.
 
-			(p notNil and:[p ~~ #Error]) ifTrue:[
-			    sel := p selector.
-			    (sel == #methodsFor:) ifTrue:[
-				p receiver isUnaryMessage ifTrue:[
-				    cls := p receiver receiver name.
-				    changeClass := (Smalltalk classNamed:cls) class.
-				    cls := cls , ' class'.
-				] ifFalse:[
-				    cls := p receiver name.
-				    changeClass := Smalltalk classNamed:cls
-				].
-				category := (p args at:1) evaluate.
-			    ].
-			].
-			done := false.
-			first := true.
-			[done] whileFalse:[
-			    text := aStream nextChunk.
-			    text isNil ifTrue:[
-				done := true
+			headerLine := nil.
+			changeDelta := ' '.
+
+			sawExcla ifFalse:[
+			    (chunkText startsWith:'''---- snap') ifTrue:[
+				changeType := ''.
+				headerLine := chunkText.
+				changeString := (chunkText contractTo:maxLen).
 			    ] ifFalse:[
-				done := text isEmpty
-			    ].
-			    done ifFalse:[
-				first ifFalse:[
-				    Transcript showCr:'only one method per ''methodsFor:'' handled'.
-				] ifTrue:[
-				    first := false.
-				    "
-				     try to find the selector
-				    "
-				    sel := nil.
-				    cls notNil ifTrue:[
-					p := Parser 
-						 parseMethodSpecification:text
-						 in:nil
-						 ignoreErrors:true
-						 ignoreWarnings:true.
-					(p notNil and:[p ~~ #Error]) ifTrue:[
-					    sel := p selector.
-					]
+
+				|p sel cls|
+
+				headerLine := chunkText , ' (doIt)'.
+
+				"
+				 first, assume doIt - then lets have a more detailed look ...
+				"
+				(chunkText startsWith:'''---- file') ifTrue:[
+				    changeType := ''.
+				] ifFalse:[
+				    changeType := '(doIt)'.
+				].    
+				changeString := (chunkText contractTo:maxLen).
+
+				p := Parser parseExpression:chunkText.
+				(p notNil 
+				 and:[p ~~ #Error
+				 and:[p isMessage]]) ifTrue:[
+				    sel := p selector.
+				].
+				(sel == #removeSelector:) ifTrue:[
+				    p receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver name.
+					changeClass := (Smalltalk classNamed:cls)
 				    ].
+				    sel := (p args at:1) evaluate.
 
-				    sel isNil ifTrue:[
-					changeString := (chunkText contractTo:maxLen).
-					changeType := '(change)'.
-				    ] ifFalse:[
-					changeString :=  self contractClass:cls selector:sel to:maxLen.
-					changeType := '(method in: ''' , category , ''')'.
-				    ].
-				    sel isNil ifTrue:[
-					headerLine := chunkText , ' (change)'.
-				    ] ifFalse:[
-					headerLine := cls , ' ' , sel , ' ' , '(change category: ''' , category , ''')'.
-				    ].
-
-				    compareChanges ifTrue:[    
+				    compareChanges ifTrue:[
 					(changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
 					    changeDelta := '?'
 					] ifFalse:[
-					    (changeClass implements:sel asSymbol) ifFalse:[
-						changeDelta := '+'.
-					    ] ifTrue:[
-						|m currentText|
+					    (changeClass implements:sel asSymbol) ifTrue:[
+						changeDelta := '-'.
+					    ]
+					]
+				    ].
+				    changeType := '(remove)'.
+				    changeString := self contractClass:cls selector:sel to:maxLen.
+				].
+				(p isMessage 
+				and:[p receiver isMessage
+				and:[p receiver selector == #compiledMethodAt:]]) ifTrue:[
+				    p receiver receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls)
+				    ].
+				    (sel == #category:) ifTrue:[
+					sel := (p receiver args at:1) evaluate.
+					changeType := '(category change)'.
+					changeString := self contractClass:cls selector:sel to:maxLen.
+				    ].
+				    (sel == #privacy:) ifTrue:[
+					sel := (p receiver args at:1) evaluate.
+					changeType := '(privacy change)'.
+					changeString := self contractClass:cls selector:sel to:maxLen.
+				    ].
+				].
+				(#(#'subclass:'
+				  #'variableSubclass:'
+				  #'variableByteSubclass:'
+				  #'variableWordSubclass:'
+				  #'variableLongSubclass:'
+				  #'variableFloatSubclass:'
+				  #'variableDoubleSubclass:'
+				 ) includes:sel) ifTrue:[
+				    changeType := '(class definition)'.
+				].
+			    ]
+			] ifTrue:[ "sawExcla"
+			    |done first p sel cls text|
+
+			    "
+			     method definitions actually consist of
+			     two (or more) chunks; skip next chunk(s)
+			     up to an empty one.
+			     The system only writes one chunk,
+			     and we cannot handle more in this ChangesBrowser ....
+			    "
+			    cls := nil.
+			    p := Parser parseExpression:chunkText.
 
-						m := changeClass compiledMethodAt:sel asSymbol.
-						currentText := m source.
-						currentText notNil ifTrue:[
-						    text asString = currentText asString ifTrue:[
-							changeDelta := '='
+			    (p notNil and:[p ~~ #Error]) ifTrue:[
+				sel := p selector.
+				(sel == #methodsFor:) ifTrue:[
+				    p receiver isUnaryMessage ifTrue:[
+					cls := p receiver receiver name.
+					changeClass := (Smalltalk classNamed:cls) class.
+					cls := cls , ' class'.
+				    ] ifFalse:[
+					cls := p receiver name.
+					changeClass := Smalltalk classNamed:cls
+				    ].
+				    category := (p args at:1) evaluate.
+				].
+			    ].
+			    done := false.
+			    first := true.
+			    [done] whileFalse:[
+				text := aStream nextChunk.
+				text isNil ifTrue:[
+				    done := true
+				] ifFalse:[
+				    done := text isEmpty
+				].
+				done ifFalse:[
+				    first ifFalse:[
+					Transcript showCr:'only one method per ''methodsFor:'' handled'.
+				    ] ifTrue:[
+					first := false.
+					"
+					 try to find the selector
+					"
+					sel := nil.
+					cls notNil ifTrue:[
+					    p := Parser 
+						     parseMethodSpecification:text
+						     in:nil
+						     ignoreErrors:true
+						     ignoreWarnings:true.
+					    (p notNil and:[p ~~ #Error]) ifTrue:[
+						sel := p selector.
+					    ]
+					].
+
+					sel isNil ifTrue:[
+					    changeString := (chunkText contractTo:maxLen).
+					    changeType := '(change)'.
+					] ifFalse:[
+					    changeString :=  self contractClass:cls selector:sel to:maxLen.
+					    changeType := '(method in: ''' , category , ''')'.
+					].
+					sel isNil ifTrue:[
+					    headerLine := chunkText , ' (change)'.
+					] ifFalse:[
+					    headerLine := cls , ' ' , sel , ' ' , '(change category: ''' , category , ''')'.
+					].
+
+					compareChanges ifTrue:[    
+					    (changeClass isNil or:[changeClass isLoaded not]) ifTrue:[
+						changeDelta := '?'
+					    ] ifFalse:[
+						(changeClass implements:sel asSymbol) ifFalse:[
+						    changeDelta := '+'.
+						] ifTrue:[
+						    |m currentText|
+
+						    m := changeClass compiledMethodAt:sel asSymbol.
+						    currentText := m source.
+						    currentText notNil ifTrue:[
+							text asString = currentText asString ifTrue:[
+							    changeDelta := '='
+							]
 						    ]
 						]
 					    ]
@@ -497,18 +501,19 @@
 				    ]
 				]
 			    ]
-			]
-		    ].
-		    changeString notNil ifTrue:[
-			entry := MultiColListEntry new.
-			entry tabulatorSpecification:tabSpec.
-			entry colAt:1 put:changeString.
-			entry colAt:2 put:changeDelta.
-			entry colAt:3 put:changeType.
-			changeHeaderLines add:entry
-		    ] ifFalse:[
-			headerLine notNil ifTrue:[
-			    changeHeaderLines add:headerLine
+			].
+			changeString notNil ifTrue:[
+			    entry := MultiColListEntry new.
+			    entry tabulatorSpecification:tabSpec.
+			    entry colAt:1 put:changeDelta.
+			    entry colAt:2 put:changeString.
+			    entry colAt:3 put:changeType.
+			    entry colAt:4 put:timeStampInfo.
+			    changeHeaderLines add:entry
+			] ifFalse:[
+			    headerLine notNil ifTrue:[
+				changeHeaderLines add:headerLine
+			    ]
 			]
 		    ]
 		]
@@ -524,6 +529,7 @@
     self checkIfFileHasChanged
 
     "Modified: 27.8.1995 / 23:06:55 / claus"
+    "Modified: 18.11.1995 / 17:24:31 / cg"
 !
 
 contractClass:className selector:selector to:maxLen
@@ -553,7 +559,10 @@
     changeChunks removeIndex:changeNr.
     changePositions removeIndex:changeNr.
     changeClassNames removeIndex:changeNr.
-    changeHeaderLines removeIndex:changeNr
+    changeHeaderLines removeIndex:changeNr.
+    changeTimeStamps removeIndex:changeNr
+
+    "Modified: 18.11.1995 / 17:08:44 / cg"
 !
 
 classNameOfChange:changeNr
@@ -806,7 +815,7 @@
      That way, if anything happens, either the original file is left unchanged,
      or we have at least a backup of the previous change file."
 
-    |inStream outStream tempfile|
+    |inStream outStream tempfile stamp|
 
     tempfile := Filename newTemporaryIn:nil.
     tempfile exists ifTrue:[tempfile remove].
@@ -831,6 +840,10 @@
 	    inStream position:(changePositions at:index).
 	    sawExcla := inStream peekFor:excla.
 	    chunk := inStream nextChunk.
+	    (stamp := changeTimeStamps at:index) notNil ifTrue:[
+		outStream nextPutAll:'''---- timestamp ' , stamp , ' ----'''.
+		outStream nextPut:excla; cr.
+	    ].
 
 	    sawExcla ifTrue:[
 		outStream nextPut:excla.
@@ -862,6 +875,8 @@
 	dir renameFile:tempfile name newName:changeFileName.
 	anyChanges := false
     ]
+
+    "Modified: 18.11.1995 / 17:12:37 / cg"
 !
 
 deleteChange:changeNr
@@ -1517,7 +1532,7 @@
 
     upperFrame := View origin:(0.0 @ 0.0) corner:(1.0 @ 0.3) in:panel.
 
-    v := ScrollableView for:SelectionInListView in:upperFrame.
+    v := HVScrollableView for:SelectionInListView miniScrollerH:true in:upperFrame.
     v origin:(0.0 @ 0.0) corner:(0.8 @ 1.0).
     changeListView := v scrolledView.
     changeListView delegate:self.
@@ -1539,6 +1554,14 @@
 
     anyChanges := false.
     ObjectMemory addDependent:self.   "to get shutdown-update"
+
+    tabSpec := TabulatorSpecification new.
+    tabSpec unit:#inch.
+    tabSpec positions:#(-1      0      5      8 ).
+    "                   +/-    cls>>sel  type   info"
+    tabSpec align:    #(#left  #left     #left  #left).
+
+    "Modified: 18.11.1995 / 17:30:59 / cg"
 !
 
 focusSequence
@@ -1583,9 +1606,18 @@
     aBoolean ~~ compareChanges ifTrue:[
 	compareChanges := aBoolean.
 	compareChanges ifTrue:[
+	    tabSpec positions:#(0      0.15      5      8 ).
 	    self doUpdate
+	] ifFalse:[
+	    "/
+	    "/ set tabs to hide compare-column
+	    "/
+	    tabSpec positions:#(-1      0      5      8 ).
+	    changeListView clear; redraw.
 	]
     ]
+
+    "Modified: 18.11.1995 / 17:30:20 / cg"
 !
 
 changeListMenu