File indexing completed on 2024-05-19 12:55:38

0001 /* This file is part of the KDE project
0002    Copyright (C) 2005-2016 Jarosław Staniek <staniek@kde.org>
0003 
0004    This library is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This library is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this library; see the file COPYING.LIB.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "KexiDataSourceComboBox.h"
0021 #include <KexiIcon.h>
0022 #include <kexi.h>
0023 #include <kexiproject.h>
0024 #include <kexipart.h>
0025 #include <kexipartmanager.h>
0026 #include <kexipartinfo.h>
0027 #include <kexipartitem.h>
0028 
0029 #include <KDbConnection>
0030 
0031 #include <QDebug>
0032 #include <QLineEdit>
0033 
0034 #ifdef KEXI_SHOW_UNIMPLEMENTED
0035 #define ADD_DEFINEQUERY_ROW
0036 #endif
0037 
0038 //! @internal
0039 class Q_DECL_HIDDEN KexiDataSourceComboBox::Private
0040 {
0041 public:
0042     Private()
0043             : tableIcon(KexiIcon("table"))
0044             , queryIcon(KexiIcon("query"))
0045             , tablesCount(0)
0046             , prevIndex(-1)
0047             , showTables(true)
0048             , showQueries(true) {
0049     }
0050     int firstTableIndex() const {
0051         int index = 1; //skip empty row
0052 #ifdef ADD_DEFINEQUERY_ROW
0053         index++; /*skip 'define query' row*/
0054 #endif
0055         return index;
0056     }
0057     int firstQueryIndex() const {
0058         return firstTableIndex() + tablesCount;
0059     }
0060 
0061     QPointer<KexiProject> prj;
0062     QIcon tableIcon, queryIcon;
0063     int tablesCount;
0064     int prevIndex; //!< Used in slotActivated()
0065     bool showTables;
0066     bool showQueries;
0067 };
0068 
0069 //------------------------
0070 
0071 KexiDataSourceComboBox::KexiDataSourceComboBox(QWidget *parent)
0072         : KComboBox(true/*rw*/, parent)
0073         , d(new Private())
0074 {
0075     setInsertPolicy(NoInsert);
0076     setCompletionMode(KCompletion::CompletionPopupAuto);
0077     setMaxVisibleItems(16);
0078     connect(this, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
0079     connect(this, SIGNAL(returnPressed(QString)),
0080             this, SLOT(slotReturnPressed(QString)));
0081     connect(this, SIGNAL(editTextChanged(QString)), this, SLOT(slotTextChanged(QString)));
0082 }
0083 
0084 KexiDataSourceComboBox::~KexiDataSourceComboBox()
0085 {
0086     delete d;
0087 }
0088 
0089 KexiProject* KexiDataSourceComboBox::project() const
0090 {
0091     return d->prj;
0092 }
0093 
0094 void KexiDataSourceComboBox::setProject(KexiProject *prj, bool showTables, bool showQueries)
0095 {
0096     if (static_cast<KexiProject *>(d->prj) == prj)
0097         return;
0098 
0099     if (d->prj) {
0100         disconnect(d->prj, 0, this, 0);
0101     }
0102     d->prj = prj;
0103     d->showTables = showTables;
0104     d->showQueries = showQueries;
0105     clear();
0106     d->tablesCount = 0;
0107     if (!d->prj)
0108         return;
0109 
0110     //needed for updating contents of the combo box
0111     connect(d->prj, SIGNAL(newItemStored(KexiPart::Item*)),
0112             this, SLOT(slotNewItemStored(KexiPart::Item*)));
0113     connect(d->prj, SIGNAL(itemRemoved(KexiPart::Item)),
0114             this, SLOT(slotItemRemoved(KexiPart::Item)));
0115     connect(d->prj, SIGNAL(itemRenamed(KexiPart::Item,QString)),
0116             this, SLOT(slotItemRenamed(KexiPart::Item,QString)));
0117 
0118     KDbConnection *conn = d->prj->dbConnection();
0119     if (!conn)
0120         return;
0121 
0122     addItem(""); //special item: empty but not null
0123 #ifdef ADD_DEFINEQUERY_ROW
0124     //special item: define query
0125     addItem(xi18n("Define Query..."));
0126 #endif
0127 
0128     KCompletion *comp = completionObject();
0129 
0130     if (d->showTables) {
0131         //tables
0132         KexiPart::Info* partInfo = Kexi::partManager().infoForPluginId("org.kexi-project.table");
0133         if (!partInfo)
0134             return;
0135         KexiPart::ItemList list;
0136         prj->getSortedItems(&list, partInfo);
0137         list.sort();
0138         d->tablesCount = 0;
0139         foreach(KexiPart::Item *item, list) {
0140             addItem(d->tableIcon, item->name()); //or caption()?
0141             comp->addItem(item->name());
0142             d->tablesCount++;
0143         }
0144     }
0145 
0146     if (d->showQueries) {
0147         //queries
0148         KexiPart::Info* partInfo = Kexi::partManager().infoForPluginId("org.kexi-project.query");
0149         if (!partInfo)
0150             return;
0151         KexiPart::ItemList list;
0152         prj->getSortedItems(&list, partInfo);
0153         list.sort();
0154         foreach(KexiPart::Item *item, list) {
0155             addItem(d->queryIcon, item->name()); //or caption()?
0156             comp->addItem(item->name());
0157         }
0158     }
0159     setCurrentIndex(0);
0160 }
0161 
0162 void KexiDataSourceComboBox::setDataSource(const QString& pluginId, const QString& name)
0163 {
0164     if (name.isEmpty()) {
0165         if (currentIndex() != 0) {
0166             clearEditText();
0167             setCurrentIndex(0);
0168             d->prevIndex = -1;
0169             emit dataSourceChanged();
0170         }
0171         return;
0172     }
0173 
0174     QString _pluginId(pluginId);
0175     if (_pluginId.isEmpty())
0176         _pluginId = "org.kexi-project.table";
0177     int i = findItem(_pluginId, name);
0178     if (i == -1) {
0179         if (pluginId.isEmpty())
0180             i = findItem("org.kexi-project.query", name);
0181         if (i == -1) {
0182             if (currentIndex() != 0) {
0183                 setCurrentIndex(0);
0184             }
0185             return;
0186         }
0187     }
0188     if (currentIndex() != i) {
0189         setCurrentIndex(i);
0190         slotActivated(i);
0191     }
0192 }
0193 
0194 void KexiDataSourceComboBox::slotNewItemStored(KexiPart::Item* item)
0195 {
0196     QString name(item->name());
0197     //insert a new item, maintaining sort order and splitting to tables and queries
0198     if (item->pluginId() == "org.kexi-project.table") {
0199         int i = 1; /*skip empty row*/
0200 #ifdef ADD_DEFINEQUERY_ROW
0201         i++; /*skip 'define query' row*/
0202 #endif
0203         for (; i < d->firstQueryIndex() && name >= itemText(i); i++) {
0204         }
0205         insertItem(i, d->tableIcon, name);
0206         completionObject()->addItem(name);
0207         d->tablesCount++;
0208     } else if (item->pluginId() == "org.kexi-project.query") {
0209         int i;
0210         for (i = d->firstQueryIndex(); i < count() && name >= itemText(i); i++) {
0211         }
0212         insertItem(i, d->queryIcon, name);
0213         completionObject()->addItem(name);
0214     }
0215 }
0216 
0217 int KexiDataSourceComboBox::findItem(const QString& pluginId, const QString& name)
0218 {
0219     int i, end;
0220     if (pluginId == "org.kexi-project.table") {
0221         i = 0;
0222 #ifdef ADD_DEFINEQUERY_ROW
0223         i++; //skip 'define query'
0224 #endif
0225         end = d->firstQueryIndex();
0226     } else if (pluginId == "org.kexi-project.query") {
0227         i = d->firstQueryIndex();
0228         end = count();
0229     } else
0230         return -1;
0231 
0232     QString nameString(name);
0233 
0234     for (; i < end; i++)
0235         if (itemText(i) == nameString)
0236             return i;
0237 
0238     return -1;
0239 }
0240 
0241 void KexiDataSourceComboBox::slotItemRemoved(const KexiPart::Item& item)
0242 {
0243     const int i = findItem(item.pluginId(), item.name());
0244     if (i == -1)
0245         return;
0246     removeItem(i);
0247     completionObject()->removeItem(item.name());
0248     if (item.pluginId() == "org.kexi-project.table")
0249         d->tablesCount--;
0250 #if 0 //disabled because even invalid data source can be set
0251     if (currentItem() == i) {
0252         if (i == (count() - 1))
0253             setCurrentItem(i - 1);
0254         else
0255             setCurrentItem(i);
0256     }
0257 #endif
0258 }
0259 
0260 void KexiDataSourceComboBox::slotItemRenamed(const KexiPart::Item& item, const QString& oldName)
0261 {
0262     const int i = findItem(item.pluginId(), QString(oldName));
0263     if (i == -1) {
0264         return;
0265     }
0266     setItemText(i, item.name());
0267     completionObject()->removeItem(oldName);
0268     completionObject()->addItem(item.name());
0269     setEditText(oldName); //still keep old name
0270 }
0271 
0272 void KexiDataSourceComboBox::slotActivated(int index)
0273 {
0274     if (index >= 0 && index < count() && d->prevIndex != currentIndex()) {
0275         d->prevIndex = currentIndex();
0276         emit dataSourceChanged();
0277     }
0278 }
0279 
0280 void KexiDataSourceComboBox::slotTextChanged(const QString &text)
0281 {
0282     Q_UNUSED(text)
0283     //! @todo This place may be useful when we alow to enter values not being on the list
0284 }
0285 
0286 QString KexiDataSourceComboBox::selectedPluginId() const
0287 {
0288     if (selectedName().isEmpty()) {
0289         return QString();
0290     }
0291     const int index = currentIndex();
0292     if (index >= d->firstTableIndex() && index < (int)d->firstQueryIndex()) {
0293         return "org.kexi-project.table";
0294     }
0295     else if (index >= (int)d->firstQueryIndex() && index < count()) {
0296         return "org.kexi-project.query";
0297     }
0298     return QString();
0299 }
0300 
0301 QString KexiDataSourceComboBox::selectedName() const
0302 {
0303     if (isSelectionValid()) {
0304         return itemText(currentIndex());
0305     }
0306     return currentText();
0307 }
0308 
0309 bool KexiDataSourceComboBox::isSelectionValid() const
0310 {
0311     const int index = currentIndex();
0312     return index >= d->firstTableIndex() && index < count() && itemText(index) == currentText();
0313 }
0314 
0315 void KexiDataSourceComboBox::slotReturnPressed(const QString & text)
0316 {
0317     //if selected text is valid: no completion is required.
0318     if (isSelectionValid()) {
0319         return;
0320     }
0321 
0322     //text is available: select item for this text:
0323     bool changed = false;
0324     if (text.isEmpty() && 0 != currentIndex()) {
0325         setCurrentIndex(0);
0326         changed = true;
0327     } else {
0328         const int index = findText(text, Qt::MatchExactly);
0329         if (index >= 0 && index != currentIndex()) {
0330             setCurrentIndex(index);
0331             changed = true;
0332         }
0333     }
0334     if (changed) {
0335         emit dataSourceChanged();
0336     }
0337 }
0338 
0339 void KexiDataSourceComboBox::focusOutEvent(QFocusEvent *e)
0340 {
0341     KComboBox::focusOutEvent(e);
0342     slotReturnPressed(currentText());
0343 }
0344