9#include <iconloader_p.h>
10#include <spacer_widget_p.h>
11#include <qlayout_widget_p.h>
13#include <QtDesigner/abstractformwindow.h>
14#include <QtDesigner/abstractformeditor.h>
15#include <QtDesigner/abstractformwindowmanager.h>
16#include <QtDesigner/qextensionmanager.h>
17#include <QtDesigner/abstractintegration.h>
18#include <QtDesigner/container.h>
19#include <QtDesigner/abstractmetadatabase.h>
20#include <QtDesigner/abstractformwindowcursor.h>
21#include <abstractdialoggui_p.h>
23#include <QtWidgets/qbuttongroup.h>
24#include <QtWidgets/qmenu.h>
25#include <QtWidgets/qcombobox.h>
26#include <QtWidgets/qapplication.h>
27#include <QtWidgets/qitemdelegate.h>
28#include <QtWidgets/qitemeditorfactory.h>
29#include <QtWidgets/qtreeview.h>
30#include <QtWidgets/qheaderview.h>
31#include <QtWidgets/qboxlayout.h>
32#include <QtWidgets/qtoolbutton.h>
33#include <QtWidgets/qbuttongroup.h>
34#include <QtWidgets/qtoolbar.h>
36#include <QtGui/qaction.h>
37#include <QtGui/qstandarditemmodel.h>
39#include <QtCore/qabstractitemmodel.h>
40#include <QtCore/qdebug.h>
41#include <QtCore/qsortfilterproxymodel.h>
45using namespace Qt::StringLiterals;
51 const QMetaObject *mo = w->metaObject();
52 if (mo != &QLayoutWidget::staticMetaObject && mo != &Spacer::staticMetaObject) {
53 const QString name = w->objectName().trimmed();
63 QWidget *mainContainer = form->mainContainer();
69 if (
const QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension *>(form->core()->extensionManager(), mainContainer)) {
70 const int count = c->count();
71 for (
int i = 0 ; i < count; i++)
72 addWidgetToObjectList(c->widget(i), result);
75 const QDesignerFormWindowCursorInterface *cursor = form->cursor();
76 const int widgetCount = cursor->widgetCount();
77 for (
int i = 0; i < widgetCount; ++i)
78 addWidgetToObjectList(cursor->widget(i), result);
80 const QDesignerMetaDataBaseInterface *mdb = form->core()->metaDataBase();
83 const auto actions = mainContainer->findChildren<QAction*>();
84 for (QAction *a : actions) {
85 if (!a->isSeparator()) {
86 if (QMenu *menu = a->menu()) {
88 result.push_back(menu->objectName());
91 result.push_back(a->objectName());
97 const auto buttonGroups = mainContainer->findChildren<QButtonGroup *>();
98 for (QButtonGroup * b : buttonGroups) {
100 result.append(b->objectName());
112 QAbstractItemModel(parent)
118 if (m_editor == editor)
123 disconnect(m_editor.data(), &SignalSlotEditor::connectionAdded,
124 this, &ConnectionModel::connectionAdded);
125 disconnect(m_editor.data(), &SignalSlotEditor::connectionRemoved,
126 this, &ConnectionModel::connectionRemoved);
127 disconnect(m_editor.data(), &SignalSlotEditor::aboutToRemoveConnection,
128 this, &ConnectionModel::aboutToRemoveConnection);
129 disconnect(m_editor.data(), &SignalSlotEditor::aboutToAddConnection,
130 this, &ConnectionModel::aboutToAddConnection);
131 disconnect(m_editor.data(), &SignalSlotEditor::connectionChanged,
132 this, &ConnectionModel::connectionChanged);
136 connect(m_editor.data(), &SignalSlotEditor::connectionAdded,
137 this, &ConnectionModel::connectionAdded);
138 connect(m_editor.data(), &SignalSlotEditor::connectionRemoved,
139 this, &ConnectionModel::connectionRemoved);
140 connect(m_editor.data(), &SignalSlotEditor::aboutToRemoveConnection,
141 this, &ConnectionModel::aboutToRemoveConnection);
142 connect(m_editor.data(), &SignalSlotEditor::aboutToAddConnection,
143 this, &ConnectionModel::aboutToAddConnection);
144 connect(m_editor.data(), &SignalSlotEditor::connectionChanged,
145 this, &ConnectionModel::connectionChanged);
153 if (orientation == Qt::Vertical || role != Qt::DisplayRole)
156 static const QVariant senderTitle = tr(
"Sender");
157 static const QVariant signalTitle = tr(
"Signal");
158 static const QVariant receiverTitle = tr(
"Receiver");
159 static const QVariant slotTitle = tr(
"Slot");
167 return receiverTitle;
177 if (parent.isValid() || !m_editor)
178 return QModelIndex();
179 if (row < 0 || row >= m_editor->connectionCount())
180 return QModelIndex();
181 return createIndex(row, column);
186 if (!index.isValid() || !m_editor)
188 if (index.row() < 0 || index.row() >= m_editor->connectionCount())
190 return m_editor->connection(index.row());
196 return createIndex(m_editor->indexOfConnection(con), 0);
201 return QModelIndex();
206 if (parent.isValid() || !m_editor)
208 return m_editor->connectionCount();
213 if (parent.isValid())
220 const int row = index.row();
221 return m_editor !=
nullptr && row >= 0 && row < m_editor->connectionCount()
222 ?
static_cast<
const SignalSlotConnection*>(m_editor->connection(row))
228 enum { deprecatedMember = 0 };
237 case Qt::ForegroundRole:
238 return deprecatedMember ? QColor(Qt::red) : QVariant();
240 if (deprecatedMember) {
241 QFont font = QApplication::font();
242 font.setItalic(
true);
246 case Qt::DisplayRole:
248 return ConnectionModel::columnText(con, index.column());
258 static const QString senderDefault = tr(
"<sender>");
259 static const QString signalDefault = tr(
"<signal>");
260 static const QString receiverDefault = tr(
"<receiver>");
261 static const QString slotDefault = tr(
"<slot>");
265 const QString sender = con->sender();
266 return sender.isEmpty() ? senderDefault : sender;
269 const QString signalName = con->signal();
270 return signalName.isEmpty() ? signalDefault : signalName;
273 const QString receiver = con->receiver();
274 return receiver.isEmpty() ? receiverDefault : receiver;
277 const QString slotName = con->slot();
278 return slotName.isEmpty() ? slotDefault : slotName;
286 if (!index.isValid() || !m_editor)
288 if (data.metaType().id() != QMetaType::QString)
291 SignalSlotConnection *con =
static_cast<SignalSlotConnection*>(m_editor->connection(index.row()));
292 QDesignerFormWindowInterface *form = m_editor->formWindow();
294 QString s = data.toString();
295 switch (index.column()) {
297 if (!s.isEmpty() && !objectNameList(form).contains(s))
299 m_editor->setSource(con, s);
302 if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Source), SignalMember, s))
304 m_editor->setSignal(con, s);
307 if (!s.isEmpty() && !objectNameList(form).contains(s))
309 m_editor->setTarget(con, s);
312 if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Target), SlotMember, s))
314 m_editor->setSlot(con, s);
334 int idx = m_editor->indexOfConnection(con);
335 beginRemoveRows(QModelIndex(), idx, idx);
341 beginInsertRows(QModelIndex(), idx, idx);
346 return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
352 const int idx = m_editor->indexOfConnection(con);
353 SignalSlotConnection *changedCon =
static_cast<SignalSlotConnection*>(m_editor->connection(idx));
355 for (
int i=0; i<m_editor->connectionCount(); ++i) {
358 c =
static_cast<SignalSlotConnection*>(m_editor->connection(i));
359 if (c->sender() == changedCon->sender() && c->signal() == changedCon->signal()
360 && c->receiver() == changedCon->receiver() && c->slot() == changedCon->slot()) {
361 const QString message = tr(
"The connection already exists!<br>%1").arg(changedCon->toString());
362 m_editor->formWindow()->core()->dialogGui()->message(m_editor->parentWidget(), QDesignerDialogGuiInterface::SignalSlotEditorMessage,
363 QMessageBox::Warning, tr(
"Signal and Slot Editor"), message, QMessageBox::Ok);
367 emit dataChanged(createIndex(idx, 0), createIndex(idx, 3));
372 emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
379class InlineEditorModel :
public QStandardItemModel
383 enum { TitleItem = 1 };
385 InlineEditorModel(
int rows,
int cols, QObject *parent =
nullptr);
387 void addTitle(
const QString &title);
388 void addTextList(
const QMap<QString,
bool> &text_list);
389 void addText(
const QString &text);
390 bool isTitle(
int idx)
const;
392 int findText(
const QString &text)
const;
394 Qt::ItemFlags flags(
const QModelIndex &index)
const override;
397InlineEditorModel::InlineEditorModel(
int rows,
int cols, QObject *parent)
398 : QStandardItemModel(rows, cols, parent)
402void InlineEditorModel::addTitle(
const QString &title)
404 const int cnt = rowCount();
407 setData(cat_idx, QString(title + u':'), Qt::DisplayRole);
408 setData(cat_idx, TitleItem, Qt::UserRole);
409 QFont font = QApplication::font();
411 setData(cat_idx, font, Qt::FontRole);
414bool InlineEditorModel::isTitle(
int idx)
const
419 return data(index(idx, 0), Qt::UserRole).toInt() == TitleItem;
422void InlineEditorModel::addText(
const QString &text)
424 const int cnt = rowCount();
426 setData(index(cnt, 0), text, Qt::DisplayRole);
429void InlineEditorModel::addTextList(
const QMap<QString,
bool> &text_list)
431 int cnt = rowCount();
432 insertRows(cnt, text_list.size());
433 QFont font = QApplication::font();
434 font.setItalic(
true);
435 QVariant fontVariant = QVariant::fromValue(font);
436 for (
auto it = text_list.cbegin(), itEnd = text_list.cend(); it != itEnd; ++it) {
438 setData(text_idx, it.key(), Qt::DisplayRole);
440 setData(text_idx, fontVariant, Qt::FontRole);
441 setData(text_idx, QColor(Qt::red), Qt::ForegroundRole);
446Qt::ItemFlags InlineEditorModel::flags(
const QModelIndex &index)
const
448 return isTitle(index.row())
449 ? Qt::ItemFlags(Qt::ItemIsEnabled)
450 : Qt::ItemFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
453int InlineEditorModel::findText(
const QString &text)
const
455 const int cnt = rowCount();
456 for (
int i = 0; i < cnt; ++i) {
458 if (data(idx, Qt::UserRole).toInt() == TitleItem)
460 if (data(idx, Qt::DisplayRole).toString() == text)
467class InlineEditor :
public QComboBox
470 Q_PROPERTY(QString text READ text WRITE setText USER
true)
472 InlineEditor(QWidget *parent =
nullptr);
474 QString text()
const;
475 void setText(
const QString &text);
477 void addTitle(
const QString &title);
478 void addText(
const QString &text);
479 void addTextList(
const QMap<QString,
bool> &text_list);
482 void checkSelection(
int idx);
485 InlineEditorModel *m_model;
489InlineEditor::InlineEditor(
QWidget *parent) :
492 setModel(m_model =
new InlineEditorModel(0, 4,
this));
495 connect(
this, &QComboBox::activated,
496 this, &InlineEditor::checkSelection);
499void InlineEditor::checkSelection(
int idx)
504 if (m_model->isTitle(idx))
505 setCurrentIndex(m_idx);
510void InlineEditor::addTitle(
const QString &title)
512 m_model->addTitle(title);
515void InlineEditor::addTextList(
const QMap<QString,
bool> &text_list)
517 m_model->addTextList(text_list);
520void InlineEditor::addText(
const QString &text)
522 m_model->addText(text);
525QString InlineEditor::text()
const
527 return currentText();
530void InlineEditor::setText(
const QString &text)
532 m_idx = m_model->findText(text);
535 setCurrentIndex(m_idx);
540class ConnectionDelegate :
public QItemDelegate
544 ConnectionDelegate(QWidget *parent =
nullptr);
546 void setForm(QDesignerFormWindowInterface *form);
549 const QStyleOptionViewItem &option,
553 void emitCommitData();
556 QDesignerFormWindowInterface *m_form;
559ConnectionDelegate::ConnectionDelegate(
QWidget *parent)
560 : QItemDelegate(parent)
564 static QItemEditorFactory *factory =
nullptr;
565 if (factory ==
nullptr) {
566 factory =
new QItemEditorFactory;
567 QItemEditorCreatorBase *creator
568 =
new QItemEditorCreator<InlineEditor>(
"text");
569 factory->registerEditor(QMetaType::QString, creator);
572 setItemEditorFactory(factory);
575void ConnectionDelegate::setForm(QDesignerFormWindowInterface *form)
581 const QStyleOptionViewItem &option,
584 if (m_form ==
nullptr)
587 QWidget *w = QItemDelegate::createEditor(parent, option, index);
588 InlineEditor *inline_editor = qobject_cast<InlineEditor*>(w);
589 Q_ASSERT(inline_editor !=
nullptr);
590 const QAbstractItemModel *model = index.model();
592 const QModelIndex obj_name_idx = model->index(index.row(), index.column() <= 1 ? 0 : 2);
593 const QString obj_name = model->data(obj_name_idx, Qt::DisplayRole).toString();
595 switch (index.column()) {
598 const QStringList &obj_name_list = objectNameList(m_form);
599 QMap<QString,
bool> markedNameList;
600 markedNameList.insert(tr(
"<object>"),
false);
601 inline_editor->addTextList(markedNameList);
602 markedNameList.clear();
603 for (
const QString &name : obj_name_list)
604 markedNameList.insert(name,
false);
605 inline_editor->addTextList(markedNameList);
611 const QModelIndex peer_index = model->index(index.row(), type == qdesigner_internal::SignalMember ? 3 : 1);
612 const QString peer = model->data(peer_index, Qt::DisplayRole).toString();
614 const qdesigner_internal::ClassesMemberFunctions class_list = qdesigner_internal::reverseClassesMemberFunctions(obj_name, type, peer, m_form);
617 for (
const qdesigner_internal::ClassMemberFunctions &classInfo : class_list) {
618 if (classInfo.m_className.isEmpty() || classInfo.m_memberList.isEmpty())
621 QMap<QString,
bool> markedMemberList;
622 for (
const QString &member : std::as_const(classInfo.m_memberList))
623 markedMemberList.insert(member,
false);
624 inline_editor->addTitle(classInfo.m_className);
625 inline_editor->addTextList(markedMemberList);
633 connect(inline_editor, &QComboBox::activated,
634 this, &ConnectionDelegate::emitCommitData);
636 return inline_editor;
639void ConnectionDelegate::emitCommitData()
641 InlineEditor *editor = qobject_cast<InlineEditor*>(sender());
642 emit commitData(editor);
650
651
656 m_view(
new QTreeView),
658 m_add_button(
new QToolButton),
659 m_remove_button(
new QToolButton),
661 m_model(
new ConnectionModel(
this)),
662 m_proxy_model(
new QSortFilterProxyModel(
this)),
663 m_handling_selection_change(
false)
665 m_proxy_model->setSourceModel(m_model);
666 m_view->setModel(m_proxy_model);
667 m_view->setSortingEnabled(
true);
668 m_view->setItemDelegate(
new ConnectionDelegate(
this));
669 m_view->setEditTriggers(QAbstractItemView::DoubleClicked
670 | QAbstractItemView::EditKeyPressed);
671 m_view->setRootIsDecorated(
false);
672 m_view->setTextElideMode (Qt::ElideMiddle);
673 connect(m_view->selectionModel(), &QItemSelectionModel::currentChanged,
674 this, &SignalSlotEditorWindow::updateUi);
675 connect(m_view->header(), &QHeaderView::sectionDoubleClicked,
676 m_view, &QTreeView::resizeColumnToContents);
678 QVBoxLayout *layout =
new QVBoxLayout(
this);
679 layout->setContentsMargins(QMargins());
680 layout->setSpacing(0);
682 QToolBar *toolBar =
new QToolBar;
683 toolBar->setIconSize(QSize(22, 22));
684 m_add_button->setIcon(createIconSet(
"plus.png"_L1));
685 connect(m_add_button, &QAbstractButton::clicked,
this, &SignalSlotEditorWindow::addConnection);
686 toolBar->addWidget(m_add_button);
688 m_remove_button->setIcon(createIconSet(
"minus.png"_L1));
689 connect(m_remove_button, &QAbstractButton::clicked,
this, &SignalSlotEditorWindow::removeConnection);
690 toolBar->addWidget(m_remove_button);
692 layout->addWidget(toolBar);
693 layout->addWidget(m_view);
695 connect(core->formWindowManager(),
696 &QDesignerFormWindowManagerInterface::activeFormWindowChanged,
697 this, &SignalSlotEditorWindow::setActiveFormWindow);
704 QDesignerIntegrationInterface *integration = m_core->integration();
706 if (!m_editor.isNull()) {
707 disconnect(m_view->selectionModel(),
708 &QItemSelectionModel::currentChanged,
709 this, &SignalSlotEditorWindow::updateEditorSelection);
710 disconnect(m_editor.data(), &SignalSlotEditor::connectionSelected,
711 this, &SignalSlotEditorWindow::updateDialogSelection);
712 disconnect(m_editor.data(), &SignalSlotEditor::connectionAdded,
713 this, &SignalSlotEditorWindow::resizeColumns);
715 disconnect(integration, &QDesignerIntegrationInterface::objectNameChanged,
716 this, &SignalSlotEditorWindow::objectNameChanged);
720 m_editor = form ? form->findChild<SignalSlotEditor*>() :
nullptr;
721 m_model->setEditor(m_editor);
722 if (!m_editor.isNull()) {
723 ConnectionDelegate *delegate
724 = qobject_cast<ConnectionDelegate*>(m_view->itemDelegate());
725 if (delegate !=
nullptr)
726 delegate->setForm(form);
728 connect(m_view->selectionModel(),
729 &QItemSelectionModel::currentChanged,
730 this, &SignalSlotEditorWindow::updateEditorSelection);
731 connect(m_editor.data(), &SignalSlotEditor::connectionSelected,
732 this, &SignalSlotEditorWindow::updateDialogSelection);
733 connect(m_editor.data(), &SignalSlotEditor::connectionAdded,
734 this, &SignalSlotEditorWindow::resizeColumns);
736 connect(integration, &QDesignerIntegrationInterface::objectNameChanged,
737 this, &SignalSlotEditorWindow::objectNameChanged);
747 if (m_handling_selection_change || m_editor ==
nullptr)
750 QModelIndex index = m_proxy_model->mapFromSource(m_model->connectionToIndex(con));
751 if (!index.isValid() || index == m_view->currentIndex())
753 m_handling_selection_change =
true;
754 m_view->scrollTo(index, QTreeView::EnsureVisible);
755 m_view->setCurrentIndex(index);
756 m_handling_selection_change =
false;
763 if (m_handling_selection_change || m_editor ==
nullptr)
766 if (m_editor ==
nullptr)
769 Connection *con = m_model->indexToConnection(m_proxy_model->mapToSource(index));
770 if (m_editor->selected(con))
772 m_handling_selection_change =
true;
773 m_editor->selectNone();
774 m_editor->setSelected(con,
true);
775 m_handling_selection_change =
false;
780void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *,
const QString &,
const QString &)
788 if (m_editor.isNull())
791 m_editor->addEmptyConnection();
797 if (m_editor.isNull())
800 m_editor->deleteSelected();
806 m_add_button->setEnabled(!m_editor.isNull());
807 m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid());
812 for (
int c = 0, count = m_model->columnCount(); c < count; ++c)
813 m_view->resizeColumnToContents(c);
820#include "signalsloteditorwindow.moc"
QModelIndex connectionToIndex(Connection *con) const
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns the data for the given role and section in the header with the specified orientation.
void setEditor(SignalSlotEditor *editor=nullptr)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.
static void addWidgetToObjectList(const QWidget *w, QStringList &r)
static QStringList objectNameList(QDesignerFormWindowInterface *form)