1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ** $QT_END_LICENSE$
26 **
27 ****************************************************************************/
28
29 #include "signalsloteditorwindow.h"
30 #include "signalsloteditor_p.h"
31 #include "signalsloteditor.h"
32 #include "signalslot_utils_p.h"
33
34 #include <iconloader_p.h>
35 #include <spacer_widget_p.h>
36 #include <qlayout_widget_p.h>
37
38 #include <QtDesigner/abstractformwindow.h>
39 #include <QtDesigner/abstractformeditor.h>
40 #include <QtDesigner/abstractformwindowmanager.h>
41 #include <QtDesigner/qextensionmanager.h>
42 #include <QtDesigner/abstractintegration.h>
43 #include <QtDesigner/container.h>
44 #include <QtDesigner/abstractmetadatabase.h>
45 #include <QtDesigner/abstractformwindowcursor.h>
46 #include <abstractdialoggui_p.h>
47
48 #include <QtCore/qabstractitemmodel.h>
49 #include <QtCore/qdebug.h>
50 #include <QtWidgets/qaction.h>
51 #include <QtWidgets/qbuttongroup.h>
52 #include <QtWidgets/qmenu.h>
53 #include <QtCore/qsortfilterproxymodel.h>
54 #include <QtGui/qstandarditemmodel.h>
55 #include <QtWidgets/qcombobox.h>
56 #include <QtWidgets/qapplication.h>
57 #include <QtWidgets/qitemdelegate.h>
58 #include <QtWidgets/qitemeditorfactory.h>
59 #include <QtWidgets/qtreeview.h>
60 #include <QtWidgets/qheaderview.h>
61 #include <QtWidgets/qboxlayout.h>
62 #include <QtWidgets/qtoolbutton.h>
63 #include <QtWidgets/qbuttongroup.h>
64 #include <QtWidgets/qtoolbar.h>
65
66 QT_BEGIN_NAMESPACE
67
68 // Add suitable form widgets to a list of objects for the signal slot
69 // editor. Prevent special widgets from showing up there.
addWidgetToObjectList(const QWidget * w,QStringList & r)70 static void addWidgetToObjectList(const QWidget *w, QStringList &r)
71 {
72 const QMetaObject *mo = w->metaObject();
73 if (mo != &QLayoutWidget::staticMetaObject && mo != &Spacer::staticMetaObject) {
74 const QString name = w->objectName().trimmed();
75 if (!name.isEmpty())
76 r.push_back(name);
77 }
78 }
79
objectNameList(QDesignerFormWindowInterface * form)80 static QStringList objectNameList(QDesignerFormWindowInterface *form)
81 {
82 QStringList result;
83
84 QWidget *mainContainer = form->mainContainer();
85 if (!mainContainer)
86 return result;
87
88 // Add main container container pages (QStatusBar, QWizardPages) etc.
89 // to the list. Pages of containers on the form are not added, however.
90 if (const QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension *>(form->core()->extensionManager(), mainContainer)) {
91 const int count = c->count();
92 for (int i = 0 ; i < count; i++)
93 addWidgetToObjectList(c->widget(i), result);
94 }
95
96 const QDesignerFormWindowCursorInterface *cursor = form->cursor();
97 const int widgetCount = cursor->widgetCount();
98 for (int i = 0; i < widgetCount; ++i)
99 addWidgetToObjectList(cursor->widget(i), result);
100
101 const QDesignerMetaDataBaseInterface *mdb = form->core()->metaDataBase();
102
103 // Add managed actions and actions with managed menus
104 const auto actions = mainContainer->findChildren<QAction*>();
105 for (QAction *a : actions) {
106 if (!a->isSeparator()) {
107 if (QMenu *menu = a->menu()) {
108 if (mdb->item(menu))
109 result.push_back(menu->objectName());
110 } else {
111 if (mdb->item(a))
112 result.push_back(a->objectName());
113 }
114 }
115 }
116
117 // Add managed buttons groups
118 const auto buttonGroups = mainContainer->findChildren<QButtonGroup *>();
119 for (QButtonGroup * b : buttonGroups) {
120 if (mdb->item(b))
121 result.append(b->objectName());
122 }
123
124 result.sort();
125 return result;
126 }
127
128 namespace qdesigner_internal {
129
130 // ------------ ConnectionModel
131
ConnectionModel(QObject * parent)132 ConnectionModel::ConnectionModel(QObject *parent) :
133 QAbstractItemModel(parent)
134 {
135 }
136
setEditor(SignalSlotEditor * editor)137 void ConnectionModel::setEditor(SignalSlotEditor *editor)
138 {
139 if (m_editor == editor)
140 return;
141 beginResetModel();
142
143 if (m_editor) {
144 disconnect(m_editor.data(), &SignalSlotEditor::connectionAdded,
145 this, &ConnectionModel::connectionAdded);
146 disconnect(m_editor.data(), &SignalSlotEditor::connectionRemoved,
147 this, &ConnectionModel::connectionRemoved);
148 disconnect(m_editor.data(), &SignalSlotEditor::aboutToRemoveConnection,
149 this, &ConnectionModel::aboutToRemoveConnection);
150 disconnect(m_editor.data(), &SignalSlotEditor::aboutToAddConnection,
151 this, &ConnectionModel::aboutToAddConnection);
152 disconnect(m_editor.data(), &SignalSlotEditor::connectionChanged,
153 this, &ConnectionModel::connectionChanged);
154 }
155 m_editor = editor;
156 if (m_editor) {
157 connect(m_editor.data(), &SignalSlotEditor::connectionAdded,
158 this, &ConnectionModel::connectionAdded);
159 connect(m_editor.data(), &SignalSlotEditor::connectionRemoved,
160 this, &ConnectionModel::connectionRemoved);
161 connect(m_editor.data(), &SignalSlotEditor::aboutToRemoveConnection,
162 this, &ConnectionModel::aboutToRemoveConnection);
163 connect(m_editor.data(), &SignalSlotEditor::aboutToAddConnection,
164 this, &ConnectionModel::aboutToAddConnection);
165 connect(m_editor.data(), &SignalSlotEditor::connectionChanged,
166 this, &ConnectionModel::connectionChanged);
167 }
168 endResetModel();
169 }
170
headerData(int section,Qt::Orientation orientation,int role) const171 QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation,
172 int role) const
173 {
174 if (orientation == Qt::Vertical || role != Qt::DisplayRole)
175 return QVariant();
176
177 static const QVariant senderTitle = tr("Sender");
178 static const QVariant signalTitle = tr("Signal");
179 static const QVariant receiverTitle = tr("Receiver");
180 static const QVariant slotTitle = tr("Slot");
181
182 switch (section) {
183 case 0:
184 return senderTitle;
185 case 1:
186 return signalTitle;
187 case 2:
188 return receiverTitle;
189 case 3:
190 return slotTitle;
191 }
192 return QVariant();
193 }
194
index(int row,int column,const QModelIndex & parent) const195 QModelIndex ConnectionModel::index(int row, int column,
196 const QModelIndex &parent) const
197 {
198 if (parent.isValid() || !m_editor)
199 return QModelIndex();
200 if (row < 0 || row >= m_editor->connectionCount())
201 return QModelIndex();
202 return createIndex(row, column);
203 }
204
indexToConnection(const QModelIndex & index) const205 Connection *ConnectionModel::indexToConnection(const QModelIndex &index) const
206 {
207 if (!index.isValid() || !m_editor)
208 return nullptr;
209 if (index.row() < 0 || index.row() >= m_editor->connectionCount())
210 return nullptr;
211 return m_editor->connection(index.row());
212 }
213
connectionToIndex(Connection * con) const214 QModelIndex ConnectionModel::connectionToIndex(Connection *con) const
215 {
216 Q_ASSERT(m_editor);
217 return createIndex(m_editor->indexOfConnection(con), 0);
218 }
219
parent(const QModelIndex &) const220 QModelIndex ConnectionModel::parent(const QModelIndex&) const
221 {
222 return QModelIndex();
223 }
224
rowCount(const QModelIndex & parent) const225 int ConnectionModel::rowCount(const QModelIndex &parent) const
226 {
227 if (parent.isValid() || !m_editor)
228 return 0;
229 return m_editor->connectionCount();
230 }
231
columnCount(const QModelIndex & parent) const232 int ConnectionModel::columnCount(const QModelIndex &parent) const
233 {
234 if (parent.isValid())
235 return 0;
236 return 4;
237 }
238
connectionAt(const QModelIndex & index) const239 const SignalSlotConnection *ConnectionModel::connectionAt(const QModelIndex &index) const
240 {
241 const int row = index.row();
242 return m_editor != nullptr && row >= 0 && row < m_editor->connectionCount()
243 ? static_cast<const SignalSlotConnection*>(m_editor->connection(row))
244 : nullptr;
245 }
246
data(const QModelIndex & index,int role) const247 QVariant ConnectionModel::data(const QModelIndex &index, int role) const
248 {
249 enum { deprecatedMember = 0 };
250
251 const SignalSlotConnection *con = connectionAt(index);
252 if (con == nullptr)
253 return QVariant();
254
255 // Mark deprecated slots red/italic. Not currently in use (historically for Qt 3 slots in Qt 4),
256 // but may be used again in the future.
257 switch (role) {
258 case Qt::ForegroundRole:
259 return deprecatedMember ? QColor(Qt::red) : QVariant();
260 case Qt::FontRole:
261 if (deprecatedMember) {
262 QFont font = QApplication::font();
263 font.setItalic(true);
264 return font;
265 }
266 return QVariant();
267 case Qt::DisplayRole:
268 case Qt::EditRole:
269 return ConnectionModel::columnText(con, index.column());
270 default:
271 break;
272 }
273
274 return QVariant();
275 }
276
columnText(const SignalSlotConnection * con,int column)277 QString ConnectionModel::columnText(const SignalSlotConnection *con, int column)
278 {
279 static const QString senderDefault = tr("<sender>");
280 static const QString signalDefault = tr("<signal>");
281 static const QString receiverDefault = tr("<receiver>");
282 static const QString slotDefault = tr("<slot>");
283
284 switch (column) {
285 case 0: {
286 const QString sender = con->sender();
287 return sender.isEmpty() ? senderDefault : sender;
288 }
289 case 1: {
290 const QString signalName = con->signal();
291 return signalName.isEmpty() ? signalDefault : signalName;
292 }
293 case 2: {
294 const QString receiver = con->receiver();
295 return receiver.isEmpty() ? receiverDefault : receiver;
296 }
297 case 3: {
298 const QString slotName = con->slot();
299 return slotName.isEmpty() ? slotDefault : slotName;
300 }
301 }
302 return QString();
303 }
304
setData(const QModelIndex & index,const QVariant & data,int)305 bool ConnectionModel::setData(const QModelIndex &index, const QVariant &data, int)
306 {
307 if (!index.isValid() || !m_editor)
308 return false;
309 if (data.type() != QVariant::String)
310 return false;
311
312 SignalSlotConnection *con = static_cast<SignalSlotConnection*>(m_editor->connection(index.row()));
313 QDesignerFormWindowInterface *form = m_editor->formWindow();
314
315 QString s = data.toString();
316 switch (index.column()) {
317 case 0:
318 if (!s.isEmpty() && !objectNameList(form).contains(s))
319 s.clear();
320 m_editor->setSource(con, s);
321 break;
322 case 1:
323 if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Source), SignalMember, s))
324 s.clear();
325 m_editor->setSignal(con, s);
326 break;
327 case 2:
328 if (!s.isEmpty() && !objectNameList(form).contains(s))
329 s.clear();
330 m_editor->setTarget(con, s);
331 break;
332 case 3:
333 if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Target), SlotMember, s))
334 s.clear();
335 m_editor->setSlot(con, s);
336 break;
337 }
338
339 return true;
340 }
341
connectionAdded(Connection *)342 void ConnectionModel::connectionAdded(Connection*)
343 {
344 endInsertRows();
345 }
346
connectionRemoved(int)347 void ConnectionModel::connectionRemoved(int)
348 {
349 endRemoveRows();
350 }
351
aboutToRemoveConnection(Connection * con)352 void ConnectionModel::aboutToRemoveConnection(Connection *con)
353 {
354 Q_ASSERT(m_editor);
355 int idx = m_editor->indexOfConnection(con);
356 beginRemoveRows(QModelIndex(), idx, idx);
357 }
358
aboutToAddConnection(int idx)359 void ConnectionModel::aboutToAddConnection(int idx)
360 {
361 Q_ASSERT(m_editor);
362 beginInsertRows(QModelIndex(), idx, idx);
363 }
364
flags(const QModelIndex &) const365 Qt::ItemFlags ConnectionModel::flags(const QModelIndex&) const
366 {
367 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
368 }
369
connectionChanged(Connection * con)370 void ConnectionModel::connectionChanged(Connection *con)
371 {
372 Q_ASSERT(m_editor);
373 const int idx = m_editor->indexOfConnection(con);
374 SignalSlotConnection *changedCon = static_cast<SignalSlotConnection*>(m_editor->connection(idx));
375 SignalSlotConnection *c = nullptr;
376 for (int i=0; i<m_editor->connectionCount(); ++i) {
377 if (i == idx)
378 continue;
379 c = static_cast<SignalSlotConnection*>(m_editor->connection(i));
380 if (c->sender() == changedCon->sender() && c->signal() == changedCon->signal()
381 && c->receiver() == changedCon->receiver() && c->slot() == changedCon->slot()) {
382 const QString message = tr("The connection already exists!<br>%1").arg(changedCon->toString());
383 m_editor->formWindow()->core()->dialogGui()->message(m_editor->parentWidget(), QDesignerDialogGuiInterface::SignalSlotEditorMessage,
384 QMessageBox::Warning, tr("Signal and Slot Editor"), message, QMessageBox::Ok);
385 break;
386 }
387 }
388 emit dataChanged(createIndex(idx, 0), createIndex(idx, 3));
389 }
390
updateAll()391 void ConnectionModel::updateAll()
392 {
393 emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
394 }
395 }
396
397 namespace {
398 // ---------------------- InlineEditorModel
399
400 class InlineEditorModel : public QStandardItemModel
401 {
402 Q_OBJECT
403 public:
404 enum { TitleItem = 1 };
405
406 InlineEditorModel(int rows, int cols, QObject *parent = nullptr);
407
408 void addTitle(const QString &title);
409 void addTextList(const QMap<QString, bool> &text_list);
410 void addText(const QString &text);
411 bool isTitle(int idx) const;
412
413 int findText(const QString &text) const;
414
415 Qt::ItemFlags flags(const QModelIndex &index) const override;
416 };
417
InlineEditorModel(int rows,int cols,QObject * parent)418 InlineEditorModel::InlineEditorModel(int rows, int cols, QObject *parent)
419 : QStandardItemModel(rows, cols, parent)
420 {
421 }
422
addTitle(const QString & title)423 void InlineEditorModel::addTitle(const QString &title)
424 {
425 const int cnt = rowCount();
426 insertRows(cnt, 1);
427 QModelIndex cat_idx = index(cnt, 0);
428 setData(cat_idx, QString(title + QLatin1Char(':')), Qt::DisplayRole);
429 setData(cat_idx, TitleItem, Qt::UserRole);
430 QFont font = QApplication::font();
431 font.setBold(true);
432 setData(cat_idx, font, Qt::FontRole);
433 }
434
isTitle(int idx) const435 bool InlineEditorModel::isTitle(int idx) const
436 {
437 if (idx == -1)
438 return false;
439
440 return data(index(idx, 0), Qt::UserRole).toInt() == TitleItem;
441 }
442
addText(const QString & text)443 void InlineEditorModel::addText(const QString &text)
444 {
445 const int cnt = rowCount();
446 insertRows(cnt, 1);
447 setData(index(cnt, 0), text, Qt::DisplayRole);
448 }
449
addTextList(const QMap<QString,bool> & text_list)450 void InlineEditorModel::addTextList(const QMap<QString, bool> &text_list)
451 {
452 int cnt = rowCount();
453 insertRows(cnt, text_list.size());
454 QFont font = QApplication::font();
455 font.setItalic(true);
456 QVariant fontVariant = QVariant::fromValue(font);
457 QMap<QString, bool>::ConstIterator it = text_list.constBegin();
458 const QMap<QString, bool>::ConstIterator itEnd = text_list.constEnd();
459 while (it != itEnd) {
460 const QModelIndex text_idx = index(cnt++, 0);
461 setData(text_idx, it.key(), Qt::DisplayRole);
462 if (it.value()) {
463 setData(text_idx, fontVariant, Qt::FontRole);
464 setData(text_idx, QColor(Qt::red), Qt::ForegroundRole);
465 }
466 ++it;
467 }
468 }
469
flags(const QModelIndex & index) const470 Qt::ItemFlags InlineEditorModel::flags(const QModelIndex &index) const
471 {
472 return isTitle(index.row())
473 ? Qt::ItemFlags(Qt::ItemIsEnabled)
474 : Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
475 }
476
findText(const QString & text) const477 int InlineEditorModel::findText(const QString &text) const
478 {
479 const int cnt = rowCount();
480 for (int i = 0; i < cnt; ++i) {
481 const QModelIndex idx = index(i, 0);
482 if (data(idx, Qt::UserRole).toInt() == TitleItem)
483 continue;
484 if (data(idx, Qt::DisplayRole).toString() == text)
485 return i;
486 }
487 return -1;
488 }
489
490 // ------------ InlineEditor
491 class InlineEditor : public QComboBox
492 {
493 Q_OBJECT
494 Q_PROPERTY(QString text READ text WRITE setText USER true)
495 public:
496 InlineEditor(QWidget *parent = nullptr);
497
498 QString text() const;
499 void setText(const QString &text);
500
501 void addTitle(const QString &title);
502 void addText(const QString &text);
503 void addTextList(const QMap<QString, bool> &text_list);
504
505 private slots:
506 void checkSelection(int idx);
507
508 private:
509 InlineEditorModel *m_model;
510 int m_idx = -1;
511 };
512
InlineEditor(QWidget * parent)513 InlineEditor::InlineEditor(QWidget *parent) :
514 QComboBox(parent)
515 {
516 setModel(m_model = new InlineEditorModel(0, 4, this));
517 setFrame(false);
518 m_idx = -1;
519 connect(this, QOverload<int>::of(&QComboBox::activated),
520 this, &InlineEditor::checkSelection);
521 }
522
checkSelection(int idx)523 void InlineEditor::checkSelection(int idx)
524 {
525 if (idx == m_idx)
526 return;
527
528 if (m_model->isTitle(idx))
529 setCurrentIndex(m_idx);
530 else
531 m_idx = idx;
532 }
533
addTitle(const QString & title)534 void InlineEditor::addTitle(const QString &title)
535 {
536 m_model->addTitle(title);
537 }
538
addTextList(const QMap<QString,bool> & text_list)539 void InlineEditor::addTextList(const QMap<QString, bool> &text_list)
540 {
541 m_model->addTextList(text_list);
542 }
543
addText(const QString & text)544 void InlineEditor::addText(const QString &text)
545 {
546 m_model->addText(text);
547 }
548
text() const549 QString InlineEditor::text() const
550 {
551 return currentText();
552 }
553
setText(const QString & text)554 void InlineEditor::setText(const QString &text)
555 {
556 m_idx = m_model->findText(text);
557 if (m_idx == -1)
558 m_idx = 0;
559 setCurrentIndex(m_idx);
560 }
561
562 // ------------------ ConnectionDelegate
563
564 class ConnectionDelegate : public QItemDelegate
565 {
566 Q_OBJECT
567 public:
568 ConnectionDelegate(QWidget *parent = nullptr);
569
570 void setForm(QDesignerFormWindowInterface *form);
571
572 QWidget *createEditor(QWidget *parent,
573 const QStyleOptionViewItem &option,
574 const QModelIndex &index) const override;
575
576 private slots:
577 void emitCommitData();
578
579 private:
580 QDesignerFormWindowInterface *m_form;
581 };
582
ConnectionDelegate(QWidget * parent)583 ConnectionDelegate::ConnectionDelegate(QWidget *parent)
584 : QItemDelegate(parent)
585 {
586 m_form = nullptr;
587
588 static QItemEditorFactory *factory = nullptr;
589 if (factory == nullptr) {
590 factory = new QItemEditorFactory;
591 QItemEditorCreatorBase *creator
592 = new QItemEditorCreator<InlineEditor>("text");
593 factory->registerEditor(QVariant::String, creator);
594 }
595
596 setItemEditorFactory(factory);
597 }
598
setForm(QDesignerFormWindowInterface * form)599 void ConnectionDelegate::setForm(QDesignerFormWindowInterface *form)
600 {
601 m_form = form;
602 }
603
createEditor(QWidget * parent,const QStyleOptionViewItem & option,const QModelIndex & index) const604 QWidget *ConnectionDelegate::createEditor(QWidget *parent,
605 const QStyleOptionViewItem &option,
606 const QModelIndex &index) const
607 {
608 if (m_form == nullptr)
609 return nullptr;
610
611 QWidget *w = QItemDelegate::createEditor(parent, option, index);
612 InlineEditor *inline_editor = qobject_cast<InlineEditor*>(w);
613 Q_ASSERT(inline_editor != nullptr);
614 const QAbstractItemModel *model = index.model();
615
616 const QModelIndex obj_name_idx = model->index(index.row(), index.column() <= 1 ? 0 : 2);
617 const QString obj_name = model->data(obj_name_idx, Qt::DisplayRole).toString();
618
619 switch (index.column()) {
620 case 0:
621 case 2: { // object names
622 const QStringList &obj_name_list = objectNameList(m_form);
623 QMap<QString, bool> markedNameList;
624 markedNameList.insert(tr("<object>"), false);
625 inline_editor->addTextList(markedNameList);
626 markedNameList.clear();
627 for (const QString &name : obj_name_list)
628 markedNameList.insert(name, false);
629 inline_editor->addTextList(markedNameList);
630 }
631 break;
632 case 1:
633 case 3: { // signals, slots
634 const qdesigner_internal::MemberType type = index.column() == 1 ? qdesigner_internal::SignalMember : qdesigner_internal::SlotMember;
635 const QModelIndex peer_index = model->index(index.row(), type == qdesigner_internal::SignalMember ? 3 : 1);
636 const QString peer = model->data(peer_index, Qt::DisplayRole).toString();
637
638 const qdesigner_internal::ClassesMemberFunctions class_list = qdesigner_internal::reverseClassesMemberFunctions(obj_name, type, peer, m_form);
639
640 inline_editor->addText(type == qdesigner_internal::SignalMember ? tr("<signal>") : tr("<slot>"));
641 for (const qdesigner_internal::ClassMemberFunctions &classInfo : class_list) {
642 if (classInfo.m_className.isEmpty() || classInfo.m_memberList.isEmpty())
643 continue;
644 // Mark deprecated members by passing bool=true.
645 QMap<QString, bool> markedMemberList;
646 for (const QString &member : qAsConst(classInfo.m_memberList))
647 markedMemberList.insert(member, false);
648 inline_editor->addTitle(classInfo.m_className);
649 inline_editor->addTextList(markedMemberList);
650 }
651 }
652 break;
653 default:
654 break;
655 }
656
657 connect(inline_editor, QOverload<int>::of(&QComboBox::activated),
658 this, &ConnectionDelegate::emitCommitData);
659
660 return inline_editor;
661 }
662
emitCommitData()663 void ConnectionDelegate::emitCommitData()
664 {
665 InlineEditor *editor = qobject_cast<InlineEditor*>(sender());
666 emit commitData(editor);
667 }
668
669 }
670
671 namespace qdesigner_internal {
672
673 /*******************************************************************************
674 ** SignalSlotEditorWindow
675 */
676
SignalSlotEditorWindow(QDesignerFormEditorInterface * core,QWidget * parent)677 SignalSlotEditorWindow::SignalSlotEditorWindow(QDesignerFormEditorInterface *core,
678 QWidget *parent) :
679 QWidget(parent),
680 m_view(new QTreeView),
681 m_editor(nullptr),
682 m_add_button(new QToolButton),
683 m_remove_button(new QToolButton),
684 m_core(core),
685 m_model(new ConnectionModel(this)),
686 m_proxy_model(new QSortFilterProxyModel(this)),
687 m_handling_selection_change(false)
688 {
689 m_proxy_model->setSourceModel(m_model);
690 m_view->setModel(m_proxy_model);
691 m_view->setSortingEnabled(true);
692 m_view->setItemDelegate(new ConnectionDelegate(this));
693 m_view->setEditTriggers(QAbstractItemView::DoubleClicked
694 | QAbstractItemView::EditKeyPressed);
695 m_view->setRootIsDecorated(false);
696 m_view->setTextElideMode (Qt::ElideMiddle);
697 connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged,
698 this, &SignalSlotEditorWindow::updateUi);
699 connect(m_view->header(), &QHeaderView::sectionDoubleClicked,
700 m_view, &QTreeView::resizeColumnToContents);
701
702 QVBoxLayout *layout = new QVBoxLayout(this);
703 layout->setContentsMargins(QMargins());
704 layout->setSpacing(0);
705
706 QToolBar *toolBar = new QToolBar;
707 toolBar->setIconSize(QSize(22, 22));
708 m_add_button->setIcon(createIconSet(QStringLiteral("plus.png")));
709 connect(m_add_button, &QAbstractButton::clicked, this, &SignalSlotEditorWindow::addConnection);
710 toolBar->addWidget(m_add_button);
711
712 m_remove_button->setIcon(createIconSet(QStringLiteral("minus.png")));
713 connect(m_remove_button, &QAbstractButton::clicked, this, &SignalSlotEditorWindow::removeConnection);
714 toolBar->addWidget(m_remove_button);
715
716 layout->addWidget(toolBar);
717 layout->addWidget(m_view);
718
719 connect(core->formWindowManager(),
720 &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
721 this, &SignalSlotEditorWindow::setActiveFormWindow);
722
723 updateUi();
724 }
725
setActiveFormWindow(QDesignerFormWindowInterface * form)726 void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *form)
727 {
728 QDesignerIntegrationInterface *integration = m_core->integration();
729
730 if (!m_editor.isNull()) {
731 disconnect(m_view->selectionModel(),
732 &QItemSelectionModel::currentChanged,
733 this, &SignalSlotEditorWindow::updateEditorSelection);
734 disconnect(m_editor.data(), &SignalSlotEditor::connectionSelected,
735 this, &SignalSlotEditorWindow::updateDialogSelection);
736 disconnect(m_editor.data(), &SignalSlotEditor::connectionAdded,
737 this, &SignalSlotEditorWindow::resizeColumns);
738 if (integration) {
739 disconnect(integration, &QDesignerIntegrationInterface::objectNameChanged,
740 this, &SignalSlotEditorWindow::objectNameChanged);
741 }
742 }
743
744 m_editor = form->findChild<SignalSlotEditor*>();
745 m_model->setEditor(m_editor);
746 if (!m_editor.isNull()) {
747 ConnectionDelegate *delegate
748 = qobject_cast<ConnectionDelegate*>(m_view->itemDelegate());
749 if (delegate != nullptr)
750 delegate->setForm(form);
751
752 connect(m_view->selectionModel(),
753 &QItemSelectionModel::currentChanged,
754 this, &SignalSlotEditorWindow::updateEditorSelection);
755 connect(m_editor.data(), &SignalSlotEditor::connectionSelected,
756 this, &SignalSlotEditorWindow::updateDialogSelection);
757 connect(m_editor.data(), &SignalSlotEditor::connectionAdded,
758 this, &SignalSlotEditorWindow::resizeColumns);
759 if (integration) {
760 connect(integration, &QDesignerIntegrationInterface::objectNameChanged,
761 this, &SignalSlotEditorWindow::objectNameChanged);
762 }
763 }
764
765 resizeColumns();
766 updateUi();
767 }
768
updateDialogSelection(Connection * con)769 void SignalSlotEditorWindow::updateDialogSelection(Connection *con)
770 {
771 if (m_handling_selection_change || m_editor == nullptr)
772 return;
773
774 QModelIndex index = m_proxy_model->mapFromSource(m_model->connectionToIndex(con));
775 if (!index.isValid() || index == m_view->currentIndex())
776 return;
777 m_handling_selection_change = true;
778 m_view->scrollTo(index, QTreeView::EnsureVisible);
779 m_view->setCurrentIndex(index);
780 m_handling_selection_change = false;
781
782 updateUi();
783 }
784
updateEditorSelection(const QModelIndex & index)785 void SignalSlotEditorWindow::updateEditorSelection(const QModelIndex &index)
786 {
787 if (m_handling_selection_change || m_editor == nullptr)
788 return;
789
790 if (m_editor == nullptr)
791 return;
792
793 Connection *con = m_model->indexToConnection(m_proxy_model->mapToSource(index));
794 if (m_editor->selected(con))
795 return;
796 m_handling_selection_change = true;
797 m_editor->selectNone();
798 m_editor->setSelected(con, true);
799 m_handling_selection_change = false;
800
801 updateUi();
802 }
803
objectNameChanged(QDesignerFormWindowInterface *,QObject *,const QString &,const QString &)804 void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *, const QString &, const QString &)
805 {
806 if (m_editor)
807 m_model->updateAll();
808 }
809
addConnection()810 void SignalSlotEditorWindow::addConnection()
811 {
812 if (m_editor.isNull())
813 return;
814
815 m_editor->addEmptyConnection();
816 updateUi();
817 }
818
removeConnection()819 void SignalSlotEditorWindow::removeConnection()
820 {
821 if (m_editor.isNull())
822 return;
823
824 m_editor->deleteSelected();
825 updateUi();
826 }
827
updateUi()828 void SignalSlotEditorWindow::updateUi()
829 {
830 m_add_button->setEnabled(!m_editor.isNull());
831 m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid());
832 }
833
resizeColumns()834 void SignalSlotEditorWindow::resizeColumns()
835 {
836 for (int c = 0, count = m_model->columnCount(); c < count; ++c)
837 m_view->resizeColumnToContents(c);
838 }
839
840 } // namespace qdesigner_internal
841
842 QT_END_NAMESPACE
843
844 #include "signalsloteditorwindow.moc"
845