OSDN Git Service

07378fd5d213bca18002408a4f5da5205262e0cb
[qt-creator-jp/qt-creator-jp.git] / src / libs / utils / submitfieldwidget.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "submitfieldwidget.h"
35
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>
44
45 #include <QtCore/QList>
46 #include <QtCore/QDebug>
47
48 enum { debug = 0 };
49 enum { spacing = 2 };
50
51 static void inline setComboBlocked(QComboBox *cb, int index)
52 {
53     const bool blocked = cb->blockSignals(true);
54     cb->setCurrentIndex(index);
55     cb->blockSignals(blocked);
56 }
57
58 /*!
59     \class Utils::SubmitFieldWidget
60     \brief A widget for editing submit message fields like "reviewed-by:",
61     "signed-off-by:".
62
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.
68 */
69
70 namespace Utils {
71
72 // Field/Row entry
73 struct FieldEntry {
74     FieldEntry();
75     void createGui(const QIcon &removeIcon);
76     void deleteGuiLater();
77
78     QComboBox *combo;
79     QHBoxLayout *layout;
80     QLineEdit *lineEdit;
81     QToolBar *toolBar;
82     QToolButton *clearButton;
83     QToolButton *browseButton;
84     int comboIndex;
85 };
86
87 FieldEntry::FieldEntry() :
88     combo(0),
89     layout(0),
90     lineEdit(0),
91     toolBar(0),
92     clearButton(0),
93     browseButton(0),
94     comboIndex(0)
95 {
96 }
97
98 void FieldEntry::createGui(const QIcon &removeIcon)
99 {
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);
116 }
117
118 void FieldEntry::deleteGuiLater()
119 {
120     clearButton->deleteLater();
121     browseButton->deleteLater();
122     toolBar->deleteLater();
123     lineEdit->deleteLater();
124     combo->deleteLater();
125     layout->deleteLater();
126 }
127
128 // ------- SubmitFieldWidgetPrivate
129 struct SubmitFieldWidgetPrivate {
130     SubmitFieldWidgetPrivate();
131
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);
137
138     const QIcon removeFieldIcon;
139     QStringList fields;
140     QCompleter *completer;
141     bool hasBrowseButton;
142     bool allowDuplicateFields;
143
144     QList <FieldEntry> fieldEntries;
145     QVBoxLayout *layout;
146 };
147
148 SubmitFieldWidgetPrivate::SubmitFieldWidgetPrivate() :
149         removeFieldIcon(QLatin1String(":/utils/images/removesubmitfield.png")),
150         completer(0),
151         hasBrowseButton(false),
152         allowDuplicateFields(false),
153         layout(0)
154 {
155 }
156
157 int SubmitFieldWidgetPrivate::findSender(const QObject *o) const
158 {
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)
163             return i;
164     }
165     return -1;
166 }
167
168 int SubmitFieldWidgetPrivate::findField(const QString &ft, int excluded) const
169 {
170     const int count = fieldEntries.size();
171     for (int i = 0; i < count; i++)
172         if (i != excluded && fieldText(i) == ft)
173             return i;
174     return -1;
175 }
176
177 QString SubmitFieldWidgetPrivate::fieldText(int pos) const
178 {
179     return fieldEntries.at(pos).combo->currentText();
180 }
181
182 QString SubmitFieldWidgetPrivate::fieldValue(int pos) const
183 {
184     return fieldEntries.at(pos).lineEdit->text();
185 }
186
187 void SubmitFieldWidgetPrivate::focusField(int pos)
188 {
189     fieldEntries.at(pos).lineEdit->setFocus(Qt::TabFocusReason);
190 }
191
192 // SubmitFieldWidget
193 SubmitFieldWidget::SubmitFieldWidget(QWidget *parent) :
194         QWidget(parent),
195         m_d(new SubmitFieldWidgetPrivate)
196 {
197     m_d->layout = new QVBoxLayout;
198     m_d->layout->setMargin(0);
199     m_d->layout->setSpacing(spacing);
200     setLayout(m_d->layout);
201 }
202
203 SubmitFieldWidget::~SubmitFieldWidget()
204 {
205     delete m_d;
206 }
207
208 void SubmitFieldWidget::setFields(const QStringList & f)
209 {
210     // remove old fields
211     for (int i = m_d->fieldEntries.size() - 1 ; i >= 0 ; i--)
212         removeField(i);
213
214     m_d->fields = f;
215     if (!f.empty())
216         createField(f.front());
217 }
218
219 QStringList SubmitFieldWidget::fields() const
220 {
221     return m_d->fields;
222 }
223
224 bool SubmitFieldWidget::hasBrowseButton() const
225 {
226     return m_d->hasBrowseButton;
227 }
228
229 void SubmitFieldWidget::setHasBrowseButton(bool d)
230 {
231     if (m_d->hasBrowseButton == d)
232         return;
233     m_d->hasBrowseButton = d;
234     foreach(const FieldEntry &fe, m_d->fieldEntries)
235         fe.browseButton->setVisible(d);
236 }
237
238 bool SubmitFieldWidget::allowDuplicateFields() const
239 {
240     return m_d->allowDuplicateFields;
241 }
242
243 void SubmitFieldWidget::setAllowDuplicateFields(bool v)
244 {
245     m_d->allowDuplicateFields = v;
246 }
247
248 QCompleter *SubmitFieldWidget::completer() const
249 {
250     return m_d->completer;
251 }
252
253 void SubmitFieldWidget::setCompleter(QCompleter *c)
254 {
255     if (c == m_d->completer)
256         return;
257     m_d->completer = c;
258     foreach(const FieldEntry &fe, m_d->fieldEntries)
259         fe.lineEdit->setCompleter(c);
260 }
261
262 QString SubmitFieldWidget::fieldValue(int pos) const
263 {
264     return m_d->fieldValue(pos);
265 }
266
267 void SubmitFieldWidget::setFieldValue(int pos, const QString &value)
268 {
269     m_d->fieldEntries.at(pos).lineEdit->setText(value);
270 }
271
272 QString SubmitFieldWidget::fieldValues() const
273 {
274     const QChar blank = QLatin1Char(' ');
275     const QChar newLine = QLatin1Char('\n');
276     // Format as "RevBy: value\nSigned-Off: value\n"
277     QString rc;
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();
282             rc += blank;
283             rc += value;
284             rc += newLine;
285         }
286     }
287     return rc;
288 }
289
290 void SubmitFieldWidget::createField(const QString &f)
291 {
292     FieldEntry fe;
293     fe.createGui(m_d->removeFieldIcon);
294     fe.combo->addItems(m_d->fields);
295     if (!f.isEmpty()) {
296         const int index = fe.combo->findText(f);
297         if (index != -1) {
298             setComboBlocked(fe.combo, index);
299             fe.comboIndex = index;
300         }
301     }
302
303     connect(fe.browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseButtonClicked()));
304     if (!m_d->hasBrowseButton)
305         fe.browseButton->setVisible(false);
306
307     if (m_d->completer)
308         fe.lineEdit->setCompleter(m_d->completer);
309
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);
316 }
317
318 void SubmitFieldWidget::slotRemove()
319 {
320     // Never remove first entry
321     const int index = m_d->findSender(sender());
322     switch (index) {
323     case -1:
324         break;
325     case 0:
326         m_d->fieldEntries.front().lineEdit->clear();
327         break;
328     default:
329         removeField(index);
330         break;
331     }
332 }
333
334 void SubmitFieldWidget::removeField(int index)
335 {
336     FieldEntry fe = m_d->fieldEntries.takeAt(index);
337     QLayoutItem * item = m_d->layout->takeAt(index);
338     fe.deleteGuiLater();
339     delete item;
340 }
341
342 void SubmitFieldWidget::slotComboIndexChanged(int comboIndex)
343 {
344     const int pos = m_d->findSender(sender());
345     if (debug)
346         qDebug() << '>' << Q_FUNC_INFO << pos;
347     if (pos == -1)
348         return;
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;
353     } else {
354         setComboBlocked(m_d->fieldEntries.at(pos).combo, previousIndex);
355     }
356     if (debug)
357         qDebug() << '<' << Q_FUNC_INFO << pos;
358 }
359
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)
363 {
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);
370             return false;
371         }
372     }
373     // Empty value: just change the field
374     if (m_d->fieldValue(pos).isEmpty())
375         return true;
376     // Non-empty: Create a new field and reset the triggering combo
377     createField(newField);
378     return false;
379 }
380
381 void SubmitFieldWidget::slotBrowseButtonClicked()
382 {
383     const int pos = m_d->findSender(sender());
384     emit browseButtonClicked(pos, m_d->fieldText(pos));
385 }
386
387 }