Index: svn/CMakeLists.txt =================================================================== --- svn/CMakeLists.txt +++ svn/CMakeLists.txt @@ -4,13 +4,24 @@ set(fileviewsvnplugin_SRCS fileviewsvnplugin.cpp + svncommands.cpp +) + +set(fileviewsvnplugin_HDRS + fileviewsvnplugin.h + svncommands.h +) + +set(SVNCOMMITDIALOG + svncommitdialog.h + svncommitdialog.cpp ) kconfig_add_kcfg_files(fileviewsvnplugin_SRCS fileviewsvnpluginsettings.kcfgc ) -add_library(fileviewsvnplugin MODULE ${fileviewsvnplugin_SRCS}) +add_library(fileviewsvnplugin MODULE ${fileviewsvnplugin_SRCS} ${fileviewsvnplugin_HDRS} ${SVNCOMMITDIALOG}) target_link_libraries(fileviewsvnplugin Qt5::Core Qt5::Widgets Index: svn/fileviewsvnplugin.h =================================================================== --- svn/fileviewsvnplugin.h +++ svn/fileviewsvnplugin.h @@ -47,10 +47,17 @@ signals: /// Invokes m_showUpdatesAction->setChecked(checked) on the UI thread. void setShowUpdatesChecked(bool checked); + + /** + * Is emitted if current SVN directory status got updated. Not necessarily means + * it's changed. Emitted right after #endRetrieval(). + */ + void versionInfoUpdated(); + private slots: void updateFiles(); void showLocalChanges(); - void commitFiles(); + void commitDialog(); void addFiles(); void removeFiles(); void revertFiles(); @@ -60,6 +67,11 @@ void slotShowUpdatesToggled(bool checked); + void revertFile(const QString& filePath); + void diffFile(const QString& filePath); + void addFile(const QString& filePath); + void commitFiles(const QString& msg); + private: /** * Executes the command "svn {svnCommand}" for the files that have been Index: svn/fileviewsvnplugin.cpp =================================================================== --- svn/fileviewsvnplugin.cpp +++ svn/fileviewsvnplugin.cpp @@ -44,6 +44,9 @@ #include #include +#include "svncommitdialog.h" +#include "svncommands.h" + K_PLUGIN_FACTORY(FileViewSvnPluginFactory, registerPlugin();) FileViewSvnPlugin::FileViewSvnPlugin(QObject* parent, const QList& args) : @@ -83,7 +86,7 @@ m_commitAction->setIcon(QIcon::fromTheme("svn-commit")); m_commitAction->setText(i18nc("@item:inmenu", "SVN Commit...")); connect(m_commitAction, SIGNAL(triggered()), - this, SLOT(commitFiles())); + this, SLOT(commitDialog())); m_addAction = new QAction(this); m_addAction->setIcon(QIcon::fromTheme("list-add")); @@ -163,6 +166,7 @@ case 'A': version = AddedVersion; break; case 'D': version = RemovedVersion; break; case 'C': version = ConflictingVersion; break; + case '!': version = MissingVersion; break; default: if (filePath.contains('*')) { version = UpdateRequiredVersion; @@ -205,6 +209,7 @@ void FileViewSvnPlugin::endRetrieval() { + emit versionInfoUpdated(); } KVersionControlPlugin::ItemVersion FileViewSvnPlugin::itemVersion(const KFileItem& item) const @@ -349,118 +354,19 @@ } } -void FileViewSvnPlugin::commitFiles() +void FileViewSvnPlugin::commitDialog() { - QDialog dialog(0, Qt::Dialog); - - QVBoxLayout* boxLayout = new QVBoxLayout(&dialog); - - boxLayout->addWidget(new QLabel(i18nc("@label", "Description:"), - &dialog)); - QPlainTextEdit* editor = new QPlainTextEdit(&dialog); - boxLayout->addWidget(editor, 1); - - QFrame* line = new QFrame(&dialog); - line->setFrameShape(QFrame::HLine); - line->setFrameShadow(QFrame::Sunken); - boxLayout->addWidget(line); - - const QStringList header = { i18nc("@title:column", "Path"), - i18nc("@title:column", "Status") }; - const int columnPath = 0; - const int columnStatus = 1; - QTableWidget *changes = new QTableWidget(m_versionInfoHash.size(), header.size(), &dialog); - changes->setHorizontalHeaderLabels(header); - changes->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - changes->horizontalHeader()->setSectionResizeMode(columnStatus, QHeaderView::ResizeToContents); - changes->verticalHeader()->setVisible(false); - changes->setSortingEnabled(false); - - QHash::const_iterator it = m_versionInfoHash.cbegin(); - for ( int row = 0 ; it != m_versionInfoHash.cend(); ++it, ++row ) { - QTableWidgetItem *path = new QTableWidgetItem( it.key() ); - QTableWidgetItem *status = new QTableWidgetItem; - - path->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - status->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - - changes->setItem(row, columnPath, path); - changes->setItem(row, columnStatus, status); - - switch(it.value()) { - case UnversionedVersion: - status->setText( i18nc("@item:intable", "Unversioned") ); - break; - case LocallyModifiedVersion: - status->setText( i18nc("@item:intable", "Modified") ); - break; - case AddedVersion: - status->setText( i18nc("@item:intable", "Added") ); - break; - case RemovedVersion: - status->setText( i18nc("@item:intable", "Deleted") ); - break; - case ConflictingVersion: - status->setText( i18nc("@item:intable", "Conflict") ); - break; - case MissingVersion: - status->setText( i18nc("@item:intable", "Missing") ); - break; - case UpdateRequiredVersion: - status->setText( i18nc("@item:intable", "Update required") ); - break; - default: - // For SVN normaly we shouldn't be here with: - // NormalVersion, LocallyModifiedUnstagedVersion, IgnoredVersion. - // 'default' is for any future changes in ItemVersion enum. - qWarning() << QString("Unknown SVN status for item %1, ItemVersion = %2").arg(it.key()).arg(it.value()); - status->setText(""); - } - } - // Sort by status: unversioned is at the bottom. - changes->sortByColumn(columnStatus, Qt::AscendingOrder); - boxLayout->addWidget(changes, 3); - - dialog.setWindowTitle(i18nc("@title:window", "SVN Commit")); - auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, &dialog); - connect(buttonBox, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); - connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); - auto okButton = buttonBox->button(QDialogButtonBox::Ok); - - okButton->setDefault(true); - okButton->setText(i18nc("@action:button", "Commit")); - boxLayout->addWidget(buttonBox); - - KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), - "SvnCommitDialog"); - dialog.winId(); // Workaround for QTBUG-40584, line 1/2. See KWindowConfig::restoreWindowSize() docs. - KWindowConfig::restoreWindowSize(dialog.windowHandle(), dialogConfig); - dialog.resize(dialog.windowHandle()->size()); // Workaround for QTBUG-40584, line 2/2. See KWindowConfig::restoreWindowSize() docs. - - if (dialog.exec() == QDialog::Accepted) { - // Write the commit description into a temporary file, so - // that it can be read by the command "svn commit -F". The temporary - // file must stay alive until slotOperationCompleted() is invoked and will - // be destroyed when the version plugin is destructed. - if (!m_tempFile.open()) { - emit errorMessage(i18nc("@info:status", "Commit of SVN changes failed.")); - return; - } + SvnCommitDialog *svnCommitDialog = new SvnCommitDialog(&m_versionInfoHash); - QTextStream out(&m_tempFile); - const QString fileName = m_tempFile.fileName(); - out << editor->toPlainText(); - m_tempFile.close(); - - QStringList arguments; - arguments << "-F" << fileName; - execSvnCommand("commit", arguments, - i18nc("@info:status", "Committing SVN changes..."), - i18nc("@info:status", "Commit of SVN changes failed."), - i18nc("@info:status", "Committed SVN changes.")); - } + connect(this, &FileViewSvnPlugin::versionInfoUpdated, svnCommitDialog, &SvnCommitDialog::refreshChangesList); - KWindowConfig::saveWindowSize(dialog.windowHandle(), dialogConfig, KConfigBase::Persistent); + connect(svnCommitDialog, &SvnCommitDialog::revertFile, this, &FileViewSvnPlugin::revertFile); + connect(svnCommitDialog, &SvnCommitDialog::diffFile, this, &FileViewSvnPlugin::diffFile); + connect(svnCommitDialog, &SvnCommitDialog::addFile, this, &FileViewSvnPlugin::addFile); + connect(svnCommitDialog, &SvnCommitDialog::commit, this, &FileViewSvnPlugin::commitFiles); + + svnCommitDialog->setAttribute(Qt::WA_DeleteOnClose); + svnCommitDialog->show(); } void FileViewSvnPlugin::addFiles() @@ -520,6 +426,76 @@ emit itemVersionsChanged(); } +void FileViewSvnPlugin::revertFile(const QString& filePath) +{ + m_contextItems.append( QUrl::fromLocalFile(filePath) ); + m_contextDir.clear(); + + execSvnCommand("revert", QStringList() << filePath, + i18nc("@info:status", "Reverting changes to file..."), + i18nc("@info:status", "Revert file failed."), + i18nc("@info:status", "File reverted.")); +} + +void FileViewSvnPlugin::diffFile(const QString& filePath) +{ + // For a diff we will export last known file revision from a remote and compare. We will not use + // basic SVN action 'svn diff --extensions -U ' because we should count lines + // or set maximum number for this. + // With a maximum number (2147483647) 'svn diff' starts to work slowly. + + QTemporaryFile *file = new QTemporaryFile(this); + ///\todo Calling a blocking operation: with a slow connection this might take some time. + if (!SVNCommands::exportLocalFile(filePath, SVNCommands::localRevision(filePath), file)) { + emit errorMessage(i18nc("@info:status", "Could not show local SVN changes for a file: could not get file.")); + file->deleteLater(); + } + + const bool started = QProcess::startDetached( + QLatin1String("kompare"), + QStringList { + file->fileName(), + filePath + } + ); + if (!started) { + emit errorMessage(i18nc("@info:status", "Could not show local SVN changes: could not start kompare.")); + file->deleteLater(); + } +} + +void FileViewSvnPlugin::addFile(const QString& filePath) +{ + m_contextItems.append( QUrl::fromLocalFile(filePath) ); + m_contextDir.clear(); + + addFiles(); +} + +void FileViewSvnPlugin::commitFiles(const QString& msg) +{ + // Write the commit description into a temporary file, so + // that it can be read by the command "svn commit -F". The temporary + // file must stay alive until slotOperationCompleted() is invoked and will + // be destroyed when the version plugin is destructed. + if (!m_tempFile.open()) { + emit errorMessage(i18nc("@info:status", "Commit of SVN changes failed.")); + return; + } + + QTextStream out(&m_tempFile); + const QString fileName = m_tempFile.fileName(); + out << msg; + m_tempFile.close(); + + QStringList arguments; + arguments << "-F" << fileName; + execSvnCommand("commit", arguments, + i18nc("@info:status", "Committing SVN changes..."), + i18nc("@info:status", "Commit of SVN changes failed."), + i18nc("@info:status", "Committed SVN changes.")); +} + void FileViewSvnPlugin::execSvnCommand(const QString& svnCommand, const QStringList& arguments, const QString& infoMsg, Index: svn/svncommands.h =================================================================== --- /dev/null +++ svn/svncommands.h @@ -0,0 +1,77 @@ +/*************************************************************************** + * Copyright (C) 2019-2020 * + * by Nikolai Krasheninnikov * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SVNCOMMANDS_H +#define SVNCOMMANDS_H + +#include +#include + +#include + +class QTemporaryFile; +class QFileDevice; + +/** + * \brief SVN support functions. + * + * \note All functions are synchronous i.e. blocking. Each of them waits for svn process to finish. + */ +class SVNCommands { +public: + /** + * Returns file \p filePath local revision. Local revision means last known file revision, not + * last SVN repository revision. + * + * \return Local revision, 0 in case of error. + */ + static ulong localRevision(const QString& filePath); + + /** + * For file \p filePath return its full remote repository URL path. + * + * \return Remote path, empty QString in case of error. + */ + static QString remoteItemUrl(const QString& filePath); + + /** + * Export remote URL \p remoteUrl at revision \p rev to a file \p file. File should already be + * opened or ready to be opened. Freeing resources is up to the caller. + * + * \return True if export success, false either. + * + * \note \p file should already be created with \p new. + */ + static bool exportRemoteFile(const QString& remoteUrl, ulong rev, QFileDevice *file); + static bool exportRemoteFile(const QString& remoteUrl, ulong rev, QTemporaryFile *file); + + /** + * Export local file \p filePath at revision \p rev to a file \p file. File should already be + * opened or ready to be opened. Freeing resources is up to the caller. + * + * \return True if export success, false either. + * + * \note \p file should already be created with \p new. + */ + static bool exportLocalFile(const QString& filePath, ulong rev, QFileDevice *file); + static bool exportLocalFile(const QString& filePath, ulong rev, QTemporaryFile *file); +}; + +#endif // SVNCOMMANDS_H Index: svn/svncommands.cpp =================================================================== --- /dev/null +++ svn/svncommands.cpp @@ -0,0 +1,165 @@ +/*************************************************************************** + * Copyright (C) 2019-2020 * + * by Nikolai Krasheninnikov * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "svncommands.h" + +#include +#include +#include +#include + +namespace { + +// Helper function: returns template file name for QTemporaryFile. +QString templateFileName(const QString& url, ulong rev) +{ + const QString tmpFileName = url.section('/', -1); + + return QDir::tempPath() + QString("/%1.r%2.XXXXXX").arg(tmpFileName).arg(rev); +} + +} + +ulong SVNCommands::localRevision(const QString& filePath) +{ + QProcess process; + + process.start( + QLatin1String("svn"), + QStringList { + QStringLiteral("info"), + QStringLiteral("--show-item"), + QStringLiteral("last-changed-revision"), + filePath + } + ); + + if (!process.waitForFinished() || process.exitCode() != 0) { + return 0; + } + + QTextStream stream(&process); + ulong revision = 0; + stream >> revision; + + if (stream.status() == QTextStream::Ok) { + return revision; + } else { + return 0; + } +} + +QString SVNCommands::remoteItemUrl(const QString& filePath) +{ + QProcess process; + + process.start( + QLatin1String("svn"), + QStringList { + QStringLiteral("info"), + QStringLiteral("--show-item"), + QStringLiteral("url"), + filePath + } + ); + + if (!process.waitForFinished() || process.exitCode() != 0) { + return 0; + } + + QTextStream stream(&process); + QString url; + stream >> url; + + if (stream.status() == QTextStream::Ok) { + return url; + } else { + return QString(); + } +} + +bool SVNCommands::exportRemoteFile(const QString& remoteUrl, ulong rev, QFileDevice *file) +{ + if (file == nullptr) { + return false; + } + if (!file->isOpen() && !file->open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) { + return false; + } + + QProcess process; + + process.start( + QLatin1String("svn"), + QStringList { + QStringLiteral("export"), + QStringLiteral("--force"), + QStringLiteral("-r%1").arg(rev), + remoteUrl, + file->fileName() + } + ); + + if (!process.waitForFinished() || process.exitCode() != 0) { + return false; + } else { + return true; + } +} + +bool SVNCommands::exportRemoteFile(const QString& remoteUrl, ulong rev, QTemporaryFile *file) +{ + if (file == nullptr) { + return false; + } + + file->setFileTemplate( templateFileName(remoteUrl, rev) ); + + return exportRemoteFile(remoteUrl, rev, dynamic_cast(file)); +} + +bool SVNCommands::exportLocalFile(const QString& filePath, ulong rev, QFileDevice *file) +{ + if (file == nullptr) { + return false; + } + + const QString fileUrl = remoteItemUrl(filePath); + if (fileUrl.isEmpty()) { + return false; + } + + if (!exportRemoteFile(fileUrl, rev, file)) { + return false; + } else { + return true; + } +} + +bool SVNCommands::exportLocalFile(const QString& filePath, ulong rev, QTemporaryFile *file) +{ + if (file == nullptr) { + return false; + } + + file->setFileTemplate( templateFileName(filePath, rev) ); + + return exportLocalFile(filePath, rev, dynamic_cast(file)); +} Index: svn/svncommitdialog.h =================================================================== --- /dev/null +++ svn/svncommitdialog.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2019-2020 * + * by Nikolai Krasheninnikov * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef SVNCOMMITDIALOG_H +#define SVNCOMMITDIALOG_H + +#include +#include + +#include + +class QPlainTextEdit; +class QTableWidget; + +/** + * \brief SVN Commit dialog class. + */ +class SvnCommitDialog : public QDialog { + Q_OBJECT +public: + explicit SvnCommitDialog(const QHash *versionInfo, QWidget *parent = nullptr); + virtual ~SvnCommitDialog() override; + +signals: + void commit(const QString& msg); + void revertFile(const QString& filePath); + void diffFile(const QString& filePath); + void addFile(const QString& filePath); + +public slots: + void refreshChangesList(); + void show(); + +private slots: + void contextMenu(const QPoint& pos); + +private: + const QHash *m_versionInfoHash; + QPlainTextEdit *m_editor; + QTableWidget *m_changes; + QAction *m_actRevertFile; + QAction *m_actDiffFile; + QAction *m_actAddFile; + +protected: + virtual void keyPressEvent(QKeyEvent *event) override; +}; + +#endif // SVNCOMMITDIALOG_H Index: svn/svncommitdialog.cpp =================================================================== --- /dev/null +++ svn/svncommitdialog.cpp @@ -0,0 +1,240 @@ +/*************************************************************************** + * Copyright (C) 2019-2020 * + * by Nikolai Krasheninnikov * + * * + * 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. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "svncommitdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct svnInfo_t { + QString filePath; + KVersionControlPlugin::ItemVersion fileVersion; +}; +Q_DECLARE_METATYPE(svnInfo_t); + +enum columns_t { + columnPath, + columnStatus +}; + +SvnCommitDialog::SvnCommitDialog(const QHash *versionInfo, QWidget *parent) : + QDialog(parent), + m_versionInfoHash(versionInfo) +{ + Q_ASSERT(versionInfo); + + QVBoxLayout* boxLayout = new QVBoxLayout(this); + + boxLayout->addWidget(new QLabel(i18nc("@label", "Description:"), this)); + m_editor = new QPlainTextEdit(this); + boxLayout->addWidget(m_editor, 1); + + QFrame* line = new QFrame(this); + line->setFrameShape(QFrame::HLine); + line->setFrameShadow(QFrame::Sunken); + boxLayout->addWidget(line); + + const QStringList header = { i18nc("@title:column", "Path"), + i18nc("@title:column", "Status") }; + m_changes = new QTableWidget(this); + m_changes->setColumnCount(header.size()); + m_changes->setHorizontalHeaderLabels(header); + m_changes->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + m_changes->horizontalHeader()->setSectionResizeMode(columnStatus, QHeaderView::ResizeToContents); + m_changes->verticalHeader()->setVisible(false); + m_changes->setSortingEnabled(false); + m_changes->setSelectionMode(QAbstractItemView::SingleSelection); + m_changes->setSelectionBehavior(QAbstractItemView::SelectRows); + m_changes->setContextMenuPolicy(Qt::CustomContextMenu); + + refreshChangesList(); + boxLayout->addWidget(m_changes, 3); + + setWindowTitle(i18nc("@title:window", "SVN Commit")); + auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttonBox, &QDialogButtonBox::accepted, [this] () { + emit commit(m_editor->toPlainText()); + QDialog::accept(); + } ); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + auto okButton = buttonBox->button(QDialogButtonBox::Ok); + + okButton->setDefault(true); + okButton->setText(i18nc("@action:button", "Commit")); + boxLayout->addWidget(buttonBox); + + m_actRevertFile = new QAction(i18nc("@item:inmenu", "Revert"), this); + m_actRevertFile->setIcon(QIcon::fromTheme("document-revert")); + connect(m_actRevertFile, &QAction::triggered, [this] () { + const QString filePath = m_actRevertFile->data().value().filePath; + emit revertFile(filePath); + } ); + + m_actDiffFile = new QAction(i18nc("@item:inmenu", "Show changes"), this); + m_actDiffFile->setIcon(QIcon::fromTheme("view-split-left-right")); + connect(m_actDiffFile, &QAction::triggered, [this] () { + const QString filePath = m_actDiffFile->data().value().filePath; + emit diffFile(filePath); + } ); + + m_actAddFile = new QAction(i18nc("@item:inmenu", "Add file"), this); + m_actAddFile->setIcon(QIcon::fromTheme("list-add")); + connect(m_actAddFile, &QAction::triggered, [this] () { + const QString filePath = m_actAddFile->data().value().filePath; + emit addFile(filePath); + } ); + + connect(m_changes, &QWidget::customContextMenuRequested, this, &SvnCommitDialog::contextMenu); +} + +SvnCommitDialog::~SvnCommitDialog() +{ + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "SvnCommitDialog"); + KWindowConfig::saveWindowSize(windowHandle(), dialogConfig, KConfigBase::Persistent); +} + +void SvnCommitDialog::refreshChangesList() +{ + m_changes->clearSelection(); + m_changes->setRowCount(m_versionInfoHash->size()); + + QHash::const_iterator it = m_versionInfoHash->cbegin(); + for ( int row = 0 ; it != m_versionInfoHash->cend(); ++it, ++row ) { + QTableWidgetItem *path = new QTableWidgetItem( it.key() ); + QTableWidgetItem *status = new QTableWidgetItem; + + path->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + status->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); + + m_changes->setItem(row, columnPath, path); + m_changes->setItem(row, columnStatus, status); + + svnInfo_t info { it.key(), it.value() }; + path->setData(Qt::UserRole, QVariant::fromValue(info)); + status->setData(Qt::UserRole, QVariant::fromValue(info)); + + switch(it.value()) { + case KVersionControlPlugin::UnversionedVersion: + status->setText( i18nc("@item:intable", "Unversioned") ); + break; + case KVersionControlPlugin::LocallyModifiedVersion: + status->setText( i18nc("@item:intable", "Modified") ); + break; + case KVersionControlPlugin::AddedVersion: + status->setText( i18nc("@item:intable", "Added") ); + break; + case KVersionControlPlugin::RemovedVersion: + status->setText( i18nc("@item:intable", "Deleted") ); + break; + case KVersionControlPlugin::ConflictingVersion: + status->setText( i18nc("@item:intable", "Conflict") ); + break; + case KVersionControlPlugin::MissingVersion: + status->setText( i18nc("@item:intable", "Missing") ); + break; + case KVersionControlPlugin::UpdateRequiredVersion: + status->setText( i18nc("@item:intable", "Update required") ); + break; + default: + // For SVN normaly we shouldn't be here with: + // NormalVersion, LocallyModifiedUnstagedVersion, IgnoredVersion. + // 'default' is for any future changes in ItemVersion enum. + qWarning() << QString("Unknown SVN status for item %1, ItemVersion = %2").arg(it.key()).arg(it.value()); + status->setText(""); + } + } + // Sort by status: unversioned is at the bottom. + m_changes->sortByColumn(columnStatus, Qt::AscendingOrder); +} + +void SvnCommitDialog::show() +{ + QWidget::show(); + + // Restore window size after show() for workaround for QTBUG-40584. See KWindowConfig::restoreWindowSize() docs. + KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "SvnCommitDialog"); + KWindowConfig::restoreWindowSize(windowHandle(), dialogConfig); +} + +void SvnCommitDialog::contextMenu(const QPoint& pos) +{ + QTableWidgetItem *item = m_changes->item( m_changes->currentRow(), 0 ); + if (item == nullptr) { + return; + } + + const QVariant data = item->data(Qt::UserRole); + m_actRevertFile->setData( data ); + m_actDiffFile->setData( data ); + m_actAddFile->setData( data ); + + m_actRevertFile->setEnabled(false); + m_actDiffFile->setEnabled(false); + m_actAddFile->setEnabled(false); + + const svnInfo_t info = data.value(); + switch(info.fileVersion) { + case KVersionControlPlugin::UnversionedVersion: + m_actAddFile->setEnabled(true); + break; + case KVersionControlPlugin::LocallyModifiedVersion: + m_actRevertFile->setEnabled(true); + m_actDiffFile->setEnabled(true); + break; + case KVersionControlPlugin::AddedVersion: + case KVersionControlPlugin::RemovedVersion: + m_actRevertFile->setEnabled(true); + break; + case KVersionControlPlugin::MissingVersion: + m_actRevertFile->setEnabled(true); + break; + default: + // For this items we don't show any menu: return now. + return; + } + + QMenu *menu = new QMenu(this); + menu->addAction(m_actAddFile); + menu->addAction(m_actRevertFile); + menu->addAction(m_actDiffFile); + + // Adjust popup menu position for QTableWidget header height. + const QPoint popupPoint = QPoint(pos.x(), pos.y() + m_changes->horizontalHeader()->height()); + menu->exec( m_changes->mapToGlobal(popupPoint) ); +} + +void SvnCommitDialog::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_F5) { + refreshChangesList(); + } +}