diff --git a/vcs/vcspluginhelper.cpp b/vcs/vcspluginhelper.cpp index 0655d64699..0c6699f3ac 100644 --- a/vcs/vcspluginhelper.cpp +++ b/vcs/vcspluginhelper.cpp @@ -1,489 +1,506 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * Copyright 2010 Aleix Pol Gonzalez * * * * 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 "vcspluginhelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_KOMPARE #include #endif #include #include #include #include "vcsstatusinfo.h" #include #include #include #include "widgets/vcsdiffpatchsources.h" #include "widgets/flexibleaction.h" #include #include "vcsevent.h" #include #include #include #include #include namespace KDevelop { struct VcsPluginHelper::VcsPluginHelperPrivate { IPlugin * plugin; IBasicVersionControl * vcs; KUrl::List ctxUrls; KAction * commitAction; KAction * addAction; KAction * updateAction; KAction * historyAction; KAction * annotationAction; KAction * diffToBaseAction; KAction * revertAction; KAction * diffForRevAction; + KAction * diffForRevGlobalAction; QPointer modificationTimer; void createActions(QObject * parent) { commitAction = new KAction(KIcon("svn-commit"), i18n("Commit..."), parent); updateAction = new KAction(KIcon("svn-update"), i18n("Update"), parent); addAction = new KAction(KIcon("list-add"), i18n("Add"), parent); diffToBaseAction = new KAction(KIcon("vcs_diff"), i18n("Show Differences..."), parent); revertAction = new KAction(KIcon("archive-remove"), i18n("Revert"), parent); historyAction = new KAction(KIcon("view-history"), i18n("History..."), parent); annotationAction = new KAction(KIcon("user-properties"), i18n("Annotation..."), parent); diffForRevAction = new KAction(KIcon("vcs_diff"), i18n("Show Diff..."), parent); + diffForRevGlobalAction = new KAction(KIcon("vcs_diff"), i18n("Show Diff (all files)..."), parent); connect(commitAction, SIGNAL(triggered()), parent, SLOT(commit())); connect(addAction, SIGNAL(triggered()), parent, SLOT(add())); connect(updateAction, SIGNAL(triggered()), parent, SLOT(update())); connect(diffToBaseAction, SIGNAL(triggered()), parent, SLOT(diffToBase())); connect(revertAction, SIGNAL(triggered()), parent, SLOT(revert())); connect(historyAction, SIGNAL(triggered()), parent, SLOT(history())); connect(annotationAction, SIGNAL(triggered()), parent, SLOT(annotation())); connect(diffForRevAction, SIGNAL(triggered()), parent, SLOT(diffForRev())); + connect(diffForRevGlobalAction, SIGNAL(triggered()), parent, SLOT(diffForRevGlobal())); } bool allLocalFiles(const KUrl::List& urls) { bool ret=true; foreach(const KUrl& url, urls) { QFileInfo info(url.toLocalFile()); ret &= info.isFile(); } return ret; } QMenu* createMenu() { bool allVersioned=true; foreach(const KUrl& url, ctxUrls) { allVersioned=allVersioned && vcs->isVersionControlled(url); if(!allVersioned) break; } QMenu* menu=new QMenu(vcs->name()); menu->setIcon(KIcon(ICore::self()->pluginController()->pluginInfo(plugin).icon())); menu->addAction(commitAction); menu->addAction(updateAction); menu->addSeparator(); menu->addAction(addAction); menu->addAction(revertAction); menu->addSeparator(); menu->addAction(historyAction); menu->addAction(annotationAction); menu->addAction(diffToBaseAction); addAction->setEnabled(!allVersioned); const bool singleVersionedFile = ctxUrls.count() == 1 && allVersioned; historyAction->setEnabled(singleVersionedFile); annotationAction->setEnabled(singleVersionedFile && allLocalFiles(ctxUrls)); diffToBaseAction->setEnabled(singleVersionedFile); commitAction->setEnabled(singleVersionedFile); return menu; } }; VcsPluginHelper::VcsPluginHelper(KDevelop::IPlugin* parent, KDevelop::IBasicVersionControl* vcs) : QObject(parent) , d(new VcsPluginHelperPrivate()) { Q_ASSERT(vcs); Q_ASSERT(parent); d->plugin = parent; d->vcs = vcs; d->createActions(this); } VcsPluginHelper::~VcsPluginHelper() {} void VcsPluginHelper::setupFromContext(Context* context) { d->ctxUrls.clear(); { KDevelop::ProjectItemContext* prjctx = dynamic_cast(context); if (prjctx) { foreach(KDevelop::ProjectBaseItem* item, prjctx->items()) { if(!item->target()) d->ctxUrls.append(item->url()); } } } { KDevelop::EditorContext* editctx = dynamic_cast(context); if (editctx) { d->ctxUrls.append(editctx->url()); } } { KDevelop::FileContext* filectx = dynamic_cast(context); if (filectx) { d->ctxUrls = filectx->urls(); } } } KUrl::List const & VcsPluginHelper::contextUrlList() { return d->ctxUrls; } QMenu* VcsPluginHelper::commonActions() { /* TODO: the following logic to determine which actions need to be enabled * or disabled does not work properly. What needs to be implemented is that * project items that are vc-controlled enable all except add, project * items that are not vc-controlled enable add action. For urls that cannot * be made into a project item, or if the project has no associated VC * plugin we need to check whether a VC controls the parent dir, if we have * one we assume the urls can be added but are not currently controlled. If * the url is already version controlled then just enable all except add */ return d->createMenu(); } #define EXECUTE_VCS_METHOD( method ) \ d->plugin->core()->runController()->registerJob( d->vcs-> method ( d->ctxUrls ) ) #define SINGLEURL_SETUP_VARS \ KDevelop::IBasicVersionControl* iface = d->vcs;\ const KUrl & url = d->ctxUrls.front(); void VcsPluginHelper::revert() { VcsJob* job=d->vcs->revert(d->ctxUrls); connect(job, SIGNAL(finished(KJob*)), SLOT(revertDone(KJob*))); foreach(const KUrl& url, d->ctxUrls) { IDocument* doc=ICore::self()->documentController()->documentForUrl(url); if(doc) { KTextEditor::ModificationInterface* modif=dynamic_cast(doc->textDocument()); modif->setModifiedOnDiskWarning(false); doc->textDocument()->setModified(false); } } job->setProperty("urls", d->ctxUrls); d->plugin->core()->runController()->registerJob(job); } void VcsPluginHelper::revertDone(KJob* job) { d->modificationTimer = new QTimer; d->modificationTimer->setInterval(100); connect(d->modificationTimer, SIGNAL(timeout()), SLOT(delayedModificationWarningOn())); d->modificationTimer->setProperty("urls", job->property("urls")); d->modificationTimer->start(); } void VcsPluginHelper::delayedModificationWarningOn() { KUrl::List urls = d->modificationTimer->property("urls").value(); foreach(const KUrl& url, urls) { IDocument* doc=ICore::self()->documentController()->documentForUrl(url); if(doc) { doc->reload(); KTextEditor::ModificationInterface* modif=dynamic_cast(doc->textDocument()); modif->setModifiedOnDiskWarning(true); } } d->modificationTimer->deleteLater(); } void VcsPluginHelper::diffJobFinished(KJob* job) { KDevelop::VcsJob* vcsjob = qobject_cast(job); Q_ASSERT(vcsjob); if (vcsjob->status() == KDevelop::VcsJob::JobSucceeded) { KDevelop::VcsDiff d = vcsjob->fetchResults().value(); if(d.isEmpty()) KMessageBox::information(ICore::self()->uiController()->activeMainWindow(), i18n("There are no differences."), i18n("VCS support")); else { VCSDiffPatchSource* patch=new VCSDiffPatchSource(d); showVcsDiff(patch); } } else { KMessageBox::error(ICore::self()->uiController()->activeMainWindow(), vcsjob->errorString(), i18n("Unable to get difference.")); } } void VcsPluginHelper::diffToBase() { SINGLEURL_SETUP_VARS ICore::self()->documentController()->saveAllDocuments(); VCSDiffPatchSource* patch =new VCSDiffPatchSource(new VCSStandardDiffUpdater(iface, url)); showVcsDiff(patch); } void VcsPluginHelper::diffForRev() { QAction* action = qobject_cast( sender() ); Q_ASSERT(action); Q_ASSERT(action->data().canConvert()); VcsRevision rev = action->data().value(); SINGLEURL_SETUP_VARS ICore::self()->documentController()->saveAllDocuments(); VcsRevision prev = KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Previous); KDevelop::VcsJob* job = iface->diff(url, prev, rev ); connect(job, SIGNAL(finished(KJob*)), this, SLOT(diffJobFinished(KJob*))); d->plugin->core()->runController()->registerJob(job); } +void VcsPluginHelper::diffForRevGlobal() +{ + for(int a = 0; a < d->ctxUrls.size(); ++a) + { + KUrl& url(d->ctxUrls[a]); + IProject* project = ICore::self()->projectController()->findProjectForUrl( url ); + if( project ) + url = project->folder(); + } + diffForRev(); +} + void VcsPluginHelper::history(const VcsRevision& rev) { SINGLEURL_SETUP_VARS KDevelop::VcsJob *job = iface->log(url, rev, VcsRevision::createSpecialRevision( VcsRevision::Start )); KDialog* dlg = new KDialog(); dlg->setButtons(KDialog::Close); dlg->setCaption(i18n("%2 History (%1)", url.pathOrUrl(), iface->name())); KDevelop::VcsEventWidget* logWidget = new KDevelop::VcsEventWidget(url, job, dlg); dlg->setMainWidget(logWidget); dlg->show(); connect( dlg, SIGNAL(closeClicked()), job, SLOT(kill()) ); connect( dlg, SIGNAL(closeClicked()), dlg, SLOT(deleteLater()) ); } void VcsPluginHelper::annotation() { SINGLEURL_SETUP_VARS KDevelop::IDocument* doc = ICore::self()->documentController()->documentForUrl(url); if (!doc) doc = ICore::self()->documentController()->openDocument(url); if (doc && doc->textDocument()) { KDevelop::VcsJob* job = iface->annotate(url); if( !job ) { kWarning() << "Couldn't create annotate job for:" << url << "with iface:" << iface << dynamic_cast( iface ); return; } KTextEditor::AnnotationInterface* annotateiface = qobject_cast(doc->textDocument()); KTextEditor::AnnotationViewInterface* viewiface = qobject_cast(doc->textDocument()->activeView()); if (annotateiface && viewiface) { KDevelop::VcsAnnotationModel* model = new KDevelop::VcsAnnotationModel(job, url, doc->textDocument()); annotateiface->setAnnotationModel(model); viewiface->setAnnotationBorderVisible(true); connect(doc->textDocument()->activeView(), SIGNAL(annotationContextMenuAboutToShow(KTextEditor::View*,QMenu*,int)), this, SLOT(annotationContextMenuAboutToShow(KTextEditor::View*,QMenu*,int))); } else { KMessageBox::error(0, i18n("Cannot display annotations, missing interface KTextEditor::AnnotationInterface for the editor.")); delete job; } } else { KMessageBox::error(0, i18n("Cannot execute annotate action because the " "document was not found, or was not a text document:\n%1", url.pathOrUrl())); } } class CopyFunction : public AbstractFunction { public: CopyFunction(const QString& tocopy) : m_tocopy(tocopy) {} void operator()() { QApplication::clipboard()->setText(m_tocopy); } private: QString m_tocopy; }; class HistoryFunction : public AbstractFunction { public: HistoryFunction(VcsPluginHelper* helper, const VcsRevision& rev) : m_helper(helper), m_rev(rev) {} void operator()() { m_helper->history(m_rev); } private: VcsPluginHelper* m_helper; VcsRevision m_rev; }; void VcsPluginHelper::annotationContextMenuAboutToShow( KTextEditor::View* view, QMenu* menu, int line ) { KTextEditor::AnnotationInterface* annotateiface = qobject_cast(view->document()); VcsAnnotationModel* model = qobject_cast( annotateiface->annotationModel() ); Q_ASSERT(model); VcsRevision rev = model->revisionForLine(line); d->diffForRevAction->setData(QVariant::fromValue(rev)); + d->diffForRevGlobalAction->setData(QVariant::fromValue(rev)); menu->addSeparator(); menu->addAction(d->diffForRevAction); - menu->addAction(new FlexibleAction(KIcon("edit-copy"), i18n("Copy revision"), new CopyFunction(rev.revisionValue().toString()), menu)); - menu->addAction(new FlexibleAction(KIcon("view-history"), i18n("Revision history..."), new HistoryFunction(this, rev), menu)); + menu->addAction(d->diffForRevGlobalAction); + menu->addAction(new FlexibleAction(KIcon("edit-copy"), i18n("Copy Revision"), new CopyFunction(rev.revisionValue().toString()), menu)); + menu->addAction(new FlexibleAction(KIcon("view-history"), i18n("Revision History..."), new HistoryFunction(this, rev), menu)); } void VcsPluginHelper::update() { EXECUTE_VCS_METHOD(update); } void VcsPluginHelper::add() { EXECUTE_VCS_METHOD(add); } void VcsPluginHelper::commit() { Q_ASSERT(!d->ctxUrls.isEmpty()); ICore::self()->documentController()->saveAllDocuments(); KUrl url = d->ctxUrls.first(); // We start the commit UI no matter whether there is real differences, as it can also be used to commit untracked files VCSCommitDiffPatchSource* patchSource = new VCSCommitDiffPatchSource(new VCSStandardDiffUpdater(d->vcs, url), url, d->vcs); bool ret = showVcsDiff(patchSource); if(!ret) { VcsCommitDialog *commitDialog = new VcsCommitDialog(patchSource); commitDialog->setCommitCandidates(patchSource->infos()); commitDialog->exec(); } else { connect(patchSource, SIGNAL(reviewFinished(QString,QList)), this, SLOT(commitReviewed(QString))); connect(patchSource, SIGNAL(reviewCancelled(QString)), this, SLOT(commitReviewed(QString))); } } void VcsPluginHelper::commitReviewed(QString message) { addOldCommitMessage(message); } QStringList retrieveOldCommitMessages() { KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); return vcsGroup.readEntry("OldCommitMessages", QStringList()); } namespace { int maxMessages = 10; } void addOldCommitMessage(QString message) { if(ICore::self()->shuttingDown()) return; QStringList oldMessages = retrieveOldCommitMessages(); if(oldMessages.contains(message)) oldMessages.removeAll(message); oldMessages.push_front(message); while(oldMessages.size() > maxMessages) oldMessages.pop_back(); KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); vcsGroup.writeEntry("OldCommitMessages", oldMessages); } } #include "vcspluginhelper.moc" diff --git a/vcs/vcspluginhelper.h b/vcs/vcspluginhelper.h index d29e6db433..d94a0e490a 100644 --- a/vcs/vcspluginhelper.h +++ b/vcs/vcspluginhelper.h @@ -1,77 +1,78 @@ /*************************************************************************** * Copyright 2008 Andreas Pakulat * * * * 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. * * * ***************************************************************************/ #ifndef VCSPLUGINHELPER_H #define VCSPLUGINHELPER_H #include "vcsexport.h" #include #include #include "vcsrevision.h" class KJob; class QAction; class QActionGroup; class QMenu; namespace KTextEditor { class View; } namespace KDevelop { class VcsDiff; class IPlugin; class IBasicVersionControl; class Context; class ContextMenuExtension; class VcsCommitDialog; class ProjectBaseItem; class KDEVPLATFORMVCS_EXPORT VcsPluginHelper : public QObject { Q_OBJECT public: VcsPluginHelper(IPlugin * parent, IBasicVersionControl * vcs); virtual ~VcsPluginHelper(); void setupFromContext(KDevelop::Context*); KUrl::List const & contextUrlList(); QMenu* commonActions(); public Q_SLOTS: void commit(); void add(); void revert(); void history(const VcsRevision& rev = VcsRevision::createSpecialRevision( VcsRevision::Base )); void annotation(); void annotationContextMenuAboutToShow( KTextEditor::View* view, QMenu* menu, int line); void diffToBase(); void diffForRev(); + void diffForRevGlobal(); void update(); void diffJobFinished(KJob* job); void commitReviewed(QString); void revertDone(KJob* job); void delayedModificationWarningOn(); private: struct VcsPluginHelperPrivate; std::auto_ptr d; }; QStringList retrieveOldCommitMessages(); void addOldCommitMessage(QString); } // namespace KDevelop #endif