VersionDiffBrowser.st
author Claus Gittinger <cg@exept.de>
Tue, 14 Dec 1999 21:15:32 +0100
changeset 2488 65689c6d5bd6
parent 2486 29a320ed5f2a
child 2497 ef306e387021
permissions -rw-r--r--
changes to avoid loosing the selection when the list is updated

ApplicationModel subclass:#VersionDiffBrowser
	instanceVariableNames:'classBeingCompared versionA versionB changeSetA changeSetB
		filteredChangeSetA filteredChangeSetB changedMethodsChangeSet
		indexFromChangedToA indexFromChangedToB diffTextView'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-Browsers'
!

VersionDiffBrowser comment:'some comment
'!


!VersionDiffBrowser class methodsFor:'interface specs'!

windowSpec
    "This resource specification was automatically generated
     by the UIPainter of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIPainter may not be able to read the specification."

    "
     UIPainter new openOnClass:VersionDiffBrowser andSelector:#windowSpec
     VersionDiffBrowser new openInterface:#windowSpec
     VersionDiffBrowser open
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #windowSpec
        #window: 
       #(#WindowSpec
          #label: 'Version DiffBrowser'
          #name: 'Version DiffBrowser'
          #layout: #(#LayoutFrame 220 0 200 0 1084 0 703 0)
          #level: 0
          #min: #(#Point 10 10)
          #max: #(#Point nil nil)
          #bounds: #(#Rectangle 220 200 1085 704)
          #menu: #mainMenu
          #usePreferredExtent: false
          #returnIsOKInDialog: true
          #escapeIsCancelInDialog: true
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#VariableVerticalPanelSpec
              #name: 'VariableVerticalPanel1'
              #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
              #handles: #(#Any 0.319444 1.0)
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#HorizontalPanelViewSpec
                    #name: 'TopHorizontalPanel'
                    #horizontalLayout: #fit
                    #verticalLayout: #fit
                    #horizontalSpace: 3
                    #verticalSpace: 3
                    #component: 
                   #(#SpecCollection
                      #collection: #(
                       #(#ViewSpec
                          #name: 'BoxA'
                          #level: 0
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#LabelSpec
                                #label: 'Only in A'
                                #name: 'OnlyInALabel'
                                #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 20 0)
                                #translateLabel: true
                                #labelChannel: #onlyInALabelHolder
                              )
                             #(#SequenceViewSpec
                                #name: 'ListA'
                                #layout: #(#LayoutFrame 0 0.0 20 0.0 0 1.0 -3 1.0)
                                #model: #methodsOnlyInASelection
                                #menu: #menuA
                                #hasHorizontalScrollBar: true
                                #hasVerticalScrollBar: true
                                #miniScrollerHorizontal: true
                                #valueChangeSelector: #methodsOnlyInASelectionChanged
                                #useIndex: true
                                #sequenceList: #methodsOnlyInA
                              )
                             )
                           
                          )
                          #extent: #(#Point 286 160)
                        )
                       #(#ViewSpec
                          #name: 'BoxM'
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#LabelSpec
                                #label: 'Changed'
                                #name: 'Label2'
                                #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 20 0)
                                #translateLabel: true
                                #labelChannel: #changedLabelHolder
                              )
                             #(#SequenceViewSpec
                                #name: 'ListM'
                                #layout: #(#LayoutFrame 0 0.0 20 0.0 0 1.0 -3 1.0)
                                #model: #methodsChangedSelection
                                #menu: #menuM
                                #hasHorizontalScrollBar: true
                                #hasVerticalScrollBar: true
                                #miniScrollerHorizontal: true
                                #valueChangeSelector: #methodsChangedSelectionChanged
                                #useIndex: true
                                #sequenceList: #methodsChanged
                              )
                             )
                           
                          )
                          #extent: #(#Point 286 160)
                        )
                       #(#ViewSpec
                          #name: 'BoxB'
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#LabelSpec
                                #label: 'Only in B'
                                #name: 'OnlyInBLabel'
                                #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 20 0)
                                #translateLabel: true
                                #labelChannel: #onlyInBLabelHolder
                              )
                             #(#SequenceViewSpec
                                #name: 'ListB'
                                #layout: #(#LayoutFrame 0 0.0 20 0.0 0 1.0 -3 1.0)
                                #model: #methodsOnlyInBSelection
                                #menu: #menuB
                                #hasHorizontalScrollBar: true
                                #hasVerticalScrollBar: true
                                #miniScrollerHorizontal: true
                                #valueChangeSelector: #methodsOnlyInBSelectionChanged
                                #useIndex: true
                                #sequenceList: #methodsOnlyInB
                              )
                             )
                           
                          )
                          #extent: #(#Point 287 160)
                        )
                       )
                     
                    )
                  )
                 #(#ViewSpec
                    #name: 'Box4'
                    #component: 
                   #(#SpecCollection
                      #collection: #(
                       #(#ViewSpec
                          #name: 'diffTextViewBox'
                          #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
                          #initiallyInvisible: true
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#LabelSpec
                                #label: 'A'
                                #name: 'DiffTextLabelA'
                                #layout: #(#LayoutFrame 0 0 0 0 0 0.5 20 0)
                                #translateLabel: true
                                #labelChannel: #diffTextLabelA
                              )
                             #(#LabelSpec
                                #label: 'B'
                                #name: 'DiffTextLabelB'
                                #layout: #(#LayoutFrame 0 0.5 0 0 0 1 20 0)
                                #translateLabel: true
                                #labelChannel: #diffTextLabelB
                              )
                             #(#ArbitraryComponentSpec
                                #name: 'diffTextView'
                                #layout: #(#LayoutFrame 0 0.0 20 0.0 0 1.0 0 1.0)
                                #hasBorder: false
                                #component: #diffTextView
                              )
                             )
                           
                          )
                        )
                       #(#TextEditorSpec
                          #name: 'singleTextView'
                          #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
                          #model: #methodText
                          #hasHorizontalScrollBar: true
                          #hasVerticalScrollBar: true
                          #miniScrollerHorizontal: true
                        )
                       )
                     
                    )
                  )
                 )
               
              )
            )
           )
         
        )
      )
! !

!VersionDiffBrowser class methodsFor:'menu specs'!

mainMenu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:VersionDiffBrowser andSelector:#mainMenu
     (Menu new fromLiteralArrayEncoding:(VersionDiffBrowser mainMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'File'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'Create PatchFile'
                  #translateLabel: true
                  #value: #createPatchFile
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'Exit'
                  #translateLabel: true
                  #value: #closeRequest
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #label: 'Help'
            #translateLabel: true
            #startGroup: #right
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'Documentation'
                  #translateLabel: true
                  #value: #openDocumentation
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'About this Application'
                  #translateLabel: true
                  #value: #openAboutThisApplication
                )
               )
              nil
              nil
            )
          )
         )
        nil
        nil
      )
!

menuA
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:VersionDiffBrowser andSelector:#menu1
     (Menu new fromLiteralArrayEncoding:(VersionDiffBrowser menu1)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Inspect'
            #translateLabel: true
            #value: #inspectSelectedChangeInA
            #choiceValue: 'nil "UndefinedObject" '
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Browse'
            #translateLabel: true
            #value: #browseClassInA
          )
         )
        nil
        nil
      )
!

menuB
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:VersionDiffBrowser andSelector:#menuB
     (Menu new fromLiteralArrayEncoding:(VersionDiffBrowser menuB)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Inspect'
            #translateLabel: true
            #value: #inspectSelectedChangeInB
            #choiceValue: 'nil "UndefinedObject" '
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Browse'
            #translateLabel: true
            #value: #browseClassInB
          )
         )
        nil
        nil
      )
!

menuM
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:VersionDiffBrowser andSelector:#menu1
     (Menu new fromLiteralArrayEncoding:(VersionDiffBrowser menu1)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'Inspect both'
            #translateLabel: true
            #value: #inspectSelectedChangeInM
            #choiceValue: 'nil "UndefinedObject" '
          )
         #(#MenuItem
            #label: '-'
          )
         #(#MenuItem
            #label: 'Browse'
            #translateLabel: true
            #value: #browseClassInM
          )
         )
        nil
        nil
      )
! !

!VersionDiffBrowser class methodsFor:'startup'!

openOnClass:aClass labelA:lblA sourceA:srcA labelB:lblB sourceB:srcB 
    |browser|

    browser := self new.
    browser allButOpen.

    browser classBeingCompared:aClass.
    browser 
        labelA:lblA sourceA:srcA
        labelB:lblB sourceB:srcB.

    browser openWindow.

    ^ browser.

    "
     self openOnClass:Array versionA:'1.116' versionB:'1.113'
    "
!

openOnClass:aClass versionA:vsnA versionB:vsnB
    |browser|

    browser := self new.
    browser allButOpen.

    browser classBeingCompared:aClass.
    browser versionA:vsnA.
    browser versionB:vsnB.

    browser openWindow.

    ^ browser.

    "
     self openOnClass:Array versionA:'1.116' versionB:'1.113'
    "
! !

!VersionDiffBrowser methodsFor:'accessing'!

classBeingCompared
    "return the value of the instance variable 'classBeingCompared' (automatically generated)"

    ^ classBeingCompared!

classBeingCompared:something
    "set the value of the instance variable 'classBeingCompared' (automatically generated)"

    classBeingCompared := something.
    self updateViewsIfPossible.
!

labelA:lblA sourceA:srcA labelB:lblB sourceB:srcB
    |s|

    changeSetA := ChangeSet fromStream:(s := srcA readStream).
    s close.
    changeSetB := ChangeSet fromStream:(s := srcB readStream).
    s close.
    versionA := lblA.
    versionB := lblB.
    self updateLabels.
    self updateLists.

!

versionA
    "return the value of the instance variable 'versionA' (automatically generated)"

    ^ versionA!

versionA:something
    "set the value of the instance variable 'versionA' (automatically generated)"

    versionA := something.
    self updateViewsIfPossible.

!

versionB
    "return the value of the instance variable 'versionB' (automatically generated)"

    ^ versionB!

versionB:something
    "set the value of the instance variable 'versionB' (automatically generated)"

    versionB := something.
    self updateViewsIfPossible.

! !

!VersionDiffBrowser methodsFor:'actions'!

methodsChangedSelectionChanged
    |sel idxA idxB changeA changeB|

    sel := self methodsChangedSelection value.
    sel notNil ifTrue:[
        self methodsOnlyInASelection value:nil.
        self methodsOnlyInBSelection value:nil.

        idxA := indexFromChangedToA at:sel ifAbsent:nil.
        idxB := indexFromChangedToB at:sel ifAbsent:nil.

        changeA := changeSetA at:idxA.
        changeB := changeSetB at:idxB.

        self withWaitCursorDo:[
            self diffTextView text1:changeA source text2:changeB source.
            self diffTextView moveToNextChanged.
        ].

        self showDiffTextView.

    ]

    "Modified: / 3.9.1999 / 15:01:30 / ps"
!

methodsOnlyInASelectionChanged
    |change sel|

    sel := self methodsOnlyInASelection value.
    sel notNil ifTrue:[
        self methodsOnlyInBSelection value:nil.
        self methodsChangedSelection value:nil.

        change := filteredChangeSetA at:sel.
        self methodText value:(change source).    
        self showSingleTextView.
    ]
!

methodsOnlyInBSelectionChanged
    |change sel|

    sel := self methodsOnlyInBSelection value.
    sel notNil ifTrue:[
        self methodsOnlyInASelection value:nil.
        self methodsChangedSelection value:nil.

        change := filteredChangeSetB at:sel.
        self methodText value:(change source).    
        self showSingleTextView.
    ]
! !

!VersionDiffBrowser methodsFor:'aspects'!

changedLabelHolder
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#changedLabelHolder) isNil ifTrue:[
        builder aspectAt:#changedLabelHolder put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

diffTextLabelA
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#diffTextLabelA) isNil ifTrue:[
        builder aspectAt:#diffTextLabelA put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

diffTextLabelB
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#diffTextLabelB) isNil ifTrue:[
        builder aspectAt:#diffTextLabelB put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

methodText
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodText) isNil ifTrue:[
        builder aspectAt:#methodText put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

methodsChanged
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsChanged) isNil ifTrue:[
        builder aspectAt:#methodsChanged put:(holder :=  List new).
    ].
    ^ holder.
!

methodsChangedSelection
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsChangedSelection) isNil ifTrue:[
        builder aspectAt:#methodsChangedSelection put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

methodsOnlyInA
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsOnlyInA) isNil ifTrue:[
        builder aspectAt:#methodsOnlyInA put:(holder :=  List new).
    ].
    ^ holder.
!

methodsOnlyInASelection
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsOnlyInASelection) isNil ifTrue:[
        builder aspectAt:#methodsOnlyInASelection put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

methodsOnlyInB
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsOnlyInB) isNil ifTrue:[
        builder aspectAt:#methodsOnlyInB put:(holder :=  List new).
    ].
    ^ holder.
!

methodsOnlyInBSelection
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#methodsOnlyInBSelection) isNil ifTrue:[
        builder aspectAt:#methodsOnlyInBSelection put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

onlyInALabelHolder
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#onlyInALabelHolder) isNil ifTrue:[
        builder aspectAt:#onlyInALabelHolder put:(holder :=  ValueHolder new).
    ].
    ^ holder.
!

onlyInBLabelHolder
    "automatically generated by UIPainter ..."

    "*** the code below creates a default model when invoked."
    "*** (which may not be the one you wanted)"
    "*** Please change as required and accept in the browser."

    |holder|

    (holder := builder bindingAt:#onlyInBLabelHolder) isNil ifTrue:[
        builder aspectAt:#onlyInBLabelHolder put:(holder :=  ValueHolder new).
    ].
    ^ holder.
! !

!VersionDiffBrowser methodsFor:'initialization & release'!

closeDownViews
    "This is a hook method generated by the Browser.
     It will be invoked when your app/dialog-window is really closed."

    "/ change the code below as required ...
    "/ This should cleanup any leftover resources
    "/ (for example, temporary files)
    "/ super closeRequest will initiate the closeDown

    "/ add your code here

    "/ do not remove the one below ...
    ^ super closeDownViews
!

closeRequest
    "This is a hook method generated by the Browser.
     It will be invoked when your app/dialog-window is about to be
     closed (and has a chance to suppress the close)."

    "/ change the code below as required ...
    "/ Closing can be suppressed, by returning.
    "/ super closeRequest will initiate the closeDown

    ^ super closeRequest
!

postBuildWith:aBuilder
    "This is a hook method generated by the Browser.
     It will be invoked during the initialization of your app/dialog,
     after all of the visual components have been built, 
     but BEFORE the top window is made visible.
     Add any app-specific actions here (reading files, setting up
     values etc.)"

    "/ add any code here ...

    (aBuilder componentAt:#TopHorizontalPanel) ignoreInvisibleComponents:true.

    ^ super postBuildWith:aBuilder
! !

!VersionDiffBrowser methodsFor:'menu action'!

browseChange:aChange
    SystemBrowser 
        openInClass:aChange changeClass
        selector:aChange changeSelector.
!

browseClassInA
    self browseChange:(self selectedChangeInA).
!

browseClassInB
    self browseChange:(self selectedChangeInB).
!

browseClassInM
    self browseChange:(self selectedChangesInM first).
!

createPatchFile
    "create a pacthFile, to patch the old version (versionB) into
     the new version (versionA)"

    |oldVersion newVersion vsnMthdA vsnMthdB vsnA vsnB info|

    ^ self warn:'This function is not yet implemented'.

    vsnMthdA := changeSetA 
                detect:[:ch | ch isMethodChange
                              and:[ch changeClass isMeta
                              and:[ch changeSelector = #version]]]
                ifNone:nil.
    vsnMthdB := changeSetA 
                detect:[:ch | ch isMethodChange
                              and:[ch changeClass isMeta
                              and:[ch changeSelector = #version]]]
                ifNone:nil.

    vsnMthdA notNil ifTrue:[
        "/ extract the version        
        vsnA := Class revisionStringFromSource:vsnMthdA source.
        vsnA notNil ifTrue:[
            info := Class revisionInfoFromString:vsnA.
            info notNil ifTrue:[
                vsnA := info at:#revision ifAbsent:nil.
            ] ifFalse:[
                vsnA := nil.
            ].
        ].
    ].
    vsnMthdB notNil ifTrue:[
        "/ extract the version        
        vsnB := Class revisionStringFromSource:vsnMthdB source.
        vsnB notNil ifTrue:[
            info := Class revisionInfoFromString:vsnB.
            info notNil ifTrue:[
                vsnB := info at:#revision ifAbsent:nil.
            ] ifFalse:[
                vsnB := nil.
            ].
        ].
    ].

    (vsnA isNil 
    or:[vsnB isNil
    or:[vsnA = vsnB]]) ifTrue:[
        self warn:'The generated patch file will not be able to validate/update the class version'.
    ].

self halt.

    "Modified: / 14.12.1999 / 15:34:28 / cg"
!

inspectSelectedChangeInA
    self selectedChangeInA inspect.
!

inspectSelectedChangeInB
    self selectedChangeInB inspect.
!

inspectSelectedChangeInM
    |changes|

    changes := self selectedChangesInM.
    changes do:[:change |
        change inspect
    ].
!

selectedChangeInA
    |sel change|

    sel := self methodsOnlyInASelection value.
    sel notNil ifTrue:[
        change := (changeSetA at:sel).
    ].
    ^ change
!

selectedChangeInB
    |sel change|

    sel := self methodsOnlyInBSelection value.
    sel notNil ifTrue:[
        change := (changeSetB at:sel).
    ].
    ^ change
!

selectedChangesInM
    |sel idxA idxB|

    sel := self methodsChangedSelection value.
    sel notNil ifTrue:[
        idxA := indexFromChangedToA at:sel ifAbsent:nil.
        idxB := indexFromChangedToB at:sel ifAbsent:nil.

        ^ Array with:(changeSetA at:idxA) 
                with:(changeSetB at:idxB) 
    ].
    ^ nil
! !

!VersionDiffBrowser methodsFor:'private'!

showDiffTextView
    (self builder componentAt:#diffTextViewBox) raise; beVisible.
    (self builder componentAt:#singleTextView) beInvisible.
    (self builder componentAt:#diffTextView) realizeAllSubViews.
    self addAcceptToTextViewMenus.

!

showSingleTextView
    (self builder componentAt:#singleTextView) raise; beVisible.
    (self builder componentAt:#diffTextViewBox) beInvisible.

!

updateFromVersions
    |mgr versionAStream versionBStream|

    mgr := classBeingCompared sourceCodeManager.

    versionAStream := mgr getSourceStreamFor:classBeingCompared revision:versionA.
    changeSetA := ChangeSet fromStream:versionAStream.
    versionAStream close.

    versionBStream := mgr getSourceStreamFor:classBeingCompared revision:versionB.
    changeSetB := ChangeSet fromStream:versionBStream.
    versionBStream close.

    self updateLists

!

updateLabels
    versionA notNil ifTrue:[
        self diffTextLabelA value:versionA.
        self onlyInALabelHolder value:('Only in ' , versionA).
        (self builder componentAt:#OnlyInALabel) backgroundColor:Color green darkened.
    ].
    versionB notNil ifTrue:[
        self diffTextLabelB value:versionB.
        self onlyInBLabelHolder value:('Only in ' , versionB).
        (self builder componentAt:#OnlyInBLabel) backgroundColor:Color red darkened.
    ].
!

updateLists
    "walk over the two changeSets and add change to one of the tree lists"

    |mgr nmListA nmListB nmListM generator|

    filteredChangeSetA := ChangeSet new.
    filteredChangeSetB := ChangeSet new.
    changedMethodsChangeSet := ChangeSet new.

    indexFromChangedToA := OrderedCollection new.
    indexFromChangedToB := OrderedCollection new.

    changeSetA keysAndValuesDo:[:idxA :aChangeInA |
        |anyFound ch|

        anyFound := false.

        changeSetB keysAndValuesDo:[:idxB :aChangeInB |
            (aChangeInA isForSameAs:aChangeInB) ifTrue:[
                anyFound := true.

                "/ also in B - is it different ?
                (aChangeInA sameAs:aChangeInB) ifFalse:[
                    changedMethodsChangeSet add:aChangeInA.
                    indexFromChangedToA add:idxA.
                    indexFromChangedToB add:idxB.
                ] ifTrue:[
                    aChangeInA isMethodChange ifTrue:[
                        aChangeInA methodCategory ~= aChangeInB methodCategory ifTrue:[
                            "/ only the category is different;
                            "/ make it a MethodCategory changes.

                            ch := MethodCategoryChange new
                                    className:aChangeInA className
                                    selector:aChangeInA selector
                                    category:aChangeInA methodCategory.
                            changeSetA at:idxA put:ch.

                            ch := MethodCategoryChange new
                                    className:aChangeInB className
                                    selector:aChangeInB selector
                                    category:aChangeInB methodCategory.
                            changeSetB at:idxB put:ch.

                            changedMethodsChangeSet add:aChangeInA.
                            indexFromChangedToA add:idxA.
                            indexFromChangedToB add:idxB.
                        ]
                    ].
                ]
            ] ifFalse:[
                (aChangeInA sameAs:aChangeInB) ifTrue:[
                    anyFound := true.
                ] ifFalse:[
                ]
            ]
        ].

        anyFound ifFalse:[
            filteredChangeSetA add:aChangeInA.
        ]
    ].

    changeSetB keysAndValuesDo:[:idxB :aChangeInB |
        |anyFound|

        anyFound := false.

        changeSetA do:[:aChangeInA |
            |idxM|

            (aChangeInA isForSameAs:aChangeInB) ifTrue:[
                anyFound := true.

                "/ also in B - is it different ?
                (aChangeInA sameAs:aChangeInB) ifFalse:[
                    "/ already there ?
                    idxM := changedMethodsChangeSet findFirst:[:c | c isForSameAs:aChangeInB].
                    idxM == 0 ifTrue:[
                        changedMethodsChangeSet add:aChangeInB.
                        indexFromChangedToB add:idxB.
                    ] ifFalse:[
                        indexFromChangedToB at:idxM put:idxB
                    ]
                ]
            ] ifFalse:[
                (aChangeInA sameAs:aChangeInB) ifTrue:[
                    anyFound := true.
                ] ifFalse:[
                ]
            ]
        ].
        anyFound ifFalse:[
            filteredChangeSetB add:aChangeInB.
        ]
    ].

    generator := [:c |
        |result mPkg cPkg cls m|

        c isClassDefinitionChange ifTrue:[
            result := c printStringWithoutClass
        ] ifFalse:[
            c isDoIt ifTrue:[
                result := c source , ' (doIt)'
            ] ifFalse:[
                c className = classBeingCompared name ifTrue:[
                    result := c printStringWithoutClass.
                    c isMethodChange ifTrue:[
                        cls := c changeClass.
                        cls notNil ifTrue:[
                            m := cls compiledMethodAt:c selector.
                            (m notNil 
                            and:[m package ~= cls package]) ifTrue:[
                                result := c printStringWithoutClass , ' [' , m package , ']'.
                            ].
                        ].
                    ]
                ] ifFalse:[
                    "/ include name in private class changes
                    (c className startsWith:(classBeingCompared name , '::')) ifTrue:[
                        result := (c className copyFrom:(classBeingCompared name size + 3)) , ' ' ,  c printStringWithoutClass
                    ] ifFalse:[
                        result := c printString
                    ]
                ]
            ]
        ].
        result
    ].

    nmListA := self methodsOnlyInA.
    nmListA removeAll.
    nmListA addAll:(filteredChangeSetA collect:generator).

    nmListB := self methodsOnlyInB.
    nmListB removeAll.
    nmListB addAll:(filteredChangeSetB collect:generator).

    nmListM := self methodsChanged.
    nmListM removeAll.
    nmListM addAll:(changedMethodsChangeSet collect:generator).

    nmListA isEmpty ifTrue:[
        (self builder componentAt:#BoxA) beInvisible.
    ] ifFalse:[
        (self builder componentAt:#BoxA) beVisible.
    ].

    nmListB isEmpty ifTrue:[
        (self builder componentAt:#BoxB) beInvisible.
    ] ifFalse:[
        (self builder componentAt:#BoxB) beVisible.
    ].

    nmListM isEmpty ifTrue:[
        (nmListA notEmpty or:[nmListB notEmpty]) ifTrue:[
            (self builder componentAt:#BoxM) beInvisible.
        ]
    ] ifFalse:[
        (self builder componentAt:#BoxM) beVisible.
    ]
!

updateViewsIfPossible
    classBeingCompared notNil ifTrue:[
        self window label:'Version Diff Browser on ' , classBeingCompared name.

        self updateLabels.

        changeSetA notNil ifTrue:[
            changeSetB notNil ifTrue:[
                self updateLists.
                ^ self
            ]
        ].

        versionA notNil ifTrue:[
            versionB notNil ifTrue:[
                self updateFromVersions
            ]
        ]
    ]


! !

!VersionDiffBrowser methodsFor:'values'!

addAcceptToTextViewMenus
    |m v|

    (Array 
        with:diffTextView leftTextView
        with:diffTextView rightTextView)
    do:[:v |
        m := v editMenu.
        m addLabels:#('-' 'accept')
          selectors:(Array with:nil with:#accept)
          after:#copySelection.
        m 
            actionAt:#accept 
            put:[|idx idx2 text change|

                 idx := self methodsChangedSelection value.

                 v == diffTextView leftTextView ifTrue:[
                    idx2 := indexFromChangedToA at:idx.
                    change := changeSetA at:idx2.
                 ] ifFalse:[
                    idx2 := indexFromChangedToB at:idx.
                    change := changeSetB at:idx2.
                 ].
                 change apply
                ].
        m selectorAt:#accept put:nil.
        v menuHolder:m.
        v menuMessage:#value.
    ]

!

diffTextView
    "automatically generated by UIPainter ..."

    diffTextView isNil ifTrue:[
        diffTextView := HVScrollableView
                           for:DiffTextView
                           miniScrollerH:true miniScrollerV:false.
        diffTextView addNextPreviousButtons.

        self addAcceptToTextViewMenus.
    ].
    ^ diffTextView
! !

!VersionDiffBrowser class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libtool/VersionDiffBrowser.st,v 1.13 1999-12-14 14:33:52 cg Exp $'
! !