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