1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "submitfieldwidget.h"
36 #include <QtGui/QComboBox>
37 #include <QtGui/QHBoxLayout>
38 #include <QtGui/QVBoxLayout>
39 #include <QtGui/QLineEdit>
40 #include <QtGui/QToolButton>
41 #include <QtGui/QCompleter>
42 #include <QtGui/QIcon>
43 #include <QtGui/QToolBar>
45 #include <QtCore/QList>
46 #include <QtCore/QDebug>
51 static void inline setComboBlocked(QComboBox *cb, int index)
53 const bool blocked = cb->blockSignals(true);
54 cb->setCurrentIndex(index);
55 cb->blockSignals(blocked);
59 \class Utils::SubmitFieldWidget
60 \brief A widget for editing submit message fields like "reviewed-by:",
63 It displays them in a vertical row of combo/line edit fields
64 that is modeled after the target address controls of mail clients.
65 When choosing a different field in the combo, a new row is opened if text
66 has been entered for the current field. Optionally, a "Browse..." button and
67 completer can be added.
75 void createGui(const QIcon &removeIcon);
76 void deleteGuiLater();
82 QToolButton *clearButton;
83 QToolButton *browseButton;
87 FieldEntry::FieldEntry() :
98 void FieldEntry::createGui(const QIcon &removeIcon)
100 layout = new QHBoxLayout;
101 layout->setMargin(0);
102 layout ->setSpacing(spacing);
103 combo = new QComboBox;
104 layout->addWidget(combo);
105 lineEdit = new QLineEdit;
106 layout->addWidget(lineEdit);
107 toolBar = new QToolBar;
108 toolBar->setProperty("_q_custom_style_disabled", QVariant(true));
109 layout->addWidget(toolBar);
110 clearButton = new QToolButton;
111 clearButton->setIcon(removeIcon);
112 toolBar->addWidget(clearButton);
113 browseButton = new QToolButton;
114 browseButton->setText(QLatin1String("..."));
115 toolBar->addWidget(browseButton);
118 void FieldEntry::deleteGuiLater()
120 clearButton->deleteLater();
121 browseButton->deleteLater();
122 toolBar->deleteLater();
123 lineEdit->deleteLater();
124 combo->deleteLater();
125 layout->deleteLater();
128 // ------- SubmitFieldWidgetPrivate
129 struct SubmitFieldWidgetPrivate {
130 SubmitFieldWidgetPrivate();
132 int findSender(const QObject *o) const;
133 int findField(const QString &f, int excluded = -1) const;
134 inline QString fieldText(int) const;
135 inline QString fieldValue(int) const;
136 inline void focusField(int);
138 const QIcon removeFieldIcon;
140 QCompleter *completer;
141 bool hasBrowseButton;
142 bool allowDuplicateFields;
144 QList <FieldEntry> fieldEntries;
148 SubmitFieldWidgetPrivate::SubmitFieldWidgetPrivate() :
149 removeFieldIcon(QLatin1String(":/utils/images/removesubmitfield.png")),
151 hasBrowseButton(false),
152 allowDuplicateFields(false),
157 int SubmitFieldWidgetPrivate::findSender(const QObject *o) const
159 const int count = fieldEntries.size();
160 for (int i = 0; i < count; i++) {
161 const FieldEntry &fe = fieldEntries.at(i);
162 if (fe.combo == o || fe.browseButton == o || fe.clearButton == o || fe.lineEdit == o)
168 int SubmitFieldWidgetPrivate::findField(const QString &ft, int excluded) const
170 const int count = fieldEntries.size();
171 for (int i = 0; i < count; i++)
172 if (i != excluded && fieldText(i) == ft)
177 QString SubmitFieldWidgetPrivate::fieldText(int pos) const
179 return fieldEntries.at(pos).combo->currentText();
182 QString SubmitFieldWidgetPrivate::fieldValue(int pos) const
184 return fieldEntries.at(pos).lineEdit->text();
187 void SubmitFieldWidgetPrivate::focusField(int pos)
189 fieldEntries.at(pos).lineEdit->setFocus(Qt::TabFocusReason);
193 SubmitFieldWidget::SubmitFieldWidget(QWidget *parent) :
195 m_d(new SubmitFieldWidgetPrivate)
197 m_d->layout = new QVBoxLayout;
198 m_d->layout->setMargin(0);
199 m_d->layout->setSpacing(spacing);
200 setLayout(m_d->layout);
203 SubmitFieldWidget::~SubmitFieldWidget()
208 void SubmitFieldWidget::setFields(const QStringList & f)
211 for (int i = m_d->fieldEntries.size() - 1 ; i >= 0 ; i--)
216 createField(f.front());
219 QStringList SubmitFieldWidget::fields() const
224 bool SubmitFieldWidget::hasBrowseButton() const
226 return m_d->hasBrowseButton;
229 void SubmitFieldWidget::setHasBrowseButton(bool d)
231 if (m_d->hasBrowseButton == d)
233 m_d->hasBrowseButton = d;
234 foreach(const FieldEntry &fe, m_d->fieldEntries)
235 fe.browseButton->setVisible(d);
238 bool SubmitFieldWidget::allowDuplicateFields() const
240 return m_d->allowDuplicateFields;
243 void SubmitFieldWidget::setAllowDuplicateFields(bool v)
245 m_d->allowDuplicateFields = v;
248 QCompleter *SubmitFieldWidget::completer() const
250 return m_d->completer;
253 void SubmitFieldWidget::setCompleter(QCompleter *c)
255 if (c == m_d->completer)
258 foreach(const FieldEntry &fe, m_d->fieldEntries)
259 fe.lineEdit->setCompleter(c);
262 QString SubmitFieldWidget::fieldValue(int pos) const
264 return m_d->fieldValue(pos);
267 void SubmitFieldWidget::setFieldValue(int pos, const QString &value)
269 m_d->fieldEntries.at(pos).lineEdit->setText(value);
272 QString SubmitFieldWidget::fieldValues() const
274 const QChar blank = QLatin1Char(' ');
275 const QChar newLine = QLatin1Char('\n');
276 // Format as "RevBy: value\nSigned-Off: value\n"
278 foreach(const FieldEntry &fe, m_d->fieldEntries) {
279 const QString value = fe.lineEdit->text().trimmed();
280 if (!value.isEmpty()) {
281 rc += fe.combo->currentText();
290 void SubmitFieldWidget::createField(const QString &f)
293 fe.createGui(m_d->removeFieldIcon);
294 fe.combo->addItems(m_d->fields);
296 const int index = fe.combo->findText(f);
298 setComboBlocked(fe.combo, index);
299 fe.comboIndex = index;
303 connect(fe.browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked()));
304 if (!m_d->hasBrowseButton)
305 fe.browseButton->setVisible(false);
308 fe.lineEdit->setCompleter(m_d->completer);
310 connect(fe.combo, SIGNAL(currentIndexChanged(int)),
311 this, SLOT(slotComboIndexChanged(int)));
312 connect(fe.clearButton, SIGNAL(clicked()),
313 this, SLOT(slotRemove()));
314 m_d->layout->addLayout(fe.layout);
315 m_d->fieldEntries.push_back(fe);
318 void SubmitFieldWidget::slotRemove()
320 // Never remove first entry
321 const int index = m_d->findSender(sender());
326 m_d->fieldEntries.front().lineEdit->clear();
334 void SubmitFieldWidget::removeField(int index)
336 FieldEntry fe = m_d->fieldEntries.takeAt(index);
337 QLayoutItem * item = m_d->layout->takeAt(index);
342 void SubmitFieldWidget::slotComboIndexChanged(int comboIndex)
344 const int pos = m_d->findSender(sender());
346 qDebug() << '>' << Q_FUNC_INFO << pos;
349 // Accept new index or reset combo to previous value?
350 int &previousIndex = m_d->fieldEntries[pos].comboIndex;
351 if (comboIndexChange(pos, comboIndex)) {
352 previousIndex = comboIndex;
354 setComboBlocked(m_d->fieldEntries.at(pos).combo, previousIndex);
357 qDebug() << '<' << Q_FUNC_INFO << pos;
360 // Handle change of a combo. Return "false" if the combo
361 // is to be reset (refuse new field).
362 bool SubmitFieldWidget::comboIndexChange(int pos, int index)
364 const QString newField = m_d->fieldEntries.at(pos).combo->itemText(index);
365 // If the field is visible elsewhere: focus the existing one and refuse
366 if (!m_d->allowDuplicateFields) {
367 const int existingFieldIndex = m_d->findField(newField, pos);
368 if (existingFieldIndex != -1) {
369 m_d->focusField(existingFieldIndex);
373 // Empty value: just change the field
374 if (m_d->fieldValue(pos).isEmpty())
376 // Non-empty: Create a new field and reset the triggering combo
377 createField(newField);
381 void SubmitFieldWidget::slotBrowseButtonClicked()
383 const int pos = m_d->findSender(sender());
384 emit browseButtonClicked(pos, m_d->fieldText(pos));