/** Copyright (C) 2003-2005 Mickael Marchand 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "subversion_part.h" #include #include #include #include #include #include #include "kdevcore.h" #include "kdevmainwindow.h" #include "subversion_core.h" #include "subversion_widget.h" #include "subversionprojectwidget.h" #include "subversion_fileinfo.h" #include "subversion_global.h" #include "kdevversioncontrol.h" #include "svn_fileselectdlg_commit.h" #include "svn_logviewwidget.h" #include "svn_switchwidget.h" #include "svn_copywidget.h" #include "svn_mergewidget.h" #include "urlutil.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace SvnGlobal; static const KDevPluginInfo data("kdevsubversion"); typedef KDevGenericFactory subversionFactory; K_EXPORT_COMPONENT_FACTORY( libkdevsubversion, subversionFactory( data ) ) //bool g_projectWasJustCreated = false; subversionPart::subversionPart(TQObject *parent, const char *name, const TQStringList& ) : KDevVersionControl(&data, parent, name ? name : "Subversion" ) { setInstance(subversionFactory::instance()); m_projWidget = 0; m_impl = new subversionCore( this ); //m_impl->processWidget()->setIcon( SmallIcon("db") ); setupActions(); connect( m_impl, TQT_SIGNAL(checkoutFinished(TQString)), TQT_SIGNAL(finishedFetching(TQString)) ); // Context menu connect( core(), TQT_SIGNAL(contextMenu(TQPopupMenu *, const Context *)), this, TQT_SLOT(contextMenu(TQPopupMenu *, const Context *)) ); connect( core(), TQT_SIGNAL(projectConfigWidget(KDialogBase*)), this, TQT_SLOT(projectConfigWidget(KDialogBase*)) ); connect( core(), TQT_SIGNAL(stopButtonClicked(KDevPlugin*)), this, TQT_SLOT(slotStopButtonClicked(KDevPlugin*)) ); connect( core(), TQT_SIGNAL(projectOpened()), this, TQT_SLOT(slotProjectOpened()) ); connect( core(), TQT_SIGNAL(projectClosed()), this, TQT_SLOT(slotProjectClosed()) ); m_impl->processWidget()->setCaption(i18n( "Subversion Output" )); mainWindow()->embedOutputView( (TQWidget*)m_impl->processWidget(), i18n( "Subversion" ), i18n( "Subversion messages" ) ); TQWhatsThis::add((TQWidget*)m_impl->processWidget(), i18n("Subversion

Subversion operations window.")); } subversionPart::~subversionPart() { if ( m_projWidget ){ delete (subversionProjectWidget*) m_projWidget; m_projWidget = 0; } delete m_impl; } void subversionPart::setupActions() { actionCommit = new TDEAction( i18n("&Commit to Repository..."), 0, this, TQT_SLOT(slotActionCommit()), actionCollection(), "subversion_commit" ); actionCommit->setToolTip( i18n("Commit file(s)") ); actionCommit->setWhatsThis( i18n("Commit file(s)

Commits file to repository if modified.") ); /* actionDiff = new TDEAction( i18n("&Difference Between Revisions"), 0, this, TQT_SLOT(slotActionDiff()), actionCollection(), "subversion_diff" ); actionDiff->setToolTip( i18n("Build difference") ); actionDiff->setWhatsThis( i18n("Build difference

Builds difference between releases.") ); */ actionAdd = new TDEAction( i18n("&Add to Repository"), 0, this, TQT_SLOT(slotActionAdd()), actionCollection(), "subversion_add" ); actionAdd->setToolTip( i18n("Add file to repository") ); actionAdd->setWhatsThis( i18n("Add file to repository

Adds file to repository.") ); actionLog = new TDEAction( i18n("Show logs..."), 0, this, TQT_SLOT(slotLog()), actionCollection(), "subversion_log" ); actionBlame = new TDEAction( i18n("Blame..."), 0, this, TQT_SLOT(slotBlame()), actionCollection(), "subversion_blame"); actionRemove = new TDEAction( i18n("&Remove From Repository"), 0, this, TQT_SLOT(slotActionDel()), actionCollection(), "subversion_remove" ); actionRemove->setToolTip( i18n("Remove from repository") ); actionRemove->setWhatsThis( i18n("Remove from repository

Removes file(s) from repository.") ); actionUpdate = new TDEAction( i18n("&Update"), 0, this, TQT_SLOT(slotActionUpdate()), actionCollection(), "subversion_update" ); actionUpdate->setToolTip( i18n("Update") ); actionUpdate->setWhatsThis( i18n("Update

Updates file(s) from repository.") ); actionDiffLocal = new TDEAction( i18n("&Diff to BASE"), 0, this, TQT_SLOT(slotActionDiffLocal()), actionCollection(), "subversion_diff_local" ); actionDiffLocal->setToolTip( i18n("Diff to BASE") ); actionDiffLocal->setWhatsThis( i18n("Diff to disk

Diff current file to the BASE checked out copy.") ); actionDiffHead = new TDEAction( i18n("&Diff to HEAD"), 0, this, TQT_SLOT(slotActionDiffLocal()), actionCollection(), "subversion_diff_head" ); actionDiffHead->setToolTip( i18n("Diff to HEAD") ); actionDiffHead->setWhatsThis( i18n("Diff HEAD

Diff the current file to HEAD in svn.") ); actionRevert = new TDEAction( i18n("&Revert"), 0, this, TQT_SLOT(slotActionRevert()), actionCollection(), "subversion_revert" ); actionRevert->setToolTip( i18n("Revert") ); actionRevert->setWhatsThis( i18n("Revert

Undo local changes.") ); /* actionAddToIgnoreList = new TDEAction( i18n("&Ignore in Subversion Operations"), 0, this, TQT_SLOT(slotActionAddToIgnoreList()), actionCollection(), "subversion_ignore" ); actionAddToIgnoreList->setToolTip( i18n("Ignore in Subversion operations") ); actionAddToIgnoreList->setWhatsThis( i18n("Ignore in Subversion operations

Ignores file(s).") ); actionRemoveFromIgnoreList = new TDEAction( i18n("Do &Not Ignore in Subversion Operations"), 0, this, TQT_SLOT(slotActionRemoveFromIgnoreList()), actionCollection(), "subversion_donot_ignore" ); actionRemoveFromIgnoreList->setToolTip( i18n("Do not ignore in Subversion operations") ); actionRemoveFromIgnoreList->setWhatsThis( i18n("Do not ignore in Subversion operations

Do not ignore file(s).") ); */ actionResolve = new TDEAction( i18n("Re&solve Conflicting State"), 0, this, TQT_SLOT(slotActionResolve()), actionCollection(), "subversion_resolve" ); actionResolve->setToolTip( i18n("Resolve the conflicting state of a file after a merge") ); actionResolve->setWhatsThis( i18n("Resolve the conflicting state

Remove the conflict state that can be set on a file after a merge failed.") ); actionSwitch = new TDEAction( i18n("Switch this working copy to URL.."), 0, this, TQT_SLOT(slotSwitch()), actionCollection(), "subversion_switch" ); // warn slogCopy(), slotMerge only works on context menu. There is no main-menu action actionCopy = new TDEAction( i18n("Copy this working copy to URL.."), 0, this, TQT_SLOT(slotCopy()), actionCollection(), "subversion_copy" ); actionMerge = new TDEAction( i18n("Merge difference to working copy"), 0, this, TQT_SLOT(slotMerge()), actionCollection(), "subversion_merge" ); } TQWidget* subversionPart::newProjectWidget( TQWidget* parent ) { if ( !m_projWidget ) m_projWidget = new subversionProjectWidget(parent,"projectwidget"); return m_projWidget; } void subversionPart::createNewProject( const TQString& dirname ) { if ( !m_projWidget ) return; m_impl->createNewProject( dirname, KURL( m_projWidget->importURL->url() ), m_projWidget->yes->isChecked() ); } bool subversionPart::fetchFromRepository() { m_impl->checkout(); return true; } KDevVCSFileInfoProvider * subversionPart::fileInfoProvider() const { return m_impl->fileInfoProvider(); } void subversionPart::contextMenu( TQPopupMenu *popup, const Context *context ) { //no project, no subversion. Don't test on projectDirectory() here. If the user wants this project to have subversion support //give it to him. e.g. for out of root subprojects like with qmake if(!project()) return; kdDebug(9036) << "contextMenu()" << endl; if (context->hasType( Context::FileContext ) || context->hasType( Context::EditorContext )) { if (context->hasType( Context::FileContext )) { kdDebug(9036) << "Requested for a FileContext" << endl; const FileContext *fcontext = static_cast( context ); m_urls = fcontext->urls(); } else { kdDebug(9036) << "Requested for an EditorContext" << endl; const EditorContext *editorContext = static_cast( context ); m_urls = editorContext->url(); } // THis stuff should end up into prepareOperation() URLUtil::dump( m_urls ); if (m_urls.count() <= 0) return; TDEPopupMenu *subMenu = new TDEPopupMenu( popup ); if (context->hasType( Context::FileContext )) popup->insertSeparator(); int id = subMenu->insertItem( actionCommit->text(), this, TQT_SLOT(slotCommit()) ); // CvsService let to do log and diff operations only on one file (or directory) at time /* if (m_urls.count() == 1) { subMenu->insertItem( actionDiff->text(), this, TQT_SLOT(slotDiff()) ); subMenu->insertItem( actionLog->text(), this, TQT_SLOT(slotLog()) ); }*/ subMenu->setWhatsThis(id, i18n("Commit file(s)

Commits file to repository if modified.")); id = subMenu->insertItem( actionAdd->text(), this, TQT_SLOT(slotAdd()) ); subMenu->setWhatsThis(id, i18n("Add file to repository

Adds file to repository.")); id = subMenu->insertItem( actionRemove->text(), this, TQT_SLOT(slotDel()) ); subMenu->setWhatsThis(id, i18n("Remove from repository

Removes file(s) from repository.")); id = subMenu->insertItem( actionLog->text(), this, TQT_SLOT(slotLog()) ); subMenu->setWhatsThis(id, i18n("Show logs..

View Logs")); id = subMenu->insertItem( actionBlame->text(), this, TQT_SLOT(slotBlame()) ); subMenu->setWhatsThis(id, i18n("Blame 0:HEAD

Show Annotate")); subMenu->insertSeparator(); id = subMenu->insertItem( actionDiffLocal->text(), this, TQT_SLOT(slotDiffLocal()) ); subMenu->setWhatsThis(id, i18n("Diff

Diff file to local disk.")); id = subMenu->insertItem( actionDiffHead->text(), this, TQT_SLOT(slotDiffHead()) ); subMenu->setWhatsThis(id, i18n("Diff

Diff file to repository.")); id = subMenu->insertItem( actionUpdate->text(), this, TQT_SLOT(slotUpdate()) ); subMenu->setWhatsThis(id, i18n("Update

Updates file(s) from repository.")); id = subMenu->insertItem( actionRevert->text(), this, TQT_SLOT(slotRevert()) ); subMenu->setWhatsThis(id, i18n("Revert

Undo local changes.") ); id = subMenu->insertItem( actionResolve->text(), this, TQT_SLOT(slotResolve()) ); subMenu->setWhatsThis(id, i18n("Resolve

Resolve conflicting state.") ); id = subMenu->insertItem( actionSwitch->text(), this, TQT_SLOT(slotSwitch()) ); subMenu->setWhatsThis(id, i18n("Switch

Switch working tree.") ); id = subMenu->insertItem( actionCopy->text(), this, TQT_SLOT(slotCopy()) ); subMenu->setWhatsThis(id, i18n("Copy

Copy from/between path/URLs") ); id = subMenu->insertItem( actionMerge->text(), this, TQT_SLOT(slotMerge()) ); subMenu->setWhatsThis(id, i18n("Merge

Merge difference to working copy") ); /* subMenu->insertSeparator(); id = subMenu->insertItem( actionAddToIgnoreList->text(), this, TQT_SLOT(slotAddToIgnoreList()) ); subMenu->setWhatsThis(id, i18n("Ignore in Subversion operations

Ignores file(s).")); id = subMenu->insertItem( actionRemoveFromIgnoreList->text(), this, TQT_SLOT(slotRemoveFromIgnoreList()) ); subMenu->setWhatsThis(id, i18n("Do not ignore in Subversion operations

Do not ignore file(s).")); */ // Now insert in parent menu popup->insertItem( i18n("Subversion"), subMenu ); } } bool subversionPart::urlFocusedDocument( KURL &url ) { KParts::ReadOnlyPart *part = dynamic_cast( partController()->activePart() ); if ( part ) { if (part->url().isLocalFile() ) { url = part->url(); return true; } } return false; } void subversionPart::slotActionUpdate() { kdDebug(9036) << "subversion: slotActionUpdate()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->update( doc ); } } void subversionPart::slotUpdate() { m_impl->update (m_urls); } void subversionPart::slotActionResolve() { kdDebug(9036) << "subversion: slotActionResolve()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->resolve( doc ); } } void subversionPart::slotResolve() { m_impl->resolve (m_urls); } void subversionPart::slotSwitch() { if( m_urls.count() > 1 ){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Please select only one item for subversion switch") ); return; } if( m_urls.count() < 1 ) return; // retrieve repository info from local-copy metadata which will be displayed in dialog box KURL wcPath = m_urls.first(); TQMap< KURL, SvnGlobal::SvnInfoHolder> holderMap; SvnGlobal::SvnInfoHolder holder; m_impl->clientInfo( wcPath, false, holderMap ); TQValueList< SvnGlobal::SvnInfoHolder > holderList = holderMap.values(); holder = holderList.first(); // invoke dialog box SvnSwitchDlg dlg( &holder, wcPath.path(), (TQWidget*)project()->mainWindow()->main() ); if( dlg.exec() != TQDialog::Accepted ){ return; } // check target url's validity KURL repositUrl = KURL( dlg.destUrl() ); if( !repositUrl.isValid() ){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("The destination URL is invalid") ); return; } // call core if( dlg.switchOnly() ) m_impl->switchTree( wcPath, repositUrl, -1, "HEAD", dlg.recursive() ); else if( dlg.relocation() ) m_impl->switchRelocate( wcPath, dlg.currentUrl(), repositUrl, dlg.recursive() ); else KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Fail to conduct subversion switch. No action was selected") ); } void subversionPart::slotCopy() { // error check if( m_urls.count() > 1 ){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Please select only one item for subversion switch") ); return; } if( m_urls.count() < 1 ) return; // retrieve repository info from local-copy metadata which will be displayed in dialog box KURL wcPath = m_urls.first(); TQMap< KURL, SvnGlobal::SvnInfoHolder> holderMap; SvnGlobal::SvnInfoHolder holder; m_impl->clientInfo( wcPath, false, holderMap ); TQValueList< SvnGlobal::SvnInfoHolder > holderList = holderMap.values(); holder = holderList.first(); // start input dialog SvnCopyDialog dlg( wcPath.prettyURL(), &holder, (TQWidget*)project()->mainWindow()->main()); if( dlg.exec() != TQDialog::Accepted ) return; // retrieve user input KURL srcUrl = dlg.sourceUrl(); int rev = dlg.revision(); TQString revKind = dlg.revKind(); KURL dest = dlg.destUrl(); kdDebug(9036) << " SRC: " << srcUrl << " DEST: " << dest << " Revnum: " << rev << " RevKind: " << revKind << endl; m_impl->svnCopy( srcUrl, rev, revKind, dest ); } void subversionPart::slotMerge() { // error check if( m_urls.count() > 1 ){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Please select only one item for subversion merge") ); return; } if( m_urls.count() < 1 ) return; KURL wcTarget= m_urls.first(); SvnMergeDialog dlg( wcTarget, (TQWidget*)project()->mainWindow()->main() ); if( dlg.exec() != TQDialog::Accepted ) return; KURL src1 = dlg.source1(); SvnRevision rev1 = dlg.rev1(); KURL src2 = dlg.source2(); SvnRevision rev2 = dlg.rev2(); m_impl->merge( src1, rev1.revNum, rev1.revKind, src2, rev2.revNum, rev2.revKind, wcTarget, dlg.recurse(), dlg.ignoreAncestry(), dlg.force(), dlg.dryRun() ); } void subversionPart::slotActionCommit() { kdDebug(9036) << "subversion: slotActionCommit()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->commit( doc, true, true ); } } void subversionPart::slotActionAdd() { kdDebug(9036) << "subversion: slotActionAdd()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->add( doc ); } } void subversionPart::slotActionDel() { kdDebug(9036) << "subversion: slotActionDel()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->del( doc ); } } void subversionPart::slotActionRevert() { kdDebug(9036) << "subversion: slotActionRevert()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->revert( doc ); } } void subversionPart::slotActionDiffLocal() { kdDebug(9036) << "subversion: slotActionDiffLocal()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->diff( doc, "BASE" ); } } void subversionPart::slotActionDiffHead() { kdDebug(9036) << "subversion: slotActionDiffHead()" << endl; KURL doc; if (urlFocusedDocument( doc )) { m_impl->diff( doc, "HEAD" ); } } void subversionPart::slotCommit() { SVNFileSelectDlgCommit dialog( m_urls, this, 0 ); if( dialog.exec() == TQDialog::Accepted ){ KURL::List tobeCommittedUrls = dialog.checkedUrls(); bool recursive = dialog.recursive(); bool keepLocks = dialog.keepLocks(); m_impl->commit(tobeCommittedUrls, recursive, keepLocks ); } } void subversionPart::slotAdd() { m_impl->add( m_urls ); } void subversionPart::slotLog() { if (m_urls.count() > 1){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Please select only one item for subversion log") ); return; } SvnLogViewOptionDlg dlg; if( dlg.exec() ){ int revstart = dlg.revstart(); TQString revkindstart = dlg.revKindStart(); int revend = dlg.revend(); TQString revkindend = dlg.revKindEnd(); bool strictNode = dlg.strictNode(); m_impl->svnLog (m_urls, revstart, revkindstart, revend, revkindend, true/*changedPath*/, strictNode); } else{ return; } } void subversionPart::slotBlame() { if (m_urls.count() > 1){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Please select only one item to see annotate") ); return; } if (m_urls.count() < 1){ KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Select file to see blame") ); return; } KURL url = m_urls.first(); m_impl->blame(url, SvnGlobal::path_to_reposit, 0, "", -1, "BASE"); } void subversionPart::slotDel() { m_impl->del (m_urls); } // note: currently diffAsync does not support merging. But svncore::diff() // cannot be invoked on directory, while diffAsync can. void subversionPart::slotDiffLocal() { // m_impl->diff (m_urls, "BASE"); if( m_urls.count() < 1 ){ // Impossible to reach here but.. KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Select file or directory to see diff") ); return; } m_impl->diffAsync( *(m_urls.begin()), *(m_urls.begin()), -1, "BASE", -1, "WORKING", true ); } void subversionPart::slotDiffHead() { // m_impl->diff (m_urls, "HEAD"); if( m_urls.count() < 1 ){ // Impossible to reach here but.. KMessageBox::error( (TQWidget*)project()->mainWindow()->main(), i18n("Select file or directory to see diff") ); return; } m_impl->diffAsync( *(m_urls.begin()), *(m_urls.begin()), -1, "WORKING", -1, "HEAD", true ); } void subversionPart::slotRevert() { m_impl->revert (m_urls); } void subversionPart::slotProjectOpened() { kdDebug(9036) << "subversion :projectOpened" << endl; /* if ( g_projectWasJustCreated ) { //saveOptions(); g_projectWasJustCreated = false; } */ //loadOptions(); /// \FIXME slots //connect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList&)), this, TQT_SLOT(slotAddFilesToProject(const TQStringList &)) ); //connect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList&)), this, TQT_SLOT(slotRemovedFilesFromProject(const TQStringList &)) ); } void subversionPart::slotProjectClosed() { kdDebug(9036) << "subversion :projectClosed" << endl; //saveOptions(); /// \FIXME slots //disconnect( project(), TQT_SIGNAL(addedFilesToProject(const TQStringList&)), this, TQT_SLOT(slotAddFilesToProject(const TQStringList &)) ); //disconnect( project(), TQT_SIGNAL(removedFilesFromProject(const TQStringList&)), this, TQT_SLOT(slotRemovedFilesFromProject(const TQStringList &)) ); } void subversionPart::savePartialProjectSession(TQDomElement* dom) { kdDebug(9036) << "subversion : savePartialProjectSession" << endl; TQDomDocument doc = dom->ownerDocument(); TQDomElement svn = doc.createElement( "subversion" ); svn.setAttribute( "base", base.url() ); dom->appendChild( svn ); } void subversionPart::restorePartialProjectSession(const TQDomElement* dom) { kdDebug(9036) << "subversion : restorePartialProjectSession" << endl; TQDomElement svn = dom->namedItem("subversion").toElement(); base = svn.attribute( "base", "" ); } bool subversionPart::isValidDirectory( const TQString &dirPath) const { TQString svn = "/.svn/"; TQDir svndir( dirPath + svn ); TQString entriesFileName = dirPath + svn + "entries"; kdDebug(9036) << "dirpath " << dirPath+"/.svn/" << " exists:" << svndir.exists() << endl; kdDebug(9036) << "entries " << entriesFileName << " exists:" << TQFile::exists( entriesFileName ) << endl; return svndir.exists() && TQFile::exists( entriesFileName ); } #include "subversion_part.moc"