From: Peter Penz Date: Mon, 27 Jul 2009 05:31:48 +0000 (+0000) Subject: Improved Subversion test plugin to allow committing, updating, diffing, adding and... X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/commitdiff_plain/66ad27aba14cdfea777b9ebe1beb161f80f26463 Improved Subversion test plugin to allow committing, updating, diffing, adding and removing of files. As soon as the test plugin gets moved to kdesdk, the code should be improved to use the libsvn interface. svn path=/trunk/KDE/kdebase/apps/; revision=1002839 --- diff --git a/src/dolphinfileitemdelegate.cpp b/src/dolphinfileitemdelegate.cpp index 367435ced..ddd4435a7 100644 --- a/src/dolphinfileitemdelegate.cpp +++ b/src/dolphinfileitemdelegate.cpp @@ -33,7 +33,9 @@ DolphinFileItemDelegate::DolphinFileItemDelegate(QObject* parent) : KFileItemDelegate(parent), - m_hasMinimizedNameColumn(false) + m_hasMinimizedNameColumn(false), + m_cachedSize(), + m_cachedEmblems() { } @@ -65,12 +67,10 @@ void DolphinFileItemDelegate::paint(QPainter* painter, const QVariant data = dolphinModel->data(revisionIndex, Qt::DecorationRole); const RevisionControlPlugin::RevisionState state = static_cast(data.toInt()); - if (state != RevisionControlPlugin::LocalRevision) { - // TODO: extend KFileItemDelegate to be able to get the icon boundaries - const QRect iconRect(option.rect.x(), option.rect.y(), - KIconLoader::SizeSmall, KIconLoader::SizeSmall); - const QPixmap emblem = emblemForState(state, iconRect.size()); - painter->drawPixmap(iconRect.x(), iconRect.y(), emblem); + if (state != RevisionControlPlugin::UnversionedRevision) { + const QRect rect = iconRect(option, index); + const QPixmap emblem = emblemForState(state, rect.size()); + painter->drawPixmap(rect.x(), rect.y() + rect.height() - emblem.height(), emblem); } } } @@ -105,23 +105,42 @@ void DolphinFileItemDelegate::adjustOptionWidth(QStyleOptionViewItemV4& option, } } -QPixmap DolphinFileItemDelegate::emblemForState(RevisionControlPlugin::RevisionState state, const QSize& size) +QPixmap DolphinFileItemDelegate::emblemForState(RevisionControlPlugin::RevisionState state, const QSize& size) const { - // TODO #1: all icons that are use here will be replaced by revision control emblems provided by the + // TODO: all icons that are use here will be replaced by revision control emblems provided by the // Oxygen team before KDE 4.4 - // TODO #2: cache the icons - switch (state) { - case RevisionControlPlugin::LatestRevision: - return KIcon("dialog-ok-apply").pixmap(size); - case RevisionControlPlugin::ConflictingRevision: - return KIcon("application-exit").pixmap(size); - case RevisionControlPlugin::UpdateRequiredRevision: - return KIcon("rating").pixmap(size); - case RevisionControlPlugin::EditingRevision: - return KIcon("emblem-important").pixmap(size); - default: - break; + Q_ASSERT(state <= RevisionControlPlugin::ConflictingRevision); + if ((m_cachedSize != size) || !m_cachedEmblems[state].isNull()) { + m_cachedSize = size; + + const int iconHeight = size.height(); + int emblemHeight = KIconLoader::SizeSmall; + if (iconHeight >= KIconLoader::SizeEnormous) { + emblemHeight = KIconLoader::SizeMedium; + } else if (iconHeight >= KIconLoader::SizeLarge) { + emblemHeight = KIconLoader::SizeSmallMedium; + } else if (iconHeight >= KIconLoader::SizeMedium) { + emblemHeight = KIconLoader::SizeSmall; + } else { + // TODO: it depends on the final icons whether a smaller size works + emblemHeight = KIconLoader::SizeSmall /* / 2 */; + } + + const QSize emblemSize(emblemHeight, emblemHeight); + for (int i = 0; i <= RevisionControlPlugin::ConflictingRevision; ++i) { + QString iconName; + switch (state) { + case RevisionControlPlugin::NormalRevision: iconName = "dialog-ok-apply"; break; + case RevisionControlPlugin::UpdateRequiredRevision: iconName = "rating"; break; + case RevisionControlPlugin::LocallyModifiedRevision: iconName = "emblem-important"; break; + case RevisionControlPlugin::AddedRevision: iconName = "list-add"; break; + case RevisionControlPlugin::ConflictingRevision: iconName = "application-exit"; break; + default: Q_ASSERT(false); break; + } + + m_cachedEmblems[i] = KIcon(iconName).pixmap(emblemSize); + } } - return QPixmap(); + return m_cachedEmblems[state]; } diff --git a/src/dolphinfileitemdelegate.h b/src/dolphinfileitemdelegate.h index 2f8d3c35f..bd6bfdf07 100644 --- a/src/dolphinfileitemdelegate.h +++ b/src/dolphinfileitemdelegate.h @@ -66,10 +66,12 @@ private: const DolphinModel* dolphinModel, const QModelIndex& index); - static QPixmap emblemForState(RevisionControlPlugin::RevisionState state, const QSize& size); + QPixmap emblemForState(RevisionControlPlugin::RevisionState state, const QSize& size) const; private: bool m_hasMinimizedNameColumn; + mutable QSize m_cachedSize; + mutable QPixmap m_cachedEmblems[RevisionControlPlugin::ConflictingRevision + 1]; }; inline void DolphinFileItemDelegate::setMinimizedNameColumn(bool minimized) diff --git a/src/dolphinmodel.cpp b/src/dolphinmodel.cpp index 542d550ae..207b5a40f 100644 --- a/src/dolphinmodel.cpp +++ b/src/dolphinmodel.cpp @@ -69,7 +69,7 @@ bool DolphinModel::setData(const QModelIndex& index, const QVariant& value, int const QPersistentModelIndex key = index; const RevisionControlPlugin::RevisionState state = static_cast(value.toInt()); - if (m_revisionHash.value(key, RevisionControlPlugin::LocalRevision) != state) { + if (m_revisionHash.value(key, RevisionControlPlugin::UnversionedRevision) != state) { if (!m_hasRevisionData) { connect(this, SIGNAL(rowsRemoved (const QModelIndex&, int, int)), this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); @@ -96,22 +96,22 @@ QVariant DolphinModel::data(const QModelIndex& index, int role) const case Qt::DecorationRole: if (index.column() == DolphinModel::Revision) { - return m_revisionHash.value(index, RevisionControlPlugin::LocalRevision); + return m_revisionHash.value(index, RevisionControlPlugin::UnversionedRevision); } break; case Qt::DisplayRole: if (index.column() == DolphinModel::Revision) { - switch (m_revisionHash.value(index, RevisionControlPlugin::LocalRevision)) { - case RevisionControlPlugin::LatestRevision: - return i18nc("@item::intable", "Latest"); - case RevisionControlPlugin::EditingRevision: - return i18nc("@item::intable", "Editing"); + switch (m_revisionHash.value(index, RevisionControlPlugin::UnversionedRevision)) { + case RevisionControlPlugin::NormalRevision: + return i18nc("@item::intable", "Normal"); + case RevisionControlPlugin::LocallyModifiedRevision: + return i18nc("@item::intable", "Locally modified"); case RevisionControlPlugin::UpdateRequiredRevision: return i18nc("@item::intable", "Update required"); - case RevisionControlPlugin::LocalRevision: + case RevisionControlPlugin::UnversionedRevision: default: - return i18nc("@item::intable", "Local"); + return i18nc("@item::intable", "Unversioned"); } } break; @@ -141,6 +141,12 @@ int DolphinModel::columnCount(const QModelIndex& parent) const return KDirModel::columnCount(parent) + (ExtraColumnCount - ColumnCount); } +void DolphinModel::clearRevisionData() +{ + m_revisionHash.clear(); + m_hasRevisionData = false; +} + bool DolphinModel::hasRevisionData() const { return m_hasRevisionData; @@ -148,11 +154,11 @@ bool DolphinModel::hasRevisionData() const void DolphinModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) { - Q_ASSERT(hasRevisionData()); - - const int column = parent.column(); - for (int row = start; row <= end; ++row) { - m_revisionHash.remove(parent.child(row, column)); + if (m_hasRevisionData) { + const int column = parent.column(); + for (int row = start; row <= end; ++row) { + m_revisionHash.remove(parent.child(row, column)); + } } } diff --git a/src/dolphinmodel.h b/src/dolphinmodel.h index 9ae3d5f7c..1fd96c5f6 100644 --- a/src/dolphinmodel.h +++ b/src/dolphinmodel.h @@ -46,6 +46,7 @@ public: virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + void clearRevisionData(); bool hasRevisionData() const; private slots: diff --git a/src/dolphinview.cpp b/src/dolphinview.cpp index a12e7a991..2fc664f21 100644 --- a/src/dolphinview.cpp +++ b/src/dolphinview.cpp @@ -522,6 +522,10 @@ void DolphinView::updateView(const KUrl& url, const KUrl& rootUrl) loadDirectory(url); } + // When changing the URL there is no need to keep the revision + // data of the previous URL. + m_dolphinModel->clearRevisionData(); + emit startedPathLoading(url); } diff --git a/src/revisioncontrolobserver.cpp b/src/revisioncontrolobserver.cpp index f3bf08ce4..1e7347a9b 100644 --- a/src/revisioncontrolobserver.cpp +++ b/src/revisioncontrolobserver.cpp @@ -26,6 +26,7 @@ #include #include +#include #include /** @@ -36,7 +37,7 @@ class UpdateItemStatesThread : public QThread { public: - UpdateItemStatesThread(QObject* parent); + UpdateItemStatesThread(QObject* parent, QMutex* pluginMutex); void setData(RevisionControlPlugin* plugin, const QList& itemStates); QList itemStates() const; @@ -46,11 +47,13 @@ protected: private: RevisionControlPlugin* m_plugin; + QMutex* m_pluginMutex; QList m_itemStates; }; -UpdateItemStatesThread::UpdateItemStatesThread(QObject* parent) : - QThread(parent) +UpdateItemStatesThread::UpdateItemStatesThread(QObject* parent, QMutex* pluginMutex) : + QThread(parent), + m_pluginMutex(pluginMutex) { } @@ -68,7 +71,8 @@ void UpdateItemStatesThread::run() // it is assumed that all items have the same parent directory const QString directory = m_itemStates.first().item.url().directory(KUrl::AppendTrailingSlash); - + + QMutexLocker locker(m_pluginMutex); if (m_plugin->beginRetrieval(directory)) { const int count = m_itemStates.count(); for (int i = 0; i < count; ++i) { @@ -83,7 +87,7 @@ QList UpdateItemStatesThread::itemStates() c return m_itemStates; } -// --- +// ------------------------------------------------------------------------------------------------ RevisionControlObserver::RevisionControlObserver(QAbstractItemView* view) : QObject(view), @@ -93,6 +97,7 @@ RevisionControlObserver::RevisionControlObserver(QAbstractItemView* view) : m_dirLister(0), m_dolphinModel(0), m_dirVerificationTimer(0), + m_pluginMutex(QMutex::Recursive), m_plugin(0), m_updateItemStatesThread(0) { @@ -100,8 +105,8 @@ RevisionControlObserver::RevisionControlObserver(QAbstractItemView* view) : QAbstractProxyModel* proxyModel = qobject_cast(view->model()); m_dolphinModel = (proxyModel == 0) ? - qobject_cast(view->model()) : - qobject_cast(proxyModel->sourceModel()); + qobject_cast(view->model()) : + qobject_cast(proxyModel->sourceModel()); if (m_dolphinModel != 0) { m_dirLister = m_dolphinModel->dirLister(); connect(m_dirLister, SIGNAL(completed()), @@ -129,6 +134,7 @@ RevisionControlObserver::~RevisionControlObserver() QList RevisionControlObserver::contextMenuActions(const KFileItemList& items) const { if (m_dolphinModel->hasRevisionData() && (m_plugin != 0)) { + QMutexLocker locker(&m_pluginMutex); return m_plugin->contextMenuActions(items); } return QList(); @@ -137,6 +143,7 @@ QList RevisionControlObserver::contextMenuActions(const KFileItemList& QList RevisionControlObserver::contextMenuActions(const QString& directory) const { if (m_dolphinModel->hasRevisionData() && (m_plugin != 0)) { + QMutexLocker locker(&m_pluginMutex); return m_plugin->contextMenuActions(directory); } @@ -162,28 +169,44 @@ void RevisionControlObserver::verifyDirectory() revisionControlUrl.addPath(m_plugin->fileName()); const KFileItem item = m_dirLister->findByUrl(revisionControlUrl); - if (item.isNull() && m_revisionedDirectory) { - // The directory is not versioned. Reset the verification timer to a higher - // value, so that browsing through non-versioned directories is not slown down - // by an immediate verification. - m_dirVerificationTimer->setInterval(500); - m_revisionedDirectory = false; - disconnect(m_dirLister, SIGNAL(refreshItems(const QList>&)), - this, SLOT(delayedDirectoryVerification())); - disconnect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), - this, SLOT(delayedDirectoryVerification())); - } else if (!item.isNull()) { + + bool foundRevisionInfo = !item.isNull(); + if (!foundRevisionInfo && m_revisionedDirectory) { + // Revision control systems like Git provide the revision information + // file only in the root directory. Check whether the revision information file can + // be found in one of the parent directories. + + // TODO... + } + + if (foundRevisionInfo) { if (!m_revisionedDirectory) { + m_revisionedDirectory = true; + // The directory is versioned. Assume that the user will further browse through // versioned directories and decrease the verification timer. m_dirVerificationTimer->setInterval(100); - m_revisionedDirectory = true; connect(m_dirLister, SIGNAL(refreshItems(const QList>&)), this, SLOT(delayedDirectoryVerification())); connect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), this, SLOT(delayedDirectoryVerification())); + connect(m_plugin, SIGNAL(revisionStatesChanged(const QString&)), + this, SLOT(delayedDirectoryVerification())); } updateItemStates(); + } else if (m_revisionedDirectory) { + m_revisionedDirectory = false; + + // The directory is not versioned. Reset the verification timer to a higher + // value, so that browsing through non-versioned directories is not slown down + // by an immediate verification. + m_dirVerificationTimer->setInterval(500); + disconnect(m_dirLister, SIGNAL(refreshItems(const QList>&)), + this, SLOT(delayedDirectoryVerification())); + disconnect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), + this, SLOT(delayedDirectoryVerification())); + disconnect(m_plugin, SIGNAL(revisionStatesChanged(const QString&)), + this, SLOT(delayedDirectoryVerification())); } } @@ -216,7 +239,7 @@ void RevisionControlObserver::updateItemStates() { Q_ASSERT(m_plugin != 0); if (m_updateItemStatesThread == 0) { - m_updateItemStatesThread = new UpdateItemStatesThread(this); + m_updateItemStatesThread = new UpdateItemStatesThread(this, &m_pluginMutex); connect(m_updateItemStatesThread, SIGNAL(finished()), this, SLOT(applyUpdatedItemStates())); } @@ -238,8 +261,8 @@ void RevisionControlObserver::updateItemStates() ItemState itemState; itemState.index = index; itemState.item = m_dolphinModel->itemForIndex(index); - itemState.revision = RevisionControlPlugin::LocalRevision; - + itemState.revision = RevisionControlPlugin::UnversionedRevision; + itemStates.append(itemState); } diff --git a/src/revisioncontrolobserver.h b/src/revisioncontrolobserver.h index 27c7a27a0..ae19e219f 100644 --- a/src/revisioncontrolobserver.h +++ b/src/revisioncontrolobserver.h @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,7 @@ private: QTimer* m_dirVerificationTimer; + mutable QMutex m_pluginMutex; RevisionControlPlugin* m_plugin; UpdateItemStatesThread* m_updateItemStatesThread; diff --git a/src/revisioncontrolplugin.cpp b/src/revisioncontrolplugin.cpp index b3b407c61..1e19ddcf0 100644 --- a/src/revisioncontrolplugin.cpp +++ b/src/revisioncontrolplugin.cpp @@ -19,14 +19,6 @@ #include "revisioncontrolplugin.h" -#include -#include -#include -#include -#include -#include -#include - RevisionControlPlugin::RevisionControlPlugin() { } @@ -39,28 +31,59 @@ RevisionControlPlugin::~RevisionControlPlugin() // ---------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + SubversionPlugin::SubversionPlugin() : - m_directory(), + m_retrievalDir(), m_revisionInfoHash(), m_updateAction(0), + m_showLocalChangesAction(0), m_commitAction(0), m_addAction(0), - m_removeAction(0) + m_removeAction(0), + m_contextDir(), + m_contextItems() { m_updateAction = new KAction(this); m_updateAction->setIcon(KIcon("view-refresh")); m_updateAction->setText(i18nc("@item:inmenu", "SVN Update")); + connect(m_updateAction, SIGNAL(triggered()), + this, SLOT(updateFiles())); + + m_showLocalChangesAction = new KAction(this); + m_showLocalChangesAction->setIcon(KIcon("view-split-left-right")); + m_showLocalChangesAction->setText(i18nc("@item:inmenu", "Show Local SVN Changes")); + connect(m_showLocalChangesAction, SIGNAL(triggered()), + this, SLOT(showLocalChanges())); m_commitAction = new KAction(this); m_commitAction->setText(i18nc("@item:inmenu", "SVN Commit...")); + connect(m_commitAction, SIGNAL(triggered()), + this, SLOT(commitFiles())); m_addAction = new KAction(this); m_addAction->setIcon(KIcon("list-add")); m_addAction->setText(i18nc("@item:inmenu", "SVN Add")); + connect(m_addAction, SIGNAL(triggered()), + this, SLOT(addFiles())); m_removeAction = new KAction(this); m_removeAction->setIcon(KIcon("list-remove")); m_removeAction->setText(i18nc("@item:inmenu", "SVN Delete")); + connect(m_removeAction, SIGNAL(triggered()), + this, SLOT(removeFiles())); } SubversionPlugin::~SubversionPlugin() @@ -75,7 +98,7 @@ QString SubversionPlugin::fileName() const bool SubversionPlugin::beginRetrieval(const QString& directory) { Q_ASSERT(directory.endsWith('/')); - m_directory = directory; + m_retrievalDir = directory; const QString path = directory + ".svn/text-base/"; QDir dir(path); @@ -83,15 +106,16 @@ bool SubversionPlugin::beginRetrieval(const QString& directory) const int size = fileInfoList.size(); QString fileName; for (int i = 0; i < size; ++i) { - fileName = fileInfoList.at(i).fileName(); + const QFileInfo fileInfo = fileInfoList.at(i); + fileName = fileInfo.fileName(); // Remove the ".svn-base" postfix to be able to compare the filenames // in a fast way in SubversionPlugin::revisionState(). fileName.chop(sizeof(".svn-base") / sizeof(char) - 1); if (!fileName.isEmpty()) { RevisionInfo info; - info.size = fileInfoList.at(i).size(); - info.timeStamp = fileInfoList.at(i).lastModified(); - m_revisionInfoHash.insert(fileName, info); + info.size = fileInfo.size(); + info.timeStamp = fileInfo.lastModified(); + m_revisionInfoHash.insert(directory + fileName, info); } } return size > 0; @@ -103,37 +127,63 @@ void SubversionPlugin::endRetrieval() RevisionControlPlugin::RevisionState SubversionPlugin::revisionState(const KFileItem& item) { - const QString name = item.name(); + const QString itemUrl = item.localPath(); if (item.isDir()) { - QFile file(m_directory + name + "/.svn"); + QFile file(itemUrl + "/.svn"); if (file.open(QIODevice::ReadOnly)) { file.close(); - // TODO... - return RevisionControlPlugin::LatestRevision; + return RevisionControlPlugin::NormalRevision; } - } else if (m_revisionInfoHash.contains(name)) { - const RevisionInfo info = m_revisionInfoHash.value(item.name()); + } else if (m_revisionInfoHash.contains(itemUrl)) { + const RevisionInfo info = m_revisionInfoHash.value(itemUrl); const QDateTime localTimeStamp = item.time(KFileItem::ModificationTime).dateTime(); const QDateTime versionedTimeStamp = info.timeStamp; if (localTimeStamp > versionedTimeStamp) { if ((info.size != item.size()) || !equalRevisionContent(item.name())) { - return RevisionControlPlugin::EditingRevision; + return RevisionControlPlugin::LocallyModifiedRevision; } } else if (localTimeStamp < versionedTimeStamp) { if ((info.size != item.size()) || !equalRevisionContent(item.name())) { return RevisionControlPlugin::UpdateRequiredRevision; } } - return RevisionControlPlugin::LatestRevision; + return RevisionControlPlugin::NormalRevision; } - return RevisionControlPlugin::LocalRevision; + return RevisionControlPlugin::UnversionedRevision; } -QList SubversionPlugin::contextMenuActions(const KFileItemList& items) const +QList SubversionPlugin::contextMenuActions(const KFileItemList& items) { - Q_UNUSED(items); + Q_ASSERT(!items.isEmpty()); + + m_contextItems = items; + m_contextDir.clear(); + + // iterate all items and check the revision state to know which + // actions can be enabled + const int itemsCount = items.count(); + int revisionedCount = 0; + int editingCount = 0; + foreach (const KFileItem& item, items) { + const RevisionState state = revisionState(item); + if (state != UnversionedRevision) { + ++revisionedCount; + } + + switch (state) { + case LocallyModifiedRevision: + case ConflictingRevision: + ++editingCount; + break; + default: + break; + } + } + m_commitAction->setEnabled(editingCount > 0); + m_addAction->setEnabled(revisionedCount == 0); + m_removeAction->setEnabled(revisionedCount == itemsCount); QList actions; actions.append(m_updateAction); @@ -143,24 +193,90 @@ QList SubversionPlugin::contextMenuActions(const KFileItemList& items) return actions; } -QList SubversionPlugin::contextMenuActions(const QString& directory) const +QList SubversionPlugin::contextMenuActions(const QString& directory) { - Q_UNUSED(directory); + m_contextDir = directory; + m_contextItems.clear(); QList actions; actions.append(m_updateAction); + actions.append(m_showLocalChangesAction); actions.append(m_commitAction); return actions; } +void SubversionPlugin::updateFiles() +{ + execSvnCommand("update"); +} + +void SubversionPlugin::showLocalChanges() +{ + Q_ASSERT(!m_contextDir.isEmpty()); + Q_ASSERT(m_contextItems.isEmpty()); + + const QString command = "mkfifo /tmp/fifo; svn diff " + + KShell::quoteArg(m_contextDir) + + " > /tmp/fifo & kompare /tmp/fifo; rm /tmp/fifo"; + KRun::runCommand(command, 0); +} + +void SubversionPlugin::commitFiles() +{ + KDialog dialog(0, Qt::Dialog); + + KVBox* box = new KVBox(&dialog); + new QLabel(i18nc("@label", "Description:"), box); + QTextEdit* editor = new QTextEdit(box); + + dialog.setMainWidget(box); + dialog.setCaption(i18nc("@title:window", "SVN Commit")); + dialog.setButtons(KDialog::Ok | KDialog::Cancel); + dialog.setDefaultButton(KDialog::Ok); + dialog.setButtonText(KDialog::Ok, i18nc("@action:button", "Commit")); + + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), + "SvnCommitDialog"); + dialog.restoreDialogSize(dialogConfig); + + if (dialog.exec() == QDialog::Accepted) { + const QString description = editor->toPlainText(); + execSvnCommand("commit -m " + KShell::quoteArg(description)); + } + + dialog.saveDialogSize(dialogConfig, KConfigBase::Persistent); +} + +void SubversionPlugin::addFiles() +{ + execSvnCommand("add"); +} + +void SubversionPlugin::removeFiles() +{ + execSvnCommand("remove"); +} + +void SubversionPlugin::execSvnCommand(const QString& svnCommand) +{ + const QString command = "svn " + svnCommand + ' '; + if (!m_contextDir.isEmpty()) { + KRun::runCommand(command + KShell::quoteArg(m_contextDir), 0); + } else { + foreach (const KFileItem& item, m_contextItems) { + KRun::runCommand(command + KShell::quoteArg(item.localPath()), 0); + } + } +} + bool SubversionPlugin::equalRevisionContent(const QString& name) const { - QFile localFile(m_directory + '/' + name); + QFile localFile(m_retrievalDir + '/' + name); if (!localFile.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } - QFile revisionedFile(m_directory + "/.svn/text-base/" + name + ".svn-base"); + QFile revisionedFile(m_retrievalDir + "/.svn/text-base/" + name + ".svn-base"); if (!revisionedFile.open(QIODevice::ReadOnly | QIODevice::Text)) { return false; } @@ -175,4 +291,3 @@ bool SubversionPlugin::equalRevisionContent(const QString& name) const return localText.atEnd() && revisionedText.atEnd(); } - diff --git a/src/revisioncontrolplugin.h b/src/revisioncontrolplugin.h index bbe66b3c3..7863cfacb 100644 --- a/src/revisioncontrolplugin.h +++ b/src/revisioncontrolplugin.h @@ -34,13 +34,7 @@ class QAction; * @brief Base class for revision control plugins. * * Enables the file manager to show the revision state - * of a revisioned file. The methods - * RevisionControlPlugin::beginRetrieval(), - * RevisionControlPlugin::endRetrieval() and - * RevisionControlPlugin::revisionState() are invoked - * from a separate thread to assure that the GUI thread - * won't be blocked. All other methods are invoked in the - * scope of the GUI thread. + * of a revisioned file. */ class LIBDOLPHINPRIVATE_EXPORT RevisionControlPlugin : public QObject { @@ -49,12 +43,34 @@ class LIBDOLPHINPRIVATE_EXPORT RevisionControlPlugin : public QObject public: enum RevisionState { - LocalRevision, - LatestRevision, + /** The file is not under revision control. */ + UnversionedRevision, + /** + * The file is under revision control and represents + * the latest version. + */ + NormalRevision, + /** + * The file is under revision control and a newer + * version exists on the main branch. + */ UpdateRequiredRevision, - EditingRevision, + /** + * The file is under revision control and has been + * modified locally. + */ + LocallyModifiedRevision, + /** + * The file has not been under revision control but + * has been marked to get added with the next commit. + */ + AddedRevision, + /** + * The file is under revision control and has been locally + * modified. A modification has also been done on the main + * branch. + */ ConflictingRevision - // TODO... }; RevisionControlPlugin(); @@ -97,14 +113,14 @@ public: * If an action triggers a change of the revisions, the signal * RevisionControlPlugin::revisionStatesChanged() must be emitted. */ - virtual QList contextMenuActions(const KFileItemList& items) const = 0; + virtual QList contextMenuActions(const KFileItemList& items) = 0; /** * Returns the list of actions that should be shown in the context menu * for the directory \p directory. If an action triggers a change of the revisions, * the signal RevisionControlPlugin::revisionStatesChanged() must be emitted. */ - virtual QList contextMenuActions(const QString& directory) const = 0; + virtual QList contextMenuActions(const QString& directory) = 0; signals: /** @@ -124,11 +140,13 @@ signals: // TODO: This is just a temporary test class. It will be made available as // plugin outside Dolphin later. -#include +#include #include class LIBDOLPHINPRIVATE_EXPORT SubversionPlugin : public RevisionControlPlugin { + Q_OBJECT + public: SubversionPlugin(); virtual ~SubversionPlugin(); @@ -136,10 +154,23 @@ public: virtual bool beginRetrieval(const QString& directory); virtual void endRetrieval(); virtual RevisionControlPlugin::RevisionState revisionState(const KFileItem& item); - virtual QList contextMenuActions(const KFileItemList& items) const; - virtual QList contextMenuActions(const QString& directory) const; + virtual QList contextMenuActions(const KFileItemList& items); + virtual QList contextMenuActions(const QString& directory); + +private slots: + void updateFiles(); + void showLocalChanges(); + void commitFiles(); + void addFiles(); + void removeFiles(); private: + /** + * Executes the command "svn {svnCommand}" for the files that have been + * set by getting the context menu actions (see contextMenuActions()). + */ + void execSvnCommand(const QString& svnCommand); + /** * Returns true, if the content of the local file \p name is equal to the * content of the revisioned file. @@ -153,13 +184,16 @@ private: QDateTime timeStamp; }; - QString m_directory; + QString m_retrievalDir; QHash m_revisionInfoHash; QAction* m_updateAction; + QAction* m_showLocalChangesAction; QAction* m_commitAction; QAction* m_addAction; QAction* m_removeAction; + mutable QString m_contextDir; + mutable KFileItemList m_contextItems; }; #endif // REVISIONCONTROLPLUGIN_H