diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp index 3513c27715d4eea1caef363dfc32d09bc70486eb..8f854217de84166164c82eb9b4050a406f43040b 100644 --- a/src/libs/utils/submiteditorwidget.cpp +++ b/src/libs/utils/submiteditorwidget.cpp @@ -234,6 +234,16 @@ void SubmitEditorWidget::setFileNameColumn(int c) m_d->m_fileNameColumn = c; } +QAbstractItemView::SelectionMode SubmitEditorWidget::fileListSelectionMode() const +{ + return m_d->m_ui.fileView->selectionMode(); +} + +void SubmitEditorWidget::setFileListSelectionMode(QAbstractItemView::SelectionMode sm) +{ + m_d->m_ui.fileView->setSelectionMode(sm); +} + void SubmitEditorWidget::setFileModel(QAbstractItemModel *model) { m_d->m_ui.fileView->clearSelection(); // trigger the change signals diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h index 788709f773b723c3866373dc246385a66268b30a..8d8883e1f9b59348bf7989fe387c23f7f888ffb1 100644 --- a/src/libs/utils/submiteditorwidget.h +++ b/src/libs/utils/submiteditorwidget.h @@ -37,6 +37,7 @@ #include "utils_global.h" #include <QtGui/QWidget> +#include <QtGui/QAbstractItemView> QT_BEGIN_NAMESPACE class QPlainTextEdit; @@ -75,6 +76,7 @@ class QWORKBENCH_UTILS_EXPORT SubmitEditorWidget : public QWidget Q_DISABLE_COPY(SubmitEditorWidget) Q_PROPERTY(QString descriptionText READ descriptionText WRITE setDescriptionText DESIGNABLE true) Q_PROPERTY(int fileNameColumn READ fileNameColumn WRITE setFileNameColumn DESIGNABLE false) + Q_PROPERTY(QAbstractItemView::SelectionMode fileListSelectionMode READ fileListSelectionMode WRITE setFileListSelectionMode DESIGNABLE true) public: explicit SubmitEditorWidget(QWidget *parent = 0); virtual ~SubmitEditorWidget(); @@ -92,6 +94,9 @@ public: int fileNameColumn() const; void setFileNameColumn(int c); + QAbstractItemView::SelectionMode fileListSelectionMode() const; + void setFileListSelectionMode(QAbstractItemView::SelectionMode sm); + void setFileModel(QAbstractItemModel *model); QAbstractItemModel *fileModel() const; diff --git a/src/plugins/git/commitdata.cpp b/src/plugins/git/commitdata.cpp index ada478e3743c283f1e681839a9adadd40e74c60f..65962ef557c614c41e86bc55bdf4eb7f5a4a4c69 100644 --- a/src/plugins/git/commitdata.cpp +++ b/src/plugins/git/commitdata.cpp @@ -130,6 +130,11 @@ static inline bool addStateFileSpecification(const QString &line, QList<CommitDa \endcode */ +bool CommitData::filesEmpty() const +{ + return stagedFiles.empty() && unstagedFiles.empty() && untrackedFiles.empty(); +} + bool CommitData::parseFilesFromStatus(const QString &output) { enum State { None, CommitFiles, NotUpdatedFiles, UntrackedFiles }; @@ -183,7 +188,7 @@ bool CommitData::parseFilesFromStatus(const QString &output) } } } - return !stagedFiles.empty() || !unstagedFiles.empty() || !untrackedFiles.empty(); + return true; } // Convert a spec pair list to a list of file names, optionally diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h index 6cb5ddb42b9ca4f46996f7f1b3788bc4360867c1..ca9779c69ece88df990d83a9bef19059bcfbead3 100644 --- a/src/plugins/git/commitdata.h +++ b/src/plugins/git/commitdata.h @@ -77,6 +77,8 @@ struct CommitData // from a git status output bool parseFilesFromStatus(const QString &output); + bool filesEmpty() const; + // Convenience to retrieve the file names from // the specification list. Optionally filter for a certain state QStringList stagedFileNames(const QString &stateFilter = QString()) const; diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index d1ebda300b4bcfbe4329fc55bc4103d19f1fead3..5ae6d0b7dac1bb5bf0fe86d5af041055772c7482 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -184,12 +184,14 @@ VCSBase::VCSBaseEditor return rc; } -void GitClient::diff(const QString &workingDirectory, const QStringList &fileNames) +void GitClient::diff(const QString &workingDirectory, + const QStringList &diffArgs, + const QStringList &fileNames) { if (Git::Constants::debug) qDebug() << "diff" << workingDirectory << fileNames; QStringList arguments; - arguments << QLatin1String("diff") << QLatin1String("--") << fileNames; + arguments << QLatin1String("diff") << diffArgs << QLatin1String("--") << fileNames; const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); const QString title = tr("Git Diff"); @@ -199,14 +201,16 @@ void GitClient::diff(const QString &workingDirectory, const QStringList &fileNam } -void GitClient::diff(const QString &workingDirectory, const QString &fileName) +void GitClient::diff(const QString &workingDirectory, + const QStringList &diffArgs, + const QString &fileName) { if (Git::Constants::debug) qDebug() << "diff" << workingDirectory << fileName; QStringList arguments; arguments << QLatin1String("diff"); if (!fileName.isEmpty()) - arguments << QLatin1String("--") << fileName; + arguments << diffArgs << QLatin1String("--") << fileName; const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); const QString title = tr("Git Diff %1").arg(fileName); @@ -710,6 +714,10 @@ bool GitClient::getCommitData(const QString &workingDirectory, } // Filter out untracked files that are not part of the project filterUntrackedFilesOfProject(repoDirectory, &d->untrackedFiles); + if (d->filesEmpty()) { + *errorMessage = msgNoChangedFiles(); + return false; + } d->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name")); d->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email")); diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index f09daa00ab8e74d0106a12abfd6118cc83db2e57..43a0957d913a68061de7c0917526f6a099db0a2a 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -78,8 +78,8 @@ public: static QString findRepositoryForFile(const QString &fileName); static QString findRepositoryForDirectory(const QString &dir); - void diff(const QString &workingDirectory, const QString &fileName); - void diff(const QString &workingDirectory, const QStringList &fileNames); + void diff(const QString &workingDirectory, const QStringList &diffArgs, const QString &fileName); + void diff(const QString &workingDirectory, const QStringList &diffArgs, const QStringList &fileNames); void status(const QString &workingDirectory); void log(const QString &workingDirectory, const QString &fileName); diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index 2ef08083393531aca4164993246fa6b0356dedcd..50214c7c3542f0b37c1e03ab75310a0a5415316a 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -450,19 +450,24 @@ void GitPlugin::extensionsInitialized() m_projectExplorer = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); } -void GitPlugin::submitEditorDiff(const QStringList &files) +void GitPlugin::submitEditorDiffStaged(const QStringList &files) { - if (files.empty()) - return; - m_gitClient->diff(m_submitRepository, files); + if (!files.empty()) + m_gitClient->diff(m_submitRepository, QStringList(QLatin1String("--cached")), files); +} + +void GitPlugin::submitEditorDiffUnstaged(const QStringList &files) +{ + if (!files.empty()) + m_gitClient->diff(m_submitRepository, QStringList(), files); } void GitPlugin::diffCurrentFile() { - QFileInfo fileInfo = currentFile(); - QString fileName = fileInfo.fileName(); - QString workingDirectory = fileInfo.absolutePath(); - m_gitClient->diff(workingDirectory, fileName); + const QFileInfo fileInfo = currentFile(); + const QString fileName = fileInfo.fileName(); + const QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->diff(workingDirectory, QStringList(), fileName); } void GitPlugin::diffCurrentProject() @@ -470,7 +475,7 @@ void GitPlugin::diffCurrentProject() QString workingDirectory = getWorkingDirectory(); if (workingDirectory.isEmpty()) return; - m_gitClient->diff(workingDirectory, QString()); + m_gitClient->diff(workingDirectory, QStringList(), QString()); } QFileInfo GitPlugin::currentFile() const @@ -640,7 +645,8 @@ Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const Commit m_undoAction->setEnabled(false); m_redoAction->setEnabled(false); submitEditor->setCommitData(cd); - connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(submitEditorDiff(QStringList))); + connect(submitEditor, SIGNAL(diffStaged(QStringList)), this, SLOT(submitEditorDiffStaged(QStringList))); + connect(submitEditor, SIGNAL(diffUnstaged(QStringList)), this, SLOT(submitEditorDiffUnstaged(QStringList))); return editor; } diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 39287e1939a253c20c607c9625999b40b1e2fec1..9fdde403d672ba6ec08a760ec8c956c95c37f655 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -108,7 +108,8 @@ public slots: private slots: void diffCurrentFile(); void diffCurrentProject(); - void submitEditorDiff(const QStringList &); + void submitEditorDiffUnstaged(const QStringList &); + void submitEditorDiffStaged(const QStringList &); void submitCurrentLog(); void statusFile(); void statusProject(); diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp index d34a78240d21301e168a6f1249fbf48ded86db1a..fbdc4e51325b415f23cbb18a62fedf1f2c351654 100644 --- a/src/plugins/git/gitsubmiteditor.cpp +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -43,10 +43,22 @@ namespace Git { namespace Internal { +enum { FileTypeRole = Qt::UserRole + 1 }; +enum FileType { StagedFile , UnstagedFile, UntrackedFile }; + +/* The problem with git is that no diff can be obtained to for a random + * multiselection of staged/unstaged files; it requires the --cached + * option for staged files. So, we set the file list to + * single selection and sort the files manual according to a type + * flag we add to the model. */ + GitSubmitEditor::GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent) : - VCSBaseSubmitEditor(parameters, new GitSubmitEditorWidget(parent)) + VCSBaseSubmitEditor(parameters, new GitSubmitEditorWidget(parent)), + m_model(0) { setDisplayName(tr("Git Commit")); + setFileListSelectionMode(QAbstractItemView::SingleSelection); + connect(this, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotDiffSelected(QStringList))); } GitSubmitEditorWidget *GitSubmitEditor::submitEditorWidget() @@ -54,14 +66,20 @@ GitSubmitEditorWidget *GitSubmitEditor::submitEditorWidget() return static_cast<GitSubmitEditorWidget *>(widget()); } -static void addStateFileListToModel(const QList<CommitData::StateFilePair> &l, - VCSBase::SubmitFileModel *model, - bool checked) +// Utility to add a list of state/file pairs to the model +// setting a file type. +static void addStateFileListToModel(const QList<CommitData::StateFilePair> &l, + bool checked, FileType ft, + VCSBase::SubmitFileModel *model) { + typedef QList<CommitData::StateFilePair>::const_iterator ConstIterator; - const ConstIterator cend = l.constEnd(); - for (ConstIterator it = l.constBegin(); it != cend; ++it) - model->addFile(it->second, it->first, checked); + if (!l.empty()) { + const ConstIterator cend = l.constEnd(); + const QVariant fileTypeData(ft); + for (ConstIterator it = l.constBegin(); it != cend; ++it) + model->addFile(it->second, it->first, checked).front()->setData(fileTypeData, FileTypeRole); + } } void GitSubmitEditor::setCommitData(const CommitData &d) @@ -69,16 +87,37 @@ void GitSubmitEditor::setCommitData(const CommitData &d) submitEditorWidget()->setPanelData(d.panelData); submitEditorWidget()->setPanelInfo(d.panelInfo); - VCSBase::SubmitFileModel *model = new VCSBase::SubmitFileModel(this); - addStateFileListToModel(d.stagedFiles, model, true); - addStateFileListToModel(d.unstagedFiles, model, false); + m_model = new VCSBase::SubmitFileModel(this); + addStateFileListToModel(d.stagedFiles, true, StagedFile, m_model); + addStateFileListToModel(d.unstagedFiles, false, UnstagedFile, m_model); if (!d.untrackedFiles.empty()) { const QString untrackedSpec = QLatin1String("untracked"); + const QVariant fileTypeData(UntrackedFile); const QStringList::const_iterator cend = d.untrackedFiles.constEnd(); for (QStringList::const_iterator it = d.untrackedFiles.constBegin(); it != cend; ++it) - model->addFile(*it, untrackedSpec, false); + m_model->addFile(*it, untrackedSpec, false).front()->setData(fileTypeData, FileTypeRole); + } + setFileModel(m_model); +} + +void GitSubmitEditor::slotDiffSelected(const QStringList &files) +{ + QList<QStandardItem *> fileRow = m_model->findRow(files.front(), fileNameColumn()); + if (fileRow.empty()) + return; + const FileType ft = static_cast<FileType>(fileRow.front()->data(FileTypeRole).toInt()); + switch (ft) { + case StagedFile: + emit diffStaged(files); + break; + case UnstagedFile: + emit diffUnstaged(files); + break; + case UntrackedFile: + break; } - setFileModel(model); + + } GitSubmitEditorPanelData GitSubmitEditor::panelData() const diff --git a/src/plugins/git/gitsubmiteditor.h b/src/plugins/git/gitsubmiteditor.h index a1a80ac22cf11be556f6dfe39fb27a602c79e91f..78b99682d0d2949d694084fe2813a18768472eae 100644 --- a/src/plugins/git/gitsubmiteditor.h +++ b/src/plugins/git/gitsubmiteditor.h @@ -38,6 +38,10 @@ #include <QtCore/QStringList> +namespace VCSBase { + class SubmitFileModel; +} + namespace Git { namespace Internal { @@ -54,8 +58,17 @@ public: void setCommitData(const CommitData &); GitSubmitEditorPanelData panelData() const; +signals: + void diffStaged(const QStringList &); + void diffUnstaged(const QStringList &); + +private slots: + void slotDiffSelected(const QStringList &); + private: inline GitSubmitEditorWidget *submitEditorWidget(); + + VCSBase::SubmitFileModel *m_model; }; } // namespace Internal diff --git a/src/plugins/vcsbase/submitfilemodel.cpp b/src/plugins/vcsbase/submitfilemodel.cpp index f12eab38d14bd0291346c346dfe88a08435eed79..e38c1f4e389ca5c2a776f21f26cb358b0f940119 100644 --- a/src/plugins/vcsbase/submitfilemodel.cpp +++ b/src/plugins/vcsbase/submitfilemodel.cpp @@ -48,20 +48,47 @@ SubmitFileModel::SubmitFileModel(QObject *parent) : setHorizontalHeaderLabels(headerLabels); } -QList<QStandardItem *> SubmitFileModel::addFile(const QString &fileName, const QString &status, bool checked) +QList<QStandardItem *> SubmitFileModel::createFileRow(const QString &fileName, const QString &status, bool checked) { if (VCSBase::Constants::Internal::debug) qDebug() << Q_FUNC_INFO << fileName << status << checked; QStandardItem *statusItem = new QStandardItem(status); statusItem->setCheckable(true); statusItem->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + statusItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsUserCheckable|Qt::ItemIsEnabled); QStandardItem *fileItem = new QStandardItem(fileName); + fileItem->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); QList<QStandardItem *> row; row << statusItem << fileItem; + return row; +} + +QList<QStandardItem *> SubmitFileModel::addFile(const QString &fileName, const QString &status, bool checked) +{ + const QList<QStandardItem *> row = createFileRow(fileName, status, checked); appendRow(row); return row; } +QList<QStandardItem *> SubmitFileModel::rowAt(int row) const +{ + const int colCount = columnCount(); + QList<QStandardItem *> rc; + for (int c = 0; c < colCount; c++) + rc.push_back(item(row, c)); + return rc; +} + +QList<QStandardItem *> SubmitFileModel::findRow(const QString &text, int column) const +{ + // Single item + const QList<QStandardItem *> items = findItems(text, Qt::MatchExactly, column); + if (items.empty()) + return items; + // Compile row + return rowAt(items.front()->row()); + } + unsigned SubmitFileModel::filter(const QStringList &filter, int column) { unsigned rc = 0; diff --git a/src/plugins/vcsbase/submitfilemodel.h b/src/plugins/vcsbase/submitfilemodel.h index fef89df83bef6b4e4d1d92d14566c822cfb69550..e6a0cd76f20d6eb1e673f91119a8b2242b5da5a7 100644 --- a/src/plugins/vcsbase/submitfilemodel.h +++ b/src/plugins/vcsbase/submitfilemodel.h @@ -49,9 +49,16 @@ class VCSBASE_EXPORT SubmitFileModel : public QStandardItemModel public: explicit SubmitFileModel(QObject *parent = 0); - // Convenience to add a file plus status text. + // Convenience to create and add rows containing a file plus status text. + static QList<QStandardItem *> createFileRow(const QString &fileName, const QString &status = QString(), bool checked = true); QList<QStandardItem *> addFile(const QString &fileName, const QString &status = QString(), bool checked = true); + // Find convenience that returns the whole row (as opposed to QStandardItemModel::find). + QList<QStandardItem *> findRow(const QString &text, int column = 0) const; + + // Convenience to obtain a row + QList<QStandardItem *> rowAt(int row) const; + // Filter for entries contained in the filter list. Returns the // number of deleted entries. unsigned filter(const QStringList &filter, int column); diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index d130c2664596505fe65e1ccb4ed37956fa4ecca7..25fbce7634c63e8a6f206df75ef113bd91448039 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -139,6 +139,17 @@ void VCSBaseSubmitEditor::setFileNameColumn(int c) m_d->m_widget->setFileNameColumn(c); } +QAbstractItemView::SelectionMode VCSBaseSubmitEditor::fileListSelectionMode() const +{ + return m_d->m_widget->fileListSelectionMode(); +} + +void VCSBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionMode sm) +{ + m_d->m_widget->setFileListSelectionMode(sm); +} + + void VCSBaseSubmitEditor::slotDescriptionChanged() { } diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 91b10d488f99b0e8754a2699267cda3178c1abcc..d3e9166b35fd0be98a7b158755e43812e1fe692d 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -39,6 +39,7 @@ #include <coreplugin/editormanager/ieditor.h> #include <QtCore/QList> +#include <QtGui/QAbstractItemView> QT_BEGIN_NAMESPACE class QIcon; @@ -92,6 +93,7 @@ class VCSBASE_EXPORT VCSBaseSubmitEditor : public Core::IEditor { Q_OBJECT Q_PROPERTY(int fileNameColumn READ fileNameColumn WRITE setFileNameColumn DESIGNABLE false) + Q_PROPERTY(QAbstractItemView::SelectionMode fileListSelectionMode READ fileListSelectionMode WRITE setFileListSelectionMode DESIGNABLE true) public: typedef QList<int> Context; @@ -105,6 +107,9 @@ public: int fileNameColumn() const; void setFileNameColumn(int c); + QAbstractItemView::SelectionMode fileListSelectionMode() const; + void setFileListSelectionMode(QAbstractItemView::SelectionMode sm); + // Core::IEditor virtual bool createNew(const QString &contents); virtual bool open(const QString &fileName);