source: trunk/tools/designer/src/lib/shared/signalslotdialog.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 19.0 KB
RevLine 
[2]1/****************************************************************************
2**
[846]3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
[561]4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
[2]6**
7** This file is part of the Qt Designer of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
[561]24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
[2]27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
[561]36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
[2]38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "signalslotdialog_p.h"
43#include "ui_signalslotdialog.h"
44#include "metadatabase_p.h"
45#include "widgetdatabase_p.h"
46
47#include "qdesigner_formwindowcommand_p.h"
48#include "iconloader_p.h"
49
50#include <QtDesigner/QDesignerMemberSheetExtension>
51#include <QtDesigner/QExtensionManager>
52#include <QtDesigner/QDesignerFormEditorInterface>
53#include <QtDesigner/QDesignerFormWindowInterface>
54#include <QtDesigner/QDesignerWidgetFactoryInterface>
55#include <abstractdialoggui_p.h>
56
57#include <QtGui/QStandardItemModel>
58#include <QtGui/QRegExpValidator>
59#include <QtGui/QItemDelegate>
60#include <QtGui/QLineEdit>
61#include <QtGui/QApplication>
62#include <QtGui/QMessageBox>
63
64#include <QtCore/QRegExp>
65#include <QtCore/QDebug>
66
67QT_BEGIN_NAMESPACE
68
69// Regexp to match a function signature, arguments potentially
70// with namespace colons.
71static const char *signatureRegExp = "^[\\w+_]+\\(([\\w+:]\\*?,?)*\\)$";
72static const char *methodNameRegExp = "^[\\w+_]+$";
73
74static QStandardItem *createEditableItem(const QString &text)
75{
76 QStandardItem *rc = new QStandardItem(text);
77 rc->setFlags(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable);
78 return rc;
79}
80
81static QStandardItem *createDisabledItem(const QString &text)
82{
83 QStandardItem *rc = new QStandardItem(text);
84 Qt::ItemFlags flags = rc->flags();
85 rc->setFlags(flags & ~(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable));
86 return rc;
87}
88
89static void fakeMethodsFromMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, QStringList &slotList, QStringList &signalList)
90{
91 slotList.clear();
92 signalList.clear();
93 if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast<qdesigner_internal::MetaDataBase *>(core->metaDataBase()))
94 if (const qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o)) {
95 slotList = item->fakeSlots();
96 signalList = item->fakeSignals();
97 }
98}
99
100static void fakeMethodsToMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, const QStringList &slotList, const QStringList &signalList)
101{
102 if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast<qdesigner_internal::MetaDataBase *>(core->metaDataBase())) {
103 qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o);
104 Q_ASSERT(item);
105 item->setFakeSlots(slotList);
106 item->setFakeSignals(signalList);
107 }
108}
109
110static void existingMethodsFromMemberSheet(QDesignerFormEditorInterface *core,
111 QObject *o,
112 QStringList &slotList, QStringList &signalList)
113{
114 slotList.clear();
115 signalList.clear();
116
117 QDesignerMemberSheetExtension *msheet = qt_extension<QDesignerMemberSheetExtension*>(core->extensionManager(), o);
118 if (!msheet)
119 return;
120
121 for (int i = 0, count = msheet->count(); i < count; ++i)
122 if (msheet->isVisible(i)) {
123 if (msheet->isSlot(i))
124 slotList += msheet->signature(i);
125 else
126 if (msheet->isSignal(i))
127 signalList += msheet->signature(i);
128 }
129}
130
131namespace {
132 // Internal helper class: A Delegate that validates using RegExps and additionally checks
133 // on closing (adds missing parentheses).
134 class SignatureDelegate : public QItemDelegate {
135 public:
136 SignatureDelegate(QObject * parent = 0);
137 virtual QWidget * createEditor (QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const;
138 virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
139
140 private:
141 const QRegExp m_signatureRegexp;
142 const QRegExp m_methodNameRegexp;
143 };
144
145 SignatureDelegate::SignatureDelegate(QObject * parent) :
146 QItemDelegate(parent),
147 m_signatureRegexp(QLatin1String(signatureRegExp)),
148 m_methodNameRegexp(QLatin1String(methodNameRegExp))
149 {
150 Q_ASSERT(m_signatureRegexp.isValid());
151 Q_ASSERT(m_methodNameRegexp.isValid());
152 }
153
154 QWidget * SignatureDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const
155 {
156 QWidget *rc = QItemDelegate::createEditor(parent, option, index);
157 QLineEdit *le = qobject_cast<QLineEdit *>(rc);
158 Q_ASSERT(le);
159 le->setValidator(new QRegExpValidator(m_signatureRegexp, le));
160 return rc;
161 }
162
163 void SignatureDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const
164 {
165 QLineEdit *le = qobject_cast<QLineEdit *>(editor);
166 Q_ASSERT(le);
167 // Did the user just type a name? .. Add parentheses
168 QString signature = le->text();
169 if (!m_signatureRegexp.exactMatch(signature )) {
170 if (m_methodNameRegexp.exactMatch(signature )) {
171 signature += QLatin1String("()");
172 le->setText(signature);
173 } else {
174 return;
175 }
176 }
177 QItemDelegate::setModelData(editor, model, index);
178 }
179
180 // ------ FakeMethodMetaDBCommand: Undo Command to change fake methods in the meta DB.
181 class FakeMethodMetaDBCommand : public qdesigner_internal::QDesignerFormWindowCommand {
182
183 public:
184 explicit FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow);
185
186 void init(QObject *o,
187 const QStringList &oldFakeSlots, const QStringList &oldFakeSignals,
188 const QStringList &newFakeSlots, const QStringList &newFakeSignals);
189
190 virtual void undo() { fakeMethodsToMetaDataBase(core(), m_object, m_oldFakeSlots, m_oldFakeSignals); }
191 virtual void redo() { fakeMethodsToMetaDataBase(core(), m_object, m_newFakeSlots, m_newFakeSignals); }
192
193 private:
194 QObject *m_object;
195 QStringList m_oldFakeSlots;
196 QStringList m_oldFakeSignals;
197 QStringList m_newFakeSlots;
198 QStringList m_newFakeSignals;
199 };
200
201 FakeMethodMetaDBCommand::FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow) :
202 qdesigner_internal::QDesignerFormWindowCommand(QApplication::translate("Command", "Change signals/slots"), formWindow),
203 m_object(0)
204 {
205 }
206
207 void FakeMethodMetaDBCommand::init(QObject *o,
208 const QStringList &oldFakeSlots, const QStringList &oldFakeSignals,
209 const QStringList &newFakeSlots, const QStringList &newFakeSignals)
210 {
211 m_object = o;
212 m_oldFakeSlots = oldFakeSlots;
213 m_oldFakeSignals = oldFakeSignals;
214 m_newFakeSlots = newFakeSlots;
215 m_newFakeSignals = newFakeSignals;
216 }
217}
218
219namespace qdesigner_internal {
220
221// ------ SignalSlotDialogData
222void SignalSlotDialogData::clear()
223{
224 m_existingMethods.clear();
225 m_fakeMethods.clear();
226}
227
228// ------ SignatureModel
229SignatureModel::SignatureModel(QObject *parent) :
230 QStandardItemModel(parent)
231{
232}
233
234bool SignatureModel::setData(const QModelIndex &index, const QVariant &value, int role)
235{
236 if (role != Qt::EditRole)
237 return QStandardItemModel::setData(index, value, role);
238 // check via signal (unless it is the same), in which case we can't be bothered.
239 const QStandardItem *item = itemFromIndex(index);
240 Q_ASSERT(item);
241 const QString signature = value.toString();
242 if (item->text() == signature)
243 return true;
244
245 bool ok = true;
246 emit checkSignature(signature, &ok);
247 if (!ok)
248 return false;
249
250 return QStandardItemModel::setData(index, value, role);
251}
252
253// ------ SignaturePanel
254SignaturePanel::SignaturePanel(QObject *parent, QListView *listView, QToolButton *addButton, QToolButton *removeButton, const QString &newPrefix) :
255 QObject(parent),
256 m_newPrefix(newPrefix),
257 m_model(new SignatureModel(this)),
258 m_listView(listView),
259 m_removeButton(removeButton)
260{
261 m_removeButton->setEnabled(false);
262
263 connect(addButton, SIGNAL(clicked()), this, SLOT(slotAdd()));
264 connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemove()));
265
266 m_listView->setModel(m_model);
267 SignatureDelegate *delegate = new SignatureDelegate(this);
268 m_listView->setItemDelegate(delegate);
269 connect(m_model, SIGNAL(checkSignature(QString,bool*)), this, SIGNAL(checkSignature(QString,bool*)));
270
[561]271 connect(m_listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
272 this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection)));
[2]273}
274
275void SignaturePanel::slotAdd()
276{
277 m_listView->selectionModel()->clearSelection();
278 // find unique name
279 for (int i = 1; ; i++) {
280 QString newSlot = m_newPrefix;
281 newSlot += QString::number(i); // Always add number, Avoid setting 'slot' for first entry
282 newSlot += QLatin1Char('(');
283 // check for function name independent of parameters
284 if (m_model->findItems(newSlot, Qt::MatchStartsWith, 0).empty()) {
285 newSlot += QLatin1Char(')');
286 QStandardItem * item = createEditableItem(newSlot);
287 m_model->appendRow(item);
288 const QModelIndex index = m_model->indexFromItem (item);
289 m_listView->setCurrentIndex (index);
290 m_listView->edit(index);
291 return;
292 }
293 }
294}
295
296int SignaturePanel::count(const QString &signature) const
297{
298 return m_model->findItems(signature).size();
299}
300
301void SignaturePanel::slotRemove()
302{
303 const QModelIndexList selectedIndexes = m_listView->selectionModel()->selectedIndexes ();
304 if (selectedIndexes.empty())
305 return;
306
307 closeEditor();
308 // scroll to previous
309 if (const int row = selectedIndexes.front().row())
310 m_listView->setCurrentIndex (selectedIndexes.front().sibling(row - 1, 0));
311
312 for (int i = selectedIndexes.size() - 1; i >= 0; i--)
313 qDeleteAll(m_model->takeRow(selectedIndexes[i].row()));
314}
315
316void SignaturePanel::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &)
317{
318 m_removeButton->setEnabled(!selected.indexes().empty());
319}
320
321void SignaturePanel::setData(const SignalSlotDialogData &d)
322{
323 m_model->clear();
324
325 QStandardItem *lastExisting = 0;
326 foreach(const QString &s, d.m_existingMethods) {
327 lastExisting = createDisabledItem(s);
328 m_model->appendRow(lastExisting);
329 }
330 foreach(const QString &s, d.m_fakeMethods)
331 m_model->appendRow(createEditableItem(s));
332 if (lastExisting)
333 m_listView->scrollTo(m_model->indexFromItem(lastExisting));
334}
335
336QStringList SignaturePanel::fakeMethods() const
337{
338 QStringList rc;
339 if (const int rowCount = m_model->rowCount())
340 for (int i = 0; i < rowCount; i++) {
341 const QStandardItem *item = m_model->item(i);
342 if (item->flags() & Qt::ItemIsEditable)
343 rc += item->text();
344 }
345 return rc;
346}
347
348void SignaturePanel::closeEditor()
349{
350 const QModelIndex idx = m_listView->currentIndex();
351 if (idx.isValid())
352 m_listView->closePersistentEditor(idx);
353}
354
355// ------ SignalSlotDialog
356
357SignalSlotDialog::SignalSlotDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent, FocusMode mode) :
358 QDialog(parent),
359 m_focusMode(mode),
360 m_ui(new Ui::SignalSlotDialogClass),
361 m_dialogGui(dialogGui)
362{
363 setModal(true);
364 m_ui->setupUi(this);
365
366 const QIcon plusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("plus.png"));
367 const QIcon minusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("minus.png"));
368 m_ui->addSlotButton->setIcon(plusIcon);
369 m_ui->removeSlotButton->setIcon(minusIcon);
370 m_ui->addSignalButton->setIcon(plusIcon);
371 m_ui->removeSignalButton->setIcon(minusIcon);
372
373 m_slotPanel = new SignaturePanel(this, m_ui->slotListView, m_ui->addSlotButton, m_ui->removeSlotButton, QLatin1String("slot"));
374 m_signalPanel = new SignaturePanel(this, m_ui->signalListView, m_ui->addSignalButton, m_ui->removeSignalButton, QLatin1String("signal"));
375 connect(m_slotPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*)));
376 connect(m_signalPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*)));
377
378 connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
379 connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
380
381 switch(m_focusMode) {
382 case FocusSlots:
383 m_ui->slotListView->setFocus(Qt::OtherFocusReason);
384 break;
385 case FocusSignals:
386 m_ui->signalListView->setFocus(Qt::OtherFocusReason);
387 break;
388 }
389}
390
391SignalSlotDialog::~SignalSlotDialog()
392{
393 delete m_ui;
394}
395
396void SignalSlotDialog::slotCheckSignature(const QString &signature, bool *ok)
397{
398 QString errorMessage;
399 do {
400 if (m_slotPanel->count(signature)) {
401 errorMessage = tr("There is already a slot with the signature '%1'.").arg(signature);
402 *ok = false;
403 break;
404 }
405 if (m_signalPanel->count(signature)) {
406 errorMessage = tr("There is already a signal with the signature '%1'.").arg(signature);
407 *ok = false;
408 break;
409 }
410 } while (false);
411 if (!*ok)
412 m_dialogGui->message(this, QDesignerDialogGuiInterface::SignalSlotDialogMessage,
413 QMessageBox::Warning, tr("%1 - Duplicate Signature").arg(windowTitle()), errorMessage, QMessageBox::Close);
414}
415
416QDialog::DialogCode SignalSlotDialog::showDialog(SignalSlotDialogData &slotData, SignalSlotDialogData &signalData)
417{
418 m_slotPanel->setData(slotData);
419 m_signalPanel->setData(signalData);
420
421 const DialogCode rc = static_cast<DialogCode>(exec());
422 if (rc == Rejected)
423 return rc;
424
425 slotData.m_fakeMethods = m_slotPanel->fakeMethods();
426 signalData.m_fakeMethods = m_signalPanel->fakeMethods();
427 return rc;
428}
429
430bool SignalSlotDialog::editMetaDataBase(QDesignerFormWindowInterface *fw, QObject *object, QWidget *parent, FocusMode mode)
431{
432 QDesignerFormEditorInterface *core = fw->core();
433 SignalSlotDialog dlg(core->dialogGui(), parent, mode);
434 dlg.setWindowTitle(tr("Signals/Slots of %1").arg(object->objectName()));
435
436 SignalSlotDialogData slotData;
437 SignalSlotDialogData signalData;
438
439 existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods);
440 fakeMethodsFromMetaDataBase(core, object, slotData.m_fakeMethods, signalData.m_fakeMethods);
441
442 const QStringList oldSlots = slotData.m_fakeMethods;
443 const QStringList oldSignals = signalData.m_fakeMethods;
444
445 if (dlg.showDialog(slotData, signalData) == QDialog::Rejected)
446 return false;
447
448 if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods)
449 return false;
450
451 FakeMethodMetaDBCommand *cmd = new FakeMethodMetaDBCommand(fw);
452 cmd->init(object, oldSlots, oldSignals, slotData.m_fakeMethods, signalData.m_fakeMethods);
453 fw->commandHistory()->push(cmd);
454 return true;
455}
456
457bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QWidget *parent, FocusMode mode)
458{
459 const int index = core->widgetDataBase()->indexOfClassName(promotedClassName);
460 if (index == -1)
461 return false;
462
463 const QString baseClassName = core->widgetDataBase()->item(index)->extends();
464 if (baseClassName.isEmpty())
465 return false;
466
467 QWidget *widget = core->widgetFactory()->createWidget(baseClassName, 0);
468 if (!widget)
469 return false;
470 const bool rc = editPromotedClass(core, promotedClassName, widget, parent, mode);
471 widget->deleteLater();
472 return rc;
473}
474
475bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, QObject *baseObject, QWidget *parent, FocusMode mode)
476{
477 if (!baseObject->isWidgetType())
478 return false;
479
480 const QString promotedClassName = promotedCustomClassName(core, qobject_cast<QWidget*>(baseObject));
481 if (promotedClassName.isEmpty())
482 return false;
483 return editPromotedClass(core, promotedClassName, baseObject, parent, mode);
484}
485
486
487bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QObject *object, QWidget *parent, FocusMode mode)
488{
489 WidgetDataBase *db = qobject_cast<WidgetDataBase *>(core->widgetDataBase());
490 if (!db)
491 return false;
492
493 const int index = core->widgetDataBase()->indexOfClassName(promotedClassName);
494 if (index == -1)
495 return false;
496
497 WidgetDataBaseItem* item = static_cast<WidgetDataBaseItem*>(db->item(index));
498
499 SignalSlotDialogData slotData;
500 SignalSlotDialogData signalData;
501
502 existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods);
503 slotData.m_fakeMethods = item->fakeSlots();
504 signalData.m_fakeMethods = item->fakeSignals();
505
506 const QStringList oldSlots = slotData.m_fakeMethods;
507 const QStringList oldSignals = signalData.m_fakeMethods;
508
509 SignalSlotDialog dlg(core->dialogGui(), parent, mode);
510 dlg.setWindowTitle(tr("Signals/Slots of %1").arg(promotedClassName));
511
512 if (dlg.showDialog(slotData, signalData) == QDialog::Rejected)
513 return false;
514
515 if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods)
516 return false;
517
518 item->setFakeSlots(slotData.m_fakeMethods);
519 item->setFakeSignals(signalData.m_fakeMethods);
520
521 return true;
522}
523
524}
525
526QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.