File indexing completed on 2024-05-12 16:39:44

0001 /* This file is part of the KDE project
0002    Copyright (C) 2004 Cedric Pasteur <cedric.pasteur@free.fr>
0003    Copyright (C) 2004-2009 Jarosław Staniek <staniek@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018  * Boston, MA 02110-1301, USA.
0019 */
0020 
0021 #include "connectiondialog.h"
0022 #include <KexiIcon.h>
0023 #include "kexitableview.h"
0024 #include "events.h"
0025 #include "form.h"
0026 #include "objecttree.h"
0027 
0028 #include <KDbTableViewData>
0029 
0030 #include <KMessageBox>
0031 #include <KLocalizedString>
0032 
0033 #include <QLabel>
0034 #include <QRegularExpression>
0035 #include <QPushButton>
0036 
0037 using namespace KFormDesigner;
0038 
0039 class Q_DECL_HIDDEN ConnectionDialog::Private
0040 {
0041 public:
0042     explicit Private(Form *f);
0043     ~Private();
0044 
0045     Form *form;
0046     ConnectionBuffer *buffer;
0047     KexiTableView  *table;
0048     KDbTableViewData  *data;
0049     KDbTableViewData *widgetsColumnData,
0050     *slotsColumnData, *signalsColumnData;
0051     QLabel  *pixmapLabel, *textLabel;
0052     QPushButton *addButton, *removeButton;
0053 };
0054 
0055 ConnectionDialog::Private::Private(Form *f)
0056     :form(f), buffer(0)
0057 {
0058 
0059 }
0060 
0061 ConnectionDialog::Private::~Private()
0062 {
0063 
0064 }
0065 
0066 /////////////////////////////////////////////////////////////////////////////////
0067 ///////////// The dialog to edit or add/remove connections //////////////////////
0068 /////////////////////////////////////////////////////////////////////////////////
0069 ConnectionDialog::ConnectionDialog(Form *form, QWidget *parent)
0070         : KDialog(parent)
0071         , d(new Private(form))
0072 {
0073     setObjectName("connections_dialog");
0074     setModal(true);
0075     setWindowTitle(xi18nc("@title:window", "Edit Form Connections"));
0076     setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Details);
0077     setDefaultButton(KDialog::Ok);
0078 
0079     QFrame *frame = new QFrame(this);
0080     setMainWidget(frame);
0081     QHBoxLayout *layout = new QHBoxLayout(frame);
0082 
0083     // Setup the details widget /////////
0084     QWidget *details = new QWidget(frame);
0085     layout->addWidget(details);
0086     QHBoxLayout *detailsLyr = new QHBoxLayout(details);
0087     setDetailsWidget(details);
0088     setDetailsWidgetVisible(true);
0089 
0090     d->pixmapLabel = new QLabel(details);
0091     detailsLyr->addWidget(d->pixmapLabel);
0092     d->pixmapLabel->setFixedWidth(int(KIconLoader::global()->currentSize(KIconLoader::Desktop) * 1.5));
0093     d->pixmapLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
0094 
0095     d->textLabel = new QLabel(details);
0096     detailsLyr->addWidget(d->textLabel);
0097     d->textLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
0098     //setStatusOk();
0099 
0100     // And the KexiTableView ////////
0101     d->data = new KDbTableViewData();
0102     d->table = new KexiTableView(0, frame, "connections_tableview");
0103     d->table->setSpreadSheetMode(true);
0104     d->table->setInsertingEnabled(true);
0105     initTable();
0106     d->table->setData(d->data, false);
0107     d->table->adjustColumnWidthToContents(0);
0108     layout->addWidget(d->table);
0109 
0110     connect(d->table, SIGNAL(cellSelected(int,int)),
0111             this, SLOT(slotCellSelected(int,int)));
0112     connect(d->table->data(), SIGNAL(recordInserted(KDbRecordData*,bool)),
0113             this, SLOT(slotRecordInserted(KDbRecordData*,bool)));
0114 
0115     //// Setup the icon toolbar /////////////////
0116     QVBoxLayout *vlayout = new QVBoxLayout(layout);
0117     d->addButton = new QPushButton(koIcon("document-new"), xi18n("&New Connection"), frame);
0118     vlayout->addWidget(d->addButton);
0119     connect(d->addButton, SIGNAL(clicked()), this, SLOT(newItem()));
0120 
0121     d->removeButton = new QPushButton(KStandardGuiItem::del().icon(), xi18n("&Delete Connection"), frame);
0122     vlayout->addWidget(d->removeButton);
0123     connect(d->removeButton, SIGNAL(clicked()), this, SLOT(removeItem()));
0124 
0125     vlayout->addStretch();
0126 
0127     setInitialSize(QSize(600, 300));
0128     //setWFlags(WDestructiveClose);
0129 
0130     this->newItem();
0131 }
0132 
0133 ConnectionDialog::~ConnectionDialog()
0134 {
0135     delete d;
0136 }
0137 
0138 void
0139 ConnectionDialog::initTable()
0140 {
0141     KDbTableViewColumn *col0 = new KDbTableViewColumn(xi18n("OK?"), KDbField::Text);
0142     col0->field()->setSubType("QIcon");
0143     col0->setReadOnly(true);
0144     col0->field()->setDescription(xi18n("Connection correctness"));
0145     d->data->addColumn(col0);
0146 
0147     KDbTableViewColumn *col1 = new KDbTableViewColumn(xi18n("Sender"), KDbField::Enum);
0148     d->widgetsColumnData = new KDbTableViewData(KDbField::Text, KDbField::Text);
0149     col1->setRelatedData(d->widgetsColumnData);
0150     d->data->addColumn(col1);
0151 
0152     KDbTableViewColumn *col2 = new KDbTableViewColumn(xi18n("Signal"), KDbField::Enum);
0153     d->signalsColumnData = new KDbTableViewData(KDbField::Text, KDbField::Text);
0154     col2->setRelatedData(d->signalsColumnData);
0155     d->data->addColumn(col2);
0156 
0157     KDbTableViewColumn *col3 = new KDbTableViewColumn(xi18n("Receiver"), KDbField::Enum);
0158     col3->setRelatedData(d->widgetsColumnData);
0159     d->data->addColumn(col3);
0160 
0161     KDbTableViewColumn *col4 = new KDbTableViewColumn(xi18n("Slot"), KDbField::Enum);
0162     d->slotsColumnData = new KDbTableViewData(KDbField::Text, KDbField::Text);
0163     col4->setRelatedData(d->slotsColumnData);
0164     d->data->addColumn(col4);
0165 
0166     QList<int> c;
0167     c << 2 << 4;
0168     d->table->maximizeColumnsWidth(c);
0169     d->table->setColumnResizeEnabled(4, true);
0170 
0171     connect(d->data, SIGNAL(aboutToChangeCell(KDbRecordData*,int,QVariant*,KDbResultInfo*)),
0172             this, SLOT(slotCellChanged(KDbRecordData*,int,QVariant*,KDbResultInfo*)));
0173     connect(d->data, SIGNAL(recordUpdated(KDbRecordData*)), this, SLOT(checkConnection(KDbRecordData*)));
0174     connect(d->table, SIGNAL(itemSelected(KDbRecordData*)), this, SLOT(checkConnection(KDbRecordData*)));
0175 }
0176 
0177 void ConnectionDialog::exec()
0178 {
0179     updateTableData();
0180     KDialog::exec();
0181 }
0182 
0183 void ConnectionDialog::slotCellSelected(int row, int col)
0184 {
0185     d->removeButton->setEnabled(row < d->table->rows());
0186     KDbRecordData *data = d->table->itemAt(row);
0187     if (!data)
0188         return;
0189     if (col == 2) // signal col
0190         updateSignalList(data);
0191     else if (col == 4) // slot col
0192         updateSlotList(data);
0193 }
0194 
0195 void ConnectionDialog::slotRecordInserted(KDbRecordData* item, bool)
0196 {
0197     d->buffer->append(new Connection());
0198     checkConnection(item);
0199 }
0200 
0201 void
0202 ConnectionDialog::slotOk()
0203 {
0204     // First we update our buffer contents
0205     for (int i = 0; i < d->table->rows(); i++) {
0206         KDbRecordData *data = d->table->itemAt(i);
0207         Connection *c = d->buffer->at(i);
0208 
0209         c->setSender((*data)[1].toString());
0210         c->setSignal((*data)[2].toString());
0211         c->setReceiver((*data)[3].toString());
0212         c->setSlot((*data)[4].toString());
0213     }
0214 
0215     // then me make it replace form's current one
0216     d->form->setConnectionBuffer(d->buffer);
0217 
0218     QDialog::accept();
0219 }
0220 
0221 void
0222 ConnectionDialog::updateTableData()
0223 {
0224     // First we update the columns data
0225     foreach (ObjectTreeItem *item, *d->form->objectTree()->hash()) {
0226         KDbRecordData *data = d->widgetsColumnData->createItem();
0227         (*data)[0] = item->name();
0228         (*data)[1] = (*data)[0];
0229         d->widgetsColumnData->append(data);
0230     }
0231 
0232     // Then we fill the columns with the form connections
0233     foreach (Connection *c, *d->form->connectionBuffer()) {
0234         KDbRecordData *newData = d->table->data()->createItem();
0235         (*newData )[1] = c->sender();
0236         (*newData )[2] = c->signal();
0237         (*newData )[3] = c->receiver();
0238         (*newData )[4] = c->slot();
0239         d->table->insertItem(newData , d->table->rows());
0240     }
0241 
0242     d->buffer = new ConnectionBuffer(*(d->form->connectionBuffer()));
0243 }
0244 
0245 void
0246 ConnectionDialog::setStatusOk(KDbRecordData *data)
0247 {
0248     d->pixmapLabel->setPixmap(koDesktopIcon("dialog-ok"));
0249     d->textLabel->setText(QString("<qt><h2>%1</h2></qt>").arg(xi18n("The connection is OK.")));
0250 
0251     if (!data)
0252         data = d->table->selectedItem();
0253     if (d->table->currentRow() >= d->table->rows())
0254         data = 0;
0255 
0256     if (data)
0257         (*data)[0] = "dialog-ok";
0258     else {
0259         d->pixmapLabel->setPixmap(QPixmap());
0260         d->textLabel->setText(QString());
0261     }
0262 }
0263 
0264 void
0265 ConnectionDialog::setStatusError(const QString &msg, KDbRecordData *data)
0266 {
0267     d->pixmapLabel->setPixmap(koDesktopIcon("dialog-cancel"));
0268     d->textLabel->setText(QString("<qt><h2>%1</h2></qt>").arg(xi18n("The connection is invalid.")) + msg);
0269 
0270     if (!data)
0271         data = d->table->selectedItem();
0272     if (d->table->currentRow() >= d->table->rows())
0273         data = 0;
0274 
0275     if (data)
0276         (*data)[0] = "dialog-cancel";
0277     else {
0278         d->pixmapLabel->setPixmap(QPixmap());
0279         d->textLabel->setText(QString());
0280     }
0281 }
0282 
0283 void
0284 ConnectionDialog::slotCellChanged(KDbRecordData *data, int col, QVariant*, KDbResultInfo*)
0285 {
0286     switch (col) {
0287         // sender changed, we clear siganl and slot
0288     case 1:
0289         (*data)[2] = QString("");
0290         // signal or receiver changed, we clear the slot cell
0291     case 2:
0292     case 3: {
0293         (*data)[4] = QString("");
0294         break;
0295     }
0296     default:
0297         break;
0298     }
0299 }
0300 
0301 void
0302 ConnectionDialog::updateSlotList(KDbRecordData *data)
0303 {
0304     d->slotsColumnData->deleteAllRecords();
0305     QString widget = (*data)[1].toString();
0306     QString signal = (*data)[2].toString();
0307 
0308     if ((widget.isEmpty()) || signal.isEmpty())
0309         return;
0310     ObjectTreeItem *tree = d->form->objectTree()->lookup(widget);
0311     if (!tree || !tree->widget())
0312         return;
0313 
0314     QString signalArg(signal);
0315     signalArg.remove(QRegularExpression(".*[(]|[)]"));
0316 
0317     const QList<QMetaMethod> list(
0318         KexiUtils::methodsForMetaObjectWithParents(tree->widget()->metaObject(),
0319                 QMetaMethod::Slot, QMetaMethod::Public));
0320     foreach(const QMetaMethod &method, list) {
0321         // we add the slot only if it is compatible with the signal
0322         QString slotArg(method.signature());
0323         slotArg.remove(QRegularExpression(".*[(]|[)]"));
0324         if (!signalArg.startsWith(slotArg, Qt::CaseSensitive) && (!signal.isEmpty())) // args not compatible
0325             continue;
0326 
0327         KDbRecordData *newData = d->slotsColumnData->createItem();
0328         (*newData)[0] = QString::fromLatin1(method.signature());
0329         (*newData)[1] = (*newData)[0];
0330         d->slotsColumnData->append(newData);
0331     }
0332 }
0333 
0334 void
0335 ConnectionDialog::updateSignalList(KDbRecordData *data)
0336 {
0337     ObjectTreeItem *tree = d->form->objectTree()->lookup((*data)[1].toString());
0338     if (!tree || !tree->widget())
0339         return;
0340 
0341     d->signalsColumnData->deleteAllRecords();
0342     const QList<QMetaMethod> list(
0343         KexiUtils::methodsForMetaObjectWithParents(tree->widget()->metaObject(),
0344                 QMetaMethod::Signal, QMetaMethod::Public));
0345     foreach(const QMetaMethod &method, list) {
0346         KDbRecordData *newData = d->signalsColumnData->createItem();
0347         (*newData )[0] = QString::fromLatin1(method.signature());
0348         (*newData )[1] = (*newData )[0];
0349         d->signalsColumnData->append(newData );
0350     }
0351 }
0352 
0353 void
0354 ConnectionDialog::checkConnection(KDbRecordData *data)
0355 {
0356     // First we check if one column is empty
0357     for (int i = 1; i < 5; i++) {
0358         if (!data || (*data)[i].toString().isEmpty()) {
0359             setStatusError(xi18n("You have not selected item: <resource>%1</resource>.",
0360                                 d->data->column(i)->captionAliasOrName()), data);
0361             return;
0362         }
0363     }
0364 
0365     // Then we check if signal/slot args are compatible
0366     QString signal = (*data)[2].toString();
0367     signal.remove(QRegularExpression(".*[(]|[)]"));   // just keep the args list
0368     QString slot = (*data)[4].toString();
0369     slot.remove(QRegularExpression(".*[(]|[)]"));
0370 
0371     if (!signal.startsWith(slot, Qt::CaseSensitive)) {
0372         setStatusError(xi18n("The signal/slot arguments are not compatible."), data);
0373         return;
0374     }
0375 
0376     setStatusOk(data);
0377 }
0378 
0379 void
0380 ConnectionDialog::newItem()
0381 {
0382     d->table->acceptRecordEditing();
0383     d->table->setCursorPosition(d->table->rows(), 1);
0384 }
0385 
0386 void
0387 ConnectionDialog::newItemByDragnDrop()
0388 {
0389     d->form->enterConnectingState();
0390     connect(d->form, SIGNAL(connectionAborted(KFormDesigner::Form*)),
0391         this, SLOT(slotConnectionAborted(KFormDesigner::Form*)));
0392     connect(d->form, SIGNAL(connectionCreated(KFormDesigner::Form*,Connection&)),
0393         this, SLOT(slotConnectionCreated(KFormDesigner::Form*,Connection&)));
0394 
0395     hide();
0396 }
0397 
0398 void
0399 ConnectionDialog::slotConnectionCreated(KFormDesigner::Form *form, Connection &connection)
0400 {
0401     show();
0402     if (form != d->form)
0403         return;
0404 
0405     Connection *c = new Connection(connection);
0406     KDbRecordData *newData = d->table->data()->createItem();
0407     (*newData)[1] = c->sender();
0408     (*newData)[2] = c->signal();
0409     (*newData)[3] = c->receiver();
0410     (*newData)[4] = c->slot();
0411     d->table->insertItem(newData, d->table->rows());
0412     d->buffer->append(c);
0413 }
0414 
0415 void
0416 ConnectionDialog::slotConnectionAborted(KFormDesigner::Form *form)
0417 {
0418     show();
0419     if (form != d->form)
0420         return;
0421 
0422     newItem();
0423 }
0424 
0425 void
0426 ConnectionDialog::removeItem()
0427 {
0428     if (d->table->currentRow() == -1 || d->table->currentRow() >= d->table->rows())
0429         return;
0430 
0431     if (KMessageBox::Yes != KMessageBox::questionYesNo(parentWidget(),
0432               xi18n("Do you want to delete this connection?"),
0433               QString(),
0434               KGuiItem(xi18nc("@action:button", "&Delete Connection")),
0435               KStandardGuiItem::no(),
0436               "AskBeforeDeleteConnection"/*config entry*/,
0437               KMessageBox::Notify | KMessageBox::Dangerous))
0438     {
0439         return;
0440     }
0441     d->buffer->removeAt(d->table->currentRow());
0442     d->table->deleteItem(d->table->selectedItem());
0443 }
0444 
0445 //! @todo KEXI3 noi18n # added to disable message extraction in Messages.sh