mirror of
https://invent.kde.org/system/dolphin
synced 2024-11-05 18:47:12 +00:00
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
This commit is contained in:
parent
47d5003283
commit
66ad27aba1
9 changed files with 313 additions and 107 deletions
|
@ -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<RevisionControlPlugin::RevisionState>(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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -69,7 +69,7 @@ bool DolphinModel::setData(const QModelIndex& index, const QVariant& value, int
|
|||
|
||||
const QPersistentModelIndex key = index;
|
||||
const RevisionControlPlugin::RevisionState state = static_cast<RevisionControlPlugin::RevisionState>(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <QAbstractProxyModel>
|
||||
#include <QAbstractItemView>
|
||||
#include <QMutexLocker>
|
||||
#include <QTimer>
|
||||
|
||||
/**
|
||||
|
@ -36,7 +37,7 @@
|
|||
class UpdateItemStatesThread : public QThread
|
||||
{
|
||||
public:
|
||||
UpdateItemStatesThread(QObject* parent);
|
||||
UpdateItemStatesThread(QObject* parent, QMutex* pluginMutex);
|
||||
void setData(RevisionControlPlugin* plugin,
|
||||
const QList<RevisionControlObserver::ItemState>& itemStates);
|
||||
QList<RevisionControlObserver::ItemState> itemStates() const;
|
||||
|
@ -46,11 +47,13 @@ protected:
|
|||
|
||||
private:
|
||||
RevisionControlPlugin* m_plugin;
|
||||
QMutex* m_pluginMutex;
|
||||
QList<RevisionControlObserver::ItemState> 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<RevisionControlObserver::ItemState> 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<QAbstractProxyModel*>(view->model());
|
||||
m_dolphinModel = (proxyModel == 0) ?
|
||||
qobject_cast<DolphinModel*>(view->model()) :
|
||||
qobject_cast<DolphinModel*>(proxyModel->sourceModel());
|
||||
qobject_cast<DolphinModel*>(view->model()) :
|
||||
qobject_cast<DolphinModel*>(proxyModel->sourceModel());
|
||||
if (m_dolphinModel != 0) {
|
||||
m_dirLister = m_dolphinModel->dirLister();
|
||||
connect(m_dirLister, SIGNAL(completed()),
|
||||
|
@ -129,6 +134,7 @@ RevisionControlObserver::~RevisionControlObserver()
|
|||
QList<QAction*> RevisionControlObserver::contextMenuActions(const KFileItemList& items) const
|
||||
{
|
||||
if (m_dolphinModel->hasRevisionData() && (m_plugin != 0)) {
|
||||
QMutexLocker locker(&m_pluginMutex);
|
||||
return m_plugin->contextMenuActions(items);
|
||||
}
|
||||
return QList<QAction*>();
|
||||
|
@ -137,6 +143,7 @@ QList<QAction*> RevisionControlObserver::contextMenuActions(const KFileItemList&
|
|||
QList<QAction*> 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<QPair<KFileItem,KFileItem>>&)),
|
||||
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<QPair<KFileItem,KFileItem>>&)),
|
||||
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<QPair<KFileItem,KFileItem>>&)),
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <kfileitem.h>
|
||||
#include <revisioncontrolplugin.h>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QString>
|
||||
|
@ -82,6 +83,7 @@ private:
|
|||
|
||||
QTimer* m_dirVerificationTimer;
|
||||
|
||||
mutable QMutex m_pluginMutex;
|
||||
RevisionControlPlugin* m_plugin;
|
||||
UpdateItemStatesThread* m_updateItemStatesThread;
|
||||
|
||||
|
|
|
@ -19,14 +19,6 @@
|
|||
|
||||
#include "revisioncontrolplugin.h"
|
||||
|
||||
#include <kaction.h>
|
||||
#include <kicon.h>
|
||||
#include <klocale.h>
|
||||
#include <kfileitem.h>
|
||||
#include <QDir>
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
RevisionControlPlugin::RevisionControlPlugin()
|
||||
{
|
||||
}
|
||||
|
@ -39,28 +31,59 @@ RevisionControlPlugin::~RevisionControlPlugin()
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include <kaction.h>
|
||||
#include <kdialog.h>
|
||||
#include <kicon.h>
|
||||
#include <klocale.h>
|
||||
#include <krun.h>
|
||||
#include <kshell.h>
|
||||
#include <kfileitem.h>
|
||||
#include <kvbox.h>
|
||||
#include <QDir>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QTextEdit>
|
||||
#include <QTextStream>
|
||||
|
||||
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<QAction*> SubversionPlugin::contextMenuActions(const KFileItemList& items) const
|
||||
QList<QAction*> 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<QAction*> actions;
|
||||
actions.append(m_updateAction);
|
||||
|
@ -143,24 +193,90 @@ QList<QAction*> SubversionPlugin::contextMenuActions(const KFileItemList& items)
|
|||
return actions;
|
||||
}
|
||||
|
||||
QList<QAction*> SubversionPlugin::contextMenuActions(const QString& directory) const
|
||||
QList<QAction*> SubversionPlugin::contextMenuActions(const QString& directory)
|
||||
{
|
||||
Q_UNUSED(directory);
|
||||
m_contextDir = directory;
|
||||
m_contextItems.clear();
|
||||
|
||||
QList<QAction*> 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<QAction*> contextMenuActions(const KFileItemList& items) const = 0;
|
||||
virtual QList<QAction*> 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<QAction*> contextMenuActions(const QString& directory) const = 0;
|
||||
virtual QList<QAction*> 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 <QFileInfoList>
|
||||
#include <kfileitem.h>
|
||||
#include <QHash>
|
||||
|
||||
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<QAction*> contextMenuActions(const KFileItemList& items) const;
|
||||
virtual QList<QAction*> contextMenuActions(const QString& directory) const;
|
||||
virtual QList<QAction*> contextMenuActions(const KFileItemList& items);
|
||||
virtual QList<QAction*> 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<QString, RevisionInfo> 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
|
||||
|
||||
|
|
Loading…
Reference in a new issue