You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
12 KiB
365 lines
12 KiB
/***************************************************************************
|
|
* Copyright (C) 1999-2001 by Bernd Gehrmann *
|
|
* bernd@kdevelop.org *
|
|
* Extended 2002 by Harald Fernengel <harry@kdevelop.org> *
|
|
* *
|
|
* 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include "perforcepart.h"
|
|
|
|
#include <tqfileinfo.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqregexp.h>
|
|
#include <tdepopupmenu.h>
|
|
#include <kdebug.h>
|
|
#include <kdevgenericfactory.h>
|
|
#include <kprocess.h>
|
|
#include <tdemessagebox.h>
|
|
#include <tdeapplication.h>
|
|
#include <tdeaction.h>
|
|
#include <kurl.h>
|
|
#include <tdeparts/part.h>
|
|
|
|
#include "kdevpartcontroller.h"
|
|
#include "kdevcore.h"
|
|
#include "kdevmakefrontend.h"
|
|
#include "kdevdifffrontend.h"
|
|
#include "kdevplugininfo.h"
|
|
#include "commitdlg.h"
|
|
#include "execcommand.h"
|
|
|
|
static const KDevPluginInfo pluginData("kdevperforce");
|
|
|
|
typedef KDevGenericFactory<PerforcePart> PerforceFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( libkdevperforce, PerforceFactory( pluginData ) )
|
|
|
|
PerforcePart::PerforcePart( TQObject *parent, const char *name, const TQStringList & )
|
|
: KDevVersionControl( &pluginData, parent, name ? name : "PerforcePart" )
|
|
{
|
|
setInstance(PerforceFactory::instance());
|
|
setupActions();
|
|
|
|
connect( core(), TQ_SIGNAL(contextMenu(TQPopupMenu *, const Context *)),
|
|
this, TQ_SLOT(contextMenu(TQPopupMenu *, const Context *)) );
|
|
}
|
|
|
|
|
|
PerforcePart::~PerforcePart()
|
|
{}
|
|
|
|
void PerforcePart::setupActions()
|
|
{
|
|
actionEdit = new TDEAction( i18n("Edit"), 0, this, TQ_SLOT(slotActionEdit()),
|
|
actionCollection(), "perforce_edit" );
|
|
actionEdit->setToolTip(i18n("Edit"));
|
|
actionEdit->setWhatsThis(i18n("<b>Edit</b><p>Opens file(s) in a client workspace for edit."));
|
|
actionRevert = new TDEAction( i18n("Revert"), 0, this, TQ_SLOT(slotActionRevert()),
|
|
actionCollection(), "perforce_revert" );
|
|
actionRevert->setToolTip(i18n("Revert"));
|
|
actionRevert->setWhatsThis(i18n("<b>Revert</b><p>Discards changes made to open files."));
|
|
actionSubmit = new TDEAction( i18n("Submit"), 0, this, TQ_SLOT(slotActionCommit()),
|
|
actionCollection(), "perforce_submit" );
|
|
actionSubmit->setToolTip(i18n("Submit"));
|
|
actionSubmit->setWhatsThis(i18n("<b>Submit</b><p>Sends changes made to open files to the depot."));
|
|
actionSync = new TDEAction( i18n("Sync"), 0, this, TQ_SLOT(slotActionUpdate()),
|
|
actionCollection(), "perforce_sync" );
|
|
actionSync->setToolTip(i18n("Sync"));
|
|
actionSync->setWhatsThis(i18n("<b>Sync</b><p>Copies files from the depot into the workspace."));
|
|
actionDiff = new TDEAction( i18n("Diff Against Repository"), 0, this, TQ_SLOT(slotActionDiff()),
|
|
actionCollection(), "perforce_diff" );
|
|
actionDiff->setToolTip(i18n("Diff against repository"));
|
|
actionDiff->setWhatsThis(i18n("<b>Diff against repository</b><p>Compares a client workspace file to a revision in the depot."));
|
|
actionAdd = new TDEAction( i18n("Add to Repository"), 0, this, TQ_SLOT(slotActionAdd()),
|
|
actionCollection(), "perforce_add" );
|
|
actionAdd->setToolTip(i18n("Add to repository"));
|
|
actionAdd->setWhatsThis(i18n("<b>Add to repository</b><p>Open file(s) in a client workspace for addition to the depot."));
|
|
actionRemove = new TDEAction( i18n("Remove From Repository"), 0, this, TQ_SLOT(slotActionRemove()),
|
|
actionCollection(), "perforce_remove" );
|
|
actionRemove->setToolTip(i18n("Remove from repository"));
|
|
actionRemove->setWhatsThis(i18n("<b>Remove from repository</b><p>Open file(s) in a client workspace for deletion from the depot."));
|
|
}
|
|
|
|
void PerforcePart::contextMenu(TQPopupMenu *popup, const Context *context)
|
|
{
|
|
if (context->hasType( Context::FileContext )) {
|
|
const FileContext *fcontext = static_cast<const FileContext*>(context);
|
|
popupfile = fcontext->urls().first().path();
|
|
TQFileInfo fi( popupfile );
|
|
popup->insertSeparator();
|
|
|
|
TDEPopupMenu *sub = new TDEPopupMenu(popup);
|
|
TQString name = fi.fileName();
|
|
sub->insertTitle( i18n("Actions for %1").arg(name) );
|
|
|
|
int id = sub->insertItem( i18n("Edit"),
|
|
this, TQ_SLOT(slotEdit()) );
|
|
sub->setWhatsThis(id, i18n("<b>Edit</b><p>Opens file(s) in a client workspace for edit."));
|
|
id = sub->insertItem( i18n("Revert"),
|
|
this, TQ_SLOT(slotRevert()) );
|
|
sub->setWhatsThis(id, i18n("<b>Revert</b><p>Discards changes made to open files."));
|
|
id = sub->insertItem( i18n("Submit"),
|
|
this, TQ_SLOT(slotCommit()) );
|
|
sub->setWhatsThis(id, i18n("<b>Submit</b><p>Sends changes made to open files to the depot."));
|
|
id = sub->insertItem( i18n("Sync"),
|
|
this, TQ_SLOT(slotUpdate()) );
|
|
sub->setWhatsThis(id, i18n("<b>Sync</b><p>Copies files from the depot into the workspace."));
|
|
sub->insertSeparator();
|
|
id = sub->insertItem( i18n("Diff Against Repository"),
|
|
this, TQ_SLOT(slotDiff()) );
|
|
sub->setWhatsThis(id, i18n("<b>Diff against repository</b><p>Compares a client workspace file to a revision in the depot."));
|
|
id = sub->insertItem( i18n("Add to Repository"),
|
|
this, TQ_SLOT(slotAdd()) );
|
|
sub->setWhatsThis(id, i18n("<b>Add to repository</b><p>Open file(s) in a client workspace for addition to the depot."));
|
|
id = sub->insertItem( i18n("Remove From Repository"),
|
|
this, TQ_SLOT(slotRemove()) );
|
|
sub->setWhatsThis(id, i18n("<b>Remove from repository</b><p>Open file(s) in a client workspace for deletion from the depot."));
|
|
id = popup->insertItem(i18n("Perforce"), sub);
|
|
}
|
|
}
|
|
|
|
void PerforcePart::execCommand( const TQString& cmd, const TQString& filename )
|
|
{
|
|
if ( filename.isEmpty() )
|
|
return;
|
|
|
|
TQFileInfo fi( filename );
|
|
if (fi.isDir()) {
|
|
KMessageBox::error( 0, i18n("Cannot handle directories, please select single files") );
|
|
return;
|
|
}
|
|
TQString dir = fi.dirPath();
|
|
TQString name = fi.fileName();
|
|
|
|
TQString command("cd ");
|
|
command += TDEProcess::quote(dir);
|
|
command += " && p4 " + cmd + " ";
|
|
command += name;
|
|
|
|
if (KDevMakeFrontend *makeFrontend = extension<KDevMakeFrontend>("TDevelop/MakeFrontend"))
|
|
makeFrontend->queueCommand(dir, command);
|
|
}
|
|
|
|
void PerforcePart::edit( const TQString& filename )
|
|
{
|
|
execCommand( "edit", filename );
|
|
}
|
|
|
|
void PerforcePart::revert( const TQString& filename )
|
|
{
|
|
if ( KMessageBox::questionYesNo( 0,
|
|
i18n("Do you really want to revert "
|
|
"the file %1 and lose all your changes?").arg( filename ), TQString(), i18n("Revert"), i18n("Do Not Revert") ) == KMessageBox::Yes ) {
|
|
execCommand( "revert", filename );
|
|
}
|
|
}
|
|
|
|
void PerforcePart::commit( const TQString& filename )
|
|
{
|
|
if ( filename.isEmpty() )
|
|
return;
|
|
|
|
TQFileInfo fi( filename );
|
|
if ( fi.isDir() ) {
|
|
KMessageBox::error( 0, i18n("Submitting of subdirectories is not supported") );
|
|
return;
|
|
}
|
|
|
|
CommitDialog d;
|
|
TQStringList lst;
|
|
lst << filename;
|
|
d.setFiles( lst );
|
|
if (d.exec() == TQDialog::Rejected)
|
|
return;
|
|
|
|
TQString message = d.changeList();
|
|
if (!message.isEmpty())
|
|
message = KShellProcess::quote(message);
|
|
|
|
TQString command("echo " + message);
|
|
command += " | p4 submit -i";
|
|
|
|
if (KDevMakeFrontend *makeFrontend = extension<KDevMakeFrontend>("TDevelop/MakeFrontend"))
|
|
makeFrontend->queueCommand("", command);
|
|
}
|
|
|
|
|
|
void PerforcePart::update( const TQString& filename )
|
|
{
|
|
if ( filename.isEmpty() )
|
|
return;
|
|
|
|
TQString dir, name;
|
|
TQFileInfo fi( filename );
|
|
if (fi.isDir()) {
|
|
dir = fi.absFilePath();
|
|
name = "..."; // three dots means "recoursive"
|
|
} else {
|
|
dir = fi.dirPath();
|
|
name = fi.fileName();
|
|
}
|
|
|
|
TQString command("cd ");
|
|
command += TDEProcess::quote(dir);
|
|
command += " && p4 sync ";
|
|
command += name;
|
|
|
|
if (KDevMakeFrontend *makeFrontend = extension<KDevMakeFrontend>("TDevelop/MakeFrontend"))
|
|
makeFrontend->queueCommand(dir, command);
|
|
}
|
|
|
|
|
|
void PerforcePart::add( const TQString& filename )
|
|
{
|
|
execCommand( "add", filename );
|
|
}
|
|
|
|
|
|
void PerforcePart::remove( const TQString& filename )
|
|
{
|
|
execCommand( "delete", filename );
|
|
}
|
|
|
|
void PerforcePart::diff( const TQString& filename )
|
|
{
|
|
if ( filename.isEmpty() )
|
|
return;
|
|
|
|
TQString name;
|
|
TQFileInfo fi( filename );
|
|
|
|
if ( fi.isDir() ) {
|
|
name = fi.absFilePath() + "...";
|
|
} else {
|
|
name = filename;
|
|
}
|
|
TQStringList args;
|
|
|
|
args << "diff";
|
|
args << "-du";
|
|
args << name;
|
|
ExecCommand* cmv = new ExecCommand( "p4", args, TQString(), TQStringList(), this );
|
|
connect( cmv, TQ_SIGNAL(finished( const TQString&, const TQString& )),
|
|
this, TQ_SLOT(slotDiffFinished( const TQString&, const TQString& )) );
|
|
}
|
|
|
|
void PerforcePart::slotDiffFinished( const TQString& diff, const TQString& err )
|
|
{
|
|
if ( diff.isNull() && err.isNull() ) {
|
|
kdDebug(9000) << "p4 diff cancelled" << endl;
|
|
return; // user pressed cancel or an error occured
|
|
}
|
|
|
|
if ( diff.isEmpty() && !err.isEmpty() ) {
|
|
KMessageBox::detailedError( 0, i18n("P4 output errors during diff."), err, i18n("Errors During Diff") );
|
|
return;
|
|
}
|
|
|
|
if ( !err.isEmpty() ) {
|
|
int s = KMessageBox::warningContinueCancelList( 0, i18n("P4 output errors during diff. Do you still want to continue?"),
|
|
TQStringList::split( "\n", err, false ), i18n("Errors During Diff") );
|
|
if ( s != KMessageBox::Continue )
|
|
return;
|
|
}
|
|
|
|
if ( diff.isEmpty() ) {
|
|
KMessageBox::information( 0, i18n("There is no difference to the repository."), i18n("No Differences Found") );
|
|
return;
|
|
}
|
|
|
|
// strip the ==== headers
|
|
static TQRegExp rx( "(^|\\n)==== ([^ ]+) -.*====\\n" );
|
|
rx.setMinimal( true );
|
|
TQString strippedDiff = diff;
|
|
strippedDiff.replace( rx, "--- \\2\n+++ \\2\n" );
|
|
|
|
if (KDevDiffFrontend *diffFrontend = extension<KDevDiffFrontend>("TDevelop/DiffFrontend"))
|
|
diffFrontend->showDiff( strippedDiff );
|
|
}
|
|
|
|
TQString PerforcePart::currentFile()
|
|
{
|
|
KParts::ReadOnlyPart *part = dynamic_cast<KParts::ReadOnlyPart*>( partController()->activePart() );
|
|
if ( part ) {
|
|
KURL url = part->url();
|
|
if ( url.isLocalFile() )
|
|
return url.path();
|
|
}
|
|
return TQString();
|
|
}
|
|
|
|
void PerforcePart::slotActionCommit()
|
|
{
|
|
commit( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotActionUpdate()
|
|
{
|
|
update( currentFile() );
|
|
}
|
|
void PerforcePart::slotActionAdd()
|
|
{
|
|
add( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotActionRemove()
|
|
{
|
|
remove( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotActionEdit()
|
|
{
|
|
edit( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotActionRevert()
|
|
{
|
|
revert( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotActionDiff()
|
|
{
|
|
diff( currentFile() );
|
|
}
|
|
|
|
void PerforcePart::slotCommit()
|
|
{
|
|
commit( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotUpdate()
|
|
{
|
|
update( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotAdd()
|
|
{
|
|
add( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotRemove()
|
|
{
|
|
remove( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotEdit()
|
|
{
|
|
edit( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotRevert()
|
|
{
|
|
revert( popupfile );
|
|
}
|
|
|
|
void PerforcePart::slotDiff()
|
|
{
|
|
diff( popupfile );
|
|
}
|
|
|
|
#include "perforcepart.moc"
|