diff options
Diffstat (limited to 'kbabel/catalogmanager/libsvn')
-rw-r--r-- | kbabel/catalogmanager/libsvn/Makefile.am | 11 | ||||
-rw-r--r-- | kbabel/catalogmanager/libsvn/svndialog.cpp | 400 | ||||
-rw-r--r-- | kbabel/catalogmanager/libsvn/svndialog.h | 151 | ||||
-rw-r--r-- | kbabel/catalogmanager/libsvn/svnhandler.cpp | 544 | ||||
-rw-r--r-- | kbabel/catalogmanager/libsvn/svnhandler.h | 138 | ||||
-rw-r--r-- | kbabel/catalogmanager/libsvn/svnresources.h | 50 |
6 files changed, 1294 insertions, 0 deletions
diff --git a/kbabel/catalogmanager/libsvn/Makefile.am b/kbabel/catalogmanager/libsvn/Makefile.am new file mode 100644 index 00000000..7c340974 --- /dev/null +++ b/kbabel/catalogmanager/libsvn/Makefile.am @@ -0,0 +1,11 @@ +noinst_LTLIBRARIES = libcatalogmanagersvn.la + +# set the include path for X, qt and KDE +INCLUDES = -I.. -I../../common $(all_includes) + +libcatalogmanagersvn_la_SOURCES = svnhandler.cpp svndialog.cpp + +noinst_HEADERS = svnhandler.h svndialog.h svnresources.h + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO diff --git a/kbabel/catalogmanager/libsvn/svndialog.cpp b/kbabel/catalogmanager/libsvn/svndialog.cpp new file mode 100644 index 00000000..69653591 --- /dev/null +++ b/kbabel/catalogmanager/libsvn/svndialog.cpp @@ -0,0 +1,400 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <[email protected]> + Copyright (C) 2005, 2006 by Nicolas GOUTTE <[email protected]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + + +// Qt include files +#include <qcheckbox.h> +#include <qcombobox.h> +#include <qfileinfo.h> +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qlistbox.h> +#include <qpushbutton.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qtextedit.h> +// KDE include files +#include <kconfig.h> +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kprocess.h> +#include <ktempfile.h> +#include <kmessagebox.h> +#include <kstringhandler.h> +// Project specific include files +#include "svndialog.h" + + +SVNDialog::SVNDialog( SVN::Command cmd, QWidget * parent, KSharedConfig* config ) + : KDialog( parent, "SVN DIALOG", true ), m_tempFile( 0 ), m_config( config ) +{ + _cmd = cmd; + p=0L; + setCaption( i18n( "SVN Dialog" ) ); + + QString temp; + + QVBoxLayout * layout = new QVBoxLayout( this, 6, 6, "MAIN LAYOUT" ); + + // Set the label's text depending on the SVN command. + switch ( cmd ) { + case SVN::Update: + temp = i18n( "Update the following files:" ); + break; + case SVN::Commit: + temp = i18n( "Commit the following files:" ); + break; + case SVN::StatusRemote: + temp = i18n( "Get remote status for the following files:" ); + break; + case SVN::StatusLocal: + temp = i18n( "Get local status for the following files:" ); + break; + case SVN::Diff: + temp = i18n( "Get diff for the following files:" ); + break; + case SVN::Info: + temp = i18n( "Get information for the following files:" ); + break; + } + layout->addWidget( new QLabel( temp, this ) ); + + // Widget for showing the list of files. + filebox = new QListBox( this ); + layout->addWidget( filebox ); + + // Add special widgets for 'svn commit'. + if ( cmd == SVN::Commit ) { + QLabel * label; + + // Combobox for displaying old log messages. + label = new QLabel( i18n( "&Old messages:" ), this ); + oldMessages = new QComboBox( this ); + oldMessages->setDuplicatesEnabled( false ); + label->setBuddy( oldMessages ); + layout->addWidget( label ); + layout->addWidget( oldMessages ); + + // Textfield for entering a log message. + label = new QLabel( i18n( "&Log message:" ), this ); + logedit = new QTextEdit( this ); + label->setBuddy( logedit ); + layout->addWidget( label ); + layout->addWidget( logedit ); + + connect( oldMessages, SIGNAL( activated( int ) ), + this, SLOT( slotComboActivated( int ) ) ); + } + + QHBoxLayout * buttons = new QHBoxLayout( 0, 0, 6, "BUTTON LAYOUT" ); + // Add special buttons for 'svn commit'. + if ( cmd == SVN::Commit ) { + autoAddBox = new QCheckBox( i18n( "Auto&matically add files if necessary" ), this ); + buttons->addWidget( autoAddBox ); + } + buttons->addItem( new QSpacerItem( 1, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ) ); + + // Set the main button's text depending on the SVN comand. + switch ( cmd ) { + case SVN::Update: + temp = i18n( "&Update" ); + break; + case SVN::Commit: + temp = i18n( "&Commit" ); + break; + case SVN::StatusRemote: + case SVN::StatusLocal: + temp = i18n( "&Get Status" ); + break; + case SVN::Diff: + temp = i18n( "&Get Diff" ); + break; + case SVN::Info: + temp = i18n( "&Get Information" ); + break; + } + mainBtn = new QPushButton( temp, this ); + mainBtn->setDefault( true ); + buttons->addWidget( mainBtn ); + + cancelBtn = new QPushButton( i18n( "C&ancel" ), this ); + buttons->addWidget( cancelBtn ); + layout->addLayout( buttons ); + + QFrame * line = new QFrame( this ); + line->setFrameStyle( QFrame::HLine | QFrame::Sunken ); + layout->addWidget( line ); + + layout->addWidget( new QLabel( i18n( "Command output:" ), this ) ); + + output = new QTextEdit( this ); + output->setReadOnly( true ); + layout->addWidget( output ); + + resize( QSize( 600, 450 ).expandedTo( minimumSizeHint( ) ) ); + + if ( cmd == SVN::Commit ) + logedit->setFocus( ); + + readSettings( ); + + connect( mainBtn, SIGNAL( clicked( ) ), this, SLOT( slotExecuteCommand( ) ) ); + connect( cancelBtn, SIGNAL( clicked( ) ), this, SLOT( reject( ) ) ); +} + +void SVNDialog::slotComboActivated( int index ) +{ + if ( index < 0 || index >= m_logMessages.count() ) + return; + logedit->setText( m_logMessages[index] ); +} + +SVNDialog::~SVNDialog() +{ + delete m_tempFile; + delete p; +} + +void SVNDialog::accept( ) +{ + saveSettings( ); + KDialog::accept( ); +} + +void SVNDialog::setFiles( const QStringList& files ) +{ + filebox->insertStringList( files ); +} + +void SVNDialog::setCommandLine( const QString& command ) +{ + _commandLine = command; +} + +void SVNDialog::setAddCommand( const QString& command ) +{ + _addCommand = command; +} + +void SVNDialog::slotExecuteCommand( ) +{ + // Nothing to do here. + if ( _commandLine.isEmpty( ) ) return; + + kdDebug() << "Preparing KProcess" << endl; + + // Create a new shell process + p = new KProcess; + p->setUseShell( true, "/bin/sh" ); + + if ( _cmd == SVN::Commit ) { + // Include command for 'svn add'. + if ( autoAddBox->isChecked( ) && !_addCommand.isEmpty( ) ) + _commandLine.prepend( _addCommand ); + + const QString msg( logedit->text() ); + + if ( msg.isEmpty() ) + { + // A good commit should never have an empty comment, so ask the user if he really wants it. + const int res = KMessageBox::warningContinueCancel( this, + i18n( "The commit log message is empty. Do you want to continue?" ) ); + if ( res != KMessageBox::Continue ) + return; + } + + // Write the commit log message from the input field to a temporary file + m_tempFile = new KTempFile; + m_tempFile->setAutoDelete( true ); + QTextStream* stream = m_tempFile->textStream(); + if ( !stream ) + { + kdError() << "Could not create QTextStream for file " << m_tempFile->name(); + delete m_tempFile; + m_tempFile = 0; + KMessageBox::error( this, i18n( "Cannot open temporary file for writing. Aborting.") ); + return; + } + stream->setEncoding( QTextStream::UnicodeUTF8 ); + *stream << msg; + m_tempFile->close(); + + if ( m_tempFile->status() ) + { + kdError() << "Could not write to file " << m_tempFile->name(); + delete m_tempFile; + m_tempFile = 0; + KMessageBox::error( this, i18n( "Cannot write to temporary file. Aborting.") ); + return; + } + + // Change the command line to have the real name of the temporary file + _commandLine.replace( "@LOG@FILE@", KProcess::quote( m_tempFile->name() ) ); + + // Update the list of log messages + if ( !msg.isEmpty() ) { + const QString shortLog = KStringHandler::csqueeze( msg, 80 ); + + + // Remove the message from the list if it already exists + m_logMessages.remove( msg ); + // Prepend the current message to the list + m_logMessages.prepend( msg ); + + // At this time of the process, we do not need the combobox anymore, so we do not squeeze the changed strings. + } + } + + // Set the KProcess' command line. + *p << _commandLine; + + connect( p, SIGNAL( receivedStdout( KProcess*, char*, int ) ), + this, SLOT ( slotProcessStdout( KProcess*, char*, int ) ) ); + connect( p, SIGNAL( receivedStderr( KProcess*, char*, int ) ), + this, SLOT ( slotProcessStderr( KProcess*, char*, int ) ) ); + connect( p, SIGNAL( processExited( KProcess* ) ), + this, SLOT( slotProcessExited( KProcess* ) ) ); + + output->append( i18n( "[ Starting command ]" ) ); + + if ( p->start( KProcess::NotifyOnExit, KProcess::Communication( KProcess::AllOutput ) ) ) { + // Disable the main button (and the log edit if in commit mode) to + // indicate activity. + mainBtn->setEnabled( false ); + if ( _cmd == SVN::Commit ) + logedit->setEnabled( false ); + } else + { + kdError() << "Process could not be started." << endl; + KMessageBox::error( this, i18n( "The process could not be started." ) ); + } +} + +void SVNDialog::slotProcessStdout( KProcess*, char * buffer, int len ) +{ + output->append( QString::fromLocal8Bit( buffer, len ) ); + // Set the cursor's position at the end of the output. + output->setCursorPosition( output->lines( ), 0 ); + + // If the command is 'svn status' or 'svn diff' collect the output of stdout. + if ( ( _cmd == SVN::StatusLocal ) || ( _cmd == SVN::StatusRemote ) || ( _cmd == SVN::Diff ) ) + _statusOutput += QString::fromLocal8Bit( buffer, len ); +} + +void SVNDialog::slotProcessStderr( KProcess*, char * buffer, int len ) +{ + // If an error occurs while executing the command display stderr in + // another color. + QColor oldColor( output->color( ) ); + output->setColor( Qt::red ); + output->append( QString::fromLocal8Bit( buffer, len ) ); + output->setColor( oldColor ); + output->setCursorPosition( output->lines( ), 0 ); +} + +void SVNDialog::slotProcessExited( KProcess * p ) +{ + if ( p->exitStatus( ) ) + output->append( i18n( "[ Exited with status %1 ]" ).arg( p->exitStatus( ) ) ); + else + output->append( i18n( "[ Finished ]" ) ); + + // The command is finished. Now we can reconnect the main button. + disconnect( mainBtn, 0, 0, 0 ); + if ( _cmd == SVN::Diff ) + mainBtn->setText( i18n( "&Show Diff" ) ); + else + mainBtn->setText( i18n( "&Close" ) ); + connect( mainBtn, SIGNAL( clicked( ) ), this, SLOT( accept( ) ) ); + + // Reenable the button and the log edit now that the process is finished. + mainBtn->setEnabled( true ); + if ( _cmd == SVN::Commit ) + logedit->setEnabled( true ); +} + +QString SVNDialog::statusOutput( ) +{ + return _statusOutput; +} + +void SVNDialog::readSettings( ) +{ + KSharedConfig * config = m_config; + config->setGroup( "SVNSupport" ); + + if ( _cmd == SVN::Commit ) { + autoAddBox->setChecked( config->readBoolEntry( "AutoAddFiles", true ) ); + + // Fill the combobox with old messages. + m_logMessages.clear(); + m_squeezedLogMessages.clear(); + for ( int cnt = 0; cnt < 10; cnt++ ) + if ( config->hasKey( QString( "CommitLogMessage%1" ).arg( cnt ) ) ) + { + const QString logMessage = config->readEntry( QString( "CommitLogMessage%1" ).arg( cnt ) ); + if ( !logMessage.isEmpty() ) + { + // If the message is too long, cut it to 80 characters (or the combo box becomes too wide) + // ### FIXME: if the string matches the squeezed 80 chars, it might overwrite another entry + const QString shortLog = KStringHandler::csqueeze( logMessage ); + m_logMessages.append( logMessage ); + m_squeezedLogMessages.append( shortLog ); + oldMessages->insertItem( shortLog ); + } + } + + } +} + +void SVNDialog::saveSettings( ) +{ + KSharedConfig * config = m_config; + config->setGroup( "SVNSupport" ); + if ( _cmd == SVN::Commit ) { + config->writeEntry( "AutoAddFiles", autoAddBox->isChecked( ) ); + + // Write the log messages to the config file. + int cnt = 0; + QStringList::const_iterator it; + for ( it = m_logMessages.constBegin( ); it != m_logMessages.constEnd( ) && cnt < 10 ; ++it, ++cnt ) + config->writeEntry( QString( "CommitLogMessage%1" ).arg( cnt ), *it ); + } + m_config->sync(); +} + +#include "svndialog.moc" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kbabel/catalogmanager/libsvn/svndialog.h b/kbabel/catalogmanager/libsvn/svndialog.h new file mode 100644 index 00000000..0c824fa8 --- /dev/null +++ b/kbabel/catalogmanager/libsvn/svndialog.h @@ -0,0 +1,151 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <[email protected]> + Copyright (C) 2005, 2006 by Nicolas GOUTTE <[email protected]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + + +#ifndef SVNDIALOG_H +#define SVNDIALOG_H + +// KDE include files +#include <kdialog.h> +// Project specific include files +#include "svnresources.h" +// Forwarding Qt classes +class QCheckBox; +class QComboBox; +class QListBox; +class QPushButton; +class QString; +class QStringList; +class QTextEdit; +// Forwarding KDE classes +class KProcess; +class KTempFile; +class KSharedConfig; + +/** + * This class represents the dialog which is used for executing SVN commands + * in KBabel's Catalog Manager. The dialog shows the list of files to be + * processed as well as the output from the command. + * + * In Commit mode there is also an option for automatically executing + * 'svn add' if necessary. + * + * @short Dialog for SVN support in Catalog Manager + */ +class SVNDialog : public KDialog +{ + Q_OBJECT + + public: + /** + * Constructor for creating the dialog. + * @param cmd The type of command to be executed. + */ + SVNDialog( SVN::Command cmd, QWidget * parent, KSharedConfig* config ); + ~SVNDialog(); + /** + * Set the list of files which will be used for the SVN command. + * @param files The list of files. + */ + void setFiles( const QStringList& files ); + /** + * Set the command line for the execution of the SVN command. + * @param command The command line. + */ + void setCommandLine( const QString& command ); + /** + * Set the command line for adding files to the SVN repository. + * This method is only used together with a 'svn commit' for automatically + * adding files which are not yet in the repository. + * @param command The command line. + */ + void setAddCommand( const QString& command ); + /** + * Return the output of a 'svn status' command. + * @returns The complete output. + */ + QString statusOutput( ); + + protected: + /** + * This method is overwritten so that the settings can be saved after + * pressing the 'Close' button. + */ + virtual void accept( ); + /** Read the dialog's settings. */ + void readSettings( ); + /** Save the dialog's settings. */ + void saveSettings( ); + + protected slots: + /** Slot for executing the SVN Command. */ + void slotExecuteCommand( ); + /** Slot for processing the stdout of the SVN Command. */ + void slotProcessStdout( KProcess*, char * buffer, int len ); + /** Slot for processing the stderr of the SVN Command. */ + void slotProcessStderr( KProcess*, char * buffer, int len ); + /** Slot for post-processing after the SVN command is fninished. */ + void slotProcessExited( KProcess * p ); + /// Slot for combox having been activated + void slotComboActivated( int ); + + private: + SVN::Command _cmd; + + QPushButton * mainBtn; + QPushButton * cancelBtn; + QListBox * filebox; + QComboBox * oldMessages; + QTextEdit * logedit; + QTextEdit * output; + QCheckBox * autoAddBox; + + KProcess * p; + + QString _commandLine; + QString _addCommand; + QString _statusOutput; + + /// Log messages (long version) + QStringList m_logMessages; + /// Log messages (short version) + QStringList m_squeezedLogMessages; + + /// Temporary file (for commits) + KTempFile* m_tempFile; + + /// Configuration data (of the KBabel project) + KSharedConfig* m_config; +}; + +#endif // SVNDIALOG_H diff --git a/kbabel/catalogmanager/libsvn/svnhandler.cpp b/kbabel/catalogmanager/libsvn/svnhandler.cpp new file mode 100644 index 00000000..8117fe62 --- /dev/null +++ b/kbabel/catalogmanager/libsvn/svnhandler.cpp @@ -0,0 +1,544 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <[email protected]> + Copyright (C) 2005, 2006 by Nicolas GOUTTE <[email protected]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + + +// System include files +#include <unistd.h> +#include <sys/stat.h> +#include <time.h> +// Qt include files +#include <qdir.h> +#include <qfile.h> +#include <qfileinfo.h> +#include <qregexp.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdom.h> +// KDE include files +#include <kapplication.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <kdebug.h> +#include <kprocess.h> +// project specific include files +#include "svnhandler.h" + +SVNHandler::SVNHandler( const QString& poBaseDir, const QString& potBaseDir ) +{ + setPOBaseDir( poBaseDir ); + setPOTBaseDir( potBaseDir ); + _autoUpdateTemplates = false; +} + +void SVNHandler::setPOBaseDir( const QString& dir ) +{ + // check if '.svn/entries' exists in the PO base directory + if ( QFileInfo( dir + "/.svn/entries" ).exists( ) ) { + _isPORepository = true; + _poBaseDir = dir; + } else + _isPORepository = false; + emit signalIsPORepository( _isPORepository ); +} + +void SVNHandler::setPOTBaseDir( const QString& dir ) +{ + // check if '.svn/entries' exists in the POT base directory + if ( QFileInfo( dir + "/.svn/entries" ).exists( ) ) { + _isPOTRepository = true; + _potBaseDir = dir; + } else + _isPOTRepository = false; + emit signalIsPOTRepository( _isPOTRepository ); +} + +QString SVNHandler::fileStatus( const FileStatus status ) const +{ + switch ( status ) { + case NO_REPOSITORY: + return i18n( "No SVN repository" ); + break; + case NOT_IN_SVN: + return i18n( "Not in SVN" ); + break; + case LOCALLY_ADDED: + return i18n( "Locally added" ); + break; + case LOCALLY_REMOVED: + return i18n( "Locally removed" ); + break; + case LOCALLY_MODIFIED: + return i18n( "Locally modified" ); + break; + case UP_TO_DATE: + return i18n( "Up-to-date" ); + break; + case CONFLICT: + return i18n( "Conflict" ); + break; + case ERROR_IN_WC: + return i18n( "Error in Working Copy" ); + default: + return i18n( "Unknown" ); + break; + } +} + +SVNHandler::FileStatus SVNHandler::fstatus( const QString& filename ) const +{ + // no valid repository + if ( !_isPORepository ) + return NO_REPOSITORY; + + QString fn( filename ); + fn = fn.remove( QRegExp( "/$" ) ); + + QFileInfo info( fn ); + + // check if '.svn/entries' exists. + QFile entries( info.dir( true ).path( ) + "/.svn/entries" ); + + if ( !entries.exists() ) + return NOT_IN_SVN; + + KProcess proc; + SVNOutputCollector out( &proc ); + + proc << "svn" << "status" << "-v" << "--xml" << info.absFilePath(); + + if( !proc.start( KProcess::Block, KProcess::Stdout ) ) + return ERROR_IN_WC; + + QDomDocument doc; + QString errorMsg; + int errorLine, errorCol; + QDomNodeList nodelist; + QDomNode node; + QDomElement entry, wcStatus; + + // Parse the output. + if ( !doc.setContent( out.getOutput(), &errorMsg, &errorLine, &errorCol ) ) { + kdDebug(8109) << "Cannot parse \"svn status -v --xml\" output for" + << filename << endl << "Line: " << errorLine << " Column: " + << errorCol << " Error: " << errorMsg << endl; + goto no_status_xml; + } + + // There should be only one "entry" element. If it doesn't exist, path + // isn't repo path at all. + nodelist = doc.elementsByTagName("entry"); + if (nodelist.count() < 1) + return NOT_IN_SVN; + + entry = nodelist.item(0).toElement(); + + // Shouldn't fail, but just in case there is some weird error. + if ( entry.attributeNode("path").value() != info.absFilePath() ) + return ERROR_IN_WC; + + for ( node = entry.firstChild(); !node.isNull(); node = node.nextSibling() ) { + if ( !node.isElement() ) + continue; + if (node.toElement().tagName() == "wc-status") + break; + } + + if ( node.isNull() ) + return ERROR_IN_WC; + + wcStatus = node.toElement(); + + if ( wcStatus.attributeNode("item").value() == "normal" ) + return UP_TO_DATE; + if ( wcStatus.attributeNode("item").value() == "modified" ) + return LOCALLY_MODIFIED; + if ( wcStatus.attributeNode("item").value() == "conflicted" ) + return CONFLICT; + if ( wcStatus.attributeNode("item").value() == "unversioned" ) + return NOT_IN_SVN; + // TODO Ignored entry should have separate return value probably. + if ( wcStatus.attributeNode("item").value() == "ignored" ) + return NOT_IN_SVN; + if ( wcStatus.attributeNode("item").value() == "added" ) + return LOCALLY_ADDED; + if ( wcStatus.attributeNode("item").value() == "deleted" ) + return LOCALLY_REMOVED; + // TODO What to do with "missing", "incomplete", "replaced", "merged", + // "obstructed", "external"? Can these appear at all in our case? + + return ERROR_IN_WC; + +no_status_xml: + if ( !entries.open( IO_ReadOnly ) ) + return ERROR_IN_WC; // we already know that it is a repository + + // Parse the entries file + if ( !doc.setContent( &entries, &errorMsg, &errorLine, &errorCol ) ) { + kdDebug() << "Cannot parse .svn/entries file for " << filename << endl + << "Line: " << errorLine << " Column: " << errorCol << " Error: " << errorMsg << endl; + return ERROR_IN_WC; + } + entries.close(); + + QDomElement element; + // File name that we are searching + const QString findName = info.fileName(); + // The entries are <entry> elements, so we have to check them + QDomNode child = doc.documentElement().firstChild(); + for ( ; !child.isNull() ; child = child.nextSibling() ) + { + if ( !child.isElement() ) + continue; + element = child.toElement(); + if ( element.tagName() != "entry" ) { + // We have another kind of element, so skip it + // Should not happend with svn 1.1.x + continue; + } + const QString name = element.attribute("name"); + if ( name == findName ) + break; + } + + if ( child.isNull() ) { + // We have not found an entry for the file + return NOT_IN_SVN; + } + + // ### TODO: should we check the attribute kind to be file and not dir? + + // ### TODO: what do copy and move add here? + const QString onSchedule = element.attribute( "schedule" ); + if ( onSchedule == "delete" ) + return LOCALLY_REMOVED; + else if ( onSchedule == "added" ) + return LOCALLY_ADDED; + + if ( element.hasAttribute( "conflict-new" ) || element.hasAttribute( "conflict-old" ) || element.hasAttribute( "conflict-wrk" ) ) { + return CONFLICT; + } + + // Note: we do not check the property time stamp + const QString textTimeStamp( element.attribute( "text-time" ) ); + + // calculate the UTC time from the file's last modified date + struct stat st; + lstat( QFile::encodeName(fn), &st ); + struct tm * tm_p = gmtime( &st.st_mtime ); + const int year = tm_p->tm_year + 1900; + const int month = tm_p->tm_mon + 1; + QString fileTime; + fileTime.sprintf( "%04i-%02i-%02iT%02i:%02i:%02i.000000Z", + year, month, tm_p->tm_mday, tm_p->tm_hour, tm_p->tm_min, tm_p->tm_sec ); + //kdDebug() << "File: " << filename << " SVN time: " << textTimeStamp << " File time: " << fileTime << endl; + if ( fileTime > textTimeStamp ) // ISO 8601 dates/times can be compared as strings if they have the exact same format. + return LOCALLY_MODIFIED; + + return UP_TO_DATE; + +} + +QString SVNHandler::svnStatus( const QString& filename ) const +{ + return map[filename]; +} + +void SVNHandler::execSVNCommand( QWidget* parent, SVN::Command cmd, const QString& filename, bool templates, KSharedConfig* config) +{ + // Unlike cvs, svn works also from outside the repository(as long as the path is in a repository of course!) + // ### FIXME: wrong, svn commit cannot work if the current directory is not a SVN one + execSVNCommand( parent, cmd, QStringList( filename ), templates, config ); +} + +void SVNHandler::execSVNCommand( QWidget* parent, SVN::Command cmd, const QStringList& files, bool templates, KSharedConfig* config ) +{ + if ( !_isPORepository ) { + // This message box should never be visible but who knows... ;-) + KMessageBox::sorry( parent, i18n( "This is not a valid SVN repository. " + "The SVN commands cannot be executed." ) ); + return; + } + + // ### TODO: instead of making a QString, use KProcess directly, so that it cares about quoting. + // ### TODO: use KProcess::setWorkingDirectory instead of using "cd" (therefore allowing to use KProcess without a shell.) + QString command("cd " + (templates ? _potBaseDir : _poBaseDir) + " && svn "); + switch ( cmd ) { + case SVN::Update: + command += "update --non-interactive"; + break; + case SVN::Commit: + // The svn client allows to choose the encoding, so we select UTF-8 + command += "commit -F @LOG@FILE@ --encoding UTF-8 --non-interactive"; + checkToAdd( files ); + break; + case SVN::StatusRemote: + command += "status -u --non-interactive"; + break; + case SVN::StatusLocal: + command += "status --non-interactive"; + break; + case SVN::Diff: + command += "diff --non-interactive"; + break; + case SVN::Info: + command += "info"; // Does not allow --non-interactive (at least svn 1.1.4). + } + + QRegExp rx; + if (templates) + rx.setPattern(_potBaseDir + "/?"); + else + rx.setPattern(_poBaseDir + "/?"); + + QStringList::ConstIterator it; + for ( it = files.begin( ); it != files.end( ); ++it ) { + QString temp = *it; + temp.remove(rx); + command += " \'" + temp + "\'"; + } + + showDialog( parent, cmd, files, command, config ); +} + +void SVNHandler::setAutoUpdateTemplates( bool update ) +{ + _autoUpdateTemplates = update; +} + +void SVNHandler::showDialog( QWidget* parent, SVN::Command cmd, const QStringList& files, const QString& commandLine, KSharedConfig* config ) +{ + SVNDialog * dia = new SVNDialog( cmd, parent, config ); + dia->setFiles( files ); + dia->setCommandLine( commandLine ); + if ( cmd == SVN::Commit ) { + dia->setAddCommand( _addCommand ); + } + + if ( dia->exec( ) == KDialog::Accepted ) { + if ( cmd == SVN::StatusLocal || cmd == SVN::StatusRemote ) + processStatusOutput( dia->statusOutput( ) ); + if ( cmd == SVN::Diff ) + processDiff( dia->statusOutput( ) ); + } + + delete dia; + + // file status display update necessary in Catalog Manager + if ( cmd == SVN::Commit ) + emit signalFilesCommitted( files ); +} + +bool SVNHandler::isInSvn( const QString& path ) +{ + if ( path.isEmpty() ) + return false; + + /* + * We need to check if a file is in a SVN repository. + * + * But as we want to do it quickly (because this function will be called for a few files in a row) + * we should avoid to parse the .svn/entries files + * + * Therefore we only check for the SVN auxilary files that are typical for a file controlled by SVN: + * - for a directory: checks if the directory has a .svn/entries file + * - for a file: check if there is a corresponding file in .svn/text-base/ + */ + + const QFileInfo info( path ); + if ( info.isDir() ) { + // It is a directory, so find a .svn/entries file + QDir dir( path ); + return dir.exists( ".svn/entries", true ); + } + else { + // It is a file, so find the corresponding file in .svn/text-base + QDir dir( info.dirPath() ); + if ( ! dir.cd( ".svn/text-base" ) ) { + // There is not even a .svn/text-base directory, so the file is not under control + return false; + } + const QString textBaseFilename( info.fileName() + ".svn-base" ); + return dir.exists( textBaseFilename, true ); + } +} + +void SVNHandler::checkToAdd( const QStringList& files ) +{ + if ( files.isEmpty( ) ) + return; + + QStringList toBeAdded; + + QStringList::ConstIterator it; + for ( it = files.begin( ); it != files.end( ); ++it ) { + // check for every entry if it needs to be added + if ( ! isInSvn( *it ) ) { + QFileInfo info( *it ); + QString temp; // will hold the dir path + if ( info.isDir( ) ) { + toBeAdded << *it; + temp = *it; + } else { + toBeAdded << *it; + temp = QFileInfo( *it ).dirPath( true ); + } + + // ### TODO: does SVN really needs this or does it do it automatically? + // check recursivlely if parent dirs have to be added as well + while ( ! isInSvn( temp ) && toBeAdded.findIndex( temp ) == -1 ) { + toBeAdded << temp; + temp = QFileInfo( temp ).dirPath( true ); + } + + } + } + + // remove an old command + _addCommand = QString(); + + // ### TODO: does SVN really need this? + // make sure the directories are added before the files + toBeAdded.sort( ); + + // ### TODO: try to make this better + // create a command line for adding the files and dirs + for ( it = toBeAdded.begin( ); it != toBeAdded.end( ); ++it ) { + QFileInfo info( *it ); + _addCommand += "cd " + info.dirPath( true ) + " && svn add " + info.fileName( ) + "; "; + } +} + +// ### TODO: convert to SVN +void SVNHandler::processStatusOutput( const QString& status ) +{ + if ( !_isPORepository ) + return; + +#if 0 + // at first we need to extract the name of the base directory on the server + QFile f( _poBaseDir + "/SVN/Root" ); // ### FIXME + if ( !f.open( IO_ReadOnly ) ) + return; + + QTextStream stream( &f ); + // extract the string after the last colon in the first line + QString basedir = stream.readLine( ).section( ':', -1 ); + + f.close( ); + + // divide the complete status output in little chunks for every file + QStringList entries = QStringList::split( QRegExp( "={67,67}" ), status ); + QStringList::Iterator it; + for ( it = entries.begin( ); it != entries.end( ); ++it ) { + QString entr = *it; + // translate the filename from repository to local + QRegExp rx( basedir + ".*,v" ); + int pos = entr.find( rx ); + QString file = _poBaseDir + entr.mid( pos + basedir.length( ), + rx.matchedLength( ) - basedir.length( ) - 2 ); + + entr = "<qt>" + entr + "</qt>"; + + // TODO: do some markup + + map.replace( file, entr ); + } +#endif +} + +void SVNHandler::processDiff( QString output ) +{ + output.remove( QRegExp( "\\[ .* \\]$" )); + output.remove( QRegExp( "^" + i18n("[ Starting command ]" ).replace("[","\\[").replace("]","\\]"))); + + KTempFile tmpFile; + *(tmpFile.textStream()) << output; + tmpFile.close(); + + QString error; + if ( KApplication::startServiceByName( "Kompare", tmpFile.name(), &error ) ) + KMessageBox::error( 0, error ); +} + +bool SVNHandler::isConsideredModified( const FileStatus status ) const +{ + /* + * A file is modified if it is either: + * - locally modified for SVN + * - directory under SVN control but not the file + */ + + // ### TODO: what about moved and copied? + return status == LOCALLY_MODIFIED || status == NOT_IN_SVN; +} + +SVNOutputCollector::SVNOutputCollector( KProcess* p ) + : m_process(0) +{ + setProcess( p ); +} + +void SVNOutputCollector::setProcess( KProcess* p ) +{ + if( m_process ) + m_process->disconnect( this ); + + m_process = p; + if( p ) { + connect( p, SIGNAL(receivedStdout(KProcess*, char*, int)), + this, SLOT(slotGatherStdout(KProcess*, char*, int)) ); + connect( p, SIGNAL(receivedStderr(KProcess*, char*, int)), + this, SLOT(slotGatherStderr(KProcess*, char*, int)) ); + } + + m_gatheredOutput.truncate( 0 ); + m_stderrOutput.truncate( 0 ); + m_stdoutOutput.truncate( 0 ); +} + +void SVNOutputCollector::slotGatherStderr( KProcess*, char* data, int len ) +{ + m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) ); + m_stderrOutput.append( QString::fromLocal8Bit( data, len ) ); +} + +void SVNOutputCollector::slotGatherStdout( KProcess*, char* data, int len ) +{ + m_gatheredOutput.append( QString::fromLocal8Bit( data, len ) ); + m_stdoutOutput.append( QString::fromLocal8Bit( data, len ) ); +} + +#include "svnhandler.moc" + +// kate: space-indent on; indent-width 2; replace-tabs on; diff --git a/kbabel/catalogmanager/libsvn/svnhandler.h b/kbabel/catalogmanager/libsvn/svnhandler.h new file mode 100644 index 00000000..67c86d73 --- /dev/null +++ b/kbabel/catalogmanager/libsvn/svnhandler.h @@ -0,0 +1,138 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <[email protected]> + Copyright (C) 2005, 2006 by Nicolas GOUTTE <[email protected]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + + +#ifndef SVNHANDLER_H +#define SVNHANDLER_H + +// Qt include files +#include <qmap.h> +#include <qobject.h> +// Project specific include files +#include "svndialog.h" +#include "svnresources.h" +// Forwarding Qt classes +class QString; +class QStringList; + +class KSharedConfig; + +/** + * This class is the backend for SVN support in Catalog Manager. + * + * @short Backend for SVN support in Catalog Manager + */ +class SVNHandler : public QObject +{ + Q_OBJECT + + public: + enum FileStatus { + NO_REPOSITORY, + NOT_IN_SVN, + LOCALLY_ADDED, + LOCALLY_REMOVED, + LOCALLY_MODIFIED, + CONFLICT, + UP_TO_DATE, + ERROR_IN_WC ///< The working copy has data that cannot be handled + }; + + SVNHandler( const QString& poBaseDir = QString::null, const QString& potBaseDir = QString::null ); + + void setPOBaseDir( const QString& dir ); + void setPOTBaseDir( const QString& dir ); + + FileStatus fstatus( const QString& filename ) const; + QString fileStatus( const FileStatus status ) const; + QString svnStatus( const QString& filename ) const; + + void execSVNCommand( QWidget* parent, SVN::Command cmd, const QString& filename, bool templates, KSharedConfig* config ); + void execSVNCommand( QWidget* parent, SVN::Command cmd, const QStringList& files, bool templates, KSharedConfig* config ); + + void setAutoUpdateTemplates( bool update ); + + /** + * True if the file was modified or has another status considered as a modification + */ + bool isConsideredModified( const FileStatus status ) const; + + signals: + void signalIsPORepository( bool ); + void signalIsPOTRepository( bool ); + void signalFilesCommitted( const QStringList& ); + + private: + void showDialog( QWidget* parent, SVN::Command cmd, const QStringList& files, const QString& commandLine, KSharedConfig* config ); + /// Check quickly if the file is part of a SVN repository + bool isInSvn( const QString& path ); + void checkToAdd( const QStringList& files ); + void processStatusOutput( const QString& status ); + void processDiff( QString output ); + + private: + QString _poBaseDir; + QString _potBaseDir; + bool _isPORepository; + bool _isPOTRepository; + bool _autoUpdateTemplates; + QString _addCommand; + + /** Mapping the output of 'svn status' against the filename. */ + QMap<QString,QString> map; +}; + +class SVNOutputCollector: public QObject +{ + Q_OBJECT + + public: + SVNOutputCollector( KProcess* ); + void setProcess( KProcess* ); + + const QString& getOutput() const { return m_gatheredOutput; } + const QString& getStderr() const { return m_stderrOutput; } + const QString& getStdout() const { return m_stdoutOutput; } + + private slots: + void slotGatherStderr( KProcess*, char*, int ); + void slotGatherStdout( KProcess*, char*, int ); + + private: + QString m_gatheredOutput; + QString m_stderrOutput; + QString m_stdoutOutput; + KProcess* m_process; +}; + +#endif // SVNHANDLER_H diff --git a/kbabel/catalogmanager/libsvn/svnresources.h b/kbabel/catalogmanager/libsvn/svnresources.h new file mode 100644 index 00000000..d9032a4f --- /dev/null +++ b/kbabel/catalogmanager/libsvn/svnresources.h @@ -0,0 +1,50 @@ +/* **************************************************************************** + This file is part of KBabel + + Copyright (C) 2002-2003 by Marco Wegner <[email protected]> + Copyright (C) 2005 by Nicolas GOUTTE <[email protected]> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. + +**************************************************************************** */ + + +#ifndef SVNRESOURCES_H +#define SVNRESOURCES_H + +namespace SVN { + enum Command + { + Update, ///< svn update + Commit, ///< svn commit + StatusLocal, ///< svn status + StatusRemote, ///< svn status -u + Diff, ///< svn diff + Info ///< svn info + }; +} + +#endif // SVNRESOURCES_H |