LP-311 Remove basic/advanced stabilization tab auto-switch (autotune/txpid lock issues)
[librepilot.git] / ground / gcs / src / libs / utils / submiteditorwidget.cpp
blob2551715b89659694e5de54924919c81a466ccc4f
1 /**
2 ******************************************************************************
4 * @file submiteditorwidget.cpp
5 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * Parts by Nokia Corporation (qt-info@nokia.com) Copyright (C) 2009.
7 * @brief
8 * @see The GNU Public License (GPL) Version 3
9 * @defgroup
10 * @{
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "submiteditorwidget.h"
30 #include "submitfieldwidget.h"
31 #include "ui_submiteditorwidget.h"
33 #include <QtCore/QDebug>
34 #include <QtCore/QPointer>
35 #include <QtCore/QTimer>
37 #include <QPushButton>
38 #include <QMenu>
39 #include <QHBoxLayout>
40 #include <QToolButton>
41 #include <QSpacerItem>
43 enum { debug = 0 };
44 enum { defaultLineWidth = 72 };
46 namespace Utils {
47 // QActionPushButton: A push button tied to an action
48 // (similar to a QToolButton)
49 class QActionPushButton : public QPushButton {
50 Q_OBJECT
51 public:
52 explicit QActionPushButton(QAction *a);
54 private slots:
55 void actionChanged();
58 QActionPushButton::QActionPushButton(QAction *a) :
59 QPushButton(a->icon(), a->text())
61 connect(a, SIGNAL(changed()), this, SLOT(actionChanged()));
62 connect(this, SIGNAL(clicked()), a, SLOT(trigger()));
63 setEnabled(a->isEnabled());
66 void QActionPushButton::actionChanged()
68 if (const QAction * a = qobject_cast<QAction *>(sender())) {
69 setEnabled(a->isEnabled());
73 // Helpers to retrieve model data
74 static inline bool listModelChecked(const QAbstractItemModel *model, int row, int column = 0)
76 const QModelIndex checkableIndex = model->index(row, column, QModelIndex());
78 return model->data(checkableIndex, Qt::CheckStateRole).toInt() == Qt::Checked;
81 static inline QString listModelText(const QAbstractItemModel *model, int row, int column)
83 const QModelIndex index = model->index(row, column, QModelIndex());
85 return model->data(index, Qt::DisplayRole).toString();
88 // Find a check item in a model
89 static bool listModelContainsCheckedItem(const QAbstractItemModel *model)
91 const int count = model->rowCount();
93 for (int i = 0; i < count; i++) {
94 if (listModelChecked(model, i, 0)) {
95 return true;
98 return false;
101 // Convenience to extract a list of selected indexes
102 QList<int> selectedRows(const QAbstractItemView *view)
104 const QModelIndexList indexList = view->selectionModel()->selectedRows(0);
106 if (indexList.empty()) {
107 return QList<int>();
109 QList<int> rc;
110 const QModelIndexList::const_iterator cend = indexList.constEnd();
111 for (QModelIndexList::const_iterator it = indexList.constBegin(); it != cend; ++it) {
112 rc.push_back(it->row());
114 return rc;
117 // ----------- SubmitEditorWidgetPrivate
119 struct SubmitEditorWidgetPrivate {
120 // A pair of position/action to extend context menus
121 typedef QPair<int, QPointer<QAction> > AdditionalContextMenuAction;
123 SubmitEditorWidgetPrivate();
125 Ui::SubmitEditorWidget m_ui;
126 bool m_filesSelected;
127 bool m_filesChecked;
128 int m_fileNameColumn;
129 int m_activatedRow;
131 QList<AdditionalContextMenuAction> descriptionEditContextMenuActions;
132 QVBoxLayout *m_fieldLayout;
133 QList<SubmitFieldWidget *> m_fieldWidgets;
134 int m_lineWidth;
137 SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() :
138 m_filesSelected(false),
139 m_filesChecked(false),
140 m_fileNameColumn(1),
141 m_activatedRow(-1),
142 m_fieldLayout(0),
143 m_lineWidth(defaultLineWidth)
146 SubmitEditorWidget::SubmitEditorWidget(QWidget *parent) :
147 QWidget(parent),
148 m_d(new SubmitEditorWidgetPrivate)
150 m_d->m_ui.setupUi(this);
151 m_d->m_ui.description->setContextMenuPolicy(Qt::CustomContextMenu);
152 m_d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap);
153 m_d->m_ui.description->setWordWrapMode(QTextOption::WordWrap);
154 connect(m_d->m_ui.description, SIGNAL(customContextMenuRequested(QPoint)),
155 this, SLOT(editorCustomContextMenuRequested(QPoint)));
157 // File List
158 m_d->m_ui.fileView->setSelectionMode(QAbstractItemView::ExtendedSelection);
159 m_d->m_ui.fileView->setRootIsDecorated(false);
160 connect(m_d->m_ui.fileView, SIGNAL(doubleClicked(QModelIndex)),
161 this, SLOT(diffActivated(QModelIndex)));
163 setFocusPolicy(Qt::StrongFocus);
164 setFocusProxy(m_d->m_ui.description);
167 SubmitEditorWidget::~SubmitEditorWidget()
169 delete m_d;
172 void SubmitEditorWidget::registerActions(QAction *editorUndoAction, QAction *editorRedoAction,
173 QAction *submitAction, QAction *diffAction)
175 if (editorUndoAction) {
176 editorUndoAction->setEnabled(m_d->m_ui.description->document()->isUndoAvailable());
177 connect(m_d->m_ui.description, SIGNAL(undoAvailable(bool)), editorUndoAction, SLOT(setEnabled(bool)));
178 connect(editorUndoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(undo()));
180 if (editorRedoAction) {
181 editorRedoAction->setEnabled(m_d->m_ui.description->document()->isRedoAvailable());
182 connect(m_d->m_ui.description, SIGNAL(redoAvailable(bool)), editorRedoAction, SLOT(setEnabled(bool)));
183 connect(editorRedoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(redo()));
186 if (submitAction) {
187 if (debug) {
188 int count = 0;
189 if (const QAbstractItemModel * model = m_d->m_ui.fileView->model()) {
190 count = model->rowCount();
192 qDebug() << Q_FUNC_INFO << submitAction << count << "items" << m_d->m_filesChecked;
194 submitAction->setEnabled(m_d->m_filesChecked);
195 connect(this, SIGNAL(fileCheckStateChanged(bool)), submitAction, SLOT(setEnabled(bool)));
196 m_d->m_ui.buttonLayout->addWidget(new QActionPushButton(submitAction));
198 if (diffAction) {
199 if (debug) {
200 qDebug() << diffAction << m_d->m_filesSelected;
202 diffAction->setEnabled(m_d->m_filesSelected);
203 connect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool)));
204 connect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected()));
205 m_d->m_ui.buttonLayout->addWidget(new QActionPushButton(diffAction));
209 void SubmitEditorWidget::unregisterActions(QAction *editorUndoAction, QAction *editorRedoAction,
210 QAction *submitAction, QAction *diffAction)
212 if (editorUndoAction) {
213 disconnect(m_d->m_ui.description, SIGNAL(undoAvailableChanged(bool)), editorUndoAction, SLOT(setEnabled(bool)));
214 disconnect(editorUndoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(undo()));
216 if (editorRedoAction) {
217 disconnect(m_d->m_ui.description, SIGNAL(redoAvailableChanged(bool)), editorRedoAction, SLOT(setEnabled(bool)));
218 disconnect(editorRedoAction, SIGNAL(triggered()), m_d->m_ui.description, SLOT(redo()));
221 if (submitAction) {
222 disconnect(this, SIGNAL(fileCheckStateChanged(bool)), submitAction, SLOT(setEnabled(bool)));
225 if (diffAction) {
226 disconnect(this, SIGNAL(fileSelectionChanged(bool)), diffAction, SLOT(setEnabled(bool)));
227 disconnect(diffAction, SIGNAL(triggered()), this, SLOT(triggerDiffSelected()));
231 // Make sure we have one terminating NL
232 static inline QString trimMessageText(const QString &t)
234 QString rc = t.trimmed();
236 rc += QLatin1Char('\n');
237 return rc;
240 // Extract the wrapped text from a text edit, which performs
241 // the wrapping only optically.
242 static QString wrappedText(const QTextEdit *e)
244 const QChar newLine = QLatin1Char('\n');
245 QString rc;
246 QTextCursor cursor(e->document());
248 cursor.movePosition(QTextCursor::Start);
249 while (!cursor.atEnd()) {
250 cursor.select(QTextCursor::LineUnderCursor);
251 rc += cursor.selectedText();
252 rc += newLine;
253 cursor.movePosition(QTextCursor::EndOfLine); // Mac needs it
254 cursor.movePosition(QTextCursor::Right);
256 return rc;
259 QString SubmitEditorWidget::descriptionText() const
261 QString rc = trimMessageText(lineWrap() ? wrappedText(m_d->m_ui.description) : m_d->m_ui.description->toPlainText());
263 // append field entries
264 foreach(const SubmitFieldWidget * fw, m_d->m_fieldWidgets)
265 rc += fw->fieldValues();
266 return rc;
269 void SubmitEditorWidget::setDescriptionText(const QString &text)
271 m_d->m_ui.description->setPlainText(text);
274 bool SubmitEditorWidget::lineWrap() const
276 return m_d->m_ui.description->lineWrapMode() != QTextEdit::NoWrap;
279 void SubmitEditorWidget::setLineWrap(bool v)
281 if (debug) {
282 qDebug() << Q_FUNC_INFO << v;
284 if (v) {
285 m_d->m_ui.description->setLineWrapColumnOrWidth(m_d->m_lineWidth);
286 m_d->m_ui.description->setLineWrapMode(QTextEdit::FixedColumnWidth);
287 } else {
288 m_d->m_ui.description->setLineWrapMode(QTextEdit::NoWrap);
292 int SubmitEditorWidget::lineWrapWidth() const
294 return m_d->m_lineWidth;
297 void SubmitEditorWidget::setLineWrapWidth(int v)
299 if (debug) {
300 qDebug() << Q_FUNC_INFO << v << lineWrap();
302 if (m_d->m_lineWidth == v) {
303 return;
305 m_d->m_lineWidth = v;
306 if (lineWrap()) {
307 m_d->m_ui.description->setLineWrapColumnOrWidth(v);
311 int SubmitEditorWidget::fileNameColumn() const
313 return m_d->m_fileNameColumn;
316 void SubmitEditorWidget::setFileNameColumn(int c)
318 m_d->m_fileNameColumn = c;
321 QAbstractItemView::SelectionMode SubmitEditorWidget::fileListSelectionMode() const
323 return m_d->m_ui.fileView->selectionMode();
326 void SubmitEditorWidget::setFileListSelectionMode(QAbstractItemView::SelectionMode sm)
328 m_d->m_ui.fileView->setSelectionMode(sm);
331 void SubmitEditorWidget::setFileModel(QAbstractItemModel *model)
333 m_d->m_ui.fileView->clearSelection(); // trigger the change signals
335 m_d->m_ui.fileView->setModel(model);
337 if (model->rowCount()) {
338 const int columnCount = model->columnCount();
339 for (int c = 0; c < columnCount; c++) {
340 m_d->m_ui.fileView->resizeColumnToContents(c);
344 connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)),
345 this, SLOT(updateSubmitAction()));
346 connect(model, SIGNAL(modelReset()),
347 this, SLOT(updateSubmitAction()));
348 connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)),
349 this, SLOT(updateSubmitAction()));
350 connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)),
351 this, SLOT(updateSubmitAction()));
352 connect(m_d->m_ui.fileView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
353 this, SLOT(updateDiffAction()));
354 updateActions();
357 QAbstractItemModel *SubmitEditorWidget::fileModel() const
359 return m_d->m_ui.fileView->model();
362 QStringList SubmitEditorWidget::selectedFiles() const
364 const QList<int> selection = selectedRows(m_d->m_ui.fileView);
366 if (selection.empty()) {
367 return QStringList();
370 QStringList rc;
371 const QAbstractItemModel *model = m_d->m_ui.fileView->model();
372 const int count = selection.size();
373 for (int i = 0; i < count; i++) {
374 rc.push_back(listModelText(model, selection.at(i), fileNameColumn()));
376 return rc;
379 QStringList SubmitEditorWidget::checkedFiles() const
381 QStringList rc;
382 const QAbstractItemModel *model = m_d->m_ui.fileView->model();
384 if (!model) {
385 return rc;
387 const int count = model->rowCount();
388 for (int i = 0; i < count; i++) {
389 if (listModelChecked(model, i, 0)) {
390 rc.push_back(listModelText(model, i, fileNameColumn()));
393 return rc;
396 QTextEdit *SubmitEditorWidget::descriptionEdit() const
398 return m_d->m_ui.description;
401 void SubmitEditorWidget::triggerDiffSelected()
403 const QStringList sel = selectedFiles();
405 if (!sel.empty()) {
406 emit diffSelected(sel);
410 void SubmitEditorWidget::diffActivatedDelayed()
412 const QStringList files = QStringList(listModelText(m_d->m_ui.fileView->model(), m_d->m_activatedRow, fileNameColumn()));
413 emit diffSelected(files);
416 void SubmitEditorWidget::diffActivated(const QModelIndex &index)
418 // We need to delay the signal, otherwise, the diff editor will not
419 // be in the foreground.
420 m_d->m_activatedRow = index.row();
421 QTimer::singleShot(0, this, SLOT(diffActivatedDelayed()));
424 void SubmitEditorWidget::updateActions()
426 updateSubmitAction();
427 updateDiffAction();
430 // Enable submit depending on having checked files
431 void SubmitEditorWidget::updateSubmitAction()
433 const bool newFilesCheckedState = hasCheckedFiles();
435 if (m_d->m_filesChecked != newFilesCheckedState) {
436 m_d->m_filesChecked = newFilesCheckedState;
437 emit fileCheckStateChanged(m_d->m_filesChecked);
441 // Enable diff depending on selected files
442 void SubmitEditorWidget::updateDiffAction()
444 const bool filesSelected = hasSelection();
446 if (m_d->m_filesSelected != filesSelected) {
447 m_d->m_filesSelected = filesSelected;
448 emit fileSelectionChanged(m_d->m_filesSelected);
452 bool SubmitEditorWidget::hasSelection() const
454 // Not present until model is set
455 if (const QItemSelectionModel * sm = m_d->m_ui.fileView->selectionModel()) {
456 return sm->hasSelection();
458 return false;
461 bool SubmitEditorWidget::hasCheckedFiles() const
463 if (const QAbstractItemModel * model = m_d->m_ui.fileView->model()) {
464 return listModelContainsCheckedItem(model);
466 return false;
469 void SubmitEditorWidget::changeEvent(QEvent *e)
471 QWidget::changeEvent(e);
473 switch (e->type()) {
474 case QEvent::LanguageChange:
475 m_d->m_ui.retranslateUi(this);
476 break;
477 default:
478 break;
482 void SubmitEditorWidget::insertTopWidget(QWidget *w)
484 m_d->m_ui.vboxLayout->insertWidget(0, w);
487 void SubmitEditorWidget::addSubmitFieldWidget(SubmitFieldWidget *f)
489 if (!m_d->m_fieldLayout) {
490 // VBox with horizontal, expanding spacer
491 m_d->m_fieldLayout = new QVBoxLayout;
492 QHBoxLayout *outerLayout = new QHBoxLayout;
493 outerLayout->addLayout(m_d->m_fieldLayout);
494 outerLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
495 QBoxLayout *descrLayout = qobject_cast<QBoxLayout *>(m_d->m_ui.descriptionBox->layout());
496 Q_ASSERT(descrLayout);
497 descrLayout->addLayout(outerLayout);
499 m_d->m_fieldLayout->addWidget(f);
500 m_d->m_fieldWidgets.push_back(f);
503 QList<SubmitFieldWidget *> SubmitEditorWidget::submitFieldWidgets() const
505 return m_d->m_fieldWidgets;
508 void SubmitEditorWidget::addDescriptionEditContextMenuAction(QAction *a)
510 m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(-1, a));
513 void SubmitEditorWidget::insertDescriptionEditContextMenuAction(int pos, QAction *a)
515 m_d->descriptionEditContextMenuActions.push_back(SubmitEditorWidgetPrivate::AdditionalContextMenuAction(pos, a));
518 void SubmitEditorWidget::editorCustomContextMenuRequested(const QPoint &pos)
520 QMenu *menu = m_d->m_ui.description->createStandardContextMenu();
522 // Extend
523 foreach(const SubmitEditorWidgetPrivate::AdditionalContextMenuAction & a, m_d->descriptionEditContextMenuActions) {
524 if (a.second) {
525 if (a.first >= 0) {
526 menu->insertAction(menu->actions().at(a.first), a.second);
527 } else {
528 menu->addAction(a.second);
532 menu->exec(m_d->m_ui.description->mapToGlobal(pos));
533 delete menu;
535 } // namespace Utils
537 #include "submiteditorwidget.moc"