# -*- coding: utf-8 -*-

# Copyright (c) 2003 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the version control systems interface to Subversion.
"""

import os
import shutil
import types

from qt import *

from VCS.VersionControl import VersionControl
from SvnDialog import SvnDialog
from CommitDialog import SvnCommitDialog
from LogDialog import SvnLogDialog
from DiffDialog import SvnDiffDialog
from StatusDialog import SvnStatusDialog
from TagDialog import SvnTagDialog
from TagBranchListDialog import SvnTagBranchListDialog
from CopyDialog import SvnCopyDialog
from CommandDialog import SvnCommandDialog
from SwitchDialog import SvnSwitchDialog
from MergeDialog import SvnMergeDialog
from AuthDialog import AuthDialog
from PropListDialog import SvnPropListDialog
from PropSetDialog import SvnPropSetDialog
import Utilities

class Subversion(VersionControl):
    """
    Class implementing the version control systems interface to Subversion.
    """
    def __init__(self, parent=None, name=None):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget (QWidget)
            
            name -- name of this object (string or QString)
        """
        VersionControl.__init__(self, parent, name)
        self.defaultOptions = {
            'global' : [''],
            'commit' : [''],
            'checkout' : [''],
            'update' : [''],
            'add' : [''],
            'remove' : [''],
            'diff' : [''],
            'log' : [''],
            'history' : [''],
            'status' : [''],
            'tag' : [''],
            'export' : ['']
        }
        
        self.options = self.defaultOptions
        self.tagList = QStringList()
        self.mergeList = [QStringList(), QStringList(), QStringList()]
        
        self.tagTypeList = QStringList()
        self.tagTypeList.append('tags')
        self.tagTypeList.append('branches')
        
        self.commandHistory = QStringList()
        self.wdHistory = QStringList()
        
        # authentication data
        self.username = None
        self.password = None
        
        # list of commands which (possibly) need authentication
        self.authcommands = [
            'update', 'switch', 'status', 'move', 'mkdir', 'merge',
            'log', 'list', 'import', 'export', 'diff', 'delete', 'copy',
            'commit', 'checkout', 'cat'
        ]
        
    def vcsExists(self):
        """
        Public method used to test for the presence of the svn executable.
        
        Returns
        
            flag indicating the existance (boolean)
        """
        self.versionStr = None
        
        self.proc = QProcess('svn', self)
        self.proc.addArgument('--version')
        self.connect(self.proc, SIGNAL('processExited()'), self.vcsExistsProcExited)
        self.connect(self.proc, SIGNAL('readyReadStdout()'), self.vcsExistsReadyReadStdout)
        return self.proc.start()
        
    def vcsInit(self, vcsDir, noDialog=0):
        """
        Public method used to initialize the subversion repository.
        
        The subversion repository has to be initialized from outside eric3
        because the respective command always works locally. Therefore we
        always return TRUE without doing anything.
        
        Arguments
        
            vcsDir -- name of the VCS directory (string)
            
            noDialog -- flag indicating quiet operations (boolean)
        
        Returns
        
            always TRUE
        """
        return 1
        
    def vcsImport(self, vcsDir, message, project, vendor, release, projectDir, noDialog=0):
        """
        Public method used to import the project into the Subversion repository.
        
        Arguments
        
            vcsDir -- name of the VCS directory (string)
            
            message -- message for this operation (string)
            
            project -- name of the archive (string)
            
            vendor -- vendor string (string) (not needed)
            
            release -- release version string (string) (not needed)
            
            projectDir -- project directory (string)
            
            noDialog -- flag indicating quiet operations
        
        Returns
        
            flag indicating an execution without errors (boolean)
        """
        msg = QString(message)
        if msg.isEmpty():
            msg = QString('***')
        
        if vcsDir.startswith('/') or vcsDir[1] == '|':
            vcsDir = 'file://%s' % vcsDir
            
        # create the dir structure to be imported into the repository
        tmpDir = '%s_tmp' % projectDir
        os.makedirs(tmpDir)
        os.mkdir(os.path.join(tmpDir, project))
        os.mkdir(os.path.join(tmpDir, project, 'branches'))
        os.mkdir(os.path.join(tmpDir, project, 'tags'))
        shutil.copytree(projectDir, os.path.join(tmpDir, project, 'trunk'))
        
        proc = QProcess('svn', self)
        proc.addArgument('import')
        self.addArguments(proc, self.options['global'])
        proc.addArgument('-m')
        proc.addArgument(msg)
        if vcsDir.startswith('http'):
            self.addArguments(proc, self.authData())
        proc.addArgument(vcsDir)
        proc.addArgument('.')
        proc.setWorkingDirectory(QDir(tmpDir))
        
        if noDialog:
            status = self.startSynchronizedProcess(proc)
        else:
            dia = SvnDialog(self.trUtf8('Importing project into Subversion repository'))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
            status = dia.normalExit()
            
        shutil.rmtree(tmpDir, 1)
        return status
        
    def vcsCheckout(self, vcsDir, project, projectDir, noDialog=0, tag=None):
        """
        Public method used to check the project out of the Subversion repository.
        
        Arguments
        
            vcsDir -- name of the VCS directory (string)
            
            project -- name of the archive (string)
            
            projectDir -- project directory to create (string)
            
            noDialog -- flag indicating quiet operations
            
            tag -- tag of version to check out (string)
        
        Returns
        
            flag indicating an execution without errors (boolean)
        """
        if vcsDir.startswith('/') or vcsDir[1] == '|':
            vcsDir = 'file://%s' % vcsDir
            
        if tag is None or tag == '':
            svnUrl = '%s/%s/trunk' % (vcsDir, project)
        else:
            if not tag.startswith('tags') and not tag.startswith('branches'):
                type, ok = QInputDialog.getItem(\
                    self.trUtf8("Subversion Checkout"),
                    self.trUtf8("The tag must be a normal tag (tags) or a branch tag (branches)."
                        " Please select from the list."),
                    self.tagTypeList,
                    0, 0)
                if not ok:
                    return 0
                tag = '%s/%s' % (str(type), tag)
            svnUrl = '%s/%s/%s' % (vcsDir, project, tag)
        proc = QProcess('svn', self)
        proc.addArgument('checkout')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['checkout'])
        if svnUrl.startswith('http'):
            self.addArguments(proc, self.authData())
        proc.addArgument(svnUrl)
        proc.addArgument(projectDir)
        
        if noDialog:
            return self.startSynchronizedProcess(proc)
        else:
            dia = SvnDialog(self.trUtf8('Checking project out of Subversion repository'))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
            return dia.normalExit()
        
    def vcsExport(self, vcsDir, project, projectDir, tag=None):
        """
        Public method used to export a directory from the Subversion repository.
        
        Arguments
        
            vcsDir -- name of the VCS directory (string)
            
            project -- name of the archive (string)
            
            projectDir -- project directory to create (string)
            
            tag -- tag of version to check out (string)
        
        Returns
        
            flag indicating an execution without errors (boolean)
        """
        if vcsDir.startswith('/') or vcsDir[1] == '|':
            vcsDir = 'file://%s' % vcsDir
            
        if tag is None:
            svnUrl = '%s/%s/trunk' % (vcsDir, project)
        else:
            if not tag.startswith('tags') and not tag.startswith('branches'):
                type, ok = QInputDialog.getItem(\
                    self.trUtf8("Subversion Export"),
                    self.trUtf8("The tag must be a normal tag (tags) or a branch tag (branches)."
                        " Please select from the list."),
                    self.tagTypeList,
                    0, 0)
                if not ok:
                    return 0
                tag = '%s/%s' % (str(type), tag)
            svnUrl = '%s/%s/%s' % (vcsDir, project, tag)
        proc = QProcess('svn', self)
        proc.addArgument('export')
        self.addArguments(proc, self.options['global'])
        if svnUrl.startswith('http'):
            self.addArguments(proc, self.authData())
        proc.addArgument(svnUrl)
        proc.addArgument(projectDir)
        
        dia = SvnDialog(self.trUtf8('Exporting project from Subversion repository'))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        return dia.normalExit()
        
    def vcsCommit(self, name, message, noDialog=0):
        """
        Public method used to make the change of a file/directory permanent in the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be committed (string)
            
            message -- message for this operation (string)
            
            noDialog -- flag indicating quiet operations
        
        Returns
        
            flag indicating an execution without errors (boolean)
        """
        dname, fname = self.splitPath(name)
        
        msg = QString(message)
        
        if not noDialog and msg.isEmpty():
            # call CommitDialog and get message from there
            dlg = SvnCommitDialog()
            if dlg.exec_loop() == QDialog.Accepted:
                msg = dlg.logMessage()
            else:
                return 0
                
        if msg.isEmpty():
            msg = QString('***')
            
        proc = QProcess('svn', self)
        proc.addArgument('commit')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['commit'])
        proc.addArgument('-m')
        proc.addArgument(msg)
        try:
            if self.svnGetReposName(dname).startswith('http'):
                self.addArguments(proc, self.authData())
        except:
            pass
        proc.addArgument(fname)
        proc.setWorkingDirectory(QDir(dname))
        
        if noDialog:
            return self.startSynchronizedProcess(proc)
        else:
            dia = SvnDialog(self.trUtf8('Commiting changes of %1 to Subversion repository')
                .arg(name))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
            return dia.normalExit()
        
    def vcsUpdate(self, name):
        """
        Public method used to update a file/directory with the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be updated (string)
        """
        dname, fname = self.splitPath(name)
        
        proc = QProcess('svn', self)
        proc.addArgument('update')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['update'])
        try:
            if self.svnGetReposName(dname).startswith('http'):
                self.addArguments(proc, self.authData())
        except:
            pass
        proc.addArgument(fname)
        proc.setWorkingDirectory(QDir(dname))
        
        dia = SvnDialog(self.trUtf8('Synchronizing %1 with the Subversion repository')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsAdd(self, name, isDir=0):
        """
        Public method used to add a file/directory to the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be added (string)
            
            isDir -- flag indicating name is a directory (boolean)
        """
        if isDir:
            dname, fname = os.path.split(str(name))
        else:
            dname, fname = self.splitPath(name)
        
        tree = []
        wdir = dname
        while not os.path.exists(os.path.join(dname, '.svn')):
            # add directories recursively, if they aren't in the repository already
            tree.insert(-1, dname)
            dname, dummy = os.path.split(dname)
            wdir = dname
            
        proc = QProcess('svn', self)
        proc.addArgument('add')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['add'])
        proc.addArgument('--non-recursive')
        self.addArguments(proc, tree)
        proc.addArgument(name)
        proc.setWorkingDirectory(QDir(wdir))
        
        dia = SvnDialog(self.trUtf8('Adding %1 to the Subversion repository')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsAddBinary(self, name, isDir=0):
        """
        Public method used to add a file/directory in binary mode to the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be added (string)
            
            isDir -- flag indicating name is a directory (boolean)
        """
        self.vcsAdd(name, isDir)
        
    def vcsAddTree(self, path):
        """
        Public method to add a directory tree rooted at path to the Subversion repository.
        
        Arguments
        
            path -- root directory of the tree to be added (string)
        """
        dname, fname = os.path.split(path)
        
        if not os.path.exists(os.path.join(dname, '.svn')):
            # add directories recursively, if they aren't in the repository already
            self.vcsAdd(path, 1)
            
        proc = QProcess('svn', self)
        proc.addArgument('add')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['add'])
        proc.addArgument(fname)
        proc.setWorkingDirectory(QDir(dname))
        
        dia = SvnDialog(self.trUtf8('Adding %1 to the Subversion repository')
            .arg(path))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsRemove(self, name, project=0):
        """
        Public method used to remove a file/directory from the Subversion repository.
        
        The default operation is to remove the local copy as well.
        
        Arguments
        
            name -- file/directory name to be removed (string)
            
            project -- flag indicating deletion of a project tree (boolean) (not needed)
            
        Returns
        
            flag indicating successfull operation (boolean)
        """
        proc = QProcess('svn', self)
        proc.addArgument('delete')
        self.addArguments(proc, self.options['global'])
        self.addArguments(proc, self.options['remove'])
        proc.addArgument(name)
        
        dia = SvnDialog(self.trUtf8('Removing %1 from the Subversion repository')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
            res = dia.normalExit()
            
        return res
        
    def vcsLog(self, name):
        """
        Public method used to view the log of a file/directory from the Subversion repository.
        
        Arguments
        
            name -- file/directory name to show the log of (string)
        """
        self.log = SvnLogDialog(self)
        self.log.show()
        self.log.start(name)
        
    def vcsDiff(self, name):
        """
        Public method used to view the difference of a file/directory to the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be diffed (string)
        """
        self.diff = SvnDiffDialog(self)
        self.diff.show()
        self.diff.start(name)
        
    def vcsStatus(self, name):
        """
        Public method used to view the status of a file in the Subversion repository.
        
        Arguments
        
            name -- file/directory name to show the status of (string)
        """
        self.status = SvnStatusDialog(self)
        self.status.show()
        self.status.start(name)
        
    def vcsTag(self, name):
        """
        Public method used to set the tag of a file/directory in the Subversion repository.
        
        Arguments
        
            name -- file/directory name to be tagged (string)
        """
        dname, fname = self.splitPath(name)
        
        dlg = SvnTagDialog(self.tagList)
        if dlg.exec_loop() == QDialog.Accepted:
            tag, tagOp = dlg.getParameters()
            self.tagList.remove(tag)
            self.tagList.prepend(tag)
        else:
            return
        
        reposURL = self.svnGetReposName(dname)
        if reposURL is None:
            QMessageBox.critical(None,
                self.trUtf8("Subversion Error"),
                self.trUtf8("""The URL of the project repository could not be"""
                    """ retrieved from the working copy. The tag operation will"""
                    """ be aborted"""),
                self.trUtf8("OK"),
                None,
                None,
                0,-1)
            return
            
        rx_base = QRegExp('(.+)/(trunk|tags|branches).*')
        if not rx_base.exactMatch(reposURL):
            QMessageBox.critical(None,
                self.trUtf8("Subversion Error"),
                self.trUtf8("""The URL of the project repository has an"""
                    """ invalid format. The tag operation will"""
                    """ be aborted"""),
                self.trUtf8("OK"),
                None,
                None,
                0,-1)
            return
            
        reposRoot = str(rx_base.cap(1))
        if tagOp in [1, 4]:
            url = '%s/tags/%s' % (reposRoot, str(tag))
        elif tagOp in [2, 8]:
            url = '%s/branches/%s' % (reposRoot, str(tag))
            
        proc = QProcess('svn', self)
        if tagOp in [1, 2]:
            proc.addArgument('copy')
            self.addArguments(proc, self.options['global'])
            self.addArguments(proc, self.options['tag'])
            proc.addArgument('--message')
            proc.addArgument('Created tag <%s>' % str(tag))
            if reposURL.startswith('http'):
                self.addArguments(proc, self.authData())
            proc.addArgument(reposURL)
            proc.addArgument(url)
        else:
            proc.addArgument('delete')
            self.addArguments(proc, self.options['global'])
            self.addArguments(proc, self.options['tag'])
            proc.addArgument('--message')
            proc.addArgument('Deleted tag <%s>' % str(tag))
            if reposURL.startswith('http'):
                self.addArguments(proc, self.authData())
            proc.addArgument(url)
        
        dia = SvnDialog(self.trUtf8('Tagging %1 in the Subversion repository')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsRevert(self, name):
        """
        Public method used to revert changes made to a file/directory.
        
        Arguments
        
            name -- file/directory name to be reverted (string)
        """
        proc = QProcess('svn', self)
        proc.addArgument('revert')
        self.addArguments(proc, self.options['global'])
        if os.path.isdir(name):
            proc.addArgument('--recursive')
        proc.addArgument(name)
        
        dia = SvnDialog(self.trUtf8('Reverting changes to %1')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
    
    def vcsSwitch(self, name):
        """
        Public method used to switch a directory to a different tag/branch.
        
        Arguments
        
            name -- directory name to be switched (string)
        """
        dname, fname = self.splitPath(name)
        
        dlg = SvnSwitchDialog(self.tagList)
        if dlg.exec_loop() == QDialog.Accepted:
            tag, tagType = dlg.getParameters()
            self.tagList.remove(tag)
            self.tagList.prepend(tag)
        else:
            return
        
        reposURL = self.svnGetReposName(dname)
        if reposURL is None:
            QMessageBox.critical(None,
                self.trUtf8("Subversion Error"),
                self.trUtf8("""The URL of the project repository could not be"""
                    """ retrieved from the working copy. The switch operation will"""
                    """ be aborted"""),
                self.trUtf8("OK"),
                None,
                None,
                0,-1)
            return
            
        rx_base = QRegExp('(.+)/(trunk|tags|branches).*')
        if not rx_base.exactMatch(reposURL):
            QMessageBox.critical(None,
                self.trUtf8("Subversion Error"),
                self.trUtf8("""The URL of the project repository has an"""
                    """ invalid format. The switch operation will"""
                    """ be aborted"""),
                self.trUtf8("OK"),
                None,
                None,
                0,-1)
            return
            
        reposRoot = str(rx_base.cap(1))
        tn = tag
        if tagType == 1:
            url = '%s/tags/%s' % (reposRoot, str(tag))
        elif tagType == 2:
            url = '%s/branches/%s' % (reposRoot, str(tag))
        elif tagType == 4:
            url = '%s/trunk' % (reposRoot)
            tn = QString('trunk')
            
        proc = QProcess('svn', self)
        proc.addArgument('switch')
        if reposURL.startswith('http'):
            self.addArguments(proc, self.authData())
        proc.addArgument(url)
        proc.addArgument(name)
        
        dia = SvnDialog(self.trUtf8('Switching to %1')
            .arg(tn))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsMerge(self, name):
        """
        Public method used to merge a URL/revision into the local project.
        
        Arguments
        
            name -- file/directory name to be merged (string)
        """
        dname, fname = self.splitPath(name)
        
        dlg = SvnMergeDialog(self.mergeList[0], self.mergeList[1], self.mergeList[2])
        if dlg.exec_loop() == QDialog.Accepted:
            urlrev1, urlrev2, target = dlg.getParameters()
        else:
            return
            
        # remember URL or revision
        self.mergeList[0].remove(urlrev1)
        self.mergeList[0].prepend(urlrev1)
        self.mergeList[1].remove(urlrev2)
        self.mergeList[1].prepend(urlrev2)

        rx_rev = QRegExp('\\d+')
        
        proc = QProcess('svn', self)
        proc.addArgument('merge')
        self.addArguments(proc, self.options['global'])
        if rx_rev.exactMatch(urlrev1):
            proc.addArgument('-r')
            proc.addArgument(QString('%1:%2').arg(urlrev1).arg(urlrev2))
            if target.isEmpty():
                proc.addArgument(name)
            else:
                if target.startsWith('http'):
                    self.addArguments(proc, self.authData())
                proc.addArgument(target)
                
            # remember target
            self.mergeList[2].remove(target)
            self.mergeList[2].prepend(target)
        else:
            if urlrev1.startsWith('http') or urlrev2.startsWith('http'):
                self.addArguments(proc, self.authData())
            proc.addArgument(urlrev1)
            proc.addArgument(urlrev2)
        proc.addArgument(fname)
        proc.setWorkingDirectory(QDir(dname))
        
        dia = SvnDialog(self.trUtf8('Merging %1')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
        
    def vcsRegisteredState(self, name):
        """
        Public method used to get the registered state of a file in the vcs.
        
        Arguments
        
            name -- filename to check (string)
            
        Returns
        
            a combination of canBeCommited and canBeAdded or
            0 in order to signal an error
        """
        dname, fname = self.splitPath(name)
        dname = str(dname)
        fname = str(fname)
        
        if fname == '.':
            if os.path.isdir(os.path.join(dname, '.svn')):
                return self.canBeCommitted
            else:
                return self.canBeAdded
                
        ename = os.path.join(dname, '.svn', 'entries')
        try:
            f = open(ename, 'rb')
            lines = f.readlines()
            f.close()
        except:
            return 0
            
        for line in lines:
            line = line.strip()
            if line.startswith('name=') and line[6:-1] == fname:
                return self.canBeCommitted
                
        return self.canBeAdded
        
    def vcsAllRegisteredStates(self, names, dname):
        """
        Public method used to get the registered states of a number of files in the vcs.
        
        Arguments
        
            names -- dictionary with all filenames to be checked as keys
            
            dname -- directory to check in (string)
            
        Returns
        
            the received dictionary completed with a combination of 
            canBeCommited and canBeAdded or None in order to signal an error
        """
        dname = str(dname)
        ename = os.path.join(dname, '.svn', 'entries')
        try:
            f = open(ename, 'rb')
            lines = f.readlines()
            f.close()
        except:
            return names
            
        for line in lines:
            line = line.strip()
            if line.startswith('name='):
                name = os.path.normcase(os.path.join(dname, line[6:-1]))
                if names.has_key(name):
                    names[name] = self.canBeCommitted
                
        return names
            
    def vcsName(self):
        """
        Public method returning the name of the vcs.
        
        Returns
        
            always 'Subversion' (string)
        """
        return "Subversion"

    def vcsCleanup(self, name):
        """
        Public method used to cleanup the working copy.
        
        Arguments
        
            name -- directory name to be cleaned up (string)
        """
        proc = QProcess('svn', self)
        proc.addArgument('cleanup')
        self.addArguments(proc, self.options['global'])
        proc.addArgument(name)
        
        dia = SvnDialog(self.trUtf8('Cleaning up %1')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
    
    def vcsCommandLine(self, name):
        """
        Public method used to execute arbitrary subversion commands.
        
        Arguments
        
            name -- directory name of the working directory (string)
        """
        dlg = SvnCommandDialog(self.commandHistory, self.wdHistory, name)
        if dlg.exec_loop() == QDialog.Accepted:
            command, wd = dlg.getData()
            commandList = Utilities.parseOptionString(command)
            
            # This moves any previous occurence of these arguments to the head
            # of the list.
            self.commandHistory.remove(command)
            self.commandHistory.prepend(command)
            self.wdHistory.remove(wd)
            self.wdHistory.prepend(wd)
            
            proc = QProcess('svn', self)
            self.addArguments(proc, commandList)
            if commandList[0] in self.authcommands:
                ad = self.authData()
                if len(ad):
                    self.addArguments(proc, ad)
            if not wd.isEmpty():
                proc.setWorkingDirectory(QDir(wd))
            
            dia = SvnDialog(self.trUtf8('Subversion command'))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
        
    ############################################################################
    ## Public Subversion specific methods are below.
    ############################################################################
    
    def svnGetReposName(self, path):
        """
        Public method used to retrieve the URL of the subversion repository path.
        
        Arguments
        
            path -- local path to get the svn repository path for (string)
            
        Returns
        
            string with the repository path URL
        """
        ename = os.path.join(path, '.svn', 'entries')
        try:
            f = open(ename, 'rb')
            lines = f.readlines()
            f.close()
        except:
            return names
            
        for line in lines:
            line = line.strip()
            if line.startswith('url='):
                reposURL = line[5:-1]
                return reposURL
                
        return None

    def svnResolve(self, name):
        """
        Public method used to resolve conflicts of a file/directory.
        
        Arguments
        
            name -- file/directory name to be resolved (string)
        """
        proc = QProcess('svn', self)
        if self.versionStr < "0.27.0":
            proc.addArgument('resolve')
        else:
            proc.addArgument('resolved')
        self.addArguments(proc, self.options['global'])
        if os.path.isdir(name):
            proc.addArgument('--recursive')
        proc.addArgument(name)
        
        dia = SvnDialog(self.trUtf8('Resolving conficts of %1')
            .arg(name))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
    
    def svnCopy(self, name, project):
        """
        Public method used to copy a file/directory.
        
        Arguments
        
            name -- file/directory name to be copied (string)
            
            project -- reference to the project object
        """
        rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+')
        dlg = SvnCopyDialog(name)
        res = 0
        if dlg.exec_loop() == QDialog.Accepted:
            target = dlg.getData()
            
            proc = QProcess('svn', self)
            proc.addArgument('copy')
            self.addArguments(proc, self.options['global'])
            if rx_prot.exactMatch(target):
                proc.addArgument('--message')
                proc.addArgument(QString('Copying %1 to %2').arg(name).arg(target))
            if target.startsWith('http'):
                self.addArguments(proc, self.authData())
            proc.addArgument(name)
            proc.addArgument(target)
            
            dia = SvnDialog(self.trUtf8('Copying %1')
                .arg(name))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
                res = dia.normalExit()
                if res:
                    project.appendFile(target)
        return res
    
    def svnMove(self, name, project):
        """
        Public method used to move a file/directory.
        
        Arguments
        
            name -- file/directory name to be moved (string)
            
            project -- reference to the project object
        """
        rx_prot = QRegExp('(file:|svn:|svn+ssh:|http:|https:).+')
        dlg = SvnCopyDialog(name, None, 1)
        res = 0
        if dlg.exec_loop() == QDialog.Accepted:
            target = dlg.getData()
            
            proc = QProcess('svn', self)
            proc.addArgument('move')
            self.addArguments(proc, self.options['global'])
            if rx_prot.exactMatch(target):
                proc.addArgument('--message')
                proc.addArgument(QString('Moving %1 to %2').arg(name).arg(target))
            if target.startsWith('http'):
                self.addArguments(proc, self.authData())
            proc.addArgument(name)
            proc.addArgument(target)
            
            dia = SvnDialog(self.trUtf8('Moving %1')
                .arg(name))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
                res = dia.normalExit()
                if res:
                    project.removeFile(name)
                    project.appendFile(target)
        return res
        
    def svnClearAuthInfo(self):
        """
        Public method used to clear the authentication info.
        """
        self.username = None
        self.password = None
        
    def svnListProps(self, name, recursive = 0):
        """
        Public method used to list the properties of a file/directory.
        
        Arguments
        
            name -- file/directory name (string or list of strings)
            
            recursive -- flag indicating a recursive list is requested
        """
        self.propList = SvnPropListDialog(self)
        self.propList.show()
        self.propList.start(name, recursive)
        
    def svnSetProp(self, name, recursive = 0):
        """
        Public method used to add a property to a file/directory.
        
        Arguments
        
            name -- file/directory name (string or list of strings)
            
            recursive -- flag indicating a recursive list is requested
        """
        dlg = SvnPropSetDialog()
        if dlg.exec_loop() == QDialog.Accepted:
            propName, fileFlag, propValue = dlg.getData()
            if propName.isEmpty():
                QMessageBox.critical(None,
                    self.trUtf8("Subversion Set Property"),
                    self.trUtf8("""You have to supply a property name. Aborting."""),
                    self.trUtf8("&OK"),
                    None,
                    None,
                    0, -1)
                return
            
            proc = QProcess('svn', self)
            proc.addArgument('propset')
            self.addArguments(proc, self.options['global'])
            if recursive:
                proc.addArgument('--recursive')
            proc.addArgument(propName)
            if fileFlag:
                proc.addArgument('--file')
            proc.addArgument(propValue)
            if type(name) is types.ListType:
                for n in name:
                    dname, fname = self.splitPath(n)
                    proc.addArgument(fname)
            else:
                dname, fname = self.splitPath(name)
                proc.addArgument(fname)
            proc.setWorkingDirectory(QDir(dname))
            
            dia = SvnDialog(self.trUtf8('Subversion Set Property'))
            res = dia.startProcess(proc)
            if res:
                dia.exec_loop()
        
    def svnDelProp(self, name, recursive = 0):
        """
        Public method used to delete a property of a file/directory.
        
        Arguments
        
            name -- file/directory name (string or list of strings)
            
            recursive -- flag indicating a recursive list is requested
        """
        propName, ok = QInputDialog.getText(\
            self.trUtf8("Subversion Delete Property"),
            self.trUtf8("Enter property name"),
            QLineEdit.Normal)
        
        if not ok:
            return
            
        if propName.isEmpty():
            QMessageBox.critical(None,
                self.trUtf8("Subversion Delete Property"),
                self.trUtf8("""You have to supply a property name. Aborting."""),
                self.trUtf8("&OK"),
                None,
                None,
                0, -1)
            return
            
        proc = QProcess('svn', self)
        proc.addArgument('propdel')
        self.addArguments(proc, self.options['global'])
        if recursive:
            proc.addArgument('--recursive')
        proc.addArgument(propName)
        if type(name) is types.ListType:
            for n in name:
                dname, fname = self.splitPath(n)
                proc.addArgument(fname)
        else:
            dname, fname = self.splitPath(name)
            proc.addArgument(fname)
        proc.setWorkingDirectory(QDir(dname))
        
        dia = SvnDialog(self.trUtf8('Subversion Set Property'))
        res = dia.startProcess(proc)
        if res:
            dia.exec_loop()
            
    def svnListTagBranch(self, path, tags = 1):
        """
        Public method used to list the available tags or branches.
        
        Arguments
        
            path -- directory name of the project (string)
            
            tags -- flag indicating listing of branches or tags
                (0 = branches, 1 = tags)
        """
        self.tagbranchList = SvnTagBranchListDialog(self)
        self.tagbranchList.show()
        self.tagbranchList.start(path, tags)

    ############################################################################
    ## Private Subversion specific methods are below.
    ############################################################################
    
    def authData(self):
        """
        Private method to get the users authentication data.
        
        Returns
        
            list of strings with the authentication parameters
        """
        if self.username is None:
            dlg = AuthDialog()
            if dlg.exec_loop() == QDialog.Accepted:
                self.username, self.password = dlg.getData()
            else:
                return []
                
        return ['--username', str(self.username), 
                '--password', str(self.password),
                '--no-auth-cache', '--non-interactive']
                
    def vcsExistsReadyReadStdout(self):
        """
        Private slot to handle the readyReadStdout signal. 
        
        It reads the output of the process, formats it and inserts it into
        the contents pane.
        """
        if self.versionStr is None:
            s = str(self.proc.readStdout())
            self.versionStr = s
        
    def vcsExistsProcExited(self):
        """
        Private slot connected to the processExited signal.
        """
        self.proc = None
        self.versionStr = self.versionStr.split()[2]
