NewChangesBrowser.st
author tz
Tue, 14 Apr 1998 15:08:18 +0200
changeset 1545 e2d6f6ca36ef
child 1546 ffea21e237ed
permissions -rw-r--r--
initial checkin

ToolApplicationModel subclass:#NewChangesBrowser
	instanceVariableNames:'changes changeFileName skipSignal compareChanges
		changeFileTimestamp modified autoUpdateBlock editingClassSource
		filterCompletionBlock'
	classVariableNames:'AutoUpdate CompressSnapshotInfo CategoryColumn DeltaInfoColumn
		TypeColumn TimeStampColumn PositionColumn'
	poolDictionaries:''
	category:'Interface-Browsers'
!

Object subclass:#Change
	instanceVariableNames:'delta string type timeStamp category chunk position className
		followUp'
	classVariableNames:''
	poolDictionaries:''
	privateIn:NewChangesBrowser
!

!NewChangesBrowser class methodsFor:'documentation'!

documentation
"
    The future Changes Browser.

    [start with:]
        NewChangesBrowser open
        NewChangesBrowser openOnFile:aFileName

    [author:]
        Thomas Zwick, eXept Software AG
"


! !

!NewChangesBrowser class methodsFor:'instance creation'!

openOnFile:aFileName
    "create c changes browser on a change file"

    ^ ((self new label:(self label , ': ', aFileName)) 
        changeFileName:aFileName) open

! !

!NewChangesBrowser class methodsFor:'accessing'!

autoSelectNext
    "returning true here, makes a Delete operation automatically
     select the next change"

    ^ true


!

label

    ^'Changes Browser'
! !

!NewChangesBrowser class methodsFor:'help specs'!

helpSpec
    "This resource specification was automatically generated
     by the UIHelpTool of ST/X."

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

    "
     UIHelpTool openOnClass:NewChangesBrowser    
    "

    <resource: #help>

    ^super helpSpec addPairsFrom:#(

#applyAll
'Apply all changes from the first one to the last made change.'

#applyForClassToEnd
'Apply all changes from the selected one up to the last made change affecting on the same class as the class of the selected change.'

#applyFromLastSnapshot
'Apply all changes from the last snapshot up to the last made change.'

#applyLine
'Apply the selected change.'

#applyToEnd
'Apply all changes from the selected one up to the last made change.'

#deleteCompress
'Deletes all multiple changes but leaving the last one.'

#deleteCompressForClass
'Deletes all multiple changes affecting on the same class as the class of the selected change but leaving the last one.'

#deleteForClassToEnd
'Deletes all changes from the selected one up to the last made change affecting on the same class as the class of the selected change.'

#deleteLine
'Deletes the selected change from the list of changes.'

#deleteToEnd
'Deletes all changes from the selected one up to the last made change.'

#fileLoadFrom
'Opens a dialog for selecting and loading another changes files.'

#fileReload
'Reloads the changes file (undo your modifications).'

#fileSave
'Saves the list of changes into current changes file name.'

#settingsAutoUpdate
'Turns on/off whether the Changes Browser makes automatically an update after a change done while running.'

#settingsColumns
'Turns on/off whether columns  are shown by the table.'

#settingsColumnsCategory
'Turns on/off whether a column for the category of the change is shown by the table.'

#settingsColumnsDeltaInfo
'Turns on/off whether a column for the delta info of the change is shown by the table.'

#settingsColumnsPosition
'Turns on/off whether a column for the position of the change in the change file is shown by the table.'

#settingsColumnsTimeStamp
'Turns on/off whether a column for the time stamp of the change is shown by the table.'

#settingsColumnsType
'Turns on/off whether a column for the type of the change is shown by the table.'

#testCompareWithCurrentVersion
'Opens a info dialog showing the compare results between the method code of the selected change with the current code.'

#testFindLastSnapshot
'Moves upwards the list of changes in order to find to the last made snapshot.'

#testFindNextSnapshot
'Moves downwards the list of changes in order to find to the next made snapshot.'

)
! !

!NewChangesBrowser class methodsFor:'image specs'!

applyFromLastSnapshotIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#applyFromLastSnapshotIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser applyFromLastSnapshotIcon'
        ifAbsentPut:[(Depth4Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(4 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@QDQ<@@@@@@@@@@AH"H@@@@@@@@@@@D"H @@@@@@@@@@@RH"@@@@@@@@@@@AH"H@@@@@@@@@@@@@@@@@@@@@@@@@<@@@@@@@@@@@@@@@@@@@@@@@;.;.; @@@@@@@@C.;.;.@@@@@@@@@@@@@@8@@N@@@@C?????C @@@N@@@O????<@@@8@@@@@@@@@C0@@@@8@@O????<O@@C @@@@?????0@@@@C @@@@@@@O@@@N@@@@DQDQDP<@C @@@@@_??DQ@@@@@@@@@A????D@@@@@@@@@DQDQDP@@@@C @@@@@@@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 255 255 255 255 0 0 0 255 0 0 0 255 0 255 255 255 255 0 255 0 255 127 0 0 0 127 0 0 0 127 0 127 127 127 127 0 127 0 127 127 127 127 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@C<@@C<@@C<@@C<@@C<@@A<@@D@@?<@@?<@@?<LC?<\C?<\C?<8O?08O?10O?10??C ??[@??_@?<^@?<_@?<_@') ; yourself); yourself]!

applyIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#applyIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser applyIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'UUUUUUUPUUUUUUUPUUUUUUU[UUUUUUUPP@@@@@@QQUUUUUTPQUUUUUTVQ**UUUTPQUUUUUTPQUUUUUTXQ***UUTPQ**UUUTPQ*****TPQ***%UTPQ****)TPQUUUUUTPQUUUUUTUP@@@@@@PUUUUUUUPUUUUUUUPUUUUUUUPUUUUUUUP') ; colorMapFromArray:#[0 0 0 255 255 255 170 170 170 255 0 0]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@_??8_??8_??8_??8_??8_??8_??8_??8_??8_??8_??8_??8_??8_??8@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@a') ; yourself); yourself]!

applyToEndIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#applyToEndIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser applyToEndIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@@@B** H@@@B** H@@@@@@ @@@@**( @@@@**(@J@@@@@H@@@@O??H@@@@O??@@@@@@@C@H@@C??3@@ @C??0@ @@@@@0@B@@UUT0B@K@UUT@@H@@@@D@H@CEUUDB@@JG?U@@@@MG?=@@@@@EUU@@B@@@@@@@@@H') ; colorMapFromArray:#[0 0 0 255 255 255 127 127 127 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@C?0@C?0@C?0@O?0@O?0@O?0@??@@??@@??LC?<\C?<\C?<8O?08O?10O?10??C ??[@??_@?<^@?<_@?<_@') ; yourself); yourself]!

compressIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#compressIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser compressIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@????<@@@:***(@@@8@@@8@@@8@@@8@@@@@@@8@@@@@@@8A@@@@@@8@@:**0@8HC**+@@8@@@@@@@8H@@@@@@8@@@@@@@8@@@@@@@8@@@@@@@8@@C????8@@C****(@@@@@@@@H@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@') ; colorMapFromArray:#[0 0 0 124 124 124 170 170 170 255 255 255]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@??0A??8A??8A??8A8A8G>A8@@A8G?98O?18_?!!8??A8@@A8G>A8@0A8C??8C??8C??8C??0@0@@D2@@G>@@D2@@') ; yourself); yourself]!

deleteIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#deleteIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser deleteIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@AUUUU@@OA??UU@@@AUUUU@@@AUUUU@B@A???U@H@A??5U@@@A???0@@@AUUUB@B@@@@@@@@@@@@@A@BK@@@@A_:@@@@@AO2G@@@@AL2@@@@@AL2C@@@@AL2@@@@@AL2@@@@@AL2H@@@@AL2@@@@@A\:@@@@@H_8 @@@@@@@H') ; colorMapFromArray:#[0 0 0 255 255 255 127 127 127 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'_?<@_?<@_?<P_?<P_?<X_?<X_?<H_? H_?O<_?XL@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@G0') ; yourself); yourself]!

deleteToEndIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#deleteToEndIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser deleteToEndIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@B** @@O@B** @@@@@@@ @@@@??< @B@@??<@@H@@@@L@@@@EUUL@@@@G?U@B@B@G?=@@@@@EUU@A@BK@@@@A_:@@@@@AO2G@@@@AL2@@@@@AL2C@@@@AL2@@@@@AL2@@@@@AL2H@@@@AL2@@@@@A\:@@@@@H_8 @@@@@@@H') ; colorMapFromArray:#[0 0 0 255 255 255 127 127 127 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C?0@C?0@C?0PO?0PO?0XO?0X??@H??@H??O<?<XL?<_<?<_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@_<@@G0') ; yourself); yourself]!

findLastSnapshotIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#findLastSnapshotIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser findLastSnapshotIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@EUUUW@A@F***(@@@F***(@@@F***(@@@F***(@@@F***(@@@F***(@@@F***(@A@F***(@M@F***(@H@F***(@@@F***(@@@F***(@I@L@@@@@@@@@@@@@A@P@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@') ; colorMapFromArray:#[0 0 0 255 255 255 255 0 0 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C??@C??@C??@C??@C??@C??@C??@C??@C??@C??@C??@C??@C??@C??B@@@B@C@B@G B@O0@@_8@@G @@G @@G @') ; yourself); yourself]!

findNextSnapshotIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

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

    "
     ImageEditor openOnClass:self andSelector:#findNextSnapshotIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'NewChangesBrowser findNextSnapshotIcon'
        ifAbsentPut:[(Depth2Image new) width: 22; height: 22; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@DA@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@D@B@@@@@@@@@@@@@@@E@@@@@@@@@EUUUW@N@F***(@@@F***(@@@F***(@@@F***(@@@F***(@@@F***(@@@V***(@H@F***(@@@F***(@@@F***(@@@F***(@@@F***(@@@L@@@@@@') ; colorMapFromArray:#[0 0 0 255 255 255 255 0 0 170 170 170]; mask:((Depth1Image new) width: 22; height: 22; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@G @@G @@G @@_8@@O0@@G @@C@@@@@@C??@C??@C??@C??@C??@C??BC??BC??BC??BC??@C??@C??@C??@C??@') ; yourself); yourself]! !

!NewChangesBrowser 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:NewChangesBrowser andSelector:#windowSpec
     NewChangesBrowser new openInterface:#windowSpec
     NewChangesBrowser open
    "

    <resource: #canvas>

    ^
     
       #(#FullSpec
          #window: 
           #(#WindowSpec
              #name: ''
              #layout: #(#LayoutFrame 329 0 181 0 828 0 580 0)
              #label: ''
              #min: #(#Point 10 10)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 329 181 829 581)
              #menu: #menu
              #usePreferredExtent: false
          )
          #component: 
           #(#SpecCollection
              #collection: 
               #(
                 #(#MenuPanelSpec
                    #name: 'menuToolbarView'
                    #layout: #(#LayoutFrame 0 0.0 0 0 0 1.0 32 0)
                    #menu: #menuToolbar
                    #style: #(#FontDescription #helvetica #medium #roman 10)
                    #showSeparatingLines: true
                )
                 #(#VariableVerticalPanelSpec
                    #name: 'variableVerticalPanel1'
                    #layout: #(#LayoutFrame 0 0.0 34 0 0 1.0 -26 1.0)
                    #component: 
                     #(#SpecCollection
                        #collection: 
                         #(
                           #(#ViewSpec
                              #name: 'Box1'
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#DataSetSpec
                                        #name: 'changesDataSetView'
                                        #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -28 1.0)
                                        #model: #selectionOfChange
                                        #menu: #menuTable
                                        #hasHorizontalScrollBar: true
                                        #hasVerticalScrollBar: true
                                        #miniScrollerHorizontal: true
                                        #dataList: #listOfChanges
                                        #useIndex: false
                                        #has3Dsepartors: true
                                        #doubleClickSelector: #doBrowseClass
                                        #columnHolder: #listOfChangeColumns
                                        #valueChangeSelector: #changeSelected:
                                    )
                                     #(#ViewSpec
                                        #name: 'Box2'
                                        #layout: #(#LayoutFrame 0 0.0 -28 1 0 1.0 0 1.0)
                                        #component: 
                                         #(#SpecCollection
                                            #collection: 
                                             #(
                                               #(#LabelSpec
                                                  #name: 'filterLabel'
                                                  #layout: #(#AlignmentOrigin 37 0 13 0.0 1 0.5)
                                                  #label: 'Filter:'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                               #(#InputFieldSpec
                                                  #name: 'filterField'
                                                  #layout: #(#LayoutFrame 41 0.0 3 0 285 0 25 0)
                                                  #model: #valueOfFilter
                                                  #immediateAccept: false
                                                  #acceptOnReturn: false
                                              )
                                               #(#ProgressIndicatorSpec
                                                  #name: 'readProgressIndicator'
                                                  #layout: #(#LayoutFrame 41 0 3 0 285 0 25 0)
                                                  #model: #valueOfReadProgress
                                                  #foregroundColor: #(#Color 0.0 60.0 60.0)
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label3'
                                                  #layout: #(#LayoutFrame 297 0.0 2 0.0 307 0 13 0)
                                                  #label: ' '
                                                  #backgroundColor: #(#Color 100.0 100.0 100.0)
                                                  #level: 1
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label6'
                                                  #layout: #(#LayoutFrame 311 0 0 0.0 396 0 14 0)
                                                  #label: '= method change'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label9'
                                                  #layout: #(#LayoutFrame 403 0.0 1 0.0 413 0 12 0)
                                                  #label: ' '
                                                  #backgroundColor: #(#Color 49.9992 49.9992 49.9992)
                                                  #level: 1
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label10'
                                                  #layout: #(#LayoutFrame 418 0 0 0.0 496 0 14 0)
                                                  #label: '= class change'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label1'
                                                  #layout: #(#LayoutFrame 297 0.0 16 0.0 307 0 27 0)
                                                  #label: ' '
                                                  #backgroundColor: #(#Color 100.0 0.0 0.0)
                                                  #level: 1
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label2'
                                                  #layout: #(#LayoutFrame 312 0 14 0.0 376 0 28 0)
                                                  #label: '= snapshot'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label5'
                                                  #layout: #(#LayoutFrame 372 0.0 16 0.0 382 0 27 0)
                                                  #label: ' '
                                                  #backgroundColor: #(#Color 0.0 80.0 80.0)
                                                  #level: 1
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label4'
                                                  #layout: #(#LayoutFrame 387 0 14 0.0 435 0 28 0)
                                                  #label: '= file in'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label7'
                                                  #layout: #(#LayoutFrame 428 0.0 16 0.0 438 0 27 0)
                                                  #label: ' '
                                                  #backgroundColor: #(#Color 0.0 0.0 100.0)
                                                  #level: 1
                                              )
                                               #(#LabelSpec
                                                  #name: 'Label8'
                                                  #layout: #(#LayoutFrame 443 0 14 0.0 504 0 28 0)
                                                  #label: '= check in'
                                                  #style: #(#FontDescription #helvetica #medium #roman 10)
                                                  #adjust: #left
                                              )
                                            )
                                        )
                                    )
                                  )
                              )
                          )
                           #(#TextEditorSpec
                              #name: 'changeTextEditor'
                              #model: #valueOfChangeText
                              #hasHorizontalScrollBar: true
                              #hasVerticalScrollBar: true
                              #miniScrollerHorizontal: true
                              #isReadOnly: true
                          )
                        )
                    )
                    #handles: #(#Any 0.5 1.0)
                )
                 #(#UISubSpecification
                    #name: 'windowSpecForInfoBar'
                    #layout: #(#LayoutFrame 0 0.0 -24 1 0 1.0 0 1.0)
                    #majorKey: #ToolApplicationModel
                    #minorKey: #windowSpecForInfoBar
                )
              )
          )
      )
! !

!NewChangesBrowser class methodsFor:'list specs'!

specOfChangeColumns

^
#(
   #(#DataSetColumnSpec
      #rendererType: #rowSelector
      #backgroundSelector: #listColor
  )
   #(#DataSetColumnSpec
      #label: 'Change'
      #labelAlignment: #left
      #model: #string
      #canSelect: false
  )
   #(#DataSetColumnSpec
      #label: 'Category'
      #labelAlignment: #left
      #model: #category
      #canSelect: false
  )
   #(#DataSetColumnSpec
      #label: 'Delta Info'
      #labelAlignment: #left
      #model: #delta
      #canSelect: false
  )
   #(#DataSetColumnSpec
      #label: 'Time Stamp'
      #labelAlignment: #left
      #model: #timeStamp
      #canSelect: false
  )
   #(#DataSetColumnSpec
      #label: 'Type'
      #labelAlignment: #left
      #model: #type
      #canSelect: false
  )
   #(#DataSetColumnSpec
      #label: 'Position'
      #labelAlignment: #left
      #model: #position
      #canSelect: false
  )
)

! !

!NewChangesBrowser class methodsFor:'menu specs'!

menu
    "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:NewChangesBrowser andSelector:#menu
     (Menu new fromLiteralArrayEncoding:(NewChangesBrowser menu)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'About'
                #activeHelpKey: #about
                #labelImage: #(#ResourceRetriever nil #menuIcon)
                #submenuChannel: #menuAbout
            )
             #(#MenuItem
                #label: 'File'
                #activeHelpKey: #file
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Reload'
                          #value: #doReload
                          #activeHelpKey: #fileReload
                          #enabled: #valueOfNotReading
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Load From...'
                          #value: #doLoadFrom
                          #activeHelpKey: #fileLoadFrom
                          #enabled: #valueOfNotReading
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Save'
                          #value: #doSave
                          #activeHelpKey: #fileSave
                          #enabled: #valueOfNotReading
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Browse Class'
                          #value: #doBrowseClass
                          #activeHelpKey: #fileBrowseClass
                          #enabled: #valueOfHavingChangeSelection
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Exit'
                          #value: #closeRequest
                          #activeHelpKey: #fileExit
                          #enabled: #valueOfNotReading
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Apply'
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Line'
                          #value: #doApply
                          #activeHelpKey: #applyLine
                          #enabled: #valueOfHavingChangeSelection
                      )
                       #(#MenuItem
                          #label: 'To End'
                          #value: #doApplyToEnd
                          #activeHelpKey: #applyToEnd
                          #enabled: #valueOfHavingSelection
                      )
                       #(#MenuItem
                          #label: 'For Class To End'
                          #value: #doApplyForClassToEnd
                          #activeHelpKey: #applyForClassToEnd
                          #enabled: #valueOfHavingChangeSelection
                      )
                       #(#MenuItem
                          #label: 'All'
                          #value: #doApplyAll
                          #activeHelpKey: #applyAll
                          #enabled: #valueOfNotReading
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'From Last Snapshot'
                          #value: #doApplyFromLastSnapshot
                          #activeHelpKey: #applyFromLastSnapshot
                          #enabled: #valueOfNotReading
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Delete'
                #activeHelpKey: #edit
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Line'
                          #value: #doDelete
                          #activeHelpKey: #deleteLine
                          #enabled: #valueOfHavingSelection
                      )
                       #(#MenuItem
                          #label: 'To End'
                          #value: #doDeleteToEnd
                          #activeHelpKey: #deleteToEnd
                          #enabled: #valueOfHavingSelection
                      )
                       #(#MenuItem
                          #label: 'For Class To End'
                          #value: #doDeleteForClassToEnd
                          #activeHelpKey: #deleteForClassToEnd
                          #enabled: #valueOfHavingChangeSelection
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Compress'
                          #value: #doCompress
                          #activeHelpKey: #deleteCompress
                          #enabled: #valueOfNotReading
                      )
                       #(#MenuItem
                          #label: 'Compress For Class'
                          #value: #doCompressForClass
                          #activeHelpKey: #deleteCompressForClass
                          #enabled: #valueOfHavingChangeSelection
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Test'
                #activeHelpKey: #test
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Find Last Snapshot'
                          #value: #doFindSnapshot:
                          #activeHelpKey: #testFindLastSnapshot
                          #enabled: #valueOfHavingSelection
                          #argument: 'last'
                      )
                       #(#MenuItem
                          #label: 'Find Next Snapshot'
                          #value: #doFindSnapshot:
                          #activeHelpKey: #testFindNextSnapshot
                          #enabled: #valueOfHavingSelection
                          #argument: 'next'
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Compare With Current Version'
                          #value: #doCompare
                          #activeHelpKey: #testCompareWithCurrentVersion
                          #enabled: #valueOfHavingChangeSelection
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Settings'
                #activeHelpKey: #settings
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Auto Update'
                          #activeHelpKey: #settingsAutoUpdate
                          #enabled: #valueOfNotReading
                          #indication: #autoUpdateMode:
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Columns'
                          #activeHelpKey: #settingsColumns
                          #submenu: 
                           #(#Menu
                              
                               #(
                                 #(#MenuItem
                                    #label: 'Category'
                                    #activeHelpKey: #settingsColumnsCategory
                                    #indication: #categoryColumn:
                                )
                                 #(#MenuItem
                                    #label: 'Delta Info'
                                    #activeHelpKey: #settingsColumnsDeltaInfo
                                    #indication: #deltaInfoColumn:
                                )
                                 #(#MenuItem
                                    #label: 'Type'
                                    #activeHelpKey: #settingsColumnsType
                                    #indication: #typeColumn:
                                )
                                 #(#MenuItem
                                    #label: 'Time Stamp'
                                    #activeHelpKey: #settingsColumnsTimeStamp
                                    #indication: #timeStampColumn:
                                )
                                 #(#MenuItem
                                    #label: 'Position'
                                    #activeHelpKey: #settingsColumnsPosition
                                    #indication: #positionColumn:
                                )
                              ) nil
                              nil
                          )
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Fonts'
                          #enabled: #valueOfNotReading
                          #submenuChannel: #menuFont
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Help'
                #startGroup: #right
                #activeHelpKey: #help
                #submenuChannel: #menuHelp
            )
          ) nil
          nil
      )
!

menuTable
    "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:NewChangesBrowser andSelector:#menuTable
     (Menu new fromLiteralArrayEncoding:(NewChangesBrowser menuTable)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'Apply'
                #value: #doApply
                #activeHelpKey: #applyLine
                #enabled: #valueOfHavingChangeSelection
            )
             #(#MenuItem
                #label: 'Apply To End'
                #value: #doApplyToEnd
                #activeHelpKey: #applyToEnd
                #enabled: #valueOfHavingSelection
            )
             #(#MenuItem
                #label: 'Apply For Class To End'
                #value: #doApplyForClassToEnd
                #activeHelpKey: #applyForClassToEnd
                #enabled: #valueOfHavingChangeSelection
            )
             #(#MenuItem
                #label: '-'
            )
             #(#MenuItem
                #label: 'Delete'
                #value: #doDelete
                #activeHelpKey: #deleteLine
                #enabled: #valueOfHavingSelection
            )
             #(#MenuItem
                #label: 'Delete To End'
                #value: #doDeleteToEnd
                #activeHelpKey: #deleteToEnd
                #enabled: #valueOfHavingSelection
            )
             #(#MenuItem
                #label: 'Delete For Class To End'
                #value: #doDeleteForClassToEnd
                #activeHelpKey: #deleteForClassToEnd
                #enabled: #valueOfHavingChangeSelection
            )
             #(#MenuItem
                #label: '-'
            )
             #(#MenuItem
                #label: 'Compress For Class'
                #value: #doCompressForClass
                #activeHelpKey: #deleteCompressForClass
                #enabled: #valueOfHavingChangeSelection
            )
          ) nil
          nil
      )
!

menuToolbar
    "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:NewChangesBrowser andSelector:#menuToolbar
     (Menu new fromLiteralArrayEncoding:(NewChangesBrowser menuToolbar)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'Load From'
                #isButton: true
                #value: #doLoadFrom
                #activeHelpKey: #fileLoadFrom
                #enabled: #valueOfNotReading
                #labelImage: #(#ResourceRetriever nil #loadIcon)
            )
             #(#MenuItem
                #label: 'Save'
                #isButton: true
                #value: #doSave
                #activeHelpKey: #fileSave
                #enabled: #valueOfNotReading
                #labelImage: #(#ResourceRetriever nil #saveIcon)
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: 'Compress'
                #isButton: true
                #value: #doCompress
                #activeHelpKey: #deleteCompress
                #enabled: #valueOfNotReading
                #labelImage: #(#ResourceRetriever nil #compressIcon)
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: 'Apply'
                #isButton: true
                #value: #doApply
                #activeHelpKey: #applyLine
                #enabled: #valueOfHavingChangeSelection
                #labelImage: #(#ResourceRetriever nil #applyIcon)
            )
             #(#MenuItem
                #label: 'Apply To End'
                #isButton: true
                #value: #doApplyToEnd
                #activeHelpKey: #applyToEnd
                #enabled: #valueOfHavingSelection
                #labelImage: #(#ResourceRetriever nil #applyToEndIcon)
            )
             #(#MenuItem
                #label: 'Apply From Last Snapshot'
                #isButton: true
                #value: #doApplyFromLastSnapshot
                #activeHelpKey: #applyFromLastSnapshot
                #enabled: #valueOfNotReading
                #labelImage: #(#ResourceRetriever nil #applyFromLastSnapshotIcon)
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: 'Delete'
                #isButton: true
                #value: #doDelete
                #activeHelpKey: #deleteLine
                #enabled: #valueOfHavingSelection
                #labelImage: #(#ResourceRetriever nil #deleteIcon)
            )
             #(#MenuItem
                #label: 'Delete To End'
                #isButton: true
                #value: #doDeleteToEnd
                #activeHelpKey: #deleteToEnd
                #enabled: #valueOfHavingSelection
                #labelImage: #(#ResourceRetriever nil #deleteToEndIcon)
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: 'Find Last Snapshot'
                #isButton: true
                #value: #doFindSnapshot:
                #activeHelpKey: #testFindLastSnapshot
                #enabled: #valueOfHavingSelection
                #argument: 'last'
                #labelImage: #(#ResourceRetriever nil #findLastSnapshotIcon)
            )
             #(#MenuItem
                #label: 'Find Next Snapshot'
                #isButton: true
                #value: #doFindSnapshot:
                #activeHelpKey: #testFindNextSnapshot
                #enabled: #valueOfHavingSelection
                #argument: 'next'
                #labelImage: #(#ResourceRetriever nil #findNextSnapshotIcon)
            )
          ) nil
          nil
      )
! !

!NewChangesBrowser methodsFor:'accesssing - columns'!

categoryColumn

    ^CategoryColumn ? (CategoryColumn := true)
!

categoryColumn: aBoolean

    self changeColumn: 'Category' add: (CategoryColumn := aBoolean)
!

deltaInfoColumn

    ^DeltaInfoColumn ? (DeltaInfoColumn := false)
!

deltaInfoColumn: aBoolean

    self changeColumn: 'Delta Info' add: (DeltaInfoColumn := aBoolean)
!

positionColumn

    ^PositionColumn ? (PositionColumn := true)
!

positionColumn: aBoolean

    self changeColumn: 'Position' add: (PositionColumn := aBoolean)
!

timeStampColumn

    ^TimeStampColumn ? (TimeStampColumn := false)
!

timeStampColumn: aBoolean

    self changeColumn: 'Time Stamp' add: (TimeStampColumn := aBoolean)

!

typeColumn

    ^TypeColumn ? (TypeColumn := false)
!

typeColumn: aBoolean

    self changeColumn: 'Type' add: (TypeColumn := aBoolean)

! !

!NewChangesBrowser methodsFor:'accesssing - views'!

filterField

    ^builder componentAt: #filterField



!

filterLabel

    ^builder componentAt: #filterLabel



!

readProgressIndicator

    ^builder componentAt: #readProgressIndicator



! !

!NewChangesBrowser methodsFor:'aspects'!

listOfChangeColumns

    |holder|
    (holder := builder bindingAt:#listOfChangeColumns) isNil ifTrue:[
        builder aspectAt:#listOfChangeColumns put:(holder := List new).
        self changeColumn: nil add: true.
        self changeColumn: 'Change' add: true.
        self categoryColumn: self categoryColumn.
        self timeStampColumn: self timeStampColumn.
        self typeColumn: self typeColumn.
        self deltaInfoColumn: self deltaInfoColumn.
        self positionColumn: self positionColumn.
    ].
    ^ holder





!

listOfChanges

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

selectionOfChange

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

valueOfChangeText

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

valueOfFilter

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

valueOfHavingChangeSelection

    |holder|
    (holder := builder bindingAt:#valueOfHavingChangeSelection) isNil ifTrue:[
        builder aspectAt:#valueOfHavingChangeSelection put:(holder :=  false asValue).
    ].
    ^ holder
!

valueOfHavingSelection

    |holder|
    (holder := builder bindingAt:#valueOfHavingSelection) isNil ifTrue:[
        builder aspectAt:#valueOfHavingSelection put:(holder :=  false asValue).
    ].
    ^ holder
!

valueOfNotReading

    |holder|
    (holder := builder bindingAt:#valueOfReading) isNil ifTrue:[
        builder aspectAt:#valueOfReading put:(holder :=  true asValue).
    ].
    ^ holder
!

valueOfReadProgress

    |holder|
    (holder := builder bindingAt:#valueOfReadProgress) isNil ifTrue:[
        builder aspectAt:#valueOfReadProgress put:(holder :=  ValueHolder with: 0).
    ].
    ^ holder
! !

!NewChangesBrowser methodsFor:'callbacks'!

changeSelected: lineNr
    "show a change in the codeView"

    |aStream sawExcla chunk selectedRow changeNr|

    lineNr == 0 ifTrue: [^nil].
    changeNr := changes indexOf: (self listOfChanges at: lineNr).
    aStream := self streamForChange: (changes at: changeNr).
    aStream isNil ifTrue:[^ self].
    sawExcla := aStream peekFor:(aStream class chunkSeparator).
    chunk := aStream nextChunk.
    sawExcla ifTrue:[
        chunk := aStream nextChunk
    ].
    aStream close.        
    self valueOfChangeText value:chunk.

    (selectedRow := changes at: changeNr ifAbsent: nil) notNil
    ifTrue:
    [
        self valueOfHavingSelection value: true.
        self valueOfHavingChangeSelection value: 
            ((selectedRow type = 'method') or: [(selectedRow type = 'class')])
    ]
! !

!NewChangesBrowser methodsFor:'compiler interface'!

wantChangeLog
    "sent by the compiler to ask if a changeLog entry should
     be written. Return false here."

    ^ false


! !

!NewChangesBrowser methodsFor:'help'!

defaultInfoLabel
    "get default label for the info bar"

    changeFileName asFilename exists ifTrue: [^changeFileName].
    ^'No change file name defined.'



! !

!NewChangesBrowser methodsFor:'initialization'!

initialize

    super initialize.

    changes        := List new.
    changeFileName := (Filename currentDirectory asAbsoluteFilename construct: ObjectMemory nameForChanges) name. 
    AutoUpdate     := AutoUpdate ? false.                                     
    compareChanges := true.

    ObjectMemory addDependent:self.
! !

!NewChangesBrowser methodsFor:'menu modes'!

autoUpdateMode
    "returns whether autoUpdate is on/off"

    ^AutoUpdate

!

autoUpdateMode: aMode
    "sets the autoUpdate to aMode"

    AutoUpdate := aMode

! !

!NewChangesBrowser methodsFor:'private'!

applyChange:change
    "fileIn a change"

    |aStream nm applyAction changeNr|

    aStream := self streamForChange:change.
    aStream isNil ifTrue:[^ self].

    nm := self classNameOfChange:change.
    nm notNil ifTrue:[
        |cls|

        cls := Smalltalk at:(nm asSymbol) ifAbsent:[].
        cls notNil ifTrue:[
            cls isLoaded ifFalse:[
                cls autoload
            ]
        ]
    ].

    applyAction := [
        |sig|

        (skipSignal notNil) ifTrue:[
            sig := skipSignal
        ] ifFalse:[
            sig := Object abortSignal
        ].
        sig catch:[
            |reader doItChunk methodsForChunk|

            "/ a followup methodsFor: chunk ...
            change followUp ifTrue:[
                methodsForChunk := change chunk.
            ] ifFalse:[
                doItChunk := aStream nextChunk.   "/ an empty chunk sometimes ...
                doItChunk notEmpty ifTrue:[
                    Compiler evaluate:doItChunk notifying:self.
                ] ifFalse:[
                    methodsForChunk := aStream nextChunk.   "/ the real one
                ]
            ].
            methodsForChunk notNil ifTrue:[
                Class methodRedefinitionSignal handle:[:ex |
                    ex proceedWith:#keep
                ] do:[
                    reader := Compiler evaluate:methodsForChunk notifying:self.
                    reader fileInFrom:aStream notifying:self passChunk:false single:true.
                ]
            ]
        ].
    ].

    "/
    "/ if I am showing the changes file, dont update it
    "/
    changeFileName = ObjectMemory nameForChanges ifTrue:[
        Class withoutUpdatingChangesDo:applyAction
    ] ifFalse:[
        applyAction value
    ].
    aStream close
!

autoSelectChange:change
    "select a change"

    self class autoSelectNext ifTrue:[         
        ((self listOfChanges indexOf: change) <= self listOfChanges size) ifTrue:[      
            self selectionOfChange value: change.
            self changeSelected:(self listOfChanges indexOf: change).
            ^ self
        ]
    ].
    self clearCodeView. 
    self selectionOfChange value:nil
!

autoSelectLast
    "select the last change"

    self autoSelectChange: self listOfChanges last
!

autoSelectOrEnd:change
    "select change or the last"

    |last|

    last := self listOfChanges size.
    change notNil ifTrue:[  
        self autoSelectChange:change
    ] ifFalse:[
        self selectionOfChange value: (self listOfChanges at: last ifAbsent: nil).
        self changeSelected: last
    ] 
!

changeColumn: aColumnLabel add: addOrRemove

    addOrRemove
    ifTrue:
    [
        self listOfChangeColumns add: 
            ((self class specOfChangeColumns collect: [:i| i decodeAsLiteralArray]) detect: [:column| column label = aColumnLabel]) 
    ]
    ifFalse:
    [
        self listOfChangeColumns remove: 
            (self listOfChangeColumns detect: [:column| column label = aColumnLabel] ifNone: [^nil])
    ]



!

checkClassIsLoaded:aClass

    |cls|
    aClass isMeta ifTrue:[
        cls := aClass soleInstance
    ] ifFalse:[
        cls := aClass
    ].
    cls isLoaded ifFalse:[
        (self confirm:(cls name , ' is an autoloaded class.\I can only compare the methods texts if its loaded first.\\Load the class first ?') withCRs)
        ifTrue:[
            cls autoload
        ]
    ].
    ^ cls isLoaded
!

checkIfFileHasChanged

    |f info |

    Processor removeTimedBlock:autoUpdateBlock.   
    f := changeFileName asFilename.
    (info := f info) isNil ifTrue:[
        self newLabel:'unaccessable'
    ] ifFalse:[
        (info modified) > changeFileTimestamp ifTrue:[
            self newLabel:'outdated'.
            AutoUpdate ifTrue:[
                self doReload
            ]
        ] ifFalse:[
            self newLabel:''
        ]
    ].
    Processor addTimedBlock:autoUpdateBlock afterSeconds:5.
!

classNameOfChange:changeNr
    "return the classname of a change 
     (for classChanges (i.e. xxx class), the non-metaClassName (i.e. xxx) is returned)"

    |name|

    name := self fullClassNameOfChange:changeNr.
    name isNil ifTrue:[^ nil].
    (name endsWith:' class') ifTrue:[
        ^ name copyWithoutLast:6
    ].
    ^ name
!

clearCodeView

    self unselectChange.
    self valueOfChangeText value: nil
!

compareChange:change
    "compare a change with current version"

    |aStream chunk sawExcla parseTree thisClass cat oldSource newSource
     parser sel oldMethod outcome showDiff d t1 t2 selector isLoaded
     method beep|

    aStream := self streamForChange:change.
    aStream isNil ifTrue:[^ self].

    showDiff := false.

    change followUp ifFalse:[
        sawExcla := aStream peekFor:(aStream class chunkSeparator).
        chunk := aStream nextChunk.
    ] ifTrue:[
        chunk := change chunk.
        sawExcla := true.
    ].

    beep := false.
    sawExcla ifFalse:[
        outcome := 'Cannot compare this change\(i.e. this is not a method change)!!'.

        parseTree := Parser parseExpression:chunk.
        (parseTree notNil and:[parseTree isMessage]) ifTrue:[
            ((selector := parseTree selector) == #removeSelector:) ifTrue:[
                thisClass := (parseTree receiver evaluate).
                thisClass isBehavior ifTrue:[
                    (self checkClassIsLoaded:thisClass) ifTrue:[
                        selector := (parseTree arg1 evaluate).
                        (thisClass includesSelector:selector) ifTrue:[
                            outcome := 'Change removes the #' , selector , ' method from ' , thisClass name.
                        ] ifFalse:[
                            outcome := 'Change has no effect\(there is no method for #' , selector , ' in ' , thisClass name , ')'
                        ]
                    ] ifFalse:[
                        beep := true.
                        outcome := 'Cannot compare this change (compare requires class to be loaded)!!'.
                    ]
                ]
            ].
            selector == #category: ifTrue:[
                parseTree receiver isMessage ifTrue:[
                    parseTree receiver selector == #compiledMethodAt: ifTrue:[
                        (method := parseTree receiver evaluate) isMethod ifTrue:[
                            method category = parseTree arg1 evaluate ifTrue:[
                                outcome := 'Change has no effect\(same category)'.
                            ] ifFalse:[
                                outcome := 'Category is different (''' , method category , ''' vs. ''' , parseTree arg1 evaluate , ''')'
                            ]
                        ] ifFalse:[
                            beep := true.
                            outcome := 'There is no such method!!'
                        ]
                    ]
                ]
            ]
        ]
    ] ifTrue:[
        parseTree := Parser parseExpression:chunk.
        (parseTree notNil 
         and:[parseTree ~~ #Error
         and:[parseTree isMessage]]) ifTrue:[
            (parseTree selector == #methodsFor:) ifTrue:[
                thisClass := (parseTree receiver evaluate).
                thisClass isBehavior ifTrue:[
                    (isLoaded := self checkClassIsLoaded:thisClass) ifFalse:[
                        outcome := 'Cannot compare this change\(compare requires class to be loaded)!!'.
                    ].

                    cat := parseTree arg1 evaluate.
                    newSource := aStream nextChunk.

                    parser := Parser parseMethod:newSource in:thisClass.
                    (parser notNil and:[parser ~~ #Error]) ifTrue:[
                        sel := parser selector.
                        oldMethod := thisClass compiledMethodAt:sel.
                        oldMethod notNil ifTrue:[
                            (oldMethod category = cat) ifFalse:[
                                Transcript showCR:'Category changed.'.
                            ].
                            oldSource := oldMethod source.
                            (oldSource = newSource) ifTrue:[
                                outcome := 'Same source.'
                            ] ifFalse:[
                                oldSource isNil ifTrue:[
                                    beep := true.
                                    outcome := 'No source for compare.'
                                ] ifFalse:[
                                    "/
                                    "/ compare for tabulator <-> space changes
                                    "/ before showing diff ...
                                    "/
                                    t1 := oldSource asCollectionOfLines collect:[:s | s withTabsExpanded].
                                    t2 := newSource asCollectionOfLines collect:[:s | s withTabsExpanded].
                                    t1 = t2 ifTrue:[
                                        outcome := 'Same source.'
                                    ] ifFalse:[
                                        outcome := 'Source changed!!'.
                                        showDiff := true.

                                        "/
                                        "/ check if only historyLine diffs
                                        "/
                                        (HistoryManager notNil 
                                        and:[HistoryManager isActive]) ifTrue:[
                                            (HistoryManager withoutHistoryLines:newSource)
                                            =
                                            (HistoryManager withoutHistoryLines:oldSource)
                                            ifTrue:[
                                                outcome := 'Same source (history only).'.
                                                showDiff := false.
                                            ]
                                        ].
                                    ]
                                ]
                            ]
                        ] ifFalse:[
                            isLoaded ifTrue:[
                                beep := true.
                                outcome := 'Method does not exist!!'
                            ]
                        ]
                    ] ifFalse:[
                        outcome := 'Change unparsable!!'
                    ].
                    (showDiff and:[oldSource notNil and:[newSource notNil]]) ifTrue:[
                        d := DiffTextView 
                                openOn:oldSource label:'Current version (in image)'
                                and:newSource label:'Change version'.
                        d label:'method differences'.
                    ]
                ] ifFalse:[
                    beep := true.
                    outcome := 'Class does not exist!!'
                ]
            ] ifFalse:[
                beep := true.
                outcome := 'Not comparable!!'
            ]
        ] ifFalse:[
            beep := true.
            outcome := 'Not comparable!!'
        ]
    ].
    aStream close.
    showDiff ifFalse:[
        beep ifTrue:[
            self warn:outcome withCRs.
        ] ifFalse:[
            self information:outcome withCRs.
        ]
    ]
!

compressForClass:aClassNameOrNil
    "compress the change-set; 
     this replaces multiple method-changes by the last (i.e. the most recent) change.
     If the class argument is nil, compress for all classes.
     otherwise, only changes for that class are compressed."

    |aStream searchIndex anyMore deleteSet index  
     str snapshotProto snapshotPrefix snapshotNameIndex fileName|

    aStream := FileStream readonlyFileNamed:changeFileName.
    aStream isNil ifTrue:[^ self].

    aClassNameOrNil isNil ifTrue:[
        self newLabel:'compressing...'.
    ] ifFalse:[
        self newLabel:'compressing for ' , aClassNameOrNil.
    ].

    CompressSnapshotInfo == true ifTrue:[
        "
         get a prototype snapshot record (to be independent of
         the actual format ..
        "
        str := WriteStream on:String new.
        Class addChangeRecordForSnapshot:'foo' to:str.
        snapshotProto := str contents.
        snapshotPrefix := snapshotProto copyTo:10.
        snapshotNameIndex := snapshotProto findString:'foo'.
    ].

    self valueOfNotReading value: false.
    self valueOfHavingSelection value: false.
    self valueOfHavingChangeSelection value: false.
    self valueOfReadProgress value: 0.
    self readProgressIndicator raise.
    self filterLabel label: 'Comp:'; redraw.

    self withExecuteCursorDo:[
        |numChanges classes selectors types excla sawExcla
         changeNr chunk aParseTree parseTreeChunk
         thisClass thisSelector codeChunk codeParser
         compressThis oldValue|

        numChanges := changes size.
        classes := Array new:numChanges.
        selectors := Array new:numChanges.
        types := Array new:numChanges.

        "starting at the end, get the change class and change selector;
         collect all in classes / selectors"

        changeNr := numChanges.
        excla := aStream class chunkSeparator.

        [changeNr >= 1] whileTrue:[
            oldValue := self valueOfReadProgress value.
                self valueOfReadProgress value: (100 - ((aStream position/aStream size) * 100) rounded).
                oldValue ~~ self valueOfReadProgress value
                    ifTrue: [self readProgressIndicator redrawEdges;redraw].
            aStream position:(changes at: changeNr) position.
            sawExcla := aStream peekFor:excla.
            chunk := aStream nextChunk.
            sawExcla ifTrue:[
                "optimize a bit if multiple methods for same category arrive"
                (chunk = parseTreeChunk) ifFalse:[
                    aParseTree := Parser parseExpression:chunk.
                    parseTreeChunk := chunk
                ].
                (aParseTree notNil 
                and:[(aParseTree ~~ #Error) 
                and:[aParseTree isMessage]]) ifTrue:[
                    (aParseTree selector == #methodsFor:) ifTrue:[
                        thisClass := (aParseTree receiver evaluate).
                        codeChunk := aStream nextChunk.
                        codeParser := Parser 
                                          parseMethodSpecification:codeChunk
                                          in:thisClass
                                          ignoreErrors:true
                                          ignoreWarnings:true.
                        (codeParser notNil and:[codeParser ~~ #Error]) ifTrue:[
                            selectors at:changeNr put:(codeParser selector).
                            classes at:changeNr put:thisClass.
                            types at:changeNr put:#methodsFor
                        ]
                    ]
                ]
            ] ifFalse:[
                aParseTree := Parser parseExpression:chunk.
                parseTreeChunk := chunk.
                (aParseTree notNil 
                and:[(aParseTree ~~ #Error) 
                and:[aParseTree isMessage]]) ifTrue:[
                    (aParseTree selector == #removeSelector:) ifTrue:[
                        selectors at:changeNr put:(aParseTree arg1 value ).
                        classes at:changeNr put:(aParseTree receiver evaluate).
                        types at:changeNr put:#removeSelector
                    ]
                ] ifFalse:[
                    CompressSnapshotInfo == true ifTrue:[
                        (chunk startsWith:snapshotPrefix) ifTrue:[
                            str := chunk readStream position:snapshotNameIndex.
                            fileName := str upTo:(Character space).
                            "
                             kludge to allow use of match-check below
                            "
                            selectors at:changeNr put:snapshotPrefix.
                            classes at:changeNr put:fileName.
                        ]
                    ]
                ]
            ].
            changeNr := changeNr - 1
        ].
        aStream close.

        "for all changes, look for another class/selector occurence later
         in the list and, if there is one, add change number to the delete set"

        deleteSet := OrderedCollection new.
        changeNr := 1.
        [changeNr < changes size] whileTrue:[
            thisClass := classes at:changeNr.

            compressThis := false.
            aClassNameOrNil isNil ifTrue:[
                compressThis := true
            ] ifFalse:[
                "/ skipping unloaded/unknown classes
                thisClass isBehavior ifTrue:[
                    thisClass isMeta ifTrue:[
                        compressThis := aClassNameOrNil = thisClass soleInstance name. 
                    ] ifFalse:[
                        compressThis := aClassNameOrNil = thisClass name
                    ]
                ]
            ].

            compressThis ifTrue:[
                thisSelector := selectors at:changeNr.
                searchIndex := changeNr.
                anyMore := true.
                [anyMore] whileTrue:[
                    searchIndex := classes indexOf:thisClass
                                        startingAt:(searchIndex + 1).
                    (searchIndex ~~ 0) ifTrue:[
                        ((selectors at:searchIndex) == thisSelector) ifTrue:[
                            thisClass notNil ifTrue:[
                                deleteSet add:changeNr.
                                anyMore := false
                            ]
                        ]
                    ] ifFalse:[
                        anyMore := false      
                    ]
                ].
            ].

            changeNr := changeNr + 1
        ].

        "finally delete what has been found"

        (deleteSet size > 0) ifTrue:[
            index := deleteSet size.
            [index > 0] whileTrue:[
                self silentDeleteChange: (changes at: (deleteSet at:index)).
                index := index - 1
            ].
            "self setChangeList"
        ].
    ].
    self valueOfNotReading value: true.
    self filterField raise.
    self filterLabel label: 'Filter:'.
    self newLabel: ''
!

contractClass:className selector:selector to:maxLen
    |s l|

    s := className , ' ', selector.
    s size > maxLen ifTrue:[
        l := maxLen - 1 - selector size max:20.
        s := (className contractTo:l) , ' ' , selector.

        s size > maxLen ifTrue:[
            l := maxLen - 1 - className size max:20.
            s := className , ' ', (selector contractTo:l).

            s size > maxLen ifTrue:[
                s := (className contractTo:(maxLen // 2 - 1)) , ' ' , (selector contractTo:maxLen // 2)
            ]
        ]
    ].
    ^ s

!

deleteChangesFrom:start to:stop
    "delete a range of changes"

    self unselectChange.
    stop to:start by:-1 do:[:changeNr|
        self silentDeleteChange:(self listOfChanges at: changeNr)
    ]
!

fullClassNameOfChange:change
    "return the full classname of a change 
     (for classChanges (i.e. xxx class), a string ending in ' class' is returned.
     - since parsing ascii methods is slow, keep result cached in 
       changeClassNames for the next query"

    |chunk aParseTree recTree sel name arg1Tree isMeta prevMethodDefNr
     words changeStream fullParseTree ownerTree ownerName oldDollarSetting|

    change isNil ifTrue:[^ nil].

    "
     first look, if not already known
    "
    name := change className.
    name notNil ifTrue:[^ name].

    prevMethodDefNr := changes indexOf: change.
    [(changes at:prevMethodDefNr) followUp] whileTrue:[
        prevMethodDefNr := prevMethodDefNr - 1.
    ].

    "
     get the chunk
    "
    chunk := (changes at:prevMethodDefNr) chunk.
    chunk isNil ifTrue:[^ nil].       "mhmh - empty"

    (chunk startsWith:'''---') ifTrue:[
        words := chunk asCollectionOfWords.
        words size > 2 ifTrue:[
            (words at:2) = 'checkin' ifTrue:[
                name := words at:3.
                change className: name.
                ^ name
            ]
        ].
    ].

    "/ fix it - otherwise, it cannot be parsed
    (chunk endsWith:'primitiveDefinitions:') ifTrue:[
        chunk := chunk , ''''''
    ].
    (chunk endsWith:'primitiveFunctions:') ifTrue:[
        chunk := chunk , ''''''
    ].
    (chunk endsWith:'primitiveVariables:') ifTrue:[
        chunk := chunk , ''''''
    ].

    "
     use parser to construct a parseTree
    "
    oldDollarSetting := Parser allowDollarInIdentifier.
    [
        Parser allowDollarInIdentifier:true.
        aParseTree := Parser parseExpression:chunk.
    ] valueNowOrOnUnwindDo:[
        Parser allowDollarInIdentifier:oldDollarSetting
    ].

    (aParseTree isNil or:[aParseTree == #Error]) ifTrue:[
        ^ nil        "seems strange ... (could be a comment)"
    ].
    aParseTree isMessage ifFalse:[
        ^ nil        "very strange ... (whats that ?)"
    ].

    "
     ask parser for selector
    "
    sel := aParseTree selector.
    recTree := aParseTree receiver.

    "
     is it a method-change, methodRemove or comment-change ?
    "
    (#(#'methodsFor:' 
       #'privateMethodsFor:' 
       #'protectedMethodsFor:' 
       #'publicMethodsFor:' 
       #'removeSelector:' 
       #'comment:'
       #'primitiveDefinitions:'
       #'primitiveFunctions:'
       #'primitiveVariables:'
       #'renameCategory:to:'
       #'instanceVariableNames:'
    ) includes:sel) ifTrue:[
        "
         yes, the className is the receiver
        "
        (recTree notNil and:[recTree ~~ #Error]) ifTrue:[
            isMeta := false.
            recTree isUnaryMessage ifTrue:[
                (recTree selector ~~ #class) ifTrue:[^ nil].
                "id class methodsFor:..."
                recTree := recTree receiver.
                isMeta := true.
            ].
            recTree isPrimary ifTrue:[
                name := recTree name.
                isMeta ifTrue:[
                    name := name , ' class'.
                ].
                change className: name.
                ^ name
            ]
        ].
        "more strange things"
        ^ nil
    ].

    "
     is it a change in a class-description ?
    "
    (('subclass:*' match:sel) 
    or:[('variable*subclass:*' match:sel)]) ifTrue:[
        "/ must parse the full changes text, to get
        "/ privacy information.

        changeStream := self streamForChange:change.
        changeStream notNil ifTrue:[
            chunk := changeStream nextChunk.
            changeStream close.
            fullParseTree := Parser parseExpression:chunk.
            (fullParseTree isNil or:[fullParseTree == #Error]) ifTrue:[
                fullParseTree := nil
            ].
            fullParseTree isMessage ifFalse:[
                fullParseTree := nil
            ].
            "/ actually, the nil case cannot happen
            fullParseTree notNil ifTrue:[
                aParseTree := fullParseTree.
                sel := aParseTree selector.
            ].
        ].

        arg1Tree := aParseTree arg1.
        (arg1Tree notNil and:[arg1Tree isConstant]) ifTrue:[
            name := arg1Tree value asString.

            "/ is it a private-class ?
            ('*privateIn:' match:sel) ifTrue:[
                ownerTree := aParseTree args last.
                ownerName := ownerTree name asString.
                name := ownerName , '::' , name
            ].
            change className: name.
            ^ name
        ].
        "very strange"
        ^ nil
    ].

    "
     is it a class remove ?
    "
    (sel == #removeClass:) ifTrue:[
        (recTree notNil 
        and:[recTree ~~ #Error
        and:[recTree isPrimary
        and:[recTree name = 'Smalltalk']]]) ifTrue:[
            arg1Tree := aParseTree arg1.
            (arg1Tree notNil and:[arg1Tree isPrimary]) ifTrue:[
                name := arg1Tree name.
                change className: name.
                ^ name
            ].
        ]
    ].

    "
     is it a method category change ?
    "
    ((sel == #category:)
    or:[sel == #privacy:]) ifTrue:[
        (recTree notNil 
        and:[recTree ~~ #Error
        and:[recTree isMessage
        and:[recTree selector == #compiledMethodAt:]]]) ifTrue:[
            isMeta := false.
            recTree := recTree receiver.
            recTree isUnaryMessage ifTrue:[
                (recTree selector ~~ #class) ifTrue:[^ nil].
                "id class "
                recTree := recTree receiver
            ].
            recTree isPrimary ifTrue:[
                isMeta ifTrue:[
                    name := name , ' class'.
                ].
                name := recTree name.
                change className: name.
                ^ name
            ]
        ]
    ].
    ^ nil
!

newLabel:how

    how size = 0 ifTrue: [^self window label:self class label].
    self window label:self class label, '(', how, ')'
!

readChangesFileInBackground:inBackground
    "read the changes file, create a list of header-lines (changeChunks)
     and a list of chunk-positions (changePositions).
     Starting with 2.10.3, the entries are multi-col entries;
     the cols are:
        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."

    |aStream maxLen i f|

    self valueOfNotReading value: false.
    self valueOfHavingSelection value: false.
    self valueOfHavingChangeSelection value: false.

    editingClassSource := false.

    maxLen := 60.

    f := changeFileName asFilename.
    aStream :=  f readStream.
    aStream isNil ifTrue:[^ nil].

    self newLabel:'updating ...'.

    i := f info.
    changeFileTimestamp := i modified.

    self valueOfReadProgress value: 0.
    self readProgressIndicator raise.
    self filterLabel label: 'Read:'; redraw.

    self withReadCursorDo:[
        |myProcess myPriority|

        "
         this is a time consuming operation (especially, if reading an
         NFS-mounted directory; therefore lower my priority ...
        "
        inBackground ifTrue:[
            myProcess := Processor activeProcess.
            myPriority := myProcess priority.
            myProcess priority:(Processor userBackgroundPriority).
        ].

        [
            |excla timeStampInfo|

            excla := aStream class chunkSeparator.

            [aStream atEnd] whileFalse:[
                |change changeDelta changeString changeType changeCategory
                 line s l changeClass sawExcla category 
                 chunkText chunkPos sel oldValue|

                change := Change new.

                "
                 get a chunk (separated by excla)
                "
                oldValue := self valueOfReadProgress value.
                self valueOfReadProgress value: (((aStream position/aStream size) * 100) rounded).
                oldValue ~~ self valueOfReadProgress value
                    ifTrue: [self readProgressIndicator redrawEdges;redraw].

                aStream skipSeparators.
                chunkPos := aStream position.

                sawExcla := aStream peekFor:excla.
                chunkText := aStream nextChunk.
                chunkText notNil ifTrue:[
                    |index headerLine cls|

                    (chunkText startsWith:'''---- timestamp ') ifTrue:[
                        timeStampInfo := (chunkText copyFrom:16 to:(chunkText size - 6)) withoutSpaces.
                    ] ifFalse:[

                        "
                         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:[
                                sel := 'primitiveDefinitions:'.
                                chunkText := chunkText copyWithoutLast:1
                            ].
                            (chunkText endsWith:'primitiveVariables:''') ifTrue:[
                                sel := 'primitiveVariables:'.
                                chunkText := chunkText copyWithoutLast:1
                            ].
                            (chunkText endsWith:'primitiveFunctions:''') ifTrue:[
                                sel := 'primitiveFunctions:'.
                                chunkText := chunkText copyWithoutLast:1
                            ].
                        ].

                        change chunk: chunkText.
                        change position: chunkPos.
                        change timeStamp: timeStampInfo.
                        change followUp: false.

                        headerLine := nil.
                        changeDelta := ' '.

                        sawExcla ifFalse:[
                            (chunkText startsWith:'''---- snap') ifTrue:[
                                changeType := ''.
                                headerLine := chunkText.
                                changeString := (chunkText contractTo:maxLen).
                                timeStampInfo := nil.
                            ] ifFalse:[

                                |p cls|

                                headerLine := chunkText , ' (doIt)'.

                                "
                                 first, assume doIt - then lets have a more detailed look ...
                                "
                                ((chunkText startsWith:'''---- file')
                                or:[(chunkText startsWith:'''---- check')]) ifTrue:[
                                    changeType := ''.
                                    timeStampInfo := nil.
                                ] ifFalse:[
                                    changeType := 'doIt'.
                                ].    
                                changeString := (chunkText contractTo:maxLen).

                                p := Parser parseExpression:chunkText inNameSpace:Smalltalk.
                                (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.

                                    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.
                                ].
                                (p ~~ #Error
                                and:[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:'
                                  #'primitiveDefinitions:'
                                  #'primitiveFunctions:'
                                  #'primitiveVariables:'
                                 ) includes:sel) ifTrue:[
                                    changeType := 'class definition'.
                                ].
                            ]
                        ] ifTrue:[
                            |done first p className cls text methodPos|

                            "
                             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 ....
                            "
                            className := nil.
                            p := Parser parseExpression:chunkText inNameSpace:Smalltalk.

                            (p notNil and:[p ~~ #Error]) ifTrue:[
                                sel := p selector.
                                (sel == #methodsFor:) ifTrue:[
                                    p receiver isUnaryMessage ifTrue:[
                                        className := p receiver receiver name.
                                        changeClass := (Smalltalk classNamed:className) class.
                                        className := className , ' class'.
                                    ] ifFalse:[
                                        className := p receiver name.
                                        changeClass := Smalltalk classNamed:className
                                    ].
                                    category := (p args at:1) evaluate.
                                ].
                            ].

                            done := false.
                            first := true.
                            [done] whileFalse:[

                                changeDelta := ' '.
                                methodPos := aStream position.

                                text := aStream nextChunk.
                                text isNil ifTrue:[
                                    done := true
                                ] ifFalse:[
                                    done := text isEmpty
                                ].
                                done ifFalse:[
                                    first ifFalse:[
                                        change := Change new.
                                        change chunk: chunkText.
                                        change string:changeString.
                                        change position: methodPos.
                                        change timeStamp: timeStampInfo.
                                        change followUp: true.

                                        editingClassSource := true.
                                    ].

                                    first := false.
                                    "
                                     try to find the selector
                                    "
                                    sel := nil.
                                    className 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'.
                                        headerLine := chunkText , ' (change)'.
                                    ] ifFalse:[
                                        changeString :=  self contractClass:className selector:sel to:maxLen.
                                        changeType := 'method definition'.
                                        changeCategory := category.
                                        headerLine := className , ' ' , sel , ' ' , '(change category: ''' , category , ''')'.
                                    ].

                                    compareChanges ifTrue:[ 
                                        changeClass isNil ifFalse:[
                                            changeClass isMeta ifTrue:[
                                                cls := changeClass soleInstance
                                            ] ifFalse:[
                                                cls := changeClass
                                            ].
                                        ].

                                        (changeClass isNil or:[cls isLoaded not]) ifTrue:[
                                            changeDelta := '?'
                                        ] ifFalse:[
                                            (changeClass implements:sel asSymbol) ifFalse:[
                                                changeDelta := '+'.
                                            ] ifTrue:[
                                                |m currentText t1 t2|

                                                m := changeClass compiledMethodAt:sel asSymbol.
                                                currentText := m source.
                                                currentText notNil ifTrue:[
                                                    text asString = currentText asString ifTrue:[
                                                        changeDelta := '='
                                                    ] ifFalse:[
                                                        t1 := currentText asCollectionOfLines collect:[:s | s withTabsExpanded].
                                                        t2 := text asCollectionOfLines collect:[:s | s withTabsExpanded].
                                                        t1 = t2 ifTrue:[
                                                            changeDelta := '='
                                                        ]
                                                    ]
                                                ]
                                            ]
                                        ]
                                    ].
                                    change delta:changeDelta.
                                    change string:changeString.
                                    change type:changeType.
                                    change category: changeCategory.
                                    change timeStamp:timeStampInfo.
                                    changes add:change.
                                ].
                                changeString := nil.
                                headerLine := nil.

                            ]
                        ].
                        changeString notNil ifTrue:[
                            change delta:changeDelta.
                            change string:changeString.
                            change type:changeType.
                            change timeStamp:timeStampInfo.
                            changes add:change.

                        ] ifFalse:[
                            headerLine notNil ifTrue:[     
                                changes add: change.
                            ]
                        ]
                    ]
                ]
            ].
            modified := false.

        ] valueNowOrOnUnwindDo:[ 
            aStream close.
            inBackground ifTrue:[myProcess priority:myPriority].
        ].
    ].
    self setChangeList.
    self setChangeSelection.
    self valueOfNotReading value: true.
    self filterField raise.
    self filterLabel label: 'Filter:'.

    self checkIfFileHasChanged. 

!

selectorOfMethodChange:change
    "return a method-changes selector, or nil if its not a methodChange"

    |source parser sel|

    source := self sourceOfMethodChange:change.
    source isNil ifTrue:[^ nil].

    parser := Parser 
                parseMethod:source 
                in:nil 
                ignoreErrors:true 
                ignoreWarnings:true.

    (parser notNil and:[parser ~~ #Error]) ifTrue:[
        sel := parser selector.
    ].
    ^ sel
!

setChangeList
    "extract type-information from changes and stuff into top selection
     view"

    self valueOfFilter value: nil.
    self listOfChanges contents: changes.
    changes size == 0
    ifTrue:
    [                     
        self unselectChange.
        self valueOfHavingSelection value: false.
        self valueOfHavingChangeSelection value: false.
    ]
    ifFalse:
    [       
        self selectionOfChange value: (self listOfChanges size > 0 ifTrue: [self listOfChanges last] ifFalse: [nil])
    ]
!

setChangeSelection
    "extract type-information from changes and stuff into top selection
     view"

    changes size == 0
    ifTrue:
    [                     
        self unselectChange.
        self valueOfHavingSelection value: false.
        self valueOfHavingChangeSelection value: false.
    ]
    ifFalse:
    [       
        self selectionOfChange value: self listOfChanges last
    ]
!

silentDeleteChange:change
    "delete a change do not update changeListView"

    modified := true.      

    changes remove:change.
    self listOfChanges remove:change
!

silentDeleteChangesFor:aClassName from:start to:stop
    "delete changes for a given class in a range.
     Return the number of deleted changes."

    |thisClassName index numDeleted|

    numDeleted := 0.
    index := stop.
    [index >= start] whileTrue:[
        thisClassName := self classNameOfChange:(self listOfChanges at: index).
        thisClassName = aClassName ifTrue:[
            self silentDeleteChange:(self listOfChanges at: index).
            numDeleted := numDeleted + 1.
        ].
        index := index - 1
    ].
    ^ numDeleted
!

sourceOfMethodChange:change
    "return a method-changes source code, or nil if its not a methodChange."

    |aStream chunk sawExcla parseTree sourceChunk|

    aStream := self streamForChange:change. 
    aStream isNil ifTrue:[^ nil].

    change followUp ifFalse:[
        sawExcla := aStream peekFor:(aStream class chunkSeparator).
        chunk := aStream nextChunk.
    ] ifTrue:[
        chunk := change chunk.
        sawExcla := true.
    ].

    sawExcla ifTrue:[
        parseTree := Parser parseExpression:chunk.
        (parseTree notNil and:[parseTree isMessage]) ifTrue:[
            (parseTree selector == #methodsFor:) ifTrue:[
                sourceChunk := aStream nextChunk.
            ]
        ].
    ].
    aStream close.
    ^ sourceChunk
!

streamForChange:change
    "answer a stream for change"

    |aStream|

    aStream := FileStream readonlyFileNamed:changeFileName.
    aStream isNil ifTrue:[^ nil].
    aStream position:change position.
    ^ aStream

!

unselectChange
    "common unselect"

    self selectionOfChange value: nil
!

withSelectedChangeDo:aBlock
    "just a helper, check for a selected change and evaluate aBlock
     with busy cursor"

    |change|

    (change := self selectionOfChange value) notNil 
    ifTrue:[
        self withExecuteCursorDo:[aBlock value:change]
    ]
! !

!NewChangesBrowser methodsFor:'startup / release'!

closeRequest

    self valueOfNotReading value ifFalse: [^nil].

    modified ifTrue:[
        (OptionBox 
              request:(resources at:'Changes file was modified!!') withCRs
              label:'Changes Browser'
              form:(WarningBox iconBitmap)
              buttonLabels:#('Cancel' ' Waste it and proceed ')
              values:#(#abort #ignore)
              default:#save
        ) == #abort ifTrue:[^self].
    ].
    super closeRequest
!

postOpenWith:aBuilder

    super postOpenWith:aBuilder.

    builder namedComponents do: 
    [:aView|    
        aView allSubViewsDo: 
        [:v|
            v redraw
        ] 
    ].
    self readChangesFileInBackground:true.

    autoUpdateBlock := [self checkIfFileHasChanged].
    Processor addTimedBlock:autoUpdateBlock afterSeconds:5.  
    self updateInfoLabel.

    self filterField entryCompletionBlock:
    (filterCompletionBlock := [:value|
        |filter filters i changesCopy|
        self clearCodeView.
        ((filter := self filterField contents) notNil and:
        [(filters := filter asArrayOfSubstrings) size > 0]) ifTrue: 
        [
            i := 0.
            changesCopy := changes copy.
            filters do: 
            [:filter|
                i := i + 1.
                changesCopy contents: 
                    (changesCopy select: [:row| 
                        filter match: (row string asArrayOfSubstrings at: i ifAbsent: [''''])])
            ].            
            self listOfChanges contents: changesCopy
        ] 
        ifFalse: 
        [
            self setChangeList
        ].
        self autoSelectLast.
    ])
!

uninitialize

    Processor removeTimedBlock:autoUpdateBlock.
    ObjectMemory removeDependent:self
! !

!NewChangesBrowser methodsFor:'user actions'!

doApply
    "user wants a change to be applied"

    self withSelectedChangeDo:[:change|
        skipSignal := nil.
        self applyChange:change.
        self autoSelectChange: (self listOfChanges at: (self listOfChanges indexOf: change) ifAbsent: nil)
    ]
!

doApplyAll
    "user wants all changes to be applied"

    self withExecuteCursorDo:[
        |change|
        self clearCodeView.
        skipSignal isNil ifTrue:[skipSignal := Signal new].
        1 to:self listOfChanges size do:[:changeNr |
            self selectionOfChange value:(change := self listOfChanges at: changeNr).
            self applyChange:change
        ].
        self autoSelectLast
    ]

!

doApplyForClassToEnd
    "user wants all changes for this class from changeNr to be applied"

    self withSelectedChangeDo:[:change|
        |thisClassName classNameToApply lastChange change2|
        (classNameToApply := self classNameOfChange:change) notNil 
        ifTrue:
        [
            self clearCodeView.
            skipSignal isNil ifTrue:[skipSignal := Signal new].
            (self listOfChanges indexOf: change) to:self listOfChanges size do:
            [:changeNr|
                change2 := self listOfChanges indexOf: changeNr.
                (thisClassName := self classNameOfChange:change2) = classNameToApply 
                ifTrue:
                [
                    self selectionOfChange value: change2.
                    self applyChange:change2.
                    lastChange := change2
                ]
            ].
            self autoSelectChange:lastChange.
        ]
    ]

!

doApplyFromLastSnapshot

    self autoSelectLast.
    (self doFindSnapshot: 'last') ifTrue: [self doApplyToEnd]
!

doApplyToEnd
    "user wants all changes from changeNr to be applied"

    self withSelectedChangeDo:[:change|
        self clearCodeView.
        skipSignal isNil ifTrue:[skipSignal := Signal new].
        (self listOfChanges indexOf: change) to: self listOfChanges size do:[:changeNr|
            self selectionOfChange value:(self listOfChanges at: changeNr).
            self applyChange:(self listOfChanges at: changeNr)
        ].
        self autoSelectChange:self listOfChanges last
    ]

!

doBrowseClass
    "user wants a browser on the class of a change"

    |className cls isMeta|

    className := self fullClassNameOfChange:self selectionOfChange value.
    className notNil ifTrue:[
        isMeta := false.
        (className endsWith:' class') ifTrue:[
            className := className copyWithoutLast:6.
            isMeta := true.
        ].
        (cls := Smalltalk classNamed:className) notNil ifTrue:[
            isMeta ifTrue:[cls := cls class].
            SystemBrowser 
                openInClass:cls 
                selector:(self selectorOfMethodChange:self selectionOfChange value)
        ]
    ]



!

doCompare
    "compare change with current system version
     - give a note in transcript"

    |change|

    (change := self selectionOfChange value) notNil ifTrue:[
        self withExecuteCursorDo:[self compareChange:change]
    ].
    self newLabel:''
!

doCompress
    "compress the change-set; this replaces multiple method-changes by the last 
     (i.e. the most recent) change"

    |changesSizeBefore|
    changesSizeBefore := changes size.
    self setChangeList.
    self unselectChange.
    self compressForClass:nil.
    filterCompletionBlock value: self valueOfFilter value.        
    self updateInfoLabel.

    self information: 
        'Compression Rate:   ', (((changesSizeBefore - changes size)/changesSizeBefore) * 100) rounded printString, '%\' withCRs,
        'Compressed Changes: ', (changesSizeBefore - changes size) printString, ' from ', changesSizeBefore printString




!

doCompressForClass
    "compress changes for the selected class.
     this replaces multiple method-changes by the last (i.e. the most recent) change."

    self withSelectedChangeDo:[:change|
        | classNameToCompress |

        (classNameToCompress := self classNameOfChange:change) notNil ifTrue:[
            self compressForClass:classNameToCompress.
            filterCompletionBlock value: self valueOfFilter value.
            self autoSelectLast
        ]
    ]

!

doDelete
    "delete currently selected change"

    |change selectionIndex|

    (change := self selectionOfChange value) notNil ifTrue:[
        selectionIndex := self listOfChanges indexOf: change.
        self unselectChange.
        self silentDeleteChange:change.
        self autoSelectOrEnd: (self listOfChanges at: selectionIndex ifAbsent: [nil]).
    ]

!

doDeleteForClassToEnd
    "delete rest of changes with same class as currently selected change"

    self withSelectedChangeDo:[:change|
        |classNameToDelete|
        (classNameToDelete := self classNameOfChange:change) notNil ifTrue:[
            self unselectChange.
            self silentDeleteChangesFor:classNameToDelete 
                                   from:(self listOfChanges indexOf: change)
                                     to:self listOfChanges size.
            self autoSelectOrEnd: nil
        ]
    ]


!

doDeleteToEnd
    "delete all changes from current to the end"

    |changeNr|

    changeNr := (self listOfChanges indexOf: self selectionOfChange value).
    changeNr ~~ 0 ifTrue:[
        self deleteChangesFrom:changeNr to: self listOfChanges size.
        self clearCodeView.
        self autoSelectOrEnd: nil
    ]

!

doFindSnapshot: what

    self listOfChanges detect: [:change| change type = 'image'] ifNone: [^self warn: 'No snapshot found!!'].

    self withSelectedChangeDo:[:change|
        |snapshotNr snapshotFound|
        snapshotNr := self listOfChanges indexOf: change.
        snapshotFound := false.
        [snapshotNr > 0 and: [snapshotFound not]] 
        whileTrue: 
        [
            what = 'last'
            ifTrue:
            [
                snapshotNr := snapshotNr - 1.
                snapshotNr == 0 ifTrue: [snapshotNr := self listOfChanges size].   
            ]
            ifFalse:
            [
                snapshotNr := snapshotNr + 1.
                snapshotNr == self listOfChanges size ifTrue: [snapshotNr := 0].
            ].
            (self listOfChanges at: snapshotNr ifAbsent: [^self autoSelectChange: (what = 'last' ifTrue: [self listOfChanges last] ifFalse: [self listOfChanges first])]) type = 'image'
            ifTrue: 
            [
                snapshotFound := true.
                self autoSelectChange:(self listOfChanges at: snapshotNr)
            ]
        ]
    ]
!

doLoadFrom

    |fileName|
    (fileName :=
        (FileSelectionBrowser
            request: 'Load Changes List From'
            fileName: changeFileName
            withFileFilters: #('c*'))) notNil
    ifTrue:
    [
        changeFileName := fileName.
        changes removeAll.
        self readChangesFileInBackground:true.
        self unselectChange.
        Processor addTimedBlock:autoUpdateBlock afterSeconds:5.
        self updateInfoLabel.
        self autoSelectLast
    ]
!

doReload
    "reloads the changes-file"

    changes removeAll.
    self unselectChange.
    self readChangesFileInBackground:true.
    filterCompletionBlock value: self valueOfFilter value.
    self autoSelectLast

!

doSave
    "write back the changes file. To avoid problems when the disk is full
     or a crash occurs while writing (well, or someone kills us), 
     first write the stuff to a new temporary file. If this works ok,
     rename the old change-file to a .bak file and finally rename the
     tempfile back to the change-file. 
     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 stamp f|

    self valueOfNotReading value ifFalse: [^nil].

    editingClassSource ifTrue:[
        (self confirm:'You are editing a classes sourceFile (not a changeFile) !!\Are you certain, you want to overwrite it ?' withCRs)
        ifFalse:[
            ^ false
        ]
    ].

    tempfile := Filename newTemporaryIn:nil.
    tempfile exists ifTrue:[tempfile remove].

    outStream := tempfile writeStream.
    outStream isNil ifTrue:[
        self warn:'Cannot create temporary file in current directory.'.
        ^ false
    ].

    changeFileName asFilename copyTo: changeFileName,'S'.
    inStream := FileStream readonlyFileNamed:changeFileName.
    inStream isNil ifTrue:[^ false].

    self withCursor:(Cursor write) do:[
        |excla sawExcla done chunk
         nChanges "{Class:SmallInteger}" |

        Stream writeErrorSignal handle:[:ex |
            self warn:('Could not update the changes file.\\' , ex errorString) withCRs.
            ^ false
        ] do:[

            excla := inStream class chunkSeparator.
            nChanges := changes size.

            1 to:nChanges do:[:index |     
                inStream position: (changes at: index) position.
                sawExcla := inStream peekFor:excla.
                chunk := inStream nextChunk.
                (stamp := (changes at:index) timeStamp) notNil ifTrue:[
                    outStream nextPutAll:'''---- timestamp ' , stamp , ' ----'''.
                    outStream nextPut:excla; cr.
                ].

                sawExcla ifTrue:[
                    outStream nextPut:excla.
                    outStream nextChunkPut:chunk.
                    outStream cr.
                    "
                     a method-definition chunk - skip followups
                    "
                    done := false.
                    [done] whileFalse:[
                        chunk := inStream nextChunk.
                        chunk isNil ifTrue:[
                            done := true
                        ] ifFalse:[
                            outStream nextChunkPut:chunk.
                            outStream cr.
                            done := chunk isEmpty
                        ]
                    ].
                ] ifFalse:[           
                    outStream nextChunkPut:chunk.
                    outStream cr
                ]
            ].
            outStream close.
            inStream close.
        ].

        f := changeFileName asFilename.
        f renameTo:(f withSuffix:'bak').
        tempfile renameTo:changeFileName.
        modified := false
    ].
    ^ true


!

doSaveAs 

    |fileName|
    (fileName :=
        (FileSelectionBrowser
            request: 'Load Changes From'
            fileName: changeFileName
            withFileFilters: #('changes*'))) notNil
    ifTrue:
    [        
        (fileName := (Filename named:fileName) writeStream) notNil
        ifTrue:
        [
            fileName close.
            changeFileName := fileName pathName.
            self doSave.
            self updateInfoLabel
        ]
    ]
! !

!NewChangesBrowser::Change methodsFor:'accessing'!

category

    ^category
!

category: aValue

    category := aValue
!

chunk

    ^chunk
!

chunk: aValue

    chunk := aValue
!

className

    ^className
!

className: aValue

    className := aValue
!

delta

    delta size = 0 ifTrue: [^''].
    delta = '='    ifTrue: [^'Current'].
    delta = '?'    ifTrue: [^'No class'].
    delta = '-'    ifTrue: [^'Removed'].
    delta = '+'    ifTrue: [^'New'].
    ^delta
!

delta: aValue

    delta := aValue
!

followUp

    ^followUp
!

followUp: aValue

    followUp := aValue
!

listColor

    (string at: 3) ~~ $- ifTrue: 
    [
        (self type = 'class') ifTrue: [^Color gray].
        ^Color white
    ].

    (string includesMatchString: '---- s') ifTrue: [^Color red].
    (string includesMatchString: '---- f') ifTrue: [^Color cyan: 100 magenta: 20 yellow: 20].
    (string includesMatchString: '---- c') ifTrue: [^Color blue].

    ^Color white
!

position

    ^position
!

position: aValue

    position := aValue
!

string

    ^string
!

string: aValue

    string := aValue
!

timeStamp

    ^timeStamp
!

timeStamp: aValue

    timeStamp := aValue
!

type

    (type = 'doIt' or: [type = 'remove']) ifTrue: [^'class'].
    (type size = 0) ifTrue: [(string includesMatchString: '---- s') ifTrue: [^'image'] ifFalse: [^'source']].
    (self category = nil) ifTrue: [^'class'].
    ^'method'
!

type: aValue

    type := aValue
! !

!NewChangesBrowser class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libtool/NewChangesBrowser.st,v 1.1 1998-04-14 13:08:18 tz Exp $'
! !