FileOperation.st
author Claus Gittinger <cg@exept.de>
Wed, 27 Apr 2005 12:06:41 +0200
changeset 6285 fc86d75c6e29
parent 6250 5b0673ccff1c
child 6355 1c37fbd30aae
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 2003 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

"{ Package: 'stx:libtool2' }"

Object subclass:#FileOperation
	instanceVariableNames:'errorString result'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-Support'
!

FileOperation subclass:#Copy
	instanceVariableNames:'colOfCopiedFiles'
	classVariableNames:''
	poolDictionaries:''
	privateIn:FileOperation
!

FileOperation subclass:#Create
	instanceVariableNames:'createdFile'
	classVariableNames:'LastCreatedDirectory LastCreatedFile'
	poolDictionaries:''
	privateIn:FileOperation
!

FileOperation subclass:#Delete
	instanceVariableNames:'fileName'
	classVariableNames:''
	poolDictionaries:''
	privateIn:FileOperation
!

FileOperation::Delete subclass:#Erase
	instanceVariableNames:''
	classVariableNames:''
	poolDictionaries:''
	privateIn:FileOperation
!

FileOperation subclass:#Move
	instanceVariableNames:'colOfMovedFiles'
	classVariableNames:''
	poolDictionaries:''
	privateIn:FileOperation
!

FileOperation subclass:#Rename
	instanceVariableNames:'renamedFiles'
	classVariableNames:''
	poolDictionaries:''
	privateIn:FileOperation
!

!FileOperation class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2003 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
! !

!FileOperation class methodsFor:'actions'!

copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarningBoolean copyFileIfSame:copyIfSameBoolean
    ^ Copy 
        copyFile:aSourceFile to:aDestFile 
        withOverWriteWarning:overWriteWarningBoolean 
        copyFileIfSame:copyIfSameBoolean
!

copyFiles:aColOfSourceFiles to:aDirectory
    ^ Copy copyFiles:aColOfSourceFiles to:aDirectory
!

createDirectoryIn:aDirectory
    ^ Create createDirectoryIn:aDirectory
!

createDirectoryIn:aDirectory initialAnswer:defaultAnswer
    ^ Create createDirectoryIn:aDirectory initialAnswer:defaultAnswer
!

createFileIn:aDirectory
    ^ Create createFileIn:aDirectory
!

createHardLinkIn:dir
    ^ Create createHardLinkIn:dir
!

createLinkIn:dir soft:soft
    ^ Create createLinkIn:dir soft:soft
!

createSoftLinkIn:dir
    ^ Create createSoftLinkIn:dir
!

deleteFiles:colOfFiles confirm:confirm
    ^ Delete deleteFiles:colOfFiles confirm:confirm
!

eraseFiles:colOfFiles confirm:confirm
    ^ Erase deleteFiles:colOfFiles confirm:confirm
!

moveFile:aSourceFile to:aDestFile
    ^ Move moveFile:aSourceFile to:aDestFile
!

moveFiles:aCollectionOfFiles to:aDestDirectory
    ^ Move moveFiles:aCollectionOfFiles to:aDestDirectory
!

renameFile:filename to:newFileString
    ^ Rename renameFile:filename to:newFileString
!

renameFiles:aCollectionofFilenames
    ^ Rename renameFiles:aCollectionofFilenames
! !

!FileOperation class methodsFor:'defaults'!

suffixForCopyOverExistingFile
    ^ '.copy'
! !

!FileOperation methodsFor:'accessing'!

errorString
    ^ errorString
!

errorString:something
    errorString := something.
!

result
    ^ result
!

result:something
    result := something.
! !

!FileOperation methodsFor:'dialogs'!

fileExistsDialogForNewFile:newFile oldFile:oldFile withCancel:withCancel
    "return true, if the file should be moved/copied.
     Ask user if oldFile exists."

    ^ self fileExistsDialogForNewFile:newFile oldFile:oldFile withCancel:withCancel withRemoveIfSame:false.
!

fileExistsDialogForNewFile:newFile oldFile:oldFile withCancel:withCancel withRemoveIfSame:withRemoveIfSame
    "return true, if the file should be moved/copied.
     If oldFile exists, ask user.
     If withRemoveIfSame is true, two additional possible values are returned:
        #removeSource and #removeDestination.
    "

    |msg oldSize newSize sameContents resources sourceType destType|

    newFile exists ifFalse:[ ^ true ].
    oldSize := oldFile fileSize.
    newSize := newFile fileSize.

    sameContents := false.

    newFile isDirectory ifTrue:[
        oldFile isDirectory ifTrue:[
            "/ could (should?) recursively look for same contents here...
        ].
    ] ifFalse:[
        oldFile isDirectory ifFalse:[
            sameContents := oldSize = newSize and:[oldFile sameContentsAs:newFile].
        ]
    ].

    "/ for now:
    oldFile isDirectory ~~ newFile isDirectory ifTrue:[
        Dialog warn:(resources string:'Will not overwrite directory with file and vice versa.').
        ^  false.
    ].

    sourceType := newFile isDirectory ifTrue:'directory' ifFalse:'file'.
    destType := oldFile isDirectory ifTrue:'directory' ifFalse:'file'.

    sameContents ifTrue:[
        msg := 'Overwrite existing destination %7:\\  %1\    size: %3 of %2\\with source (same contents):\\  %4\    size: %6 of %5'.
    ] ifFalse:[
        msg := 'Overwrite existing destination %7:\\  %1\    size: %3 of %2\\with source:\\  %4\    size: %6 of %5'.
    ].

    resources := AbstractFileBrowser classResources.
    msg := resources 
            stringWithCRs:msg
            withArgs:(Array
                with:newFile asString allBold
                with:(newFile modificationTime printStringFormat:'%(Day)-%(mon)-%(year) %h:%m:%s')
                with:newSize
                with:oldFile asString allBold 
                with:(oldFile modificationTime printStringFormat:'%(Day)-%(mon)-%(year) %h:%m:%s')
                with:oldSize
                with:destType).

    (sameContents and:[withRemoveIfSame]) ifTrue:[
        ^ OptionBox  
                  request:msg
                  label:(resources string:'Overwrite existing file')
                  image:(YesNoBox iconBitmap)
                  buttonLabels:(resources array:( #('No' 'Remove Source' 'Remove Destination' 'Yes' ) , (withCancel ifTrue:[#('Cancel')] ifFalse:[#()])))
                  values:#( false #removeSource #removeDestination true )
                  default:#removeSource
                  onCancel:nil.
    ] ifFalse:[
        ^ Dialog confirm:msg withCancel:withCancel.
    ].
! !

!FileOperation methodsFor:'queries'!

isErase
    ^ false
! !

!FileOperation::Copy class methodsFor:'actions'!

copyFile:aSourceFile to:aDestFile
    |instance|

    instance := self new.
    instance copyFile:aSourceFile to:aDestFile.
    ^ instance
!

copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning
    |instance|

    instance := self new.
    instance copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning.
    ^ instance
!

copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarningBoolean copyFileIfSame:copyIfSameBoolean
    |instance|

    instance := self new.
    instance copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarningBoolean copyFileIfSame:copyIfSameBoolean.
    ^ instance
!

copyFiles:aColOfSourceFiles to:aDirectory
    |instance|

    instance := self new.
    instance copyFiles:aColOfSourceFiles to:aDirectory.
    ^ instance
!

copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning
    |instance|

    instance := self new.
    instance copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning.
    ^ instance
!

copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning copyFileIfSame:copy
    |instance|

    instance := self new.
    instance copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning copyFileIfSame:copy.
    ^ instance
! !

!FileOperation::Copy methodsFor:'accessing'!

colOfCopiedFiles
    colOfCopiedFiles isNil ifTrue:[
        colOfCopiedFiles := OrderedCollection new.
    ].
    ^ colOfCopiedFiles
! !

!FileOperation::Copy methodsFor:'actions'!

copyFile:aSourceFile to:aDestFile
    self copyFile:aSourceFile to:aDestFile withOverWriteWarning:true
!

copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning
    self copyFile:aSourceFile to:aDestFile withOverWriteWarning:true copyFileIfSame:true
!

copyFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarningBoolean copyFileIfSame:copyIfSameBoolean
    |newFile fileString targetDirectory targetIsDirectory sourceIsDirectory suffix|

    sourceIsDirectory := aSourceFile isDirectory.
    targetIsDirectory := aDestFile isDirectory.
    targetIsDirectory ifTrue:[
        targetDirectory := aDestFile.
        newFile := aDestFile construct:(aSourceFile baseName).
    ] ifFalse:[
        targetDirectory := aDestFile directory.
        newFile := aDestFile.
    ].
    "/ do not copy if destination directory doest exist.
    targetDirectory exists ifFalse:[
        Dialog warn:'cant copy to not existing directory ', targetDirectory asString. 
        result := false.
        ^ self
    ].
    (newFile exists) ifTrue:[
        ((newFile asString = aSourceFile asString) and:[copyIfSameBoolean]) ifTrue:[
            [newFile exists] whileTrue:[
                suffix := newFile suffix.
                fileString := newFile baseName withoutSuffix, self class suffixForCopyOverExistingFile, '.', suffix.
                newFile := targetDirectory construct:fileString.
            ].
        ] ifFalse:[
            overWriteWarningBoolean ifTrue:[
                (self fileExistsDialogForNewFile:newFile oldFile:aSourceFile withCancel:false withRemoveIfSame:false) ifFalse:[ 
                    result := false.
                    ^ self.
                ]
            ] ifFalse:[
                    result := false.
                    ^ self.
            ]
        ].
    ].
    Error handle:[:ex|
        "was not able to copy it"
        WarningBox warn:'on copy file - ', ex errorString.
        self errorString:('on copy file - ', ex description asString).
        result := false.
        ^ self
    ] do:[
        sourceIsDirectory ifTrue:[
            OperatingSystem recursiveCopyDirectory:(aSourceFile pathName) to:(newFile pathName).
        ] ifFalse:[
            aSourceFile copyTo:newFile.
        ].
    ].
    DirectoryContents flushCachedDirectoryFor:(aSourceFile directory).
    result := true.
!

copyFiles:aColOfSourceFiles to:aDirectory
    ^ self copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:true. 
!

copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning 
    ^ self copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning copyFileIfSame:true.
!

copyFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning copyFileIfSame:copy
    |newFile suffix fileString sourceIsDirectory overWriteExisting|

    (aDirectory exists) ifFalse:[
        Dialog warn:'Can''t copy to non-existing directory ', aDirectory asString. 
        result := false.
        ^ self
    ].
    (aDirectory isDirectory) ifFalse:[
        Dialog warn:'Destination ', aDirectory asString, ' is not a directory'. 
        result := false.
        ^ self
    ].
    aColOfSourceFiles do:[: filename |
        newFile := aDirectory construct:(filename baseName).
        sourceIsDirectory := filename isDirectory.
        overWriteExisting := true.
        (newFile exists) ifTrue:[
            ((newFile asString = filename asString) and:[copy]) ifTrue:[
                [newFile exists] whileTrue:[
                    suffix := newFile suffix.
                    fileString := newFile withoutSuffix baseName, self class suffixForCopyOverExistingFile.
                    suffix notEmpty ifTrue:[
                        fileString := fileString, '.', suffix.
                    ].
                    newFile := aDirectory construct:fileString.
                ].
            ] ifFalse:[
                overWriteWarning ifTrue:[
                    overWriteExisting := (self fileExistsDialogForNewFile:newFile oldFile:filename withCancel:(aColOfSourceFiles size > 1) withRemoveIfSame:false).
                    overWriteExisting isNil ifTrue:[
                        " abort pressed "
                        result := false.
                        ^ self.
                    ]
                ]
            ].
        ].
        overWriteExisting ifTrue:[
            Error handle:[:ex|
                "was not able to copy it"
                result := false.
                self errorString:('on copy file - ', ex description asString).
                ( Dialog 
                    confirm:('error on copy file - ', ex description asString) 
                    title:'Copy'
                    yesLabel:'Continue' 
                    noLabel:'Abort') ifTrue:[
                        ex proceed.
                    ] ifFalse:[
                        ^ self.
                    ].
            ] do:[
                sourceIsDirectory ifTrue:[
                    OperatingSystem recursiveCopyDirectory:(filename asString)  
                                                        to:(newFile asString).
                ] ifFalse:[
                    filename copyTo:newFile.
                ].
            ].
            self colOfCopiedFiles add:filename
        ]
    ].
    DirectoryContents flushCachedDirectoryFor:aDirectory.
    result := true.
! !

!FileOperation::Create class methodsFor:'actions'!

createDirectoryIn:aDirectory

    |instance|

    instance := self new.
    instance createDirectoryIn:aDirectory.
    ^ instance
!

createDirectoryIn:aDirectory initialAnswer:defaultAnswer

    |instance|

    instance := self new.
    instance createDirectoryIn:aDirectory initialAnswer:defaultAnswer.
    ^ instance
!

createFileIn:aFile

    |instance|

    instance := self new.
    instance createFileIn:aFile.
    ^ instance
!

createHardLinkIn:aFile
    "create an new hard link in a files directory"

    ^ self createLinkIn:aFile soft:false
!

createLinkIn:aFile soft:soft
    "create an new soft or hard link in a files directory"

    |instance|

    instance := self new.
    instance createLinkIn:aFile soft:soft.
    ^ instance
!

createSoftLinkIn:aFile
    "create an new soft link in a files directory"

    ^ self createLinkIn:aFile soft:true
! !

!FileOperation::Create methodsFor:'accessing'!

createdFile
    ^ createdFile
!

createdFile:something
    createdFile := something.
! !

!FileOperation::Create methodsFor:'actions'!

createDirectoryIn:startDirectory
    |defaultDirectory|

    "/ stupid - that one already exists
"/    LastCreatedDirectory notNil ifTrue:[
"/        defaultDirectory := LastCreatedDirectory baseName.
"/    ].
    
    ^ self createDirectoryIn:startDirectory initialAnswer:defaultDirectory
!

createDirectoryIn:startDirectory initialAnswer:initialAnswerArg
    |resources initialAnswer msg startBaseName newName newDir|

    resources := AbstractFileBrowser classResources.

    startBaseName := startDirectory baseName.
    initialAnswer := initialAnswerArg.
    (startDirectory construct:initialAnswer) exists ifTrue:[
        initialAnswer := nil.
    ].
    Dialog aboutToOpenBoxNotificationSignal ignoreIn:[
        newName := Dialog
                        request:(resources string:'Create New Directory in %1:' with:startBaseName allBold)
                        initialAnswer:initialAnswer
                        okLabel:(resources string:'Create')
                        title:(resources string:'Create Directory')
                        onCancel:[
                            self result:false. 
                            ^ self
                        ].
    ].
    newName isEmpty ifTrue:[
        self result:false. 
        ^ self
    ].
    newDir := startDirectory construct:newName.
    newDir exists ifTrue:[
        Dialog warn:(newName, ' already exists.').
        result := false.
        ^ self
    ].

    newDir makeDirectory ifFalse:[
        msg := errorString := ('cannot create directory '', newName,'' !!') , '(', (OperatingSystem lastErrorString) , ')'.
        errorString := msg.
        Dialog warn:errorString.
        result := false.
        ^ self
    ].
    self createdFile:newDir.
    LastCreatedDirectory := newDir.
    result := true.
!

createFileIn:aFile
    "create an empty file"

    |resources aStream msg file directory newName defaultFile|

    resources := AbstractFileBrowser classResources.

    aFile isDirectory ifTrue:[
        directory := aFile
    ] ifFalse:[
        directory := aFile directory.
        file := aFile
    ].
    LastCreatedFile isNil ifTrue:[
        defaultFile := aFile baseName.
    ] ifFalse:[
        defaultFile := LastCreatedFile baseName.
    ].
    newName := Dialog
                    request:(resources string:'Create New File in %1:' with:directory baseName allBold)
                    initialAnswer:defaultFile
                    okLabel:(resources string:'Create')
                    title:(resources string:'Create File')
                    onCancel:[
                        self result:false. 
                        ^ self
                    ].
    newName isEmpty ifTrue:[
        self result:false. 
    ] ifFalse:[
        | newFile |
        newFile := directory construct:newName.
        newFile exists ifTrue:[
            (Dialog 
                confirm:(newName, ' already exists truncate ?')
                yesLabel:('Truncate')
                noLabel:('Cancel'))
            ifFalse:[
                self result:false. 
                ^ self
            ].
        ].
        FileStream openErrorSignal handle:[:ex|
            msg := ('Cannot create file '', newName,'' !!') , '(' , (FileStream lastErrorString) , ')'.
            errorString := msg.
            self result:false. 
            ^ Dialog warn:errorString
        ] do:[    
            aStream := newFile newReadWriteStream.
        ].
        aStream close.
        self createdFile:newFile.
        LastCreatedFile := newFile.
        self result:true. 
    ].
!

createHardLinkIn:aFile
    "create an new hard link in a files directory"

    self createLinkIn:aFile soft:false.
!

createLinkIn:aFile soft:symbolic
    "ask for the link target;
     then, create an new soft or hard link in aFile's directory"

    |resources newPath oldPath box string if1 if2|

    resources := AbstractFileBrowser classResources.

    aFile isDirectory ifTrue:[
        newPath := aFile asValue.
        oldPath := aFile asValue.
    ] ifFalse:[
        newPath := aFile directory asValue.
        oldPath := aFile asValue.
    ].

    box := Dialog new.
    box label:'Create Link'.

    string := 'Create ', (symbolic ifTrue:['Symbolic'] ifFalse:['Hard']) ,' Link from:'.
    (box addTextLabel:(resources string:string)) adjust:#left.
    if1 := box addFilenameInputFieldOn:oldPath in:nil tabable:true.
    (box addTextLabel:(resources string:'to:')) adjust:#left.
    if2 := box addFilenameInputFieldOn:newPath in:nil value tabable:true.

    box addAbortAndOkButtons.

    aFile isDirectory ifFalse:[
        box focusOnField:if1.
    ].
    box showAtPointer.

    box accepted ifFalse:[
        self result:false.
    ] ifTrue:[
        self doCreateLinkFrom:(oldPath value) to:(newPath value) soft:symbolic.
    ].
!

createSoftLinkIn:aFile
    "create an new soft link in a files directory"

    self createLinkIn:aFile soft:true.
!

doCreateLinkFrom:oldPath to:newPathArg soft:symbolic
    "actually create a soft or hard link"

    |resources err newPath newPathFile oldPathFile|

    newPath := newPathArg.

    resources := AbstractFileBrowser classResources.

    (oldPath size == 0) ifTrue:[
        self operationError:'Missing source'.
        ^ self.
    ].
    (newPath size == 0) ifTrue:[
        self operationError:'Missing link name (target)'.
        ^ self.
    ].

    newPathFile := newPath asFilename.
    oldPathFile := oldPath asFilename.

    newPathFile exists ifTrue:[
        newPathFile isDirectory ifTrue:[
            newPathFile := newPathFile construct:(oldPathFile baseName).
            newPath := newPathFile name.
        ].
    ].

    newPathFile exists ifTrue:[
        self operationError:(resources string:'%1 already exists' with:newPath allBold).
        ^ self.
    ].
    oldPathFile exists ifFalse:[
        symbolic ifTrue:[
            oldPathFile isAbsolute ifTrue:[
                self operationError:(resources string:'%1 does not exist' with:oldPath allBold).
                ^ self.
            ].
            (newPathFile directory construct:oldPath) exists ifFalse:[
                Dialog warn:(resources string:'%1 does not exist (Warning only)' with:oldPath allBold).
            ].
        ] ifFalse:[
            self operationError:(resources string:'%1 does not exist' with:oldPath allBold).
            ^ self.
        ].
    ].
    ((symbolic not) and:[oldPathFile isDirectory]) ifTrue:[
        self operationError:(resources string:'%1 is a directory' with:oldPath allBold).
        ^ self.
    ].
    ErrorSignal handle:[:ex |
        err := ex errorString.
        self operationError:err.
    ] do:[
        symbolic ifTrue:[
            OperatingSystem createSymbolicLinkFrom:oldPath to:newPath.
        ] ifFalse:[
            OperatingSystem createHardLinkFrom:oldPath to:newPath
        ].
        self createdFile:newPathFile.
        self result:true.
    ].
!

operationError:msg
    self result:false.
    Dialog warn:msg.
    self errorString:msg.
! !

!FileOperation::Delete class methodsFor:'actions'!

deleteFile:aFileOrDirectory
    "delete aFileOrDirectory"

    |instance|

    instance := self new.
    instance deleteFile:aFileOrDirectory.
    ^ instance
!

deleteFiles:aCollectionOfFiles
    ^ self deleteFiles:aCollectionOfFiles confirm:true
!

deleteFiles:aCollectionOfFiles confirm:confirm
    "delete aCollectionOfFiles"

    |instance|

    instance := self new.
    instance deleteFiles:aCollectionOfFiles confirm:confirm.
    ^ instance
! !

!FileOperation::Delete methodsFor:'actions'!

deleteFile:aFileOrDirectory
    | file isDirectory |

    aFileOrDirectory notNil ifTrue:[
        file := aFileOrDirectory asFilename.

        isDirectory := file isDirectory.

        Error handle:[:ex|
            "was not able to remove it"
            Dialog warn:(ex description, '\' withCRs, ex signal notifierString).
            self errorString:(ex description, ' - ', ex signal notifierString).
            result := false.
            ^ self.
        ] do:[
            isDirectory ifTrue:[
                file recursiveRemove
            ] ifFalse:[
                self eraseFilesContentsBeforeRemoving:file.
                file remove
            ].
        ].
        "/ flush parent directory or directory
    ].
    DirectoryContents flushCachedDirectoryFor:(file directory).
    result := true.

    "Modified: / 17-03-2004 / 12:42:02 / cg"
!

deleteFiles:colOfFiles
    ^ self deleteFiles:colOfFiles confirm:true.
!

deleteFiles:colOfFiles confirm:confirm
    |resources answer nFilesToDelete ask labels values fileTypeString msg lbls vals|

    ask := confirm.
    resources := AbstractFileBrowser classResources.

    nFilesToDelete := colOfFiles size.
    colOfFiles do:[:filenameOrString |
        |filename askForNonEmptyDirectory skip|

        filename := filenameOrString asFilename.

        skip := false.
        fileTypeString := ''.
        filename isSymbolicLink ifTrue:[
            fileTypeString := 'symbolic link '.
        ] ifFalse:[        
            filename exists ifFalse:[ 
                (Dialog 
                    confirm:('%1 does not exist.' bindWith:filename asString allBold)
                    yesLabel:(resources string:'Proceed')
                    noLabel:(resources string:'Cancel')) 
                ifFalse:[
                    ^ self.
                ].
                skip := true.
            ]
        ].
        skip ifFalse:[
            ask ifTrue:[
                nFilesToDelete = 1 ifTrue:[
                    labels := #('No' 'Yes').
                    values := #(#no #yes).
                ] ifFalse:[
                    labels := #('Cancel' 'No' 'Yes' 'Yes to All' ).
                    values := #(#cancel #no #yes #yesToAll).
                ].
                msg := self isErase ifTrue:'Really erase' ifFalse:'Really delete'.
                msg := msg ,
                       (nFilesToDelete = 1 
                            ifTrue:'\\%1%2 ?' 
                            ifFalse:'\\%1%2 \\(%3 files alltogether)').
                Dialog aboutToOpenBoxNotificationSignal handle:[:ex | ex proceed]
                do:[
                    answer := Dialog 
                        confirmWithCancel:(resources 
                                            stringWithCRs:msg 
                                            with:fileTypeString
                                            with:(filename asString allBold)
                                            with:nFilesToDelete)
                        labels:(resources array:labels)
                        values:values
                        default:(values indexOf:#yes).
                ]
            ] ifFalse:[
                answer := #yesToAll.
            ].

            answer == #cancel ifTrue:[
                ^ self.
            ].

            answer == #yesToAll ifTrue:[
                ask := false.
                answer := #yes.
            ].

            answer == #yes ifTrue:[
                askForNonEmptyDirectory := true.
                filename isSymbolicLink ifFalse:[
                    filename isNonEmptyDirectory ifTrue:[
                        colOfFiles size == 1 ifTrue:[
                            lbls := #('Cancel' 'Remove').
                            vals := #(false true).
                        ] ifFalse:[
                            lbls := #('Cancel All' 'Keep' 'Remove').
                            vals := #(nil false true).
                        ].
                        askForNonEmptyDirectory := Dialog
                                    confirmWithCancel:(resources 
                                                        stringWithCRs:'Directory ''%1'' is not empty\remove anyway ?' 
                                                        with:filename pathName allBold) 
                                    labels:( resources array:lbls )
                                    values:vals 
                                    default:vals size.
                        askForNonEmptyDirectory == nil ifTrue:[
                            ^ self
                        ].
                    ].
                ].
                askForNonEmptyDirectory ifTrue:[
                    self deleteFile:filename.
                ]
            ].
        ].
    ].
!

eraseFilesContentsBeforeRemoving:file
    "intentionally left blank"
! !

!FileOperation::Erase methodsFor:'actions'!

eraseFilesContentsBeforeRemoving
    "fill file with zeros"

    self halt.
!

eraseFilesContentsBeforeRemoving:file
    "fill file with zeros (to be really erased from the disk)"

    |writeStream fileSize remaining buffer bufferSize nWritten|

    fileSize := file fileSize.
    writeStream := file asFilename readWriteStream.

    remaining := fileSize.    
    bufferSize := 8192.
    buffer := ByteArray new:bufferSize.
    [remaining > 0] whileTrue:[
        nWritten := writeStream 
            nextPutBytes:(bufferSize min:remaining)
            from:buffer
            startingAt:1.
        remaining := remaining - nWritten.
    ].
    writeStream close.
! !

!FileOperation::Erase methodsFor:'queries'!

isErase
    ^ true
! !

!FileOperation::Move class methodsFor:'actions'!

moveFile:aSourceFile to:aDestFile
    |instance|

    instance := self new.
    instance moveFile:aSourceFile to:aDestFile.
    ^ instance
!

moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning
    |instance|

    instance := self new.
    instance moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning.
    ^ instance
!

moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning moveFileIfSame:move
    |instance|

    instance := self new.
    instance moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning moveFileIfSame:move.
    ^ instance
!

moveFiles:aColOfSourceFiles to:aDirectory
    |instance|

    instance := self new.
    instance moveFiles:aColOfSourceFiles to:aDirectory.
    ^ instance
!

moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning
    |instance|

    instance := self new.
    instance moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning.
    ^ instance
!

moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning moveFileIfSame:move
    |instance|

    instance := self new.
    instance moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning moveFileIfSame:move.
    ^ instance
! !

!FileOperation::Move methodsFor:'accessing'!

colOfMovedFiles
    colOfMovedFiles isNil ifTrue:[
        colOfMovedFiles := OrderedCollection new.
    ].
    ^ colOfMovedFiles
! !

!FileOperation::Move methodsFor:'actions'!

moveFile:aSourceFile to:aDestFile

    ^ self moveFile:aSourceFile to:aDestFile withOverWriteWarning:true.
!

moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning

    ^ self moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning moveFileIfSame:true
!

moveFile:aSourceFile to:aDestFile withOverWriteWarning:overWriteWarning moveFileIfSame:move

    |newFile fileString targetDirectory targetIsDirectory suffix doMove|

    targetIsDirectory := aDestFile isDirectory.
    targetIsDirectory ifTrue:[
        targetDirectory := aDestFile.
        newFile := aDestFile construct:(aSourceFile baseName).
    ] ifFalse:[
        targetDirectory := aDestFile directory.
        newFile := aDestFile.
    ].
    "/ do not copy if destination directory doest exist.
    targetDirectory exists ifFalse:[
        Dialog warn:'Cannot move to non-existing directory ', targetDirectory asString. 
        result := false.
        ^ self
    ].
    (newFile exists) ifTrue:[
        ((newFile asString = aSourceFile asString) and:[move]) ifTrue:[
            [newFile exists] whileTrue:[
                suffix := newFile suffix.
                fileString := newFile baseName withoutSuffix, self class suffixForCopyOverExistingFile, '.', suffix.
                newFile := targetDirectory construct:fileString.
            ].
        ] ifFalse:[
            overWriteWarning ifTrue:[
                doMove := self fileExistsDialogForNewFile:newFile oldFile:aSourceFile withCancel:false withRemoveIfSame:true.
                doMove == #removeSource ifTrue:[ 
                    self halt.
                    result := false.
                    ^ self.
                ].
                doMove == #removeDestination ifTrue:[ 
                    self halt.
                    result := false.
                    ^ self.
                ].
                doMove == true ifFalse:[ 
                    result := false.
                    ^ self.
                ].

            ] ifFalse:[
                result := false.
                ^ self.
            ]
        ].
    ].
    Error handle:[:ex|
        "was not able to copy it"
        WarningBox warn:'Error in copy file Operation: ', ex errorString.
        self errorString:('Error in copy file Operation- ', ex description asString).
        result := false.
        ^ self
    ] do:[
        aSourceFile moveTo:newFile.
    ].
    DirectoryContents flushCachedDirectoryFor:(aSourceFile directory).
    result := true.
!

moveFiles:aColOfSourceFiles to:aDirectory

    ^ self moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:true
!

moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning

    ^ self moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning moveFileIfSame:true
!

moveFiles:aColOfSourceFiles to:aDirectory withOverWriteWarning:overWriteWarning moveFileIfSame:move

    |newFile suffix fileString doMove doRemoveSource doRemoveDestination|

    aDirectory exists ifFalse:[
        (Dialog confirm:(FileBrowser classResources 
                            stringWithCRs:'Non-existing directory "%1" .\Create ?' 
                            with:aDirectory asString allBold)) ifFalse:[
            result := false.
            ^ self
        ].
        aDirectory makeDirectory.
        aDirectory exists ifFalse:[
            Dialog warn:(FileBrowser classResources 
                                stringWithCRs:'Cannot create directory "%1" !!\Create ?.' 
                                with:aDirectory asString allBold).
            result := false.
            ^ self
        ]
    ].
    (aDirectory isDirectory) ifFalse:[
        Dialog warn:('Destination %1 is not a directory.' bindWith:aDirectory asString allBold).
        result := false.
        ^ self
    ].
    aColOfSourceFiles do:[: filename |
        newFile := aDirectory construct:filename baseName.

        doMove := true.
        doRemoveSource := false.
        doRemoveDestination := false.

        (newFile exists) ifTrue:[
            ((newFile asString = filename asString) and:[move]) ifTrue:[
                [newFile exists] whileTrue:[
                    suffix := newFile suffix.
                    fileString := newFile withoutSuffix baseName , self class suffixForCopyOverExistingFile, '.', suffix.
                    newFile := aDirectory construct:fileString.
                ].
            ] ifFalse:[
                overWriteWarning ifTrue:[
                    doMove := self fileExistsDialogForNewFile:newFile oldFile:filename withCancel:(aColOfSourceFiles size > 1) withRemoveIfSame:true.
                    doMove isNil ifTrue:[   "/ cancel
                        result := false.
                        ^ self.
                    ].
                    (doMove == #removeSource) ifTrue:[
                        doRemoveSource := true.
                        doMove := false
                    ] ifFalse:[ 
                        (doMove == #removeDestination) ifTrue:[
                            doRemoveDestination := true.
                            doMove := false
                        ]
                    ]
                ]
            ].
        ].

        (doMove or:[doRemoveSource or:[doRemoveDestination]]) ifTrue:[
            Error handle:[:ex|
                "was not able to copy it"
                |descriptionString|

                result := false.
                descriptionString := ex description asString.
                self errorString:(' Error in Move-File Operation:', descriptionString).
                ( Dialog 
                    confirm:(' Error in Move-File Operation: ', descriptionString) 
                    title:'Move'
                    yesLabel:'Continue' 
                    noLabel:'Abort'
                ) ifFalse:[
                    ^ self.
                ].
            ] do:[
                doRemoveSource ifTrue:[
                    filename remove
                ] ifFalse:[
                    doRemoveDestination ifTrue:[
                        newFile remove
                    ] ifFalse:[
                        filename moveTo:newFile.
                    ]
                ]
            ].
            self colOfMovedFiles add:filename
        ]
    ].
    DirectoryContents flushCachedDirectoryFor:aDirectory.
    result := true.
! !

!FileOperation::Rename class methodsFor:'actions'!

renameFile:oldFile to:newName
    |instance|

    instance := self new.
    instance renameFile:oldFile to:newName.
    ^ instance
!

renameFiles:aColOfFiles 
    |instance|

    instance := self new.
    instance renameFiles:aColOfFiles.
    ^ instance
! !

!FileOperation::Rename methodsFor:'accessing'!

renamedFiles
    renamedFiles isNil ifTrue:[
        renamedFiles := OrderedCollection new.
    ].
    ^ renamedFiles
! !

!FileOperation::Rename methodsFor:'actions'!

renameFile:oldFile to:newName
    "rename a file (or directory)"

    |newFile msg resources|

    (oldFile isNil or:[newName isNil]) ifTrue:[
        result := false.
        ^ self.
    ].
    (oldFile asString isBlank or:[newName isBlank]) ifTrue:[
        result := false.
        ^ self.
    ].
    newName asFilename isAbsolute ifTrue:[
        newFile := newName asFilename.
    ] ifFalse:[
        (oldFile baseName = newName) ifTrue:[
            result := false
        ].
        newFile := oldFile directory construct:newName.
    ].

    oldFile pathName = newFile pathName ifTrue:[
        ^ self.
    ].

    resources := Dialog classResources.

    OperatingSystem errorSignal handle:[:ex|
        msg := resources 
                stringWithCRs:'Cannot rename file %1 to %2 !!\\(%3)' 
                with:oldFile baseName 
                with:newName 
                with:(OperatingSystem lastErrorString).
        Dialog warn:msg withCRs.
        result := false.
        ^ self.
    ] do:[
        newFile exists ifTrue:[
            (newFile sameContentsAs:oldFile) ifTrue:[
                msg := '%1 exists [with same contents] - rename (i.e. overwrite) anyway ?'
            ] ifFalse:[
                msg := '%1 exists - rename (i.e. overwrite) anyway ?'
            ].
            (Dialog confirmWithCancel:(resources string:msg with:newName allBold) default:false) ifFalse:[
                result := false.
                ^ self.
            ]
        ].
        oldFile renameTo:newFile.
        self renamedFiles add:newFile.
    ].
    result := true.
!

renameFiles:aColOfFiles 
    |resources queryBox b lastNewName lastOldName initialText oldName newName renameAll doRename|

    resources := AbstractFileBrowser classResources.
    renameAll := false.

    queryBox := FilenameEnterBox new.
    queryBox okText:(resources string:'Rename').

    aColOfFiles size > 1 ifTrue:[
        b := queryBox addAbortButtonLabelled:(resources string:'Cancel All').
        b action:[^ self].
        queryBox addButton:(Button label:(resources string:'Rename All') action:[renameAll := true. queryBox okPressed]) before:(queryBox okButton).
    ].
    aColOfFiles do:[:oldFile |
        oldName := oldFile baseName asString.
        lastNewName notNil ifTrue:[
            initialText := FileBrowser goodRenameDefaultForFile:oldName lastOld:lastOldName lastNew:lastNewName.
        ].
        doRename := false.
        (renameAll and:[initialText notNil]) ifTrue:[
            doRename := true.
            newName := initialText.
        ] ifFalse:[
            queryBox title:(resources string:'Rename %1 to:' with:(oldName allBold)).
            queryBox initialText:(initialText ? oldName).
            queryBox action:[:newEnteredName | newName := newEnteredName. doRename := true.].
            queryBox show "showAtPointer".
            "/ queryBox accepted ifFalse:[self halt].
        ].
        doRename ifTrue:[
            (self renameFile:oldFile to:newName asString) ifTrue:[
                result := true
            ].
            lastOldName := oldName.
            lastNewName := newName.
        ].
    ]
! !

!FileOperation class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libtool/FileOperation.st,v 1.67 2005-04-27 10:06:41 cg Exp $'
! !