From 34db64c752a46942f43daf77263e27e124d932b2 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 2 Jan 2013 22:12:06 +0200 Subject: Move SubmitEditorWidget from Utils to VcsBase It belongs there Change-Id: I4b4cdacf32b0eab299d4b2d5258cd4d91a759907 Reviewed-by: hjk --- src/libs/utils/images/removesubmitfield.png | Bin 578 -> 0 bytes src/libs/utils/submiteditorwidget.cpp | 719 --------------------- src/libs/utils/submiteditorwidget.h | 156 ----- src/libs/utils/submiteditorwidget.ui | 107 --- src/libs/utils/submitfieldwidget.cpp | 383 ----------- src/libs/utils/submitfieldwidget.h | 92 --- src/libs/utils/utils-lib.pri | 7 +- src/libs/utils/utils.qbs | 6 - src/libs/utils/utils.qrc | 1 - src/plugins/bazaar/bazaarcommitwidget.cpp | 2 +- src/plugins/bazaar/bazaarcommitwidget.h | 4 +- src/plugins/clearcase/clearcasesubmiteditor.cpp | 2 +- .../clearcase/clearcasesubmiteditorwidget.cpp | 2 +- .../clearcase/clearcasesubmiteditorwidget.h | 4 +- src/plugins/cvs/cvssubmiteditor.cpp | 4 +- src/plugins/git/gitsubmiteditorwidget.cpp | 2 +- src/plugins/git/gitsubmiteditorwidget.h | 4 +- src/plugins/mercurial/mercurialcommitwidget.cpp | 2 +- src/plugins/mercurial/mercurialcommitwidget.h | 4 +- .../perforce/perforcesubmiteditorwidget.cpp | 2 +- src/plugins/perforce/perforcesubmiteditorwidget.h | 4 +- src/plugins/subversion/subversionsubmiteditor.cpp | 4 +- src/plugins/vcsbase/images/removesubmitfield.png | Bin 0 -> 578 bytes src/plugins/vcsbase/submiteditorwidget.cpp | 719 +++++++++++++++++++++ src/plugins/vcsbase/submiteditorwidget.h | 157 +++++ src/plugins/vcsbase/submiteditorwidget.ui | 107 +++ src/plugins/vcsbase/submitfieldwidget.cpp | 383 +++++++++++ src/plugins/vcsbase/submitfieldwidget.h | 92 +++ src/plugins/vcsbase/vcsbase.pro | 11 +- src/plugins/vcsbase/vcsbase.qbs | 6 + src/plugins/vcsbase/vcsbase.qrc | 1 + src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 10 +- src/plugins/vcsbase/vcsbasesubmiteditor.h | 5 +- 33 files changed, 1501 insertions(+), 1501 deletions(-) delete mode 100644 src/libs/utils/images/removesubmitfield.png delete mode 100644 src/libs/utils/submiteditorwidget.cpp delete mode 100644 src/libs/utils/submiteditorwidget.h delete mode 100644 src/libs/utils/submiteditorwidget.ui delete mode 100644 src/libs/utils/submitfieldwidget.cpp delete mode 100644 src/libs/utils/submitfieldwidget.h create mode 100644 src/plugins/vcsbase/images/removesubmitfield.png create mode 100644 src/plugins/vcsbase/submiteditorwidget.cpp create mode 100644 src/plugins/vcsbase/submiteditorwidget.h create mode 100644 src/plugins/vcsbase/submiteditorwidget.ui create mode 100644 src/plugins/vcsbase/submitfieldwidget.cpp create mode 100644 src/plugins/vcsbase/submitfieldwidget.h diff --git a/src/libs/utils/images/removesubmitfield.png b/src/libs/utils/images/removesubmitfield.png deleted file mode 100644 index e4139afc55..0000000000 Binary files a/src/libs/utils/images/removesubmitfield.png and /dev/null differ diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp deleted file mode 100644 index 13714e8a5f..0000000000 --- a/src/libs/utils/submiteditorwidget.cpp +++ /dev/null @@ -1,719 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "submiteditorwidget.h" -#include "submitfieldwidget.h" -#include "ui_submiteditorwidget.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -enum { debug = 0 }; -enum { defaultLineWidth = 72 }; - -enum { checkableColumn = 0 }; - -/*! - \class Utils::SubmitEditorWidget - - \brief Presents a VCS commit message in a text editor and a - checkable list of modified files in a list window. - - The user can delete files from the list by unchecking them or diff the selection - by doubleclicking. A list model which contains the file in a column - specified by fileNameColumn should be set using setFileModel(). - - Additionally, standard creator actions can be registered: - Undo/redo will be set up to work with the description editor. - Submit will be set up to be enabled according to checkstate. - Diff will be set up to trigger diffSelected(). - - Note that the actions are connected by signals; in the rare event that there - are several instances of the SubmitEditorWidget belonging to the same - context active, the actions must be registered/unregistered in the editor - change event. - Care should be taken to ensure the widget is deleted properly when the - editor closes. -*/ - -namespace Utils { - -// QActionPushButton: A push button tied to an action -// (similar to a QToolButton) -class QActionPushButton : public QPushButton -{ - Q_OBJECT -public: - explicit QActionPushButton(QAction *a); - -private slots: - void actionChanged(); -}; - -QActionPushButton::QActionPushButton(QAction *a) : - QPushButton(a->icon(), a->text()) -{ - connect(a, SIGNAL(changed()), this, SLOT(actionChanged())); - connect(this, SIGNAL(clicked()), a, SLOT(trigger())); - setEnabled(a->isEnabled()); -} - -void QActionPushButton::actionChanged() -{ - if (const QAction *a = qobject_cast(sender())) { - setEnabled(a->isEnabled()); - setText(a->text()); - } -} - -// A helper parented on a QAction, -// making QAction::setText() a slot (which it currently is not). -class QActionSetTextSlotHelper : public QObject -{ - Q_OBJECT -public: - explicit QActionSetTextSlotHelper(QAction *a) : QObject(a) {} - -public slots: - void setText(const QString &t) { - if (QAction *action = qobject_cast(parent())) - action->setText(t); - } -}; - -// Helpers to retrieve model data -static inline bool listModelChecked(const QAbstractItemModel *model, int row, int column = 0) -{ - const QModelIndex checkableIndex = model->index(row, column, QModelIndex()); - return model->data(checkableIndex, Qt::CheckStateRole).toInt() == Qt::Checked; -} - -static void setListModelChecked(QAbstractItemModel *model, bool checked, int column = 0) -{ - const QVariant data = QVariant(int(checked ? Qt::Checked : Qt::Unchecked)); - const int count = model->rowCount(); - for (int i = 0; i < count; i++) { - const QModelIndex checkableIndex = model->index(i, column, QModelIndex()); - model->setData(checkableIndex, data, Qt::CheckStateRole); - } -} - -static inline QString listModelText(const QAbstractItemModel *model, int row, int column) -{ - const QModelIndex index = model->index(row, column, QModelIndex()); - return model->data(index, Qt::DisplayRole).toString(); -} - -// Convenience to extract a list of selected indexes -QList selectedRows(const QAbstractItemView *view) -{ - const QModelIndexList indexList = view->selectionModel()->selectedRows(0); - if (indexList.empty()) - return QList(); - QList rc; - const QModelIndexList::const_iterator cend = indexList.constEnd(); - for (QModelIndexList::const_iterator it = indexList.constBegin(); it != cend; ++it) - rc.push_back(it->row()); - return rc; -} - -// ----------- SubmitEditorWidgetPrivate - -struct SubmitEditorWidgetPrivate -{ - // A pair of position/action to extend context menus - typedef QPair > AdditionalContextMenuAction; - - SubmitEditorWidgetPrivate(); - - Ui::SubmitEditorWidget m_ui; - bool m_filesSelected; - int m_fileNameColumn; - int m_activatedRow; - bool m_emptyFileListEnabled; - - QList descriptionEditContextMenuActions; - QVBoxLayout *m_fieldLayout; - QList m_fieldWidgets; - QShortcut *m_submitShortcut; - int m_lineWidth; - - bool m_commitEnabled; - bool m_ignoreChange; - bool m_descriptionMandatory; -}; - -SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() : - m_filesSelected(false), - m_fileNameColumn(1), - m_activatedRow(-1), - m_emptyFileListEnabled(false), - m_fieldLayout(0), - m_submitShortcut(0), - m_lineWidth(defaultLineWidth), - m_commitEnabled(false), - m_ignoreChange(false), - m_descriptionMandatory(true) -{ -} - -SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) : - QWidget(parent), - d(new SubmitEditorWidgetPrivate) -{ - d->m_ui.setupUi(this); - d->m_ui.description->setContextMenuPolicy(Qt::CustomContextMenu); - d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap); - d->m_ui.description->setWordWrapMode(QTextOption::WordWrap); - connect(d->m_ui.description, SIGNAL(customContextMenuRequested(QPoint)), - this, SLOT(editorCustomContextMenuRequested(QPoint))); - connect(d->m_ui.description, SIGNAL(textChanged()), - this, SLOT(descriptionTextChanged())); - - // File List - d->m_ui.fileView->setContextMenuPolicy(Qt::CustomContextMenu); - connect(d->m_ui.fileView, SIGNAL(customContextMenuRequested(QPoint)), - this, SLOT(fileListCustomContextMenuRequested(QPoint))); - d->m_ui.fileView->setSelectionMode(QAbstractItemView::ExtendedSelection); - d->m_ui.fileView->setRootIsDecorated(false); - connect(d->m_ui.fileView, SIGNAL(doubleClicked(QModelIndex)), - this, SLOT(diffActivated(QModelIndex))); - - connect(d->m_ui.checkAllCheckBox, SIGNAL(stateChanged(int)), - this, SLOT(checkAllToggled())); - - setFocusPolicy(Qt::StrongFocus); - setFocusProxy(d->m_ui.description); -} - -SubmitEditorWidget::~SubmitEditorWidget() -{ - delete d; -} - -void SubmitEditorWidget::registerActions(QAction *editorUndoAction, QAction *editorRedoAction, - QAction *submitAction, QAction *diffAction) -{ - if (editorUndoAction) { - editorUndoAction->setEnabled(d->m_ui.description->document()->isUndoAvailable()); - connect(d->m_ui.description, SIGNAL(undoAvailable(bool)), editorUndoAction, SLOT(setEnabled(bool))); - connect(editorUndoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(undo())); - } - if (editorRedoAction) { - editorRedoAction->setEnabled(d->m_ui.description->document()->isRedoAvailable()); - connect(d->m_ui.description, SIGNAL(redoAvailable(bool)), editorRedoAction, SLOT(setEnabled(bool))); - connect(editorRedoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(redo())); - } - - if (submitAction) { - if (debug) { - int count = 0; - if (const QAbstractItemModel *model = d->m_ui.fileView->model()) - count = model->rowCount(); - qDebug() << Q_FUNC_INFO << submitAction << count << "items"; - } - d->m_commitEnabled = !canSubmit(); - connect(this, SIGNAL(submitActionEnabledChanged(bool)), submitAction, SLOT(setEnabled(bool))); - // Wire setText via QActionSetTextSlotHelper. - QActionSetTextSlotHelper *actionSlotHelper = submitAction->findChild(); - if (!actionSlotHelper) - actionSlotHelper = new QActionSetTextSlotHelper(submitAction); - connect(this, SIGNAL(submitActionTextChanged(QString)), actionSlotHelper, SLOT(setText(QString))); - d->m_ui.buttonLayout->addWidget(new QActionPushButton(submitAction)); - if (!d->m_submitShortcut) - d->m_submitShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this); - connect(d->m_submitShortcut, SIGNAL(activated()), submitAction, SLOT(trigger())); - } - if (diffAction) { - if (debug) - qDebug() << diffAction << d->m_filesSelected; - diffAction->setEnabled(d->m_filesSelected); - connect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); - connect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); - d->m_ui.buttonLayout->addWidget(new QActionPushButton(diffAction)); - } -} - -void SubmitEditorWidget::unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, - QAction *submitAction, QAction *diffAction) -{ - if (editorUndoAction) { - disconnect(d->m_ui.description, SIGNAL(undoAvailableChanged(bool)), editorUndoAction, SLOT(setEnabled(bool))); - disconnect(editorUndoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(undo())); - } - if (editorRedoAction) { - disconnect(d->m_ui.description, SIGNAL(redoAvailableChanged(bool)), editorRedoAction, SLOT(setEnabled(bool))); - disconnect(editorRedoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(redo())); - } - - if (submitAction) { - disconnect(this, SIGNAL(submitActionEnabledChanged(bool)), submitAction, SLOT(setEnabled(bool))); - // Just deactivate the QActionSetTextSlotHelper on the action - disconnect(this, SIGNAL(submitActionTextChanged(QString)), 0, 0); - } - - if (diffAction) { - disconnect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); - disconnect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); - } -} - -// Make sure we have one terminating NL. Do not trim front as leading space might be -// required for some formattings. -static inline QString trimMessageText(QString t) -{ - if (t.isEmpty()) - return t; - // Trim back of string. - const int last = t.size() - 1; - int lastWordCharacter = last; - for ( ; lastWordCharacter >= 0 && t.at(lastWordCharacter).isSpace() ; lastWordCharacter--) ; - if (lastWordCharacter != last) - t.truncate(lastWordCharacter + 1); - t += QLatin1Char('\n'); - return t; -} - -// Extract the wrapped text from a text edit, which performs -// the wrapping only optically. -static QString wrappedText(const QTextEdit *e) -{ - const QChar newLine = QLatin1Char('\n'); - QString rc; - QTextCursor cursor(e->document()); - cursor.movePosition(QTextCursor::Start); - while (!cursor.atEnd()) { - cursor.select(QTextCursor::LineUnderCursor); - rc += cursor.selectedText(); - rc += newLine; - cursor.movePosition(QTextCursor::EndOfLine); // Mac needs it - cursor.movePosition(QTextCursor::NextCharacter); - } - return rc; -} - -QString SubmitEditorWidget::descriptionText() const -{ - QString rc = trimMessageText(lineWrap() ? wrappedText(d->m_ui.description) : - d->m_ui.description->toPlainText()); - // append field entries - foreach (const SubmitFieldWidget *fw, d->m_fieldWidgets) - rc += fw->fieldValues(); - return cleanupDescription(rc); -} - -void SubmitEditorWidget::setDescriptionText(const QString &text) -{ - d->m_ui.description->setPlainText(text); -} - -bool SubmitEditorWidget::lineWrap() const -{ - return d->m_ui.description->lineWrapMode() != QTextEdit::NoWrap; -} - -void SubmitEditorWidget::setLineWrap(bool v) -{ - if (debug) - qDebug() << Q_FUNC_INFO << v; - if (v) { - d->m_ui.description->setLineWrapColumnOrWidth(d->m_lineWidth); - d->m_ui.description->setLineWrapMode(QTextEdit::FixedColumnWidth); - } else { - d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap); - } -} - -int SubmitEditorWidget::lineWrapWidth() const -{ - return d->m_lineWidth; -} - -void SubmitEditorWidget::setLineWrapWidth(int v) -{ - if (debug) - qDebug() << Q_FUNC_INFO << v << lineWrap(); - if (d->m_lineWidth == v) - return; - d->m_lineWidth = v; - if (lineWrap()) - d->m_ui.description->setLineWrapColumnOrWidth(v); -} - -bool SubmitEditorWidget::isDescriptionMandatory() const -{ - return d->m_descriptionMandatory; -} - -void SubmitEditorWidget::setDescriptionMandatory(bool v) -{ - d->m_descriptionMandatory = v; -} - -int SubmitEditorWidget::fileNameColumn() const -{ - return d->m_fileNameColumn; -} - -void SubmitEditorWidget::setFileNameColumn(int c) -{ - d->m_fileNameColumn = c; -} - -QAbstractItemView::SelectionMode SubmitEditorWidget::fileListSelectionMode() const -{ - return d->m_ui.fileView->selectionMode(); -} - -void SubmitEditorWidget::setFileListSelectionMode(QAbstractItemView::SelectionMode sm) -{ - d->m_ui.fileView->setSelectionMode(sm); -} - -void SubmitEditorWidget::setFileModel(QAbstractItemModel *model) -{ - d->m_ui.fileView->clearSelection(); // trigger the change signals - - d->m_ui.fileView->setModel(model); - - if (model->rowCount()) { - const int columnCount = model->columnCount(); - for (int c = 0; c < columnCount; c++) - d->m_ui.fileView->resizeColumnToContents(c); - } - - connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(updateSubmitAction())); - connect(model, SIGNAL(modelReset()), - this, SLOT(updateSubmitAction())); - connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this, SLOT(updateCheckAllComboBox())); - connect(model, SIGNAL(modelReset()), - this, SLOT(updateCheckAllComboBox())); - connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), - this, SLOT(updateSubmitAction())); - connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), - this, SLOT(updateSubmitAction())); - connect(d->m_ui.fileView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), - this, SLOT(updateDiffAction())); - updateActions(); -} - -QAbstractItemModel *SubmitEditorWidget::fileModel() const -{ - return d->m_ui.fileView->model(); -} - -QStringList SubmitEditorWidget::selectedFiles() const -{ - const QList selection = selectedRows(d->m_ui.fileView); - if (selection.empty()) - return QStringList(); - - QStringList rc; - const QAbstractItemModel *model = d->m_ui.fileView->model(); - const int count = selection.size(); - for (int i = 0; i < count; i++) - rc.push_back(listModelText(model, selection.at(i), fileNameColumn())); - return rc; -} - -QStringList SubmitEditorWidget::checkedFiles() const -{ - QStringList rc; - const QAbstractItemModel *model = d->m_ui.fileView->model(); - if (!model) - return rc; - const int count = model->rowCount(); - for (int i = 0; i < count; i++) - if (listModelChecked(model, i, checkableColumn)) - rc.push_back(listModelText(model, i, fileNameColumn())); - return rc; -} - -CompletingTextEdit *SubmitEditorWidget::descriptionEdit() const -{ - return d->m_ui.description; -} - -void SubmitEditorWidget::triggerDiffSelected() -{ - const QStringList sel = selectedFiles(); - if (!sel.empty()) - emit diffSelected(sel); -} - -void SubmitEditorWidget::diffActivatedDelayed() -{ - const QStringList files = QStringList(listModelText(d->m_ui.fileView->model(), d->m_activatedRow, fileNameColumn())); - emit diffSelected(files); -} - -void SubmitEditorWidget::diffActivated(const QModelIndex &index) -{ - // We need to delay the signal, otherwise, the diff editor will not - // be in the foreground. - d->m_activatedRow = index.row(); - QTimer::singleShot(0, this, SLOT(diffActivatedDelayed())); -} - -void SubmitEditorWidget::updateActions() -{ - updateSubmitAction(); - updateDiffAction(); - updateCheckAllComboBox(); -} - -// Enable submit depending on having checked files -void SubmitEditorWidget::updateSubmitAction() -{ - const unsigned checkedCount = checkedFilesCount(); - const bool newCommitState = canSubmit(); - // Emit signal to update action - if (d->m_commitEnabled != newCommitState) { - d->m_commitEnabled = newCommitState; - emit submitActionEnabledChanged(d->m_commitEnabled); - } - if (d->m_ui.fileView && d->m_ui.fileView->model()) { - // Update button text. - const int fileCount = d->m_ui.fileView->model()->rowCount(); - const QString msg = checkedCount ? - tr("%1 %2/%n File(s)", 0, fileCount) - .arg(commitName()).arg(checkedCount) : - commitName(); - emit submitActionTextChanged(msg); - } -} - -// Enable diff depending on selected files -void SubmitEditorWidget::updateDiffAction() -{ - const bool filesSelected = hasSelection(); - if (d->m_filesSelected != filesSelected) { - d->m_filesSelected = filesSelected; - emit fileSelectionChanged(d->m_filesSelected); - } -} - -void SubmitEditorWidget::updateCheckAllComboBox() -{ - d->m_ignoreChange = true; - int checkedCount = checkedFilesCount(); - if (checkedCount == 0) - d->m_ui.checkAllCheckBox->setCheckState(Qt::Unchecked); - else if (checkedCount == d->m_ui.fileView->model()->rowCount()) - d->m_ui.checkAllCheckBox->setCheckState(Qt::Checked); - else - d->m_ui.checkAllCheckBox->setCheckState(Qt::PartiallyChecked); - d->m_ignoreChange = false; -} - -bool SubmitEditorWidget::hasSelection() const -{ - // Not present until model is set - if (const QItemSelectionModel *sm = d->m_ui.fileView->selectionModel()) - return sm->hasSelection(); - return false; -} - -int SubmitEditorWidget::checkedFilesCount() const -{ - int checkedCount = 0; - if (const QAbstractItemModel *model = d->m_ui.fileView->model()) { - const int count = model->rowCount(); - for (int i = 0; i < count; ++i) - if (listModelChecked(model, i, checkableColumn)) - ++checkedCount; - } - return checkedCount; -} - -QString SubmitEditorWidget::cleanupDescription(const QString &input) const -{ - return input; -} - -void SubmitEditorWidget::changeEvent(QEvent *e) -{ - QWidget::changeEvent(e); - switch (e->type()) { - case QEvent::LanguageChange: - d->m_ui.retranslateUi(this); - break; - default: - break; - } -} - -void SubmitEditorWidget::insertTopWidget(QWidget *w) -{ - d->m_ui.vboxLayout->insertWidget(0, w); -} - -void SubmitEditorWidget::descriptionTextChanged() -{ -#if QT_VERSION < 0x050000 // Fix Qt-Bug, see QTCREATORBUG-5633 && QTCREATORBUG-6082 - static QString lastText; - const QString text = d->m_ui.description->toPlainText(); - if (lastText != text) - lastText = text; - else - return; -#endif - updateSubmitAction(); -} - -bool SubmitEditorWidget::canSubmit() const -{ - if (isDescriptionMandatory() && cleanupDescription(descriptionText()).trimmed().isEmpty()) - return false; - const unsigned checkedCount = checkedFilesCount(); - return d->m_emptyFileListEnabled || checkedCount > 0; -} - -QString SubmitEditorWidget::commitName() const -{ - return tr("&Commit"); -} - -void SubmitEditorWidget::addSubmitFieldWidget(SubmitFieldWidget *f) -{ - if (!d->m_fieldLayout) { - // VBox with horizontal, expanding spacer - d->m_fieldLayout = new QVBoxLayout; - QHBoxLayout *outerLayout = new QHBoxLayout; - outerLayout->addLayout(d->m_fieldLayout); - outerLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); - QBoxLayout *descrLayout = qobject_cast(d->m_ui.descriptionBox->layout()); - Q_ASSERT(descrLayout); - descrLayout->addLayout(outerLayout); - } - d->m_fieldLayout->addWidget(f); - d->m_fieldWidgets.push_back(f); -} - -QList SubmitEditorWidget::submitFieldWidgets() const -{ - return d->m_fieldWidgets; -} - -void SubmitEditorWidget::addDescriptionEditContextMenuAction(QAction *a) -{ - d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(-1, a)); -} - -void SubmitEditorWidget::insertDescriptionEditContextMenuAction(int pos, QAction *a) -{ - d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(pos, a)); -} - -void SubmitEditorWidget::editorCustomContextMenuRequested(const QPoint &pos) -{ - QScopedPointer menu(d->m_ui.description->createStandardContextMenu()); - // Extend - foreach (const SubmitEditorWidgetPrivate::AdditionalContextMenuAction &a, d->descriptionEditContextMenuActions) { - if (a.second) { - if (a.first >= 0) { - menu->insertAction(menu->actions().at(a.first), a.second); - } else { - menu->addAction(a.second); - } - } - } - menu->exec(d->m_ui.description->mapToGlobal(pos)); -} - -void SubmitEditorWidget::checkAllToggled() -{ - if (d->m_ignoreChange) - return; - if (d->m_ui.checkAllCheckBox->checkState() == Qt::Checked - || d->m_ui.checkAllCheckBox->checkState() == Qt::PartiallyChecked) { - setListModelChecked(d->m_ui.fileView->model(), true, checkableColumn); - } else { - setListModelChecked(d->m_ui.fileView->model(), false, checkableColumn); - } - // Reset that again, so that the user can't do it - d->m_ui.checkAllCheckBox->setTristate(false); -} - -void SubmitEditorWidget::checkAll() -{ - setListModelChecked(d->m_ui.fileView->model(), true, checkableColumn); -} - -void SubmitEditorWidget::uncheckAll() -{ - setListModelChecked(d->m_ui.fileView->model(), false, checkableColumn); -} - -void SubmitEditorWidget::fileListCustomContextMenuRequested(const QPoint & pos) -{ - // Execute menu offering to check/uncheck all - QMenu menu; - //: Check all for submit - QAction *checkAllAction = menu.addAction(tr("Check All")); - //: Uncheck all for submit - QAction *uncheckAllAction = menu.addAction(tr("Uncheck All")); - QAction *action = menu.exec(d->m_ui.fileView->mapToGlobal(pos)); - if (action == checkAllAction) { - checkAll(); - return; - } - if (action == uncheckAllAction) { - uncheckAll(); - return; - } -} - -bool SubmitEditorWidget::isEmptyFileListEnabled() const -{ - return d->m_emptyFileListEnabled; -} - -void SubmitEditorWidget::setEmptyFileListEnabled(bool e) -{ - if (e != d->m_emptyFileListEnabled) { - d->m_emptyFileListEnabled = e; - updateSubmitAction(); - } -} - -} // namespace Utils - -#include "submiteditorwidget.moc" diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h deleted file mode 100644 index 9d548e769b..0000000000 --- a/src/libs/utils/submiteditorwidget.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef SUBMITEDITORWIDGET_H -#define SUBMITEDITORWIDGET_H - -#include "utils_global.h" -#include "completingtextedit.h" - -#include -#include - -QT_BEGIN_NAMESPACE -class QListWidgetItem; -class QAction; -class QAbstractItemModel; -class QModelIndex; -class QLineEdit; -QT_END_NAMESPACE - -namespace Utils { - -class SubmitFieldWidget; -struct SubmitEditorWidgetPrivate; - -class QTCREATOR_UTILS_EXPORT SubmitEditorWidget : public QWidget -{ - Q_OBJECT - 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) - Q_PROPERTY(bool lineWrap READ lineWrap WRITE setLineWrap DESIGNABLE true) - Q_PROPERTY(int lineWrapWidth READ lineWrapWidth WRITE setLineWrapWidth DESIGNABLE true) - Q_PROPERTY(bool descriptionMandatory READ isDescriptionMandatory WRITE setDescriptionMandatory DESIGNABLE false) - Q_PROPERTY(bool emptyFileListEnabled READ isEmptyFileListEnabled WRITE setEmptyFileListEnabled DESIGNABLE true) - -public: - explicit SubmitEditorWidget(QWidget *parent = 0); - virtual ~SubmitEditorWidget(); - - // Register/Unregister actions that are managed by ActionManager with this widget. - // The submit action should have Core::Command::CA_UpdateText set as its text will - // be updated. - void registerActions(QAction *editorUndoAction, QAction *editorRedoAction, - QAction *submitAction = 0, QAction *diffAction = 0); - void unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, - QAction *submitAction = 0, QAction *diffAction = 0); - - QString descriptionText() const; - void setDescriptionText(const QString &text); - - // 'Commit' action enabled despite empty file list - bool isEmptyFileListEnabled() const; - void setEmptyFileListEnabled(bool e); - - int fileNameColumn() const; - void setFileNameColumn(int c); - - bool lineWrap() const; - void setLineWrap(bool); - - int lineWrapWidth() const; - void setLineWrapWidth(int); - - bool isDescriptionMandatory() const; - void setDescriptionMandatory(bool); - - QAbstractItemView::SelectionMode fileListSelectionMode() const; - void setFileListSelectionMode(QAbstractItemView::SelectionMode sm); - - void setFileModel(QAbstractItemModel *model); - QAbstractItemModel *fileModel() const; - - // Files to be included in submit - QStringList checkedFiles() const; - - // Selected files for diff - QStringList selectedFiles() const; - - CompletingTextEdit *descriptionEdit() const; - - void addDescriptionEditContextMenuAction(QAction *a); - void insertDescriptionEditContextMenuAction(int pos, QAction *a); - - void addSubmitFieldWidget(SubmitFieldWidget *f); - QList submitFieldWidgets() const; - - virtual bool canSubmit() const; - -signals: - void diffSelected(const QStringList &); - void fileSelectionChanged(bool someFileSelected); - void submitActionTextChanged(const QString &); - void submitActionEnabledChanged(bool); - -private slots: - void updateCheckAllComboBox(); - void checkAllToggled(); - void checkAll(); - void uncheckAll(); - -protected: - virtual QString cleanupDescription(const QString &) const; - virtual void changeEvent(QEvent *e); - virtual QString commitName() const; - void insertTopWidget(QWidget *w); - -protected slots: - void descriptionTextChanged(); - void updateSubmitAction(); - -private slots: - void triggerDiffSelected(); - void diffActivated(const QModelIndex &index); - void diffActivatedDelayed(); - void updateActions(); - void updateDiffAction(); - void editorCustomContextMenuRequested(const QPoint &); - void fileListCustomContextMenuRequested(const QPoint & pos); - -private: - bool hasSelection() const; - int checkedFilesCount() const; - - SubmitEditorWidgetPrivate *d; -}; - -} // namespace Utils - -#endif // SUBMITEDITORWIDGET_H diff --git a/src/libs/utils/submiteditorwidget.ui b/src/libs/utils/submiteditorwidget.ui deleted file mode 100644 index 526408e11f..0000000000 --- a/src/libs/utils/submiteditorwidget.ui +++ /dev/null @@ -1,107 +0,0 @@ - - - Utils::SubmitEditorWidget - - - - 0 - 0 - 582 - 502 - - - - Subversion Submit - - - - - - Qt::Vertical - - - false - - - - Descriptio&n - - - true - - - - - - false - - - - - - - - - 0 - - - - - F&iles - - - true - - - - - - Check a&ll - - - false - - - - - - - - - - - - - 0 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - Utils::CompletingTextEdit - QTextEdit -
utils/completingtextedit.h
-
-
- - -
diff --git a/src/libs/utils/submitfieldwidget.cpp b/src/libs/utils/submitfieldwidget.cpp deleted file mode 100644 index 7a9002f8d1..0000000000 --- a/src/libs/utils/submitfieldwidget.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#include "submitfieldwidget.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -enum { debug = 0 }; -enum { spacing = 2 }; - -static void inline setComboBlocked(QComboBox *cb, int index) -{ - const bool blocked = cb->blockSignals(true); - cb->setCurrentIndex(index); - cb->blockSignals(blocked); -} - -/*! - \class Utils::SubmitFieldWidget - \brief A widget for editing submit message fields like "reviewed-by:", - "signed-off-by:". - - It displays them in a vertical row of combo/line edit fields - that is modeled after the target address controls of mail clients. - When choosing a different field in the combo, a new row is opened if text - has been entered for the current field. Optionally, a "Browse..." button and - completer can be added. -*/ - -namespace Utils { - -// Field/Row entry -struct FieldEntry { - FieldEntry(); - void createGui(const QIcon &removeIcon); - void deleteGuiLater(); - - QComboBox *combo; - QHBoxLayout *layout; - QLineEdit *lineEdit; - QToolBar *toolBar; - QToolButton *clearButton; - QToolButton *browseButton; - int comboIndex; -}; - -FieldEntry::FieldEntry() : - combo(0), - layout(0), - lineEdit(0), - toolBar(0), - clearButton(0), - browseButton(0), - comboIndex(0) -{ -} - -void FieldEntry::createGui(const QIcon &removeIcon) -{ - layout = new QHBoxLayout; - layout->setMargin(0); - layout ->setSpacing(spacing); - combo = new QComboBox; - layout->addWidget(combo); - lineEdit = new QLineEdit; - layout->addWidget(lineEdit); - toolBar = new QToolBar; - toolBar->setProperty("_q_custom_style_disabled", QVariant(true)); - layout->addWidget(toolBar); - clearButton = new QToolButton; - clearButton->setIcon(removeIcon); - toolBar->addWidget(clearButton); - browseButton = new QToolButton; - browseButton->setText(QLatin1String("...")); - toolBar->addWidget(browseButton); -} - -void FieldEntry::deleteGuiLater() -{ - clearButton->deleteLater(); - browseButton->deleteLater(); - toolBar->deleteLater(); - lineEdit->deleteLater(); - combo->deleteLater(); - layout->deleteLater(); -} - -// ------- SubmitFieldWidgetPrivate -struct SubmitFieldWidgetPrivate { - SubmitFieldWidgetPrivate(); - - int findSender(const QObject *o) const; - int findField(const QString &f, int excluded = -1) const; - inline QString fieldText(int) const; - inline QString fieldValue(int) const; - inline void focusField(int); - - const QIcon removeFieldIcon; - QStringList fields; - QCompleter *completer; - bool hasBrowseButton; - bool allowDuplicateFields; - - QList fieldEntries; - QVBoxLayout *layout; -}; - -SubmitFieldWidgetPrivate::SubmitFieldWidgetPrivate() : - removeFieldIcon(QLatin1String(":/utils/images/removesubmitfield.png")), - completer(0), - hasBrowseButton(false), - allowDuplicateFields(false), - layout(0) -{ -} - -int SubmitFieldWidgetPrivate::findSender(const QObject *o) const -{ - const int count = fieldEntries.size(); - for (int i = 0; i < count; i++) { - const FieldEntry &fe = fieldEntries.at(i); - if (fe.combo == o || fe.browseButton == o || fe.clearButton == o || fe.lineEdit == o) - return i; - } - return -1; -} - -int SubmitFieldWidgetPrivate::findField(const QString &ft, int excluded) const -{ - const int count = fieldEntries.size(); - for (int i = 0; i < count; i++) - if (i != excluded && fieldText(i) == ft) - return i; - return -1; -} - -QString SubmitFieldWidgetPrivate::fieldText(int pos) const -{ - return fieldEntries.at(pos).combo->currentText(); -} - -QString SubmitFieldWidgetPrivate::fieldValue(int pos) const -{ - return fieldEntries.at(pos).lineEdit->text(); -} - -void SubmitFieldWidgetPrivate::focusField(int pos) -{ - fieldEntries.at(pos).lineEdit->setFocus(Qt::TabFocusReason); -} - -// SubmitFieldWidget -SubmitFieldWidget::SubmitFieldWidget(QWidget *parent) : - QWidget(parent), - d(new SubmitFieldWidgetPrivate) -{ - d->layout = new QVBoxLayout; - d->layout->setMargin(0); - d->layout->setSpacing(spacing); - setLayout(d->layout); -} - -SubmitFieldWidget::~SubmitFieldWidget() -{ - delete d; -} - -void SubmitFieldWidget::setFields(const QStringList & f) -{ - // remove old fields - for (int i = d->fieldEntries.size() - 1 ; i >= 0 ; i--) - removeField(i); - - d->fields = f; - if (!f.empty()) - createField(f.front()); -} - -QStringList SubmitFieldWidget::fields() const -{ - return d->fields; -} - -bool SubmitFieldWidget::hasBrowseButton() const -{ - return d->hasBrowseButton; -} - -void SubmitFieldWidget::setHasBrowseButton(bool on) -{ - if (d->hasBrowseButton == on) - return; - d->hasBrowseButton = on; - foreach (const FieldEntry &fe, d->fieldEntries) - fe.browseButton->setVisible(on); -} - -bool SubmitFieldWidget::allowDuplicateFields() const -{ - return d->allowDuplicateFields; -} - -void SubmitFieldWidget::setAllowDuplicateFields(bool v) -{ - d->allowDuplicateFields = v; -} - -QCompleter *SubmitFieldWidget::completer() const -{ - return d->completer; -} - -void SubmitFieldWidget::setCompleter(QCompleter *c) -{ - if (c == d->completer) - return; - d->completer = c; - foreach (const FieldEntry &fe, d->fieldEntries) - fe.lineEdit->setCompleter(c); -} - -QString SubmitFieldWidget::fieldValue(int pos) const -{ - return d->fieldValue(pos); -} - -void SubmitFieldWidget::setFieldValue(int pos, const QString &value) -{ - d->fieldEntries.at(pos).lineEdit->setText(value); -} - -QString SubmitFieldWidget::fieldValues() const -{ - const QChar blank = QLatin1Char(' '); - const QChar newLine = QLatin1Char('\n'); - // Format as "RevBy: value\nSigned-Off: value\n" - QString rc; - foreach (const FieldEntry &fe, d->fieldEntries) { - const QString value = fe.lineEdit->text().trimmed(); - if (!value.isEmpty()) { - rc += fe.combo->currentText(); - rc += blank; - rc += value; - rc += newLine; - } - } - return rc; -} - -void SubmitFieldWidget::createField(const QString &f) -{ - FieldEntry fe; - fe.createGui(d->removeFieldIcon); - fe.combo->addItems(d->fields); - if (!f.isEmpty()) { - const int index = fe.combo->findText(f); - if (index != -1) { - setComboBlocked(fe.combo, index); - fe.comboIndex = index; - } - } - - connect(fe.browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked())); - if (!d->hasBrowseButton) - fe.browseButton->setVisible(false); - - if (d->completer) - fe.lineEdit->setCompleter(d->completer); - - connect(fe.combo, SIGNAL(currentIndexChanged(int)), - this, SLOT(slotComboIndexChanged(int))); - connect(fe.clearButton, SIGNAL(clicked()), - this, SLOT(slotRemove())); - d->layout->addLayout(fe.layout); - d->fieldEntries.push_back(fe); -} - -void SubmitFieldWidget::slotRemove() -{ - // Never remove first entry - const int index = d->findSender(sender()); - switch (index) { - case -1: - break; - case 0: - d->fieldEntries.front().lineEdit->clear(); - break; - default: - removeField(index); - break; - } -} - -void SubmitFieldWidget::removeField(int index) -{ - FieldEntry fe = d->fieldEntries.takeAt(index); - QLayoutItem * item = d->layout->takeAt(index); - fe.deleteGuiLater(); - delete item; -} - -void SubmitFieldWidget::slotComboIndexChanged(int comboIndex) -{ - const int pos = d->findSender(sender()); - if (debug) - qDebug() << '>' << Q_FUNC_INFO << pos; - if (pos == -1) - return; - // Accept new index or reset combo to previous value? - int &previousIndex = d->fieldEntries[pos].comboIndex; - if (comboIndexChange(pos, comboIndex)) { - previousIndex = comboIndex; - } else { - setComboBlocked(d->fieldEntries.at(pos).combo, previousIndex); - } - if (debug) - qDebug() << '<' << Q_FUNC_INFO << pos; -} - -// Handle change of a combo. Return "false" if the combo -// is to be reset (refuse new field). -bool SubmitFieldWidget::comboIndexChange(int pos, int index) -{ - const QString newField = d->fieldEntries.at(pos).combo->itemText(index); - // If the field is visible elsewhere: focus the existing one and refuse - if (!d->allowDuplicateFields) { - const int existingFieldIndex = d->findField(newField, pos); - if (existingFieldIndex != -1) { - d->focusField(existingFieldIndex); - return false; - } - } - // Empty value: just change the field - if (d->fieldValue(pos).isEmpty()) - return true; - // Non-empty: Create a new field and reset the triggering combo - createField(newField); - return false; -} - -void SubmitFieldWidget::slotBrowseButtonClicked() -{ - const int pos = d->findSender(sender()); - emit browseButtonClicked(pos, d->fieldText(pos)); -} - -} diff --git a/src/libs/utils/submitfieldwidget.h b/src/libs/utils/submitfieldwidget.h deleted file mode 100644 index feea4cfd47..0000000000 --- a/src/libs/utils/submitfieldwidget.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -****************************************************************************/ - -#ifndef SUBMITFIELDWIDGET_H -#define SUBMITFIELDWIDGET_H - -#include "utils_global.h" - -#include - -QT_BEGIN_NAMESPACE -class QCompleter; -QT_END_NAMESPACE - -namespace Utils { - -struct SubmitFieldWidgetPrivate; - -class QTCREATOR_UTILS_EXPORT SubmitFieldWidget : public QWidget -{ - Q_OBJECT - Q_PROPERTY(QStringList fields READ fields WRITE setFields DESIGNABLE true) - Q_PROPERTY(bool hasBrowseButton READ hasBrowseButton WRITE setHasBrowseButton DESIGNABLE true) - Q_PROPERTY(bool allowDuplicateFields READ allowDuplicateFields WRITE setAllowDuplicateFields DESIGNABLE true) - -public: - explicit SubmitFieldWidget(QWidget *parent = 0); - virtual ~SubmitFieldWidget(); - - QStringList fields() const; - void setFields(const QStringList&); - - bool hasBrowseButton() const; - void setHasBrowseButton(bool d); - - // Allow several entries for fields ("reviewed-by: a", "reviewed-by: b") - bool allowDuplicateFields() const; - void setAllowDuplicateFields(bool); - - QCompleter *completer() const; - void setCompleter(QCompleter *c); - - QString fieldValue(int pos) const; - void setFieldValue(int pos, const QString &value); - - QString fieldValues() const; - -signals: - void browseButtonClicked(int pos, const QString &field); - -private slots: - void slotRemove(); - void slotComboIndexChanged(int); - void slotBrowseButtonClicked(); - -private: - void removeField(int index); - bool comboIndexChange(int fieldNumber, int index); - void createField(const QString &f); - - SubmitFieldWidgetPrivate *d; -}; - -} // namespace Utils - -#endif // SUBMITFIELDWIDGET_H diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 78569bc1de..2c2df3279a 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -40,12 +40,10 @@ SOURCES += $$PWD/environment.cpp \ $$PWD/fancylineedit.cpp \ $$PWD/qtcolorbutton.cpp \ $$PWD/savedaction.cpp \ - $$PWD/submiteditorwidget.cpp \ $$PWD/synchronousprocess.cpp \ $$PWD/savefile.cpp \ $$PWD/fileutils.cpp \ $$PWD/textfileformat.cpp \ - $$PWD/submitfieldwidget.cpp \ $$PWD/consoleprocess.cpp \ $$PWD/uncommentselection.cpp \ $$PWD/parameteraction.cpp \ @@ -123,14 +121,12 @@ HEADERS += \ $$PWD/fancylineedit.h \ $$PWD/qtcolorbutton.h \ $$PWD/savedaction.h \ - $$PWD/submiteditorwidget.h \ $$PWD/consoleprocess.h \ $$PWD/consoleprocess_p.h \ $$PWD/synchronousprocess.h \ $$PWD/savefile.h \ $$PWD/fileutils.h \ $$PWD/textfileformat.h \ - $$PWD/submitfieldwidget.h \ $$PWD/uncommentselection.h \ $$PWD/parameteraction.h \ $$PWD/treewidgetcolumnstretcher.h \ @@ -174,7 +170,6 @@ HEADERS += \ FORMS += $$PWD/filewizardpage.ui \ $$PWD/projectintropage.ui \ - $$PWD/newclasswidget.ui \ - $$PWD/submiteditorwidget.ui + $$PWD/newclasswidget.ui RESOURCES += $$PWD/utils.qrc diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index 34cd5d0a13..916a7df732 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -152,11 +152,6 @@ QtcLibrary { "styledbar.h", "stylehelper.cpp", "stylehelper.h", - "submiteditorwidget.cpp", - "submiteditorwidget.h", - "submiteditorwidget.ui", - "submitfieldwidget.cpp", - "submitfieldwidget.h", "synchronousprocess.cpp", "synchronousprocess.h", "tcpportsgatherer.cpp", @@ -178,7 +173,6 @@ QtcLibrary { "images/crumblepath-segment-selected-end.png", "images/crumblepath-segment-selected.png", "images/crumblepath-segment.png", - "images/removesubmitfield.png", "images/triangle_vert.png", ] diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 52ec41382b..2748bccb6c 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -1,6 +1,5 @@ - images/removesubmitfield.png images/arrow.png images/crumblepath-segment.png images/crumblepath-segment-end.png diff --git a/src/plugins/bazaar/bazaarcommitwidget.cpp b/src/plugins/bazaar/bazaarcommitwidget.cpp index 82b169dcea..d55ece70e9 100644 --- a/src/plugins/bazaar/bazaarcommitwidget.cpp +++ b/src/plugins/bazaar/bazaarcommitwidget.cpp @@ -114,7 +114,7 @@ void BazaarSubmitHighlighter::highlightBlock(const QString &text) BazaarCommitWidget::BazaarCommitWidget(QWidget *parent) : - Utils::SubmitEditorWidget(parent), + VcsBase::SubmitEditorWidget(parent), m_bazaarCommitPanel(new QWidget) { m_bazaarCommitPanelUi.setupUi(m_bazaarCommitPanel); diff --git a/src/plugins/bazaar/bazaarcommitwidget.h b/src/plugins/bazaar/bazaarcommitwidget.h index 7fe3ccb1bb..24df89a599 100644 --- a/src/plugins/bazaar/bazaarcommitwidget.h +++ b/src/plugins/bazaar/bazaarcommitwidget.h @@ -31,7 +31,7 @@ #include "ui_bazaarcommitpanel.h" -#include +#include namespace Bazaar { namespace Internal { @@ -42,7 +42,7 @@ class BranchInfo; Some extra fields have been added to the standard SubmitEditorWidget, to help to conform to the commit style that is used by both git and Bazaar*/ -class BazaarCommitWidget : public Utils::SubmitEditorWidget +class BazaarCommitWidget : public VcsBase::SubmitEditorWidget { public: diff --git a/src/plugins/clearcase/clearcasesubmiteditor.cpp b/src/plugins/clearcase/clearcasesubmiteditor.cpp index f8ca7e2c0b..35e9365852 100644 --- a/src/plugins/clearcase/clearcasesubmiteditor.cpp +++ b/src/plugins/clearcase/clearcasesubmiteditor.cpp @@ -31,7 +31,7 @@ #include "clearcasesubmiteditor.h" #include "clearcasesubmiteditorwidget.h" -#include +#include #include using namespace ClearCase::Internal; diff --git a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp index 0b937a8335..8e7c1d0162 100644 --- a/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp +++ b/src/plugins/clearcase/clearcasesubmiteditorwidget.cpp @@ -40,7 +40,7 @@ using namespace ClearCase::Internal; ClearCaseSubmitEditorWidget::ClearCaseSubmitEditorWidget(QWidget *parent) : - Utils::SubmitEditorWidget(parent), + VcsBase::SubmitEditorWidget(parent), m_actSelector(0) { setDescriptionMandatory(false); diff --git a/src/plugins/clearcase/clearcasesubmiteditorwidget.h b/src/plugins/clearcase/clearcasesubmiteditorwidget.h index ed93004cc6..5534fe8cdc 100644 --- a/src/plugins/clearcase/clearcasesubmiteditorwidget.h +++ b/src/plugins/clearcase/clearcasesubmiteditorwidget.h @@ -31,7 +31,7 @@ #ifndef CLEARCASESUBMITEDITORWIDGET_H #define CLEARCASESUBMITEDITORWIDGET_H -#include +#include QT_BEGIN_NAMESPACE class QCheckBox; @@ -43,7 +43,7 @@ namespace Internal { class ActivitySelector; -class ClearCaseSubmitEditorWidget : public Utils::SubmitEditorWidget +class ClearCaseSubmitEditorWidget : public VcsBase::SubmitEditorWidget { Q_OBJECT diff --git a/src/plugins/cvs/cvssubmiteditor.cpp b/src/plugins/cvs/cvssubmiteditor.cpp index ed08bd963a..8392e84f4e 100644 --- a/src/plugins/cvs/cvssubmiteditor.cpp +++ b/src/plugins/cvs/cvssubmiteditor.cpp @@ -30,7 +30,7 @@ #include "cvssubmiteditor.h" -#include +#include #include using namespace Cvs::Internal; @@ -38,7 +38,7 @@ using namespace VcsBase; CvsSubmitEditor::CvsSubmitEditor(const VcsBaseSubmitEditorParameters *parameters, QWidget *parentWidget) : - VcsBaseSubmitEditor(parameters, new Utils::SubmitEditorWidget(parentWidget)), + VcsBaseSubmitEditor(parameters, new VcsBase::SubmitEditorWidget(parentWidget)), m_msgAdded(tr("Added")), m_msgRemoved(tr("Removed")), m_msgModified(tr("Modified")) diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp index 9e234fd5b4..8c79265e3e 100644 --- a/src/plugins/git/gitsubmiteditorwidget.cpp +++ b/src/plugins/git/gitsubmiteditorwidget.cpp @@ -114,7 +114,7 @@ void GitSubmitHighlighter::highlightBlock(const QString &text) // ------------------ GitSubmitEditorWidget::GitSubmitEditorWidget(QWidget *parent) : - Utils::SubmitEditorWidget(parent), + VcsBase::SubmitEditorWidget(parent), m_gitSubmitPanel(new QWidget), m_hasUnmerged(false) { diff --git a/src/plugins/git/gitsubmiteditorwidget.h b/src/plugins/git/gitsubmiteditorwidget.h index 18f93c987e..191578f5f2 100644 --- a/src/plugins/git/gitsubmiteditorwidget.h +++ b/src/plugins/git/gitsubmiteditorwidget.h @@ -32,7 +32,7 @@ #include "ui_gitsubmitpanel.h" -#include +#include QT_BEGIN_NAMESPACE class QValidator; @@ -52,7 +52,7 @@ struct GitSubmitEditorPanelData; * remaining un-added and untracked files will be added 'unchecked' for the * user to click. */ -class GitSubmitEditorWidget : public Utils::SubmitEditorWidget +class GitSubmitEditorWidget : public VcsBase::SubmitEditorWidget { Q_OBJECT diff --git a/src/plugins/mercurial/mercurialcommitwidget.cpp b/src/plugins/mercurial/mercurialcommitwidget.cpp index 6bd215c262..bb14eb6806 100644 --- a/src/plugins/mercurial/mercurialcommitwidget.cpp +++ b/src/plugins/mercurial/mercurialcommitwidget.cpp @@ -114,7 +114,7 @@ void MercurialSubmitHighlighter::highlightBlock(const QString &text) MercurialCommitWidget::MercurialCommitWidget(QWidget *parent) : - Utils::SubmitEditorWidget(parent), + VcsBase::SubmitEditorWidget(parent), mercurialCommitPanel(new QWidget) { mercurialCommitPanelUi.setupUi(mercurialCommitPanel); diff --git a/src/plugins/mercurial/mercurialcommitwidget.h b/src/plugins/mercurial/mercurialcommitwidget.h index 5b3099aedd..f5c276556c 100644 --- a/src/plugins/mercurial/mercurialcommitwidget.h +++ b/src/plugins/mercurial/mercurialcommitwidget.h @@ -32,7 +32,7 @@ #include "ui_mercurialcommitpanel.h" -#include +#include namespace Mercurial { namespace Internal { @@ -41,7 +41,7 @@ namespace Internal { Some extra fields have been added to the standard SubmitEditorWidget, to help to conform to the commit style that is used by both git and Mercurial*/ -class MercurialCommitWidget : public Utils::SubmitEditorWidget +class MercurialCommitWidget : public VcsBase::SubmitEditorWidget { public: diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.cpp b/src/plugins/perforce/perforcesubmiteditorwidget.cpp index 1bbebab3a4..509fee713f 100644 --- a/src/plugins/perforce/perforcesubmiteditorwidget.cpp +++ b/src/plugins/perforce/perforcesubmiteditorwidget.cpp @@ -33,7 +33,7 @@ namespace Perforce { namespace Internal { PerforceSubmitEditorWidget::PerforceSubmitEditorWidget(QWidget *parent) : - Utils::SubmitEditorWidget(parent), + VcsBase::SubmitEditorWidget(parent), m_submitPanel(new QGroupBox) { m_submitPanelUi.setupUi(m_submitPanel); diff --git a/src/plugins/perforce/perforcesubmiteditorwidget.h b/src/plugins/perforce/perforcesubmiteditorwidget.h index 1a0ed8d7e7..7102cf30de 100644 --- a/src/plugins/perforce/perforcesubmiteditorwidget.h +++ b/src/plugins/perforce/perforcesubmiteditorwidget.h @@ -31,14 +31,14 @@ #define PERFORCESUBMITEDITORWIDGET_H #include "ui_submitpanel.h" -#include +#include namespace Perforce { namespace Internal { /* Submit editor widget with additional information pane * at the top. */ -class PerforceSubmitEditorWidget : public Utils::SubmitEditorWidget +class PerforceSubmitEditorWidget : public VcsBase::SubmitEditorWidget { public: diff --git a/src/plugins/subversion/subversionsubmiteditor.cpp b/src/plugins/subversion/subversionsubmiteditor.cpp index c68f8780a3..152d5722d9 100644 --- a/src/plugins/subversion/subversionsubmiteditor.cpp +++ b/src/plugins/subversion/subversionsubmiteditor.cpp @@ -30,14 +30,14 @@ #include "subversionsubmiteditor.h" -#include +#include #include using namespace Subversion::Internal; SubversionSubmitEditor::SubversionSubmitEditor(const VcsBase::VcsBaseSubmitEditorParameters *parameters, QWidget *parentWidget) : - VcsBase::VcsBaseSubmitEditor(parameters, new Utils::SubmitEditorWidget(parentWidget)) + VcsBase::VcsBaseSubmitEditor(parameters, new VcsBase::SubmitEditorWidget(parentWidget)) { setDisplayName(tr("Subversion Submit")); setDescriptionMandatory(false); diff --git a/src/plugins/vcsbase/images/removesubmitfield.png b/src/plugins/vcsbase/images/removesubmitfield.png new file mode 100644 index 0000000000..e4139afc55 Binary files /dev/null and b/src/plugins/vcsbase/images/removesubmitfield.png differ diff --git a/src/plugins/vcsbase/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp new file mode 100644 index 0000000000..9ad7bfaee1 --- /dev/null +++ b/src/plugins/vcsbase/submiteditorwidget.cpp @@ -0,0 +1,719 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "submiteditorwidget.h" +#include "submitfieldwidget.h" +#include "ui_submiteditorwidget.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +enum { debug = 0 }; +enum { defaultLineWidth = 72 }; + +enum { checkableColumn = 0 }; + +/*! + \class VcsBase::SubmitEditorWidget + + \brief Presents a VCS commit message in a text editor and a + checkable list of modified files in a list window. + + The user can delete files from the list by unchecking them or diff the selection + by doubleclicking. A list model which contains the file in a column + specified by fileNameColumn should be set using setFileModel(). + + Additionally, standard creator actions can be registered: + Undo/redo will be set up to work with the description editor. + Submit will be set up to be enabled according to checkstate. + Diff will be set up to trigger diffSelected(). + + Note that the actions are connected by signals; in the rare event that there + are several instances of the SubmitEditorWidget belonging to the same + context active, the actions must be registered/unregistered in the editor + change event. + Care should be taken to ensure the widget is deleted properly when the + editor closes. +*/ + +namespace VcsBase { + +// QActionPushButton: A push button tied to an action +// (similar to a QToolButton) +class QActionPushButton : public QPushButton +{ + Q_OBJECT +public: + explicit QActionPushButton(QAction *a); + +private slots: + void actionChanged(); +}; + +QActionPushButton::QActionPushButton(QAction *a) : + QPushButton(a->icon(), a->text()) +{ + connect(a, SIGNAL(changed()), this, SLOT(actionChanged())); + connect(this, SIGNAL(clicked()), a, SLOT(trigger())); + setEnabled(a->isEnabled()); +} + +void QActionPushButton::actionChanged() +{ + if (const QAction *a = qobject_cast(sender())) { + setEnabled(a->isEnabled()); + setText(a->text()); + } +} + +// A helper parented on a QAction, +// making QAction::setText() a slot (which it currently is not). +class QActionSetTextSlotHelper : public QObject +{ + Q_OBJECT +public: + explicit QActionSetTextSlotHelper(QAction *a) : QObject(a) {} + +public slots: + void setText(const QString &t) { + if (QAction *action = qobject_cast(parent())) + action->setText(t); + } +}; + +// Helpers to retrieve model data +static inline bool listModelChecked(const QAbstractItemModel *model, int row, int column = 0) +{ + const QModelIndex checkableIndex = model->index(row, column, QModelIndex()); + return model->data(checkableIndex, Qt::CheckStateRole).toInt() == Qt::Checked; +} + +static void setListModelChecked(QAbstractItemModel *model, bool checked, int column = 0) +{ + const QVariant data = QVariant(int(checked ? Qt::Checked : Qt::Unchecked)); + const int count = model->rowCount(); + for (int i = 0; i < count; i++) { + const QModelIndex checkableIndex = model->index(i, column, QModelIndex()); + model->setData(checkableIndex, data, Qt::CheckStateRole); + } +} + +static inline QString listModelText(const QAbstractItemModel *model, int row, int column) +{ + const QModelIndex index = model->index(row, column, QModelIndex()); + return model->data(index, Qt::DisplayRole).toString(); +} + +// Convenience to extract a list of selected indexes +QList selectedRows(const QAbstractItemView *view) +{ + const QModelIndexList indexList = view->selectionModel()->selectedRows(0); + if (indexList.empty()) + return QList(); + QList rc; + const QModelIndexList::const_iterator cend = indexList.constEnd(); + for (QModelIndexList::const_iterator it = indexList.constBegin(); it != cend; ++it) + rc.push_back(it->row()); + return rc; +} + +// ----------- SubmitEditorWidgetPrivate + +struct SubmitEditorWidgetPrivate +{ + // A pair of position/action to extend context menus + typedef QPair > AdditionalContextMenuAction; + + SubmitEditorWidgetPrivate(); + + Ui::SubmitEditorWidget m_ui; + bool m_filesSelected; + int m_fileNameColumn; + int m_activatedRow; + bool m_emptyFileListEnabled; + + QList descriptionEditContextMenuActions; + QVBoxLayout *m_fieldLayout; + QList m_fieldWidgets; + QShortcut *m_submitShortcut; + int m_lineWidth; + + bool m_commitEnabled; + bool m_ignoreChange; + bool m_descriptionMandatory; +}; + +SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() : + m_filesSelected(false), + m_fileNameColumn(1), + m_activatedRow(-1), + m_emptyFileListEnabled(false), + m_fieldLayout(0), + m_submitShortcut(0), + m_lineWidth(defaultLineWidth), + m_commitEnabled(false), + m_ignoreChange(false), + m_descriptionMandatory(true) +{ +} + +SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) : + QWidget(parent), + d(new SubmitEditorWidgetPrivate) +{ + d->m_ui.setupUi(this); + d->m_ui.description->setContextMenuPolicy(Qt::CustomContextMenu); + d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap); + d->m_ui.description->setWordWrapMode(QTextOption::WordWrap); + connect(d->m_ui.description, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(editorCustomContextMenuRequested(QPoint))); + connect(d->m_ui.description, SIGNAL(textChanged()), + this, SLOT(descriptionTextChanged())); + + // File List + d->m_ui.fileView->setContextMenuPolicy(Qt::CustomContextMenu); + connect(d->m_ui.fileView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(fileListCustomContextMenuRequested(QPoint))); + d->m_ui.fileView->setSelectionMode(QAbstractItemView::ExtendedSelection); + d->m_ui.fileView->setRootIsDecorated(false); + connect(d->m_ui.fileView, SIGNAL(doubleClicked(QModelIndex)), + this, SLOT(diffActivated(QModelIndex))); + + connect(d->m_ui.checkAllCheckBox, SIGNAL(stateChanged(int)), + this, SLOT(checkAllToggled())); + + setFocusPolicy(Qt::StrongFocus); + setFocusProxy(d->m_ui.description); +} + +SubmitEditorWidget::~SubmitEditorWidget() +{ + delete d; +} + +void SubmitEditorWidget::registerActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction, QAction *diffAction) +{ + if (editorUndoAction) { + editorUndoAction->setEnabled(d->m_ui.description->document()->isUndoAvailable()); + connect(d->m_ui.description, SIGNAL(undoAvailable(bool)), editorUndoAction, SLOT(setEnabled(bool))); + connect(editorUndoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(undo())); + } + if (editorRedoAction) { + editorRedoAction->setEnabled(d->m_ui.description->document()->isRedoAvailable()); + connect(d->m_ui.description, SIGNAL(redoAvailable(bool)), editorRedoAction, SLOT(setEnabled(bool))); + connect(editorRedoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(redo())); + } + + if (submitAction) { + if (debug) { + int count = 0; + if (const QAbstractItemModel *model = d->m_ui.fileView->model()) + count = model->rowCount(); + qDebug() << Q_FUNC_INFO << submitAction << count << "items"; + } + d->m_commitEnabled = !canSubmit(); + connect(this, SIGNAL(submitActionEnabledChanged(bool)), submitAction, SLOT(setEnabled(bool))); + // Wire setText via QActionSetTextSlotHelper. + QActionSetTextSlotHelper *actionSlotHelper = submitAction->findChild(); + if (!actionSlotHelper) + actionSlotHelper = new QActionSetTextSlotHelper(submitAction); + connect(this, SIGNAL(submitActionTextChanged(QString)), actionSlotHelper, SLOT(setText(QString))); + d->m_ui.buttonLayout->addWidget(new QActionPushButton(submitAction)); + if (!d->m_submitShortcut) + d->m_submitShortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Return), this); + connect(d->m_submitShortcut, SIGNAL(activated()), submitAction, SLOT(trigger())); + } + if (diffAction) { + if (debug) + qDebug() << diffAction << d->m_filesSelected; + diffAction->setEnabled(d->m_filesSelected); + connect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); + connect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); + d->m_ui.buttonLayout->addWidget(new QActionPushButton(diffAction)); + } +} + +void SubmitEditorWidget::unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction, QAction *diffAction) +{ + if (editorUndoAction) { + disconnect(d->m_ui.description, SIGNAL(undoAvailableChanged(bool)), editorUndoAction, SLOT(setEnabled(bool))); + disconnect(editorUndoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(undo())); + } + if (editorRedoAction) { + disconnect(d->m_ui.description, SIGNAL(redoAvailableChanged(bool)), editorRedoAction, SLOT(setEnabled(bool))); + disconnect(editorRedoAction, SIGNAL(triggered()), d->m_ui.description, SLOT(redo())); + } + + if (submitAction) { + disconnect(this, SIGNAL(submitActionEnabledChanged(bool)), submitAction, SLOT(setEnabled(bool))); + // Just deactivate the QActionSetTextSlotHelper on the action + disconnect(this, SIGNAL(submitActionTextChanged(QString)), 0, 0); + } + + if (diffAction) { + disconnect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool))); + disconnect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected())); + } +} + +// Make sure we have one terminating NL. Do not trim front as leading space might be +// required for some formattings. +static inline QString trimMessageText(QString t) +{ + if (t.isEmpty()) + return t; + // Trim back of string. + const int last = t.size() - 1; + int lastWordCharacter = last; + for ( ; lastWordCharacter >= 0 && t.at(lastWordCharacter).isSpace() ; lastWordCharacter--) ; + if (lastWordCharacter != last) + t.truncate(lastWordCharacter + 1); + t += QLatin1Char('\n'); + return t; +} + +// Extract the wrapped text from a text edit, which performs +// the wrapping only optically. +static QString wrappedText(const QTextEdit *e) +{ + const QChar newLine = QLatin1Char('\n'); + QString rc; + QTextCursor cursor(e->document()); + cursor.movePosition(QTextCursor::Start); + while (!cursor.atEnd()) { + cursor.select(QTextCursor::LineUnderCursor); + rc += cursor.selectedText(); + rc += newLine; + cursor.movePosition(QTextCursor::EndOfLine); // Mac needs it + cursor.movePosition(QTextCursor::NextCharacter); + } + return rc; +} + +QString SubmitEditorWidget::descriptionText() const +{ + QString rc = trimMessageText(lineWrap() ? wrappedText(d->m_ui.description) : + d->m_ui.description->toPlainText()); + // append field entries + foreach (const SubmitFieldWidget *fw, d->m_fieldWidgets) + rc += fw->fieldValues(); + return cleanupDescription(rc); +} + +void SubmitEditorWidget::setDescriptionText(const QString &text) +{ + d->m_ui.description->setPlainText(text); +} + +bool SubmitEditorWidget::lineWrap() const +{ + return d->m_ui.description->lineWrapMode() != QTextEdit::NoWrap; +} + +void SubmitEditorWidget::setLineWrap(bool v) +{ + if (debug) + qDebug() << Q_FUNC_INFO << v; + if (v) { + d->m_ui.description->setLineWrapColumnOrWidth(d->m_lineWidth); + d->m_ui.description->setLineWrapMode(QTextEdit::FixedColumnWidth); + } else { + d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap); + } +} + +int SubmitEditorWidget::lineWrapWidth() const +{ + return d->m_lineWidth; +} + +void SubmitEditorWidget::setLineWrapWidth(int v) +{ + if (debug) + qDebug() << Q_FUNC_INFO << v << lineWrap(); + if (d->m_lineWidth == v) + return; + d->m_lineWidth = v; + if (lineWrap()) + d->m_ui.description->setLineWrapColumnOrWidth(v); +} + +bool SubmitEditorWidget::isDescriptionMandatory() const +{ + return d->m_descriptionMandatory; +} + +void SubmitEditorWidget::setDescriptionMandatory(bool v) +{ + d->m_descriptionMandatory = v; +} + +int SubmitEditorWidget::fileNameColumn() const +{ + return d->m_fileNameColumn; +} + +void SubmitEditorWidget::setFileNameColumn(int c) +{ + d->m_fileNameColumn = c; +} + +QAbstractItemView::SelectionMode SubmitEditorWidget::fileListSelectionMode() const +{ + return d->m_ui.fileView->selectionMode(); +} + +void SubmitEditorWidget::setFileListSelectionMode(QAbstractItemView::SelectionMode sm) +{ + d->m_ui.fileView->setSelectionMode(sm); +} + +void SubmitEditorWidget::setFileModel(QAbstractItemModel *model) +{ + d->m_ui.fileView->clearSelection(); // trigger the change signals + + d->m_ui.fileView->setModel(model); + + if (model->rowCount()) { + const int columnCount = model->columnCount(); + for (int c = 0; c < columnCount; c++) + d->m_ui.fileView->resizeColumnToContents(c); + } + + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(updateSubmitAction())); + connect(model, SIGNAL(modelReset()), + this, SLOT(updateSubmitAction())); + connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this, SLOT(updateCheckAllComboBox())); + connect(model, SIGNAL(modelReset()), + this, SLOT(updateCheckAllComboBox())); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(updateSubmitAction())); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(updateSubmitAction())); + connect(d->m_ui.fileView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(updateDiffAction())); + updateActions(); +} + +QAbstractItemModel *SubmitEditorWidget::fileModel() const +{ + return d->m_ui.fileView->model(); +} + +QStringList SubmitEditorWidget::selectedFiles() const +{ + const QList selection = selectedRows(d->m_ui.fileView); + if (selection.empty()) + return QStringList(); + + QStringList rc; + const QAbstractItemModel *model = d->m_ui.fileView->model(); + const int count = selection.size(); + for (int i = 0; i < count; i++) + rc.push_back(listModelText(model, selection.at(i), fileNameColumn())); + return rc; +} + +QStringList SubmitEditorWidget::checkedFiles() const +{ + QStringList rc; + const QAbstractItemModel *model = d->m_ui.fileView->model(); + if (!model) + return rc; + const int count = model->rowCount(); + for (int i = 0; i < count; i++) + if (listModelChecked(model, i, checkableColumn)) + rc.push_back(listModelText(model, i, fileNameColumn())); + return rc; +} + +Utils::CompletingTextEdit *SubmitEditorWidget::descriptionEdit() const +{ + return d->m_ui.description; +} + +void SubmitEditorWidget::triggerDiffSelected() +{ + const QStringList sel = selectedFiles(); + if (!sel.empty()) + emit diffSelected(sel); +} + +void SubmitEditorWidget::diffActivatedDelayed() +{ + const QStringList files = QStringList(listModelText(d->m_ui.fileView->model(), d->m_activatedRow, fileNameColumn())); + emit diffSelected(files); +} + +void SubmitEditorWidget::diffActivated(const QModelIndex &index) +{ + // We need to delay the signal, otherwise, the diff editor will not + // be in the foreground. + d->m_activatedRow = index.row(); + QTimer::singleShot(0, this, SLOT(diffActivatedDelayed())); +} + +void SubmitEditorWidget::updateActions() +{ + updateSubmitAction(); + updateDiffAction(); + updateCheckAllComboBox(); +} + +// Enable submit depending on having checked files +void SubmitEditorWidget::updateSubmitAction() +{ + const unsigned checkedCount = checkedFilesCount(); + const bool newCommitState = canSubmit(); + // Emit signal to update action + if (d->m_commitEnabled != newCommitState) { + d->m_commitEnabled = newCommitState; + emit submitActionEnabledChanged(d->m_commitEnabled); + } + if (d->m_ui.fileView && d->m_ui.fileView->model()) { + // Update button text. + const int fileCount = d->m_ui.fileView->model()->rowCount(); + const QString msg = checkedCount ? + tr("%1 %2/%n File(s)", 0, fileCount) + .arg(commitName()).arg(checkedCount) : + commitName(); + emit submitActionTextChanged(msg); + } +} + +// Enable diff depending on selected files +void SubmitEditorWidget::updateDiffAction() +{ + const bool filesSelected = hasSelection(); + if (d->m_filesSelected != filesSelected) { + d->m_filesSelected = filesSelected; + emit fileSelectionChanged(d->m_filesSelected); + } +} + +void SubmitEditorWidget::updateCheckAllComboBox() +{ + d->m_ignoreChange = true; + int checkedCount = checkedFilesCount(); + if (checkedCount == 0) + d->m_ui.checkAllCheckBox->setCheckState(Qt::Unchecked); + else if (checkedCount == d->m_ui.fileView->model()->rowCount()) + d->m_ui.checkAllCheckBox->setCheckState(Qt::Checked); + else + d->m_ui.checkAllCheckBox->setCheckState(Qt::PartiallyChecked); + d->m_ignoreChange = false; +} + +bool SubmitEditorWidget::hasSelection() const +{ + // Not present until model is set + if (const QItemSelectionModel *sm = d->m_ui.fileView->selectionModel()) + return sm->hasSelection(); + return false; +} + +int SubmitEditorWidget::checkedFilesCount() const +{ + int checkedCount = 0; + if (const QAbstractItemModel *model = d->m_ui.fileView->model()) { + const int count = model->rowCount(); + for (int i = 0; i < count; ++i) + if (listModelChecked(model, i, checkableColumn)) + ++checkedCount; + } + return checkedCount; +} + +QString SubmitEditorWidget::cleanupDescription(const QString &input) const +{ + return input; +} + +void SubmitEditorWidget::changeEvent(QEvent *e) +{ + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + d->m_ui.retranslateUi(this); + break; + default: + break; + } +} + +void SubmitEditorWidget::insertTopWidget(QWidget *w) +{ + d->m_ui.vboxLayout->insertWidget(0, w); +} + +void SubmitEditorWidget::descriptionTextChanged() +{ +#if QT_VERSION < 0x050000 // Fix Qt-Bug, see QTCREATORBUG-5633 && QTCREATORBUG-6082 + static QString lastText; + const QString text = d->m_ui.description->toPlainText(); + if (lastText != text) + lastText = text; + else + return; +#endif + updateSubmitAction(); +} + +bool SubmitEditorWidget::canSubmit() const +{ + if (isDescriptionMandatory() && cleanupDescription(descriptionText()).trimmed().isEmpty()) + return false; + const unsigned checkedCount = checkedFilesCount(); + return d->m_emptyFileListEnabled || checkedCount > 0; +} + +QString SubmitEditorWidget::commitName() const +{ + return tr("&Commit"); +} + +void SubmitEditorWidget::addSubmitFieldWidget(SubmitFieldWidget *f) +{ + if (!d->m_fieldLayout) { + // VBox with horizontal, expanding spacer + d->m_fieldLayout = new QVBoxLayout; + QHBoxLayout *outerLayout = new QHBoxLayout; + outerLayout->addLayout(d->m_fieldLayout); + outerLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + QBoxLayout *descrLayout = qobject_cast(d->m_ui.descriptionBox->layout()); + Q_ASSERT(descrLayout); + descrLayout->addLayout(outerLayout); + } + d->m_fieldLayout->addWidget(f); + d->m_fieldWidgets.push_back(f); +} + +QList SubmitEditorWidget::submitFieldWidgets() const +{ + return d->m_fieldWidgets; +} + +void SubmitEditorWidget::addDescriptionEditContextMenuAction(QAction *a) +{ + d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(-1, a)); +} + +void SubmitEditorWidget::insertDescriptionEditContextMenuAction(int pos, QAction *a) +{ + d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(pos, a)); +} + +void SubmitEditorWidget::editorCustomContextMenuRequested(const QPoint &pos) +{ + QScopedPointer menu(d->m_ui.description->createStandardContextMenu()); + // Extend + foreach (const SubmitEditorWidgetPrivate::AdditionalContextMenuAction &a, d->descriptionEditContextMenuActions) { + if (a.second) { + if (a.first >= 0) { + menu->insertAction(menu->actions().at(a.first), a.second); + } else { + menu->addAction(a.second); + } + } + } + menu->exec(d->m_ui.description->mapToGlobal(pos)); +} + +void SubmitEditorWidget::checkAllToggled() +{ + if (d->m_ignoreChange) + return; + if (d->m_ui.checkAllCheckBox->checkState() == Qt::Checked + || d->m_ui.checkAllCheckBox->checkState() == Qt::PartiallyChecked) { + setListModelChecked(d->m_ui.fileView->model(), true, checkableColumn); + } else { + setListModelChecked(d->m_ui.fileView->model(), false, checkableColumn); + } + // Reset that again, so that the user can't do it + d->m_ui.checkAllCheckBox->setTristate(false); +} + +void SubmitEditorWidget::checkAll() +{ + setListModelChecked(d->m_ui.fileView->model(), true, checkableColumn); +} + +void SubmitEditorWidget::uncheckAll() +{ + setListModelChecked(d->m_ui.fileView->model(), false, checkableColumn); +} + +void SubmitEditorWidget::fileListCustomContextMenuRequested(const QPoint & pos) +{ + // Execute menu offering to check/uncheck all + QMenu menu; + //: Check all for submit + QAction *checkAllAction = menu.addAction(tr("Check All")); + //: Uncheck all for submit + QAction *uncheckAllAction = menu.addAction(tr("Uncheck All")); + QAction *action = menu.exec(d->m_ui.fileView->mapToGlobal(pos)); + if (action == checkAllAction) { + checkAll(); + return; + } + if (action == uncheckAllAction) { + uncheckAll(); + return; + } +} + +bool SubmitEditorWidget::isEmptyFileListEnabled() const +{ + return d->m_emptyFileListEnabled; +} + +void SubmitEditorWidget::setEmptyFileListEnabled(bool e) +{ + if (e != d->m_emptyFileListEnabled) { + d->m_emptyFileListEnabled = e; + updateSubmitAction(); + } +} + +} // namespace VcsBase + +#include "submiteditorwidget.moc" diff --git a/src/plugins/vcsbase/submiteditorwidget.h b/src/plugins/vcsbase/submiteditorwidget.h new file mode 100644 index 0000000000..bec7e45a4b --- /dev/null +++ b/src/plugins/vcsbase/submiteditorwidget.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef SUBMITEDITORWIDGET_H +#define SUBMITEDITORWIDGET_H + +#include "vcsbase_global.h" + +#include + +#include +#include + +QT_BEGIN_NAMESPACE +class QListWidgetItem; +class QAction; +class QAbstractItemModel; +class QModelIndex; +class QLineEdit; +QT_END_NAMESPACE + +namespace VcsBase { + +class SubmitFieldWidget; +struct SubmitEditorWidgetPrivate; + +class VCSBASE_EXPORT SubmitEditorWidget : public QWidget +{ + Q_OBJECT + 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) + Q_PROPERTY(bool lineWrap READ lineWrap WRITE setLineWrap DESIGNABLE true) + Q_PROPERTY(int lineWrapWidth READ lineWrapWidth WRITE setLineWrapWidth DESIGNABLE true) + Q_PROPERTY(bool descriptionMandatory READ isDescriptionMandatory WRITE setDescriptionMandatory DESIGNABLE false) + Q_PROPERTY(bool emptyFileListEnabled READ isEmptyFileListEnabled WRITE setEmptyFileListEnabled DESIGNABLE true) + +public: + explicit SubmitEditorWidget(QWidget *parent = 0); + virtual ~SubmitEditorWidget(); + + // Register/Unregister actions that are managed by ActionManager with this widget. + // The submit action should have Core::Command::CA_UpdateText set as its text will + // be updated. + void registerActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction = 0, QAction *diffAction = 0); + void unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction, + QAction *submitAction = 0, QAction *diffAction = 0); + + QString descriptionText() const; + void setDescriptionText(const QString &text); + + // 'Commit' action enabled despite empty file list + bool isEmptyFileListEnabled() const; + void setEmptyFileListEnabled(bool e); + + int fileNameColumn() const; + void setFileNameColumn(int c); + + bool lineWrap() const; + void setLineWrap(bool); + + int lineWrapWidth() const; + void setLineWrapWidth(int); + + bool isDescriptionMandatory() const; + void setDescriptionMandatory(bool); + + QAbstractItemView::SelectionMode fileListSelectionMode() const; + void setFileListSelectionMode(QAbstractItemView::SelectionMode sm); + + void setFileModel(QAbstractItemModel *model); + QAbstractItemModel *fileModel() const; + + // Files to be included in submit + QStringList checkedFiles() const; + + // Selected files for diff + QStringList selectedFiles() const; + + Utils::CompletingTextEdit *descriptionEdit() const; + + void addDescriptionEditContextMenuAction(QAction *a); + void insertDescriptionEditContextMenuAction(int pos, QAction *a); + + void addSubmitFieldWidget(SubmitFieldWidget *f); + QList submitFieldWidgets() const; + + virtual bool canSubmit() const; + +signals: + void diffSelected(const QStringList &); + void fileSelectionChanged(bool someFileSelected); + void submitActionTextChanged(const QString &); + void submitActionEnabledChanged(bool); + +private slots: + void updateCheckAllComboBox(); + void checkAllToggled(); + void checkAll(); + void uncheckAll(); + +protected: + virtual QString cleanupDescription(const QString &) const; + virtual void changeEvent(QEvent *e); + virtual QString commitName() const; + void insertTopWidget(QWidget *w); + +protected slots: + void descriptionTextChanged(); + void updateSubmitAction(); + +private slots: + void triggerDiffSelected(); + void diffActivated(const QModelIndex &index); + void diffActivatedDelayed(); + void updateActions(); + void updateDiffAction(); + void editorCustomContextMenuRequested(const QPoint &); + void fileListCustomContextMenuRequested(const QPoint & pos); + +private: + bool hasSelection() const; + int checkedFilesCount() const; + + SubmitEditorWidgetPrivate *d; +}; + +} // namespace VcsBase + +#endif // SUBMITEDITORWIDGET_H diff --git a/src/plugins/vcsbase/submiteditorwidget.ui b/src/plugins/vcsbase/submiteditorwidget.ui new file mode 100644 index 0000000000..ab37981bb2 --- /dev/null +++ b/src/plugins/vcsbase/submiteditorwidget.ui @@ -0,0 +1,107 @@ + + + VcsBase::SubmitEditorWidget + + + + 0 + 0 + 582 + 502 + + + + Subversion Submit + + + + + + Qt::Vertical + + + false + + + + Descriptio&n + + + true + + + + + + false + + + + + + + + + 0 + + + + + F&iles + + + true + + + + + + Check a&ll + + + false + + + + + + + + + + + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + Utils::CompletingTextEdit + QTextEdit +
utils/completingtextedit.h
+
+
+ + +
diff --git a/src/plugins/vcsbase/submitfieldwidget.cpp b/src/plugins/vcsbase/submitfieldwidget.cpp new file mode 100644 index 0000000000..3bd9f004c9 --- /dev/null +++ b/src/plugins/vcsbase/submitfieldwidget.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "submitfieldwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum { debug = 0 }; +enum { spacing = 2 }; + +static void inline setComboBlocked(QComboBox *cb, int index) +{ + const bool blocked = cb->blockSignals(true); + cb->setCurrentIndex(index); + cb->blockSignals(blocked); +} + +/*! + \class VcsBase::SubmitFieldWidget + \brief A widget for editing submit message fields like "reviewed-by:", + "signed-off-by:". + + It displays them in a vertical row of combo/line edit fields + that is modeled after the target address controls of mail clients. + When choosing a different field in the combo, a new row is opened if text + has been entered for the current field. Optionally, a "Browse..." button and + completer can be added. +*/ + +namespace VcsBase { + +// Field/Row entry +struct FieldEntry { + FieldEntry(); + void createGui(const QIcon &removeIcon); + void deleteGuiLater(); + + QComboBox *combo; + QHBoxLayout *layout; + QLineEdit *lineEdit; + QToolBar *toolBar; + QToolButton *clearButton; + QToolButton *browseButton; + int comboIndex; +}; + +FieldEntry::FieldEntry() : + combo(0), + layout(0), + lineEdit(0), + toolBar(0), + clearButton(0), + browseButton(0), + comboIndex(0) +{ +} + +void FieldEntry::createGui(const QIcon &removeIcon) +{ + layout = new QHBoxLayout; + layout->setMargin(0); + layout ->setSpacing(spacing); + combo = new QComboBox; + layout->addWidget(combo); + lineEdit = new QLineEdit; + layout->addWidget(lineEdit); + toolBar = new QToolBar; + toolBar->setProperty("_q_custom_style_disabled", QVariant(true)); + layout->addWidget(toolBar); + clearButton = new QToolButton; + clearButton->setIcon(removeIcon); + toolBar->addWidget(clearButton); + browseButton = new QToolButton; + browseButton->setText(QLatin1String("...")); + toolBar->addWidget(browseButton); +} + +void FieldEntry::deleteGuiLater() +{ + clearButton->deleteLater(); + browseButton->deleteLater(); + toolBar->deleteLater(); + lineEdit->deleteLater(); + combo->deleteLater(); + layout->deleteLater(); +} + +// ------- SubmitFieldWidgetPrivate +struct SubmitFieldWidgetPrivate { + SubmitFieldWidgetPrivate(); + + int findSender(const QObject *o) const; + int findField(const QString &f, int excluded = -1) const; + inline QString fieldText(int) const; + inline QString fieldValue(int) const; + inline void focusField(int); + + const QIcon removeFieldIcon; + QStringList fields; + QCompleter *completer; + bool hasBrowseButton; + bool allowDuplicateFields; + + QList fieldEntries; + QVBoxLayout *layout; +}; + +SubmitFieldWidgetPrivate::SubmitFieldWidgetPrivate() : + removeFieldIcon(QLatin1String(":/vcsbase/images/removesubmitfield.png")), + completer(0), + hasBrowseButton(false), + allowDuplicateFields(false), + layout(0) +{ +} + +int SubmitFieldWidgetPrivate::findSender(const QObject *o) const +{ + const int count = fieldEntries.size(); + for (int i = 0; i < count; i++) { + const FieldEntry &fe = fieldEntries.at(i); + if (fe.combo == o || fe.browseButton == o || fe.clearButton == o || fe.lineEdit == o) + return i; + } + return -1; +} + +int SubmitFieldWidgetPrivate::findField(const QString &ft, int excluded) const +{ + const int count = fieldEntries.size(); + for (int i = 0; i < count; i++) + if (i != excluded && fieldText(i) == ft) + return i; + return -1; +} + +QString SubmitFieldWidgetPrivate::fieldText(int pos) const +{ + return fieldEntries.at(pos).combo->currentText(); +} + +QString SubmitFieldWidgetPrivate::fieldValue(int pos) const +{ + return fieldEntries.at(pos).lineEdit->text(); +} + +void SubmitFieldWidgetPrivate::focusField(int pos) +{ + fieldEntries.at(pos).lineEdit->setFocus(Qt::TabFocusReason); +} + +// SubmitFieldWidget +SubmitFieldWidget::SubmitFieldWidget(QWidget *parent) : + QWidget(parent), + d(new SubmitFieldWidgetPrivate) +{ + d->layout = new QVBoxLayout; + d->layout->setMargin(0); + d->layout->setSpacing(spacing); + setLayout(d->layout); +} + +SubmitFieldWidget::~SubmitFieldWidget() +{ + delete d; +} + +void SubmitFieldWidget::setFields(const QStringList & f) +{ + // remove old fields + for (int i = d->fieldEntries.size() - 1 ; i >= 0 ; i--) + removeField(i); + + d->fields = f; + if (!f.empty()) + createField(f.front()); +} + +QStringList SubmitFieldWidget::fields() const +{ + return d->fields; +} + +bool SubmitFieldWidget::hasBrowseButton() const +{ + return d->hasBrowseButton; +} + +void SubmitFieldWidget::setHasBrowseButton(bool on) +{ + if (d->hasBrowseButton == on) + return; + d->hasBrowseButton = on; + foreach (const FieldEntry &fe, d->fieldEntries) + fe.browseButton->setVisible(on); +} + +bool SubmitFieldWidget::allowDuplicateFields() const +{ + return d->allowDuplicateFields; +} + +void SubmitFieldWidget::setAllowDuplicateFields(bool v) +{ + d->allowDuplicateFields = v; +} + +QCompleter *SubmitFieldWidget::completer() const +{ + return d->completer; +} + +void SubmitFieldWidget::setCompleter(QCompleter *c) +{ + if (c == d->completer) + return; + d->completer = c; + foreach (const FieldEntry &fe, d->fieldEntries) + fe.lineEdit->setCompleter(c); +} + +QString SubmitFieldWidget::fieldValue(int pos) const +{ + return d->fieldValue(pos); +} + +void SubmitFieldWidget::setFieldValue(int pos, const QString &value) +{ + d->fieldEntries.at(pos).lineEdit->setText(value); +} + +QString SubmitFieldWidget::fieldValues() const +{ + const QChar blank = QLatin1Char(' '); + const QChar newLine = QLatin1Char('\n'); + // Format as "RevBy: value\nSigned-Off: value\n" + QString rc; + foreach (const FieldEntry &fe, d->fieldEntries) { + const QString value = fe.lineEdit->text().trimmed(); + if (!value.isEmpty()) { + rc += fe.combo->currentText(); + rc += blank; + rc += value; + rc += newLine; + } + } + return rc; +} + +void SubmitFieldWidget::createField(const QString &f) +{ + FieldEntry fe; + fe.createGui(d->removeFieldIcon); + fe.combo->addItems(d->fields); + if (!f.isEmpty()) { + const int index = fe.combo->findText(f); + if (index != -1) { + setComboBlocked(fe.combo, index); + fe.comboIndex = index; + } + } + + connect(fe.browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked())); + if (!d->hasBrowseButton) + fe.browseButton->setVisible(false); + + if (d->completer) + fe.lineEdit->setCompleter(d->completer); + + connect(fe.combo, SIGNAL(currentIndexChanged(int)), + this, SLOT(slotComboIndexChanged(int))); + connect(fe.clearButton, SIGNAL(clicked()), + this, SLOT(slotRemove())); + d->layout->addLayout(fe.layout); + d->fieldEntries.push_back(fe); +} + +void SubmitFieldWidget::slotRemove() +{ + // Never remove first entry + const int index = d->findSender(sender()); + switch (index) { + case -1: + break; + case 0: + d->fieldEntries.front().lineEdit->clear(); + break; + default: + removeField(index); + break; + } +} + +void SubmitFieldWidget::removeField(int index) +{ + FieldEntry fe = d->fieldEntries.takeAt(index); + QLayoutItem * item = d->layout->takeAt(index); + fe.deleteGuiLater(); + delete item; +} + +void SubmitFieldWidget::slotComboIndexChanged(int comboIndex) +{ + const int pos = d->findSender(sender()); + if (debug) + qDebug() << '>' << Q_FUNC_INFO << pos; + if (pos == -1) + return; + // Accept new index or reset combo to previous value? + int &previousIndex = d->fieldEntries[pos].comboIndex; + if (comboIndexChange(pos, comboIndex)) { + previousIndex = comboIndex; + } else { + setComboBlocked(d->fieldEntries.at(pos).combo, previousIndex); + } + if (debug) + qDebug() << '<' << Q_FUNC_INFO << pos; +} + +// Handle change of a combo. Return "false" if the combo +// is to be reset (refuse new field). +bool SubmitFieldWidget::comboIndexChange(int pos, int index) +{ + const QString newField = d->fieldEntries.at(pos).combo->itemText(index); + // If the field is visible elsewhere: focus the existing one and refuse + if (!d->allowDuplicateFields) { + const int existingFieldIndex = d->findField(newField, pos); + if (existingFieldIndex != -1) { + d->focusField(existingFieldIndex); + return false; + } + } + // Empty value: just change the field + if (d->fieldValue(pos).isEmpty()) + return true; + // Non-empty: Create a new field and reset the triggering combo + createField(newField); + return false; +} + +void SubmitFieldWidget::slotBrowseButtonClicked() +{ + const int pos = d->findSender(sender()); + emit browseButtonClicked(pos, d->fieldText(pos)); +} + +} diff --git a/src/plugins/vcsbase/submitfieldwidget.h b/src/plugins/vcsbase/submitfieldwidget.h new file mode 100644 index 0000000000..3be16bcd7b --- /dev/null +++ b/src/plugins/vcsbase/submitfieldwidget.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef SUBMITFIELDWIDGET_H +#define SUBMITFIELDWIDGET_H + +#include "vcsbase_global.h" + +#include + +QT_BEGIN_NAMESPACE +class QCompleter; +QT_END_NAMESPACE + +namespace VcsBase { + +struct SubmitFieldWidgetPrivate; + +class VCSBASE_EXPORT SubmitFieldWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QStringList fields READ fields WRITE setFields DESIGNABLE true) + Q_PROPERTY(bool hasBrowseButton READ hasBrowseButton WRITE setHasBrowseButton DESIGNABLE true) + Q_PROPERTY(bool allowDuplicateFields READ allowDuplicateFields WRITE setAllowDuplicateFields DESIGNABLE true) + +public: + explicit SubmitFieldWidget(QWidget *parent = 0); + virtual ~SubmitFieldWidget(); + + QStringList fields() const; + void setFields(const QStringList&); + + bool hasBrowseButton() const; + void setHasBrowseButton(bool d); + + // Allow several entries for fields ("reviewed-by: a", "reviewed-by: b") + bool allowDuplicateFields() const; + void setAllowDuplicateFields(bool); + + QCompleter *completer() const; + void setCompleter(QCompleter *c); + + QString fieldValue(int pos) const; + void setFieldValue(int pos, const QString &value); + + QString fieldValues() const; + +signals: + void browseButtonClicked(int pos, const QString &field); + +private slots: + void slotRemove(); + void slotComboIndexChanged(int); + void slotBrowseButtonClicked(); + +private: + void removeField(int index); + bool comboIndexChange(int fieldNumber, int index); + void createField(const QString &f); + + SubmitFieldWidgetPrivate *d; +}; + +} // namespace VcsBase + +#endif // SUBMITFIELDWIDGET_H diff --git a/src/plugins/vcsbase/vcsbase.pro b/src/plugins/vcsbase/vcsbase.pro index 89a932f02c..ea89fb5999 100644 --- a/src/plugins/vcsbase/vcsbase.pro +++ b/src/plugins/vcsbase/vcsbase.pro @@ -31,7 +31,9 @@ HEADERS += vcsbase_global.h \ command.h \ vcsbaseclient.h \ vcsbaseclientsettings.h \ - vcsbaseeditorparameterwidget.h + vcsbaseeditorparameterwidget.h \ + submitfieldwidget.h \ + submiteditorwidget.h SOURCES += vcsplugin.cpp \ vcsbaseplugin.cpp \ @@ -59,7 +61,9 @@ SOURCES += vcsplugin.cpp \ command.cpp \ vcsbaseclient.cpp \ vcsbaseclientsettings.cpp \ - vcsbaseeditorparameterwidget.cpp + vcsbaseeditorparameterwidget.cpp \ + submitfieldwidget.cpp \ + submiteditorwidget.cpp RESOURCES += vcsbase.qrc @@ -67,5 +71,6 @@ FORMS += commonsettingspage.ui \ nicknamedialog.ui \ checkoutprogresswizardpage.ui \ basecheckoutwizardpage.ui \ - cleandialog.ui + cleandialog.ui \ + submiteditorwidget.ui diff --git a/src/plugins/vcsbase/vcsbase.qbs b/src/plugins/vcsbase/vcsbase.qbs index aefb0c04f9..b5265d7b8c 100644 --- a/src/plugins/vcsbase/vcsbase.qbs +++ b/src/plugins/vcsbase/vcsbase.qbs @@ -53,6 +53,11 @@ QtcPlugin { "nicknamedialog.ui", "submiteditorfile.cpp", "submiteditorfile.h", + "submiteditorwidget.cpp", + "submiteditorwidget.h", + "submiteditorwidget.ui", + "submitfieldwidget.cpp", + "submitfieldwidget.h", "submitfilemodel.cpp", "submitfilemodel.h", "vcsbase.qrc", @@ -79,6 +84,7 @@ QtcPlugin { "vcsplugin.cpp", "vcsplugin.h", "images/diff.png", + "images/removesubmitfield.png", "images/submit.png", ] } diff --git a/src/plugins/vcsbase/vcsbase.qrc b/src/plugins/vcsbase/vcsbase.qrc index 4da5d9ca9f..2bd826fbae 100644 --- a/src/plugins/vcsbase/vcsbase.qrc +++ b/src/plugins/vcsbase/vcsbase.qrc @@ -2,6 +2,7 @@ VcsBase.mimetypes.xml images/diff.png + images/removesubmitfield.png images/submit.png
diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index ae6d2bedd2..7eafda69ec 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -30,10 +30,12 @@ #include "vcsbasesubmiteditor.h" #include "commonvcssettings.h" -#include "vcsbaseoutputwindow.h" -#include "vcsplugin.h" #include "nicknamedialog.h" #include "submiteditorfile.h" +#include "submiteditorwidget.h" +#include "submitfieldwidget.h" +#include "vcsbaseoutputwindow.h" +#include "vcsplugin.h" #include #include @@ -51,10 +53,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -119,7 +119,7 @@ static const char *belongingClassName(const CPlusPlus::Function *function) /*! \class VcsBase::VcsBaseSubmitEditor - \brief Base class for a submit editor based on the Utils::SubmitEditorWidget. + \brief Base class for a submit editor based on the SubmitEditorWidget. Presents the commit message in a text editor and an checkable list of modified files in a list window. The user can delete diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 90b9f58f8c..75f8615f86 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -42,13 +42,12 @@ class QAbstractItemModel; class QAction; QT_END_NAMESPACE -namespace Utils { class SubmitEditorWidget; } - namespace VcsBase { namespace Internal { class CommonVcsSettings; } struct VcsBaseSubmitEditorPrivate; +class SubmitEditorWidget; class VCSBASE_EXPORT VcsBaseSubmitEditorParameters { @@ -71,7 +70,7 @@ class VCSBASE_EXPORT VcsBaseSubmitEditor : public Core::IEditor protected: explicit VcsBaseSubmitEditor(const VcsBaseSubmitEditorParameters *parameters, - Utils::SubmitEditorWidget *editorWidget); + SubmitEditorWidget *editorWidget); public: // Register the actions with the submit editor widget. -- cgit v1.2.1