/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Designer of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "signalsloteditorwindow.h" #include "signalsloteditor_p.h" #include "signalsloteditor.h" #include "qdesigner_integration_p.h" #include "signalslot_utils_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE // Add suitable form widgets to a list of objects for the signal slot // editor. Prevent special widgets from showing up there. static void addWidgetToObjectList(const QWidget *w, QStringList &r) { const QMetaObject *mo = w->metaObject(); if (mo != &QLayoutWidget::staticMetaObject && mo != &Spacer::staticMetaObject) { const QString name = w->objectName().trimmed(); if (!name.isEmpty()) r.push_back(name); } } static QStringList objectNameList(QDesignerFormWindowInterface *form) { typedef QList ActionList; typedef QList ButtonGroupList; QStringList result; QWidget *mainContainer = form->mainContainer(); if (!mainContainer) return result; // Add main container container pages (QStatusBar, QWizardPages) etc. // to the list. Pages of containers on the form are not added, however. if (const QDesignerContainerExtension *c = qt_extension(form->core()->extensionManager(), mainContainer)) { const int count = c->count(); for (int i = 0 ; i < count; i++) addWidgetToObjectList(c->widget(i), result); } const QDesignerFormWindowCursorInterface *cursor = form->cursor(); const int widgetCount = cursor->widgetCount(); for (int i = 0; i < widgetCount; ++i) addWidgetToObjectList(cursor->widget(i), result); const QDesignerMetaDataBaseInterface *mdb = form->core()->metaDataBase(); // Add managed actions and actions with managed menus const ActionList actions = qFindChildren(mainContainer); if (!actions.empty()) { const ActionList::const_iterator cend = actions.constEnd(); for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) { QAction *a = *it; if (!a->isSeparator()) { if (QMenu *menu = a->menu()) { if (mdb->item(menu)) result.push_back(menu->objectName()); } else { if (mdb->item(a)) result.push_back(a->objectName()); } } } } // Add managed buttons groups const ButtonGroupList buttonGroups = qFindChildren(mainContainer); if (!buttonGroups.empty()) { const ButtonGroupList::const_iterator cend = buttonGroups.constEnd(); for (ButtonGroupList::const_iterator it = buttonGroups.constBegin(); it != cend; ++it) if (mdb->item(*it)) result.append((*it)->objectName()); } result.sort(); return result; } namespace qdesigner_internal { // ------------ ConnectionModel ConnectionModel::ConnectionModel(QObject *parent) : QAbstractItemModel(parent) { } void ConnectionModel::setEditor(SignalSlotEditor *editor) { if (m_editor == editor) return; if (m_editor) { disconnect(m_editor, SIGNAL(connectionAdded(Connection*)), this, SLOT(connectionAdded(Connection*))); disconnect(m_editor, SIGNAL(connectionRemoved(int)), this, SLOT(connectionRemoved(int))); disconnect(m_editor, SIGNAL(aboutToRemoveConnection(Connection*)), this, SLOT(aboutToRemoveConnection(Connection*))); disconnect(m_editor, SIGNAL(aboutToAddConnection(int)), this, SLOT(aboutToAddConnection(int))); disconnect(m_editor, SIGNAL(connectionChanged(Connection*)), this, SLOT(connectionChanged(Connection*))); } m_editor = editor; if (m_editor) { connect(m_editor, SIGNAL(connectionAdded(Connection*)), this, SLOT(connectionAdded(Connection*))); connect(m_editor, SIGNAL(connectionRemoved(int)), this, SLOT(connectionRemoved(int))); connect(m_editor, SIGNAL(aboutToRemoveConnection(Connection*)), this, SLOT(aboutToRemoveConnection(Connection*))); connect(m_editor, SIGNAL(aboutToAddConnection(int)), this, SLOT(aboutToAddConnection(int))); connect(m_editor, SIGNAL(connectionChanged(Connection*)), this, SLOT(connectionChanged(Connection*))); } reset(); } QVariant ConnectionModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical || role != Qt::DisplayRole) return QVariant(); static const QVariant senderTitle = tr("Sender"); static const QVariant signalTitle = tr("Signal"); static const QVariant receiverTitle = tr("Receiver"); static const QVariant slotTitle = tr("Slot"); switch (section) { case 0: return senderTitle; case 1: return signalTitle; case 2: return receiverTitle; case 3: return slotTitle; } return QVariant(); } QModelIndex ConnectionModel::index(int row, int column, const QModelIndex &parent) const { if (parent.isValid() || !m_editor) return QModelIndex(); if (row < 0 || row >= m_editor->connectionCount()) return QModelIndex(); return createIndex(row, column); } Connection *ConnectionModel::indexToConnection(const QModelIndex &index) const { if (!index.isValid() || !m_editor) return 0; if (index.row() < 0 || index.row() >= m_editor->connectionCount()) return 0; return m_editor->connection(index.row()); } QModelIndex ConnectionModel::connectionToIndex(Connection *con) const { Q_ASSERT(m_editor); return createIndex(m_editor->indexOfConnection(con), 0); } QModelIndex ConnectionModel::parent(const QModelIndex&) const { return QModelIndex(); } int ConnectionModel::rowCount(const QModelIndex &parent) const { if (parent.isValid() || !m_editor) return 0; return m_editor->connectionCount(); } int ConnectionModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 4; } QVariant ConnectionModel::data(const QModelIndex &index, int role) const { if ((role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole && role != Qt::ForegroundRole) || !m_editor) return QVariant(); if (index.row() < 0 || index.row() >= m_editor->connectionCount()) { return QVariant(); } const SignalSlotConnection *con = static_cast(m_editor->connection(index.row())); Q_ASSERT(con != 0); if (role == Qt::FontRole || role == Qt::ForegroundRole) { bool isQt3Member = false; if (index.column() == 1) { QDesignerFormEditorInterface *core = m_editor->formWindow()->core(); isQt3Member = isQt3Signal(core, con->object(CETypes::EndPoint::Source), con->signal()); } else if (index.column() == 3) { QDesignerFormEditorInterface *core = m_editor->formWindow()->core(); isQt3Member = isQt3Signal(core, con->object(CETypes::EndPoint::Target), con->slot()); } if (isQt3Member) { if (role == Qt::ForegroundRole) return Qt::red; QFont font = QApplication::font(); font.setItalic(true); return font; } return QVariant(); } static const QVariant senderDefault = tr(""); static const QVariant signalDefault = tr(""); static const QVariant receiverDefault = tr(""); static const QVariant slotDefault = tr(""); switch (index.column()) { case 0: { const QString sender = con->sender(); if (sender.isEmpty()) return senderDefault; return sender; } case 1: { const QString signal = con->signal(); if (signal.isEmpty()) return signalDefault; return signal; } case 2: { const QString receiver = con->receiver(); if (receiver.isEmpty()) return receiverDefault; return receiver; } case 3: { const QString slot = con->slot(); if (slot.isEmpty()) return slotDefault; return slot; } } return QVariant(); } bool ConnectionModel::setData(const QModelIndex &index, const QVariant &data, int) { if (!index.isValid() || !m_editor) return false; if (data.type() != QVariant::String) return false; SignalSlotConnection *con = static_cast(m_editor->connection(index.row())); QDesignerFormWindowInterface *form = m_editor->formWindow(); QString s = data.toString(); switch (index.column()) { case 0: if (!s.isEmpty() && !objectNameList(form).contains(s)) s.clear(); m_editor->setSource(con, s); break; case 1: if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Source), SignalMember, s)) s.clear(); m_editor->setSignal(con, s); break; case 2: if (!s.isEmpty() && !objectNameList(form).contains(s)) s.clear(); m_editor->setTarget(con, s); break; case 3: if (!memberFunctionListContains(form->core(), con->object(CETypes::EndPoint::Target), SlotMember, s)) s.clear(); m_editor->setSlot(con, s); break; } return true; } void ConnectionModel::connectionAdded(Connection*) { endInsertRows(); } void ConnectionModel::connectionRemoved(int) { endRemoveRows(); } void ConnectionModel::aboutToRemoveConnection(Connection *con) { Q_ASSERT(m_editor); int idx = m_editor->indexOfConnection(con); beginRemoveRows(QModelIndex(), idx, idx); } void ConnectionModel::aboutToAddConnection(int idx) { Q_ASSERT(m_editor); beginInsertRows(QModelIndex(), idx, idx); } Qt::ItemFlags ConnectionModel::flags(const QModelIndex&) const { return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; } void ConnectionModel::connectionChanged(Connection *con) { Q_ASSERT(m_editor); const int idx = m_editor->indexOfConnection(con); SignalSlotConnection *changedCon = static_cast(m_editor->connection(idx)); SignalSlotConnection *c = 0; for (int i=0; iconnectionCount(); ++i) { if (i == idx) continue; c = static_cast(m_editor->connection(i)); if (c->sender() == changedCon->sender() && c->signal() == changedCon->signal() && c->receiver() == changedCon->receiver() && c->slot() == changedCon->slot()) { const QString message = tr("The connection already exists!
%1").arg(changedCon->toString()); m_editor->formWindow()->core()->dialogGui()->message(m_editor->parentWidget(), QDesignerDialogGuiInterface::SignalSlotEditorMessage, QMessageBox::Warning, tr("Signal and Slot Editor"), message, QMessageBox::Ok); break; } } emit dataChanged(createIndex(idx, 0), createIndex(idx, 3)); } void ConnectionModel::updateAll() { emit dataChanged(index(0, 0), index(rowCount(), columnCount())); } } namespace { // ---------------------- InlineEditorModel class InlineEditorModel : public QStandardItemModel { Q_OBJECT public: enum { TitleItem = 1 }; InlineEditorModel(int rows, int cols, QObject *parent = 0); void addTitle(const QString &title); void addTextList(const QMap &text_list); void addText(const QString &text); bool isTitle(int idx) const; int findText(const QString &text) const; virtual Qt::ItemFlags flags(const QModelIndex &index) const; }; InlineEditorModel::InlineEditorModel(int rows, int cols, QObject *parent) : QStandardItemModel(rows, cols, parent) { } void InlineEditorModel::addTitle(const QString &title) { const int cnt = rowCount(); insertRows(cnt, 1); QModelIndex cat_idx = index(cnt, 0); setData(cat_idx, title + QLatin1Char(':'), Qt::DisplayRole); setData(cat_idx, TitleItem, Qt::UserRole); QFont font = QApplication::font(); font.setBold(true); setData(cat_idx, font, Qt::FontRole); } bool InlineEditorModel::isTitle(int idx) const { if (idx == -1) return false; return data(index(idx, 0), Qt::UserRole).toInt() == TitleItem; } void InlineEditorModel::addText(const QString &text) { const int cnt = rowCount(); insertRows(cnt, 1); setData(index(cnt, 0), text, Qt::DisplayRole); } void InlineEditorModel::addTextList(const QMap &text_list) { int cnt = rowCount(); insertRows(cnt, text_list.size()); QFont font = QApplication::font(); font.setItalic(true); QVariant fontVariant = qVariantFromValue(font); QMap::ConstIterator it = text_list.constBegin(); const QMap::ConstIterator itEnd = text_list.constEnd(); while (it != itEnd) { const QModelIndex text_idx = index(cnt++, 0); setData(text_idx, it.key(), Qt::DisplayRole); if (it.value()) { setData(text_idx, fontVariant, Qt::FontRole); setData(text_idx, Qt::red, Qt::ForegroundRole); } ++it; } } Qt::ItemFlags InlineEditorModel::flags(const QModelIndex &index) const { if (isTitle(index.row())) return Qt::ItemIsEnabled; else return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } int InlineEditorModel::findText(const QString &text) const { const int cnt = rowCount(); for (int i = 0; i < cnt; ++i) { const QModelIndex idx = index(i, 0); if (data(idx, Qt::UserRole).toInt() == TitleItem) continue; if (data(idx, Qt::DisplayRole).toString() == text) return i; } return -1; } // ------------ InlineEditor class InlineEditor : public QComboBox { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText USER true) public: InlineEditor(QWidget *parent = 0); QString text() const; void setText(const QString &text); void addTitle(const QString &title); void addText(const QString &text); void addTextList(const QMap &text_list); private slots: void checkSelection(int idx); private: InlineEditorModel *m_model; int m_idx; }; InlineEditor::InlineEditor(QWidget *parent) : QComboBox(parent), m_idx(-1) { setModel(m_model = new InlineEditorModel(0, 4, this)); setFrame(false); m_idx = -1; connect(this, SIGNAL(activated(int)), this, SLOT(checkSelection(int))); } void InlineEditor::checkSelection(int idx) { if (idx == m_idx) return; if (m_model->isTitle(idx)) setCurrentIndex(m_idx); else m_idx = idx; } void InlineEditor::addTitle(const QString &title) { m_model->addTitle(title); } void InlineEditor::addTextList(const QMap &text_list) { m_model->addTextList(text_list); } void InlineEditor::addText(const QString &text) { m_model->addText(text); } QString InlineEditor::text() const { return currentText(); } void InlineEditor::setText(const QString &text) { m_idx = m_model->findText(text); if (m_idx == -1) m_idx = 0; setCurrentIndex(m_idx); } // ------------------ ConnectionDelegate class ConnectionDelegate : public QItemDelegate { Q_OBJECT public: ConnectionDelegate(QWidget *parent = 0); void setForm(QDesignerFormWindowInterface *form); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; private slots: void emitCommitData(); private: QDesignerFormWindowInterface *m_form; }; ConnectionDelegate::ConnectionDelegate(QWidget *parent) : QItemDelegate(parent) { m_form = 0; static QItemEditorFactory *factory = 0; if (factory == 0) { factory = new QItemEditorFactory; QItemEditorCreatorBase *creator = new QItemEditorCreator("text"); factory->registerEditor(QVariant::String, creator); } setItemEditorFactory(factory); } void ConnectionDelegate::setForm(QDesignerFormWindowInterface *form) { m_form = form; } QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { if (m_form == 0) return 0; QWidget *w = QItemDelegate::createEditor(parent, option, index); InlineEditor *inline_editor = qobject_cast(w); Q_ASSERT(inline_editor != 0); const QAbstractItemModel *model = index.model(); const QModelIndex obj_name_idx = model->index(index.row(), index.column() <= 1 ? 0 : 2); const QString obj_name = model->data(obj_name_idx, Qt::DisplayRole).toString(); switch (index.column()) { case 0: case 2: { // object names QStringList obj_name_list = objectNameList(m_form); QMap markedNameList; markedNameList.insert(tr(""), false); inline_editor->addTextList(markedNameList); markedNameList.clear(); foreach (const QString &name, obj_name_list) markedNameList.insert(name, false); inline_editor->addTextList(markedNameList); } break; case 1: case 3: { // signals, slots const qdesigner_internal::MemberType type = index.column() == 1 ? qdesigner_internal::SignalMember : qdesigner_internal::SlotMember; const QModelIndex peer_index = model->index(index.row(), type == qdesigner_internal::SignalMember ? 3 : 1); const QString peer = model->data(peer_index, Qt::DisplayRole).toString(); const qdesigner_internal::ClassesMemberFunctions class_list = qdesigner_internal::reverseClassesMemberFunctions(obj_name, type, peer, m_form); QObject *object = qFindChild(m_form, obj_name); inline_editor->addText(type == qdesigner_internal::SignalMember ? tr("") : tr("")); foreach (const qdesigner_internal::ClassMemberFunctions &class_info, class_list) { if (class_info.m_className.isEmpty() || class_info.m_memberList.isEmpty()) continue; QStringList memberList = class_info.m_memberList; QMap markedMemberList; foreach (const QString &member, memberList) { bool mark = false; if (type == qdesigner_internal::SignalMember) mark = qdesigner_internal::isQt3Signal(m_form->core(), object, member); else mark = qdesigner_internal::isQt3Slot(m_form->core(), object, member); if (!mark) markedMemberList.insert(member, mark); } inline_editor->addTitle(class_info.m_className); inline_editor->addTextList(markedMemberList); } } break; default: break; } connect(inline_editor, SIGNAL(activated(int)), this, SLOT(emitCommitData())); return inline_editor; } void ConnectionDelegate::emitCommitData() { InlineEditor *editor = qobject_cast(sender()); emit commitData(editor); } } namespace qdesigner_internal { /******************************************************************************* ** SignalSlotEditorWindow */ SignalSlotEditorWindow::SignalSlotEditorWindow(QDesignerFormEditorInterface *core, QWidget *parent) : QWidget(parent), m_view(new QTreeView), m_editor(0), m_add_button(new QToolButton), m_remove_button(new QToolButton), m_core(core), m_model(new ConnectionModel(this)), m_handling_selection_change(false) { m_view->setModel(m_model); m_view->setItemDelegate(new ConnectionDelegate(this)); m_view->setEditTriggers(QAbstractItemView::DoubleClicked | QAbstractItemView::EditKeyPressed); m_view->setRootIsDecorated(false); m_view->setTextElideMode (Qt::ElideMiddle); connect(m_view, SIGNAL(activated(QModelIndex)), this, SLOT(updateUi())); connect(m_view->header(), SIGNAL(sectionDoubleClicked(int)), m_view, SLOT(resizeColumnToContents(int))); QVBoxLayout *layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); QToolBar *toolBar = new QToolBar; toolBar->setIconSize(QSize(22, 22)); m_add_button->setIcon(createIconSet(QLatin1String("plus.png"))); connect(m_add_button, SIGNAL(clicked()), this, SLOT(addConnection())); toolBar->addWidget(m_add_button); m_remove_button->setIcon(createIconSet(QLatin1String("minus.png"))); connect(m_remove_button, SIGNAL(clicked()), this, SLOT(removeConnection())); toolBar->addWidget(m_remove_button); layout->addWidget(toolBar); layout->addWidget(m_view); connect(core->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), this, SLOT(setActiveFormWindow(QDesignerFormWindowInterface*))); updateUi(); } void SignalSlotEditorWindow::setActiveFormWindow(QDesignerFormWindowInterface *form) { QDesignerIntegration *integration = qobject_cast(m_core->integration()); if (!m_editor.isNull()) { disconnect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateEditorSelection(QModelIndex))); disconnect(m_editor, SIGNAL(connectionSelected(Connection*)), this, SLOT(updateDialogSelection(Connection*))); if (integration) { disconnect(integration, SIGNAL(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString)), this, SLOT(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString))); } } m_editor = qFindChild(form); m_model->setEditor(m_editor); if (!m_editor.isNull()) { ConnectionDelegate *delegate = qobject_cast(m_view->itemDelegate()); if (delegate != 0) delegate->setForm(form); connect(m_view->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(updateEditorSelection(QModelIndex))); connect(m_editor, SIGNAL(connectionSelected(Connection*)), this, SLOT(updateDialogSelection(Connection*))); if (integration) { connect(integration, SIGNAL(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString)), this, SLOT(objectNameChanged(QDesignerFormWindowInterface*,QObject*,QString,QString))); } } updateUi(); } void SignalSlotEditorWindow::updateDialogSelection(Connection *con) { if (m_handling_selection_change || m_editor == 0) return; QModelIndex index = m_model->connectionToIndex(con); if (index == m_view->currentIndex()) return; m_handling_selection_change = true; m_view->setCurrentIndex(index); m_handling_selection_change = false; updateUi(); } void SignalSlotEditorWindow::updateEditorSelection(const QModelIndex &index) { if (m_handling_selection_change || m_editor == 0) return; if (m_editor == 0) return; Connection *con = m_model->indexToConnection(index); if (m_editor->selected(con)) return; m_handling_selection_change = true; m_editor->selectNone(); m_editor->setSelected(con, true); m_handling_selection_change = false; updateUi(); } void SignalSlotEditorWindow::objectNameChanged(QDesignerFormWindowInterface *, QObject *, const QString &, const QString &) { if (m_editor) m_model->updateAll(); } void SignalSlotEditorWindow::addConnection() { if (m_editor.isNull()) return; m_editor->addEmptyConnection(); updateUi(); } void SignalSlotEditorWindow::removeConnection() { if (m_editor.isNull()) return; m_editor->deleteSelected(); updateUi(); } void SignalSlotEditorWindow::updateUi() { m_add_button->setEnabled(!m_editor.isNull()); m_remove_button->setEnabled(!m_editor.isNull() && m_view->currentIndex().isValid()); } } // namespace qdesigner_internal QT_END_NAMESPACE #include "signalsloteditorwindow.moc"