00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "perforcepart.h"
00014
00015 #include <qfileinfo.h>
00016 #include <qpopupmenu.h>
00017 #include <qregexp.h>
00018 #include <kpopupmenu.h>
00019 #include <kdebug.h>
00020 #include <kdevgenericfactory.h>
00021 #include <klocale.h>
00022 #include <kprocess.h>
00023 #include <kmessagebox.h>
00024 #include <kapplication.h>
00025 #include <kaction.h>
00026 #include <kurl.h>
00027 #include <kparts/part.h>
00028
00029 #include "kdevpartcontroller.h"
00030 #include "kdevcore.h"
00031 #include "kdevmakefrontend.h"
00032 #include "kdevdifffrontend.h"
00033 #include "commitdlg.h"
00034 #include "execcommand.h"
00035
00036 static const KAboutData data("kdevperforce", I18N_NOOP("Perforce"), "1.0");
00037
00038 typedef KDevGenericFactory<PerforcePart> PerforceFactory;
00039 K_EXPORT_COMPONENT_FACTORY( libkdevperforce, PerforceFactory( &data ) )
00040
00041 PerforcePart::PerforcePart( QObject *parent, const char *name, const QStringList & )
00042 : KDevPlugin( "Perforce", "perforce", parent, name ? name : "PerforcePart" )
00043 {
00044 setInstance(PerforceFactory::instance());
00045 setupActions();
00046
00047 connect( core(), SIGNAL(contextMenu(QPopupMenu *, const Context *)),
00048 this, SLOT(contextMenu(QPopupMenu *, const Context *)) );
00049 }
00050
00051
00052 PerforcePart::~PerforcePart()
00053 {}
00054
00055 void PerforcePart::setupActions()
00056 {
00057 actionEdit = new KAction( i18n("Edit"), 0, this, SLOT(slotActionEdit()),
00058 actionCollection(), "perforce_edit" );
00059 actionEdit->setToolTip(i18n("Edit"));
00060 actionEdit->setWhatsThis(i18n("<b>Edit</b><p>Opens file(s) in a client workspace for edit."));
00061 actionRevert = new KAction( i18n("Revert"), 0, this, SLOT(slotActionRevert()),
00062 actionCollection(), "perforce_revert" );
00063 actionRevert->setToolTip(i18n("Revert"));
00064 actionRevert->setWhatsThis(i18n("<b>Revert</b><p>Discards changes made to open files."));
00065 actionSubmit = new KAction( i18n("Submit"), 0, this, SLOT(slotActionCommit()),
00066 actionCollection(), "perforce_submit" );
00067 actionSubmit->setToolTip(i18n("Submit"));
00068 actionSubmit->setWhatsThis(i18n("<b>Submit</b><p>Sends changes made to open files to the depot."));
00069 actionSync = new KAction( i18n("Sync"), 0, this, SLOT(slotActionUpdate()),
00070 actionCollection(), "perforce_sync" );
00071 actionSync->setToolTip(i18n("Sync"));
00072 actionSync->setWhatsThis(i18n("<b>Sync</b><p>Copies files from the depot into the workspace."));
00073 actionDiff = new KAction( i18n("Diff Against Repository"), 0, this, SLOT(slotActionDiff()),
00074 actionCollection(), "perforce_diff" );
00075 actionDiff->setToolTip(i18n("Diff against repository"));
00076 actionDiff->setWhatsThis(i18n("<b>Diff against repository</b><p>Compares a client workspace file to a revision in the depot."));
00077 actionAdd = new KAction( i18n("Add to Repository"), 0, this, SLOT(slotActionAdd()),
00078 actionCollection(), "perforce_add" );
00079 actionAdd->setToolTip(i18n("Add to repository"));
00080 actionAdd->setWhatsThis(i18n("<b>Add to repository</b><p>Open file(s) in a client workspace for addition to the depot."));
00081 actionRemove = new KAction( i18n("Remove From Repository"), 0, this, SLOT(slotActionRemove()),
00082 actionCollection(), "perforce_remove" );
00083 actionRemove->setToolTip(i18n("Remove from repository"));
00084 actionRemove->setWhatsThis(i18n("<b>Remove from repository</b><p>Open file(s) in a client workspace for deletion from the depot."));
00085 }
00086
00087 void PerforcePart::contextMenu(QPopupMenu *popup, const Context *context)
00088 {
00089 if (context->hasType( Context::FileContext )) {
00090 const FileContext *fcontext = static_cast<const FileContext*>(context);
00091 popupfile = fcontext->fileName();
00092 QFileInfo fi( popupfile );
00093 popup->insertSeparator();
00094
00095 KPopupMenu *sub = new KPopupMenu(popup);
00096 QString name = fi.fileName();
00097 sub->insertTitle( i18n("Actions for %1").arg(name) );
00098
00099 int id = sub->insertItem( i18n("Edit"),
00100 this, SLOT(slotEdit()) );
00101 sub->setWhatsThis(id, i18n("<b>Edit</b><p>Opens file(s) in a client workspace for edit."));
00102 id = sub->insertItem( i18n("Revert"),
00103 this, SLOT(slotRevert()) );
00104 sub->setWhatsThis(id, i18n("<b>Revert</b><p>Discards changes made to open files."));
00105 id = sub->insertItem( i18n("Submit"),
00106 this, SLOT(slotCommit()) );
00107 sub->setWhatsThis(id, i18n("<b>Submit</b><p>Sends changes made to open files to the depot."));
00108 id = sub->insertItem( i18n("Sync"),
00109 this, SLOT(slotUpdate()) );
00110 sub->setWhatsThis(id, i18n("<b>Sync</b><p>Copies files from the depot into the workspace."));
00111 sub->insertSeparator();
00112 id = sub->insertItem( i18n("Diff Against Repository"),
00113 this, SLOT(slotDiff()) );
00114 sub->setWhatsThis(id, i18n("<b>Diff against repository</b><p>Compares a client workspace file to a revision in the depot."));
00115 id = sub->insertItem( i18n("Add to Repository"),
00116 this, SLOT(slotAdd()) );
00117 sub->setWhatsThis(id, i18n("<b>Add to repository</b><p>Open file(s) in a client workspace for addition to the depot."));
00118 id = sub->insertItem( i18n("Remove From Repository"),
00119 this, SLOT(slotRemove()) );
00120 sub->setWhatsThis(id, i18n("<b>Remove from repository</b><p>Open file(s) in a client workspace for deletion from the depot."));
00121 id = popup->insertItem(i18n("Perforce"), sub);
00122 }
00123 }
00124
00125 void PerforcePart::execCommand( const QString& cmd, const QString& filename )
00126 {
00127 if ( filename.isEmpty() )
00128 return;
00129
00130 QFileInfo fi( filename );
00131 if (fi.isDir()) {
00132 KMessageBox::error( 0, i18n("Cannot handle directories, please select single files") );
00133 return;
00134 }
00135 QString dir = fi.dirPath();
00136 QString name = fi.fileName();
00137
00138 QString command("cd ");
00139 command += KProcess::quote(dir);
00140 command += " && p4 " + cmd + " ";
00141 command += name;
00142
00143 makeFrontend()->queueCommand(dir, command);
00144 }
00145
00146 void PerforcePart::edit( const QString& filename )
00147 {
00148 execCommand( "edit", filename );
00149 }
00150
00151 void PerforcePart::revert( const QString& filename )
00152 {
00153 if ( KMessageBox::questionYesNo( 0,
00154 i18n("Do you really want to revert "
00155 "the file %1 and lose all your changes?").arg( filename ) ) == KMessageBox::Yes ) {
00156 execCommand( "revert", filename );
00157 }
00158 }
00159
00160 void PerforcePart::commit( const QString& filename )
00161 {
00162 if ( filename.isEmpty() )
00163 return;
00164
00165 QFileInfo fi( filename );
00166 if ( fi.isDir() ) {
00167 KMessageBox::error( 0, i18n("Submitting of subdirectories is not supported") );
00168 return;
00169 }
00170
00171 CommitDialog d;
00172 QStringList lst;
00173 lst << filename;
00174 d.setFiles( lst );
00175 if (d.exec() == QDialog::Rejected)
00176 return;
00177
00178 QString message = d.changeList();
00179 if (!message.isEmpty())
00180 message = KShellProcess::quote(message);
00181
00182 QString command("echo " + message);
00183 command += " | p4 submit -i";
00184
00185 makeFrontend()->queueCommand("", command);
00186 }
00187
00188
00189 void PerforcePart::update( const QString& filename )
00190 {
00191 if ( filename.isEmpty() )
00192 return;
00193
00194 QString dir, name;
00195 QFileInfo fi( filename );
00196 if (fi.isDir()) {
00197 dir = fi.absFilePath();
00198 name = "...";
00199 } else {
00200 dir = fi.dirPath();
00201 name = fi.fileName();
00202 }
00203
00204 QString command("cd ");
00205 command += KProcess::quote(dir);
00206 command += " && p4 sync ";
00207 command += name;
00208
00209 makeFrontend()->queueCommand(dir, command);
00210 }
00211
00212
00213 void PerforcePart::add( const QString& filename )
00214 {
00215 execCommand( "add", filename );
00216 }
00217
00218
00219 void PerforcePart::remove( const QString& filename )
00220 {
00221 execCommand( "delete", filename );
00222 }
00223
00224 void PerforcePart::diff( const QString& filename )
00225 {
00226 if ( filename.isEmpty() )
00227 return;
00228
00229 QString name;
00230 QFileInfo fi( filename );
00231
00232 if ( fi.isDir() ) {
00233 name = fi.absFilePath() + "...";
00234 } else {
00235 name = filename;
00236 }
00237 QStringList args;
00238
00239 args << "diff";
00240 args << name;
00241 ExecCommand* cmv = new ExecCommand( "p4", args, QString::null, QStringList(), this );
00242 connect( cmv, SIGNAL(finished( const QString&, const QString& )),
00243 this, SLOT(slotDiffFinished( const QString&, const QString& )) );
00244 }
00245
00246 void PerforcePart::slotDiffFinished( const QString& diff, const QString& err )
00247 {
00248 if ( diff.isNull() && err.isNull() ) {
00249 kdDebug(9000) << "p4 diff cancelled" << endl;
00250 return;
00251 }
00252
00253 if ( diff.isEmpty() && !err.isEmpty() ) {
00254 KMessageBox::detailedError( 0, i18n("P4 outputted errors during diff."), err, i18n("Errors During Diff") );
00255 return;
00256 }
00257
00258 if ( !err.isEmpty() ) {
00259 int s = KMessageBox::warningContinueCancelList( 0, i18n("P4 outputted errors during diff. Do you still want to continue?"),
00260 QStringList::split( "\n", err, false ), i18n("Errors During Diff") );
00261 if ( s != KMessageBox::Continue )
00262 return;
00263 }
00264
00265 if ( diff.isEmpty() ) {
00266 KMessageBox::information( 0, i18n("There is no difference to the repository."), i18n("No Differences Found") );
00267 return;
00268 }
00269
00270
00271 static QRegExp rx( "(^|\\n)====.*====\\n" );
00272 rx.setMinimal( true );
00273 QString strippedDiff = diff;
00274 strippedDiff.replace( rx, QString::null );
00275
00276 Q_ASSERT( diffFrontend() );
00277 diffFrontend()->showDiff( strippedDiff );
00278 }
00279
00280 QString PerforcePart::currentFile()
00281 {
00282 KParts::ReadOnlyPart *part = dynamic_cast<KParts::ReadOnlyPart*>( partController()->activePart() );
00283 if ( part ) {
00284 KURL url = part->url();
00285 if ( url.isLocalFile() )
00286 return url.path();
00287 }
00288 return QString::null;
00289 }
00290
00291 void PerforcePart::slotActionCommit()
00292 {
00293 commit( currentFile() );
00294 }
00295
00296 void PerforcePart::slotActionUpdate()
00297 {
00298 update( currentFile() );
00299 }
00300 void PerforcePart::slotActionAdd()
00301 {
00302 add( currentFile() );
00303 }
00304
00305 void PerforcePart::slotActionRemove()
00306 {
00307 remove( currentFile() );
00308 }
00309
00310 void PerforcePart::slotActionEdit()
00311 {
00312 edit( currentFile() );
00313 }
00314
00315 void PerforcePart::slotActionRevert()
00316 {
00317 revert( currentFile() );
00318 }
00319
00320 void PerforcePart::slotActionDiff()
00321 {
00322 diff( currentFile() );
00323 }
00324
00325 void PerforcePart::slotCommit()
00326 {
00327 commit( popupfile );
00328 }
00329
00330 void PerforcePart::slotUpdate()
00331 {
00332 update( popupfile );
00333 }
00334
00335 void PerforcePart::slotAdd()
00336 {
00337 add( popupfile );
00338 }
00339
00340 void PerforcePart::slotRemove()
00341 {
00342 remove( popupfile );
00343 }
00344
00345 void PerforcePart::slotEdit()
00346 {
00347 edit( popupfile );
00348 }
00349
00350 void PerforcePart::slotRevert()
00351 {
00352 revert( popupfile );
00353 }
00354
00355 void PerforcePart::slotDiff()
00356 {
00357 diff( popupfile );
00358 }
00359
00360 #include "perforcepart.moc"