File indexing completed on 2024-05-12 16:40:52

0001 /* This file is part of the KDE project
0002    Copyright (C) 2003-2017 Jarosław Staniek <staniek@kde.org>
0003    Copyright (C) 2012 Dimitrios T. Tanis <dimitrios.tanis@kdemail.net>
0004 
0005    This program 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 program 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 program; see the file COPYING.  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 "KexiConnectionSelectorWidget.h"
0022 #include "ui_KexiConnectionSelector.h"
0023 #include "kexiprjtypeselector.h"
0024 #include "kexidbconnectionwidget.h"
0025 #include "KexiFileWidgetInterface.h"
0026 #include <kexiutils/utils.h>
0027 #include <core/kexi.h>
0028 #include <KexiIcon.h>
0029 #include <KexiServerDriverNotFoundMessage.h>
0030 
0031 #include <KDbDriverManager>
0032 #include <KDbDriverMetaData>
0033 #include <KDbConnectionData>
0034 #include <KDbUtils>
0035 #include <KDbMessageHandler>
0036 
0037 #include <KMessageBox>
0038 #include <KUrlComboBox>
0039 
0040 #include <QDebug>
0041 #include <QDialog>
0042 #include <QPushButton>
0043 #include <QLayout>
0044 #include <QCheckBox>
0045 #include <QVBoxLayout>
0046 #include <QPixmap>
0047 #include <QFrame>
0048 #include <QStackedWidget>
0049 #include <QKeyEvent>
0050 
0051 class KexiConnectionSelector : public QWidget, public Ui_KexiConnectionSelector
0052 {
0053     Q_OBJECT
0054 public:
0055     explicit KexiConnectionSelector(QWidget *parent)
0056         : QWidget(parent)
0057     {
0058         setupUi(this);
0059         setObjectName("conn_sel");
0060         lblIcon->setPixmap(koDesktopIconCStr(Kexi::serverIconName()));
0061         lblIcon->setFixedSize(lblIcon->pixmap()->size());
0062         btn_add->setToolTip(xi18n("Add a new database connection"));
0063         btn_edit->setToolTip(xi18n("Edit selected database connection"));
0064         btn_remove->setToolTip(xi18n("Delete selected database connections"));
0065     }
0066     ~KexiConnectionSelector()
0067     {
0068     }
0069 };
0070 
0071 /*================================================================*/
0072 
0073 ConnectionDataLVItem::ConnectionDataLVItem(KDbConnectionData *data,
0074                                            const KDbDriverMetaData &driverMetaData,
0075                                            QTreeWidget* list)
0076         : QTreeWidgetItem(list)
0077         , m_data(data)
0078 {
0079     update(driverMetaData);
0080 }
0081 
0082 ConnectionDataLVItem::~ConnectionDataLVItem()
0083 {
0084 }
0085 
0086 void ConnectionDataLVItem::update(const KDbDriverMetaData &driverMetaData)
0087 {
0088     setText(0, m_data->caption() + "  ");
0089     const QString sfile = xi18n("File");
0090     QString driverName = driverMetaData.name();
0091     QString column1;
0092     if (driverMetaData.isFileBased()) {
0093         column1 = xi18nc("file (driver name)", "%1 (%2)", sfile, driverName);
0094     } else {
0095         column1 = driverName;
0096     }
0097     setText(1, column1 + "  ");
0098     setText(2, (driverMetaData.isFileBased() ? QString("<%1>").arg(sfile.toLower())
0099                                              : m_data->toUserVisibleString()) + "  ");
0100 }
0101 
0102 /*================================================================*/
0103 
0104 //! @internal
0105 class Q_DECL_HIDDEN KexiConnectionSelectorWidget::Private
0106 {
0107 public:
0108     Private()
0109             : conn_sel_shown(false)
0110             , confirmOverwrites(true)
0111     {
0112     }
0113 
0114     void updateRemoteListColumns()
0115     {
0116         remote->list->resizeColumnToContents(0); // name
0117         remote->list->resizeColumnToContents(1); // type
0118     }
0119 
0120     QWidget *fileWidget()
0121     {
0122         return fileIface ? fileIface->widget() : nullptr;
0123     }
0124 
0125     KexiFileWidgetInterface *fileIface = nullptr;
0126     KexiConnectionSelector *remote;
0127     QWidget* openExistingWidget;
0128     KexiPrjTypeSelector* prjTypeSelector;
0129     QUrl startDirOrVariable;
0130     KexiConnectionSelectorWidget::OperationMode operationMode;
0131     QStackedWidget *stack;
0132     QPointer<KexiDBConnectionSet> conn_set;
0133     KDbDriverManager manager;
0134     bool conn_sel_shown; //!< helper
0135     bool confirmOverwrites;
0136     KexiUtils::PaintBlocker* descGroupBoxPaintBlocker;
0137     bool isConnectionSelected;
0138     bool fileWidgetFrameVisible = true;
0139     QPointer<KexiServerDriverNotFoundMessage> errorMessagePopup;
0140 };
0141 
0142 /*================================================================*/
0143 
0144 KexiConnectionSelectorWidget::KexiConnectionSelectorWidget(
0145     KexiDBConnectionSet *conn_set,
0146     const QUrl& startDirOrVariable, OperationMode mode, QWidget* parent)
0147     : QWidget(parent)
0148     , d(new Private())
0149 {
0150     Q_ASSERT(conn_set);
0151     d->conn_set = conn_set;
0152     d->startDirOrVariable = startDirOrVariable;
0153     d->operationMode = mode;
0154     setWindowIcon(Kexi::defaultFileBasedDriverIcon());
0155 
0156     QBoxLayout* globalLyr = new QVBoxLayout(this);
0157     globalLyr->setContentsMargins(QMargins());
0158 
0159     //create header with radio buttons
0160     d->openExistingWidget = new QWidget(this);
0161     d->openExistingWidget->setObjectName("openExistingWidget");
0162     QVBoxLayout* openExistingWidgetLyr = new QVBoxLayout(d->openExistingWidget);
0163     openExistingWidgetLyr->setContentsMargins(0, 0, 0, 0);
0164     d->prjTypeSelector = new KexiPrjTypeSelector(d->openExistingWidget);
0165     connect(d->prjTypeSelector->buttonGroup, SIGNAL(buttonClicked(QAbstractButton*)),
0166             this, SLOT(slotPrjTypeSelected(QAbstractButton*)));
0167     openExistingWidgetLyr->addWidget(d->prjTypeSelector);
0168     d->prjTypeSelector->setContentsMargins(0, 0, 0, KexiUtils::spacingHint());
0169     //openExistingWidgetLyr->addSpacing(KexiUtils::spacingHint());
0170     QFrame* line = new QFrame(d->openExistingWidget);
0171     line->setFrameShape(QFrame::HLine);
0172     line->setFrameShadow(QFrame::Sunken);
0173     openExistingWidgetLyr->addWidget(line);
0174     globalLyr->addWidget(d->openExistingWidget);
0175 
0176     d->stack = new QStackedWidget(this);
0177     d->stack->setObjectName("stack");
0178     globalLyr->addWidget(d->stack, 1);
0179 
0180     d->remote = new KexiConnectionSelector(d->stack);
0181     connect(d->remote->btn_add, SIGNAL(clicked()), this, SLOT(slotRemoteAddBtnClicked()));
0182     connect(d->remote->btn_edit, SIGNAL(clicked()), this, SLOT(slotRemoteEditBtnClicked()));
0183     connect(d->remote->btn_remove, SIGNAL(clicked()), this, SLOT(slotRemoteRemoveBtnClicked()));
0184     d->stack->addWidget(d->remote);
0185     if (d->remote->layout())
0186         d->remote->layout()->setMargin(0);
0187     connect(d->remote->list, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
0188             this, SLOT(slotConnectionItemExecuted(QTreeWidgetItem*)));
0189     connect(d->remote->list, SIGNAL(itemSelectionChanged()),
0190             this, SLOT(slotConnectionSelectionChanged()));
0191     d->remote->list->installEventFilter(this);
0192     d->descGroupBoxPaintBlocker = new KexiUtils::PaintBlocker(d->remote->descGroupBox);
0193     d->descGroupBoxPaintBlocker->setEnabled(false);
0194     d->isConnectionSelected = false;
0195 }
0196 
0197 KexiConnectionSelectorWidget::~KexiConnectionSelectorWidget()
0198 {
0199     delete d;
0200 }
0201 
0202 void KexiConnectionSelectorWidget::showAdvancedConnection()
0203 {
0204     d->prjTypeSelector->option_server->setChecked(true);
0205     slotPrjTypeSelected(d->prjTypeSelector->option_server);
0206 }
0207 
0208 void KexiConnectionSelectorWidget::slotPrjTypeSelected(QAbstractButton *btn)
0209 {
0210     if (btn == d->prjTypeSelector->option_file) { //file-based prj type
0211         showSimpleConnection();
0212     } else if (btn == d->prjTypeSelector->option_server) { //server-based prj type
0213         if (KDbDriverManager().hasDatabaseServerDrivers()) {
0214             if (!d->conn_sel_shown) {
0215                 d->conn_sel_shown = true;
0216                 //show connections (on demand):
0217                 foreach(KDbConnectionData* connData, d->conn_set->list()) {
0218                     addConnectionData(connData);
0219                     //   else {
0220                     //this error should be more verbose:
0221                     //    qWarning() << "no driver found for '" << it.current()->driverName << "'!";
0222                     //   }
0223                 }
0224                 if (d->remote->list->topLevelItemCount() > 0) {
0225                     d->updateRemoteListColumns();
0226                     d->remote->list->sortByColumn(0, Qt::AscendingOrder);
0227                     d->remote->list->topLevelItem(0)->setSelected(true);
0228                 }
0229                 d->remote->descGroupBox->layout()->setMargin(2);
0230                 d->remote->list->setFocus();
0231                 slotConnectionSelectionChanged();
0232             }
0233             d->stack->setCurrentWidget(d->remote);
0234         }
0235         else {
0236             if (!d->errorMessagePopup) {
0237                 QWidget *errorMessagePopupParent = new QWidget(this);
0238                 QVBoxLayout *vbox = new QVBoxLayout(errorMessagePopupParent);
0239                 d->errorMessagePopup = new KexiServerDriverNotFoundMessage(errorMessagePopupParent);
0240                 vbox->addWidget(d->errorMessagePopup);
0241                 vbox->addStretch(0);
0242                 d->stack->addWidget(errorMessagePopupParent);
0243                 d->errorMessagePopup->setAutoDelete(false);
0244                 d->stack->setCurrentWidget(d->errorMessagePopup->parentWidget());
0245                 d->errorMessagePopup->animatedShow();
0246             }
0247             else {
0248                 d->stack->setCurrentWidget(d->errorMessagePopup->parentWidget());
0249             }
0250         }
0251     }
0252 }
0253 
0254 ConnectionDataLVItem* KexiConnectionSelectorWidget::addConnectionData(KDbConnectionData* data)
0255 {
0256     const KDbDriverMetaData* driverMetaData = d->manager.driverMetaData(data->driverId());
0257     return driverMetaData ?
0258                 new ConnectionDataLVItem(data, *driverMetaData, d->remote->list) : 0;
0259 }
0260 
0261 void KexiConnectionSelectorWidget::showSimpleConnection()
0262 {
0263     d->prjTypeSelector->option_file->setChecked(true);
0264     if (!d->fileIface) {
0265         d->fileIface = KexiFileWidgetInterface::createWidget(
0266             d->startDirOrVariable, d->operationMode == Opening ? KexiFileFilters::Opening
0267                                                                : KexiFileFilters::SavingFileBasedDB,
0268             d->stack);
0269         d->fileIface->setWidgetFrame(d->fileWidgetFrameVisible);
0270         d->fileIface->setConfirmOverwrites(d->confirmOverwrites);
0271         d->stack->addWidget(d->fileIface->widget());
0272         d->fileIface->connectFileSelectedSignal(this, SLOT(slotFileConnectionSelected(QString)));
0273     }
0274     d->stack->setCurrentWidget(d->fileIface->widget());
0275 }
0276 
0277 void KexiConnectionSelectorWidget::setFileWidgetFrameVisible(bool set)
0278 {
0279     d->fileWidgetFrameVisible = set;
0280     if (d->fileIface) {
0281         d->fileIface->setWidgetFrame(d->fileWidgetFrameVisible);
0282     }
0283 }
0284 
0285 KexiConnectionSelectorWidget::ConnectionType KexiConnectionSelectorWidget::selectedConnectionType() const
0286 {
0287     return (d->stack->currentWidget() == d->fileWidget()) ? FileBased : ServerBased;
0288 }
0289 
0290 KDbConnectionData* KexiConnectionSelectorWidget::selectedConnectionData() const
0291 {
0292     QList<QTreeWidgetItem *> items = d->remote->list->selectedItems();
0293     if (items.isEmpty())
0294         return 0;
0295     ConnectionDataLVItem *item = static_cast<ConnectionDataLVItem*>(items.first());
0296     if (!item)
0297         return 0;
0298     return item->data();
0299 }
0300 
0301 QString KexiConnectionSelectorWidget::selectedFile() const
0302 {
0303     if (selectedConnectionType() != KexiConnectionSelectorWidget::FileBased) {
0304         return QString();
0305     }
0306     return d->fileIface->selectedFile();
0307 }
0308 
0309 void KexiConnectionSelectorWidget::setSelectedFile(const QString& name)
0310 {
0311     if (selectedConnectionType() != KexiConnectionSelectorWidget::FileBased) {
0312         return;
0313     }
0314     return d->fileIface->setSelectedFile(name);
0315 }
0316 
0317 void KexiConnectionSelectorWidget::slotConnectionItemExecuted(QTreeWidgetItem* item)
0318 {
0319     emit connectionItemExecuted(static_cast<ConnectionDataLVItem*>(item));
0320     slotConnectionSelected();
0321 }
0322 
0323 void KexiConnectionSelectorWidget::slotConnectionItemExecuted()
0324 {
0325     QList<QTreeWidgetItem *> items = d->remote->list->selectedItems();
0326     if (items.isEmpty())
0327         return;
0328     slotConnectionItemExecuted(items.first());
0329     slotConnectionSelected();
0330 }
0331 
0332 void KexiConnectionSelectorWidget::slotConnectionSelectionChanged()
0333 {
0334     QList<QTreeWidgetItem *> items = d->remote->list->selectedItems();
0335     if (items.isEmpty())
0336         return;
0337     ConnectionDataLVItem* item = static_cast<ConnectionDataLVItem*>(items.first());
0338     d->remote->btn_edit->setEnabled(item);
0339     d->remote->btn_remove->setEnabled(item);
0340     QString desc;
0341     if (item) {
0342         desc = item->data()->description();
0343     }
0344     d->descGroupBoxPaintBlocker->setEnabled(desc.isEmpty());
0345     d->remote->descriptionLabel->setText(desc);
0346     emit connectionSelected(d->isConnectionSelected);
0347     emit connectionItemHighlighted(item);
0348 }
0349 
0350 QTreeWidget* KexiConnectionSelectorWidget::connectionsList() const
0351 {
0352     return d->remote->list;
0353 }
0354 
0355 void KexiConnectionSelectorWidget::setFocus()
0356 {
0357     QWidget::setFocus();
0358     if (d->stack->currentWidget() == d->fileWidget()) {
0359         d->fileWidget()->setFocus();
0360     } else {
0361         d->remote->list->setFocus();
0362     }
0363 }
0364 
0365 void KexiConnectionSelectorWidget::hideHelpers()
0366 {
0367     d->openExistingWidget->hide();
0368 }
0369 
0370 void KexiConnectionSelectorWidget::setConfirmOverwrites(bool set)
0371 {
0372     d->confirmOverwrites = set;
0373     if (d->fileIface) {
0374         d->fileIface->setConfirmOverwrites(d->confirmOverwrites);
0375     }
0376 }
0377 
0378 bool KexiConnectionSelectorWidget::confirmOverwrites() const
0379 {
0380     return d->confirmOverwrites;
0381 }
0382 
0383 void KexiConnectionSelectorWidget::slotRemoteAddBtnClicked()
0384 {
0385     KDbConnectionData data;
0386     KexiDBConnectionDialog dlg(this, data, QString(),
0387                                KGuiItem(xi18nc("@action:button Add Database Connection", "&Add"), koIconName("dialog-ok"), xi18n("Add database connection")));
0388     dlg.setWindowTitle(xi18nc("@title:window", "Add a New Database Connection"));
0389     if (QDialog::Accepted != dlg.exec())
0390         return;
0391 
0392     //store this conn. data
0393     KDbConnectionData *newData
0394         = new KDbConnectionData(*dlg.currentProjectData().connectionData());
0395     KDbMessageGuard mg(d->conn_set);
0396     if (!d->conn_set->addConnectionData(newData)) {
0397         delete newData;
0398         return;
0399     }
0400 
0401     ConnectionDataLVItem* item = addConnectionData(newData);
0402     if (item) {
0403         d->remote->list->clearSelection();
0404         d->updateRemoteListColumns();
0405         item->setSelected(true);
0406         slotConnectionSelectionChanged();
0407     }
0408 }
0409 
0410 void KexiConnectionSelectorWidget::slotRemoteEditBtnClicked()
0411 {
0412     QList<QTreeWidgetItem *> items = d->remote->list->selectedItems();
0413     if (items.isEmpty())
0414         return;
0415     ConnectionDataLVItem* item = static_cast<ConnectionDataLVItem*>(items.first());
0416     if (!item)
0417         return;
0418     KexiDBConnectionDialog dlg(this, *item->data(), QString(),
0419                                KGuiItem(xi18nc("@action:button Save Database Connection", "&Save"), koIconName("document-save"),
0420                                         xi18n("Save changes made to this database connection")));
0421     dlg.setWindowTitle(xi18nc("@title:window", "Edit Database Connection"));
0422     if (QDialog::Accepted != dlg.exec())
0423         return;
0424 
0425     KDbMessageGuard mg(d->conn_set);
0426     if (!d->conn_set->saveConnectionData(item->data(), *dlg.currentProjectData().connectionData())) {
0427         return;
0428     }
0429     const KDbDriverMetaData *driverMetaData = d->manager.driverMetaData(item->data()->driverId());
0430     if (driverMetaData) {
0431         item->update(*driverMetaData);
0432         d->updateRemoteListColumns();
0433         slotConnectionSelectionChanged(); //to update descr. edit
0434     }
0435 }
0436 
0437 void KexiConnectionSelectorWidget::slotRemoteRemoveBtnClicked()
0438 {
0439     QList<QTreeWidgetItem *> items = d->remote->list->selectedItems();
0440     if (items.isEmpty())
0441         return;
0442     ConnectionDataLVItem* item = static_cast<ConnectionDataLVItem*>(items.first());
0443     if (!item)
0444         return;
0445     if (KMessageBox::Yes != KMessageBox::questionYesNo(this,
0446             xi18nc("@info",
0447                 "Do you want to delete database connection <resource>%1</resource> from "
0448                 "the list of available connections?",
0449                 item->data()->toUserVisibleString()),
0450             QString(), //caption
0451             KStandardGuiItem::del(), KStandardGuiItem::cancel(),
0452             QString(), //dont'ask name
0453             KMessageBox::Notify | KMessageBox::Dangerous)) {
0454         return;
0455     }
0456 
0457     QTreeWidgetItem* nextItem = d->remote->list->itemBelow(item);
0458     if (!nextItem)
0459         nextItem = d->remote->list->itemAbove(item);
0460     KDbMessageGuard mg(d->conn_set);
0461     if (!d->conn_set->removeConnectionData(item->data()))
0462         return;
0463 
0464     delete item->data();
0465     delete item;
0466 
0467     if (nextItem)
0468         nextItem->setSelected(true);
0469     d->updateRemoteListColumns();
0470 }
0471 
0472 void KexiConnectionSelectorWidget::hideConnectonIcon()
0473 {
0474     d->remote->lblIcon->setFixedWidth(0);
0475     d->remote->lblIcon->setPixmap(QPixmap());
0476 }
0477 
0478 void KexiConnectionSelectorWidget::hideDescription()
0479 {
0480     d->remote->lblIcon->hide();
0481     d->remote->label->hide();
0482 }
0483 
0484 void KexiConnectionSelectorWidget::setExcludedMimeTypes(const QStringList &mimeTypes)
0485 {
0486     d->fileIface->setExcludedMimeTypes(mimeTypes);
0487 }
0488 
0489 bool KexiConnectionSelectorWidget::eventFilter(QObject* watched, QEvent* event)
0490 {
0491     if (event->type() == QEvent::KeyPress) {
0492         QKeyEvent *ke = static_cast<QKeyEvent*>(event);
0493         if ((ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return)
0494             && ke->modifiers() == Qt::NoModifier)
0495         {
0496             slotConnectionItemExecuted();
0497             return true;
0498         }
0499     }
0500     return QWidget::eventFilter(watched, event);
0501 }
0502 
0503 void KexiConnectionSelectorWidget::slotFileConnectionSelected(const QString &name)
0504 {
0505     Q_UNUSED(name)
0506     d->isConnectionSelected = !d->fileIface->selectedFile().isEmpty();
0507     emit connectionSelected(d->isConnectionSelected);
0508     emit fileSelected(name);
0509 }
0510 
0511 void KexiConnectionSelectorWidget::slotConnectionSelected()
0512 {
0513     d->isConnectionSelected = !d->remote->list->selectedItems().isEmpty();
0514     emit connectionSelected(d->isConnectionSelected);
0515 }
0516 
0517 bool KexiConnectionSelectorWidget::hasSelectedConnection() const
0518 {
0519     return d->isConnectionSelected;
0520 }
0521 
0522 void KexiConnectionSelectorWidget::setFileMode(KexiFileFilters::Mode mode)
0523 {
0524     if (d->fileIface) {
0525         d->fileIface->setMode(mode);
0526     }
0527 }
0528 
0529 void KexiConnectionSelectorWidget::setAdditionalMimeTypes(const QStringList &mimeTypes)
0530 {
0531     if (d->fileIface) {
0532         d->fileIface->setAdditionalMimeTypes(mimeTypes);
0533     }
0534 }
0535 
0536 bool KexiConnectionSelectorWidget::checkSelectedFile()
0537 {
0538     if (d->fileIface) {
0539         return d->fileIface->checkSelectedFile();
0540     }
0541     return false;
0542 }
0543 
0544 QString KexiConnectionSelectorWidget::highlightedFile() const
0545 {
0546     if (d->fileIface) {
0547         return d->fileIface->highlightedFile();
0548     }
0549     return QString();
0550 }
0551 
0552 #include "KexiConnectionSelectorWidget.moc"