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

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         clearEditText();
0166         setCurrentIndex(0);
0167         d->prevIndex = -1;
0168         emit dataSourceChanged();
0169         return;
0170     }
0171 
0172     QString _pluginId(pluginId);
0173     if (_pluginId.isEmpty())
0174         _pluginId = "org.kexi-project.table";
0175     int i = findItem(_pluginId, name);
0176     if (i == -1) {
0177         if (pluginId.isEmpty())
0178             i = findItem("org.kexi-project.query", name);
0179         if (i == -1) {
0180             setCurrentIndex(0);
0181             return;
0182         }
0183     }
0184     setCurrentIndex(i);
0185     slotActivated(i);
0186 }
0187 
0188 void KexiDataSourceComboBox::slotNewItemStored(KexiPart::Item* item)
0189 {
0190     QString name(item->name());
0191     //insert a new item, maintaining sort order and splitting to tables and queries
0192     if (item->pluginId() == "org.kexi-project.table") {
0193         int i = 1; /*skip empty row*/
0194 #ifdef ADD_DEFINEQUERY_ROW
0195         i++; /*skip 'define query' row*/
0196 #endif
0197         for (; i < d->firstQueryIndex() && name >= itemText(i); i++) {
0198         }
0199         insertItem(i, d->tableIcon, name);
0200         completionObject()->addItem(name);
0201         d->tablesCount++;
0202     } else if (item->pluginId() == "org.kexi-project.query") {
0203         int i;
0204         for (i = d->firstQueryIndex(); i < count() && name >= itemText(i); i++) {
0205         }
0206         insertItem(i, d->queryIcon, name);
0207         completionObject()->addItem(name);
0208     }
0209 }
0210 
0211 int KexiDataSourceComboBox::findItem(const QString& pluginId, const QString& name)
0212 {
0213     int i, end;
0214     if (pluginId == "org.kexi-project.table") {
0215         i = 0;
0216 #ifdef ADD_DEFINEQUERY_ROW
0217         i++; //skip 'define query'
0218 #endif
0219         end = d->firstQueryIndex();
0220     } else if (pluginId == "org.kexi-project.query") {
0221         i = d->firstQueryIndex();
0222         end = count();
0223     } else
0224         return -1;
0225 
0226     QString nameString(name);
0227 
0228     for (; i < end; i++)
0229         if (itemText(i) == nameString)
0230             return i;
0231 
0232     return -1;
0233 }
0234 
0235 void KexiDataSourceComboBox::slotItemRemoved(const KexiPart::Item& item)
0236 {
0237     const int i = findItem(item.pluginId(), item.name());
0238     if (i == -1)
0239         return;
0240     removeItem(i);
0241     completionObject()->removeItem(item.name());
0242     if (item.pluginId() == "org.kexi-project.table")
0243         d->tablesCount--;
0244 #if 0 //disabled because even invalid data source can be set
0245     if (currentItem() == i) {
0246         if (i == (count() - 1))
0247             setCurrentItem(i - 1);
0248         else
0249             setCurrentItem(i);
0250     }
0251 #endif
0252 }
0253 
0254 void KexiDataSourceComboBox::slotItemRenamed(const KexiPart::Item& item, const QString& oldName)
0255 {
0256     const int i = findItem(item.pluginId(), QString(oldName));
0257     if (i == -1) {
0258         return;
0259     }
0260     setItemText(i, item.name());
0261     completionObject()->removeItem(oldName);
0262     completionObject()->addItem(item.name());
0263     setEditText(oldName); //still keep old name
0264 }
0265 
0266 void KexiDataSourceComboBox::slotActivated(int index)
0267 {
0268     if (index >= 0 && index < count() && d->prevIndex != currentIndex()) {
0269         d->prevIndex = currentIndex();
0270         emit dataSourceChanged();
0271     }
0272 }
0273 
0274 void KexiDataSourceComboBox::slotTextChanged(const QString &text)
0275 {
0276     Q_UNUSED(text)
0277     //! @todo This place may be useful when we alow to enter values not being on the list
0278 }
0279 
0280 QString KexiDataSourceComboBox::selectedPluginId() const
0281 {
0282     if (selectedName().isEmpty()) {
0283         return QString();
0284     }
0285     const int index = currentIndex();
0286     if (index >= d->firstTableIndex() && index < (int)d->firstQueryIndex()) {
0287         return "org.kexi-project.table";
0288     }
0289     else if (index >= (int)d->firstQueryIndex() && index < count()) {
0290         return "org.kexi-project.query";
0291     }
0292     return QString();
0293 }
0294 
0295 QString KexiDataSourceComboBox::selectedName() const
0296 {
0297     if (isSelectionValid()) {
0298         return itemText(currentIndex());
0299     }
0300     return currentText();
0301 }
0302 
0303 bool KexiDataSourceComboBox::isSelectionValid() const
0304 {
0305     const int index = currentIndex();
0306     return index >= d->firstTableIndex() && index < count() && itemText(index) == currentText();
0307 }
0308 
0309 void KexiDataSourceComboBox::slotReturnPressed(const QString & text)
0310 {
0311     //if selected text is valid: no completion is required.
0312     if (isSelectionValid()) {
0313         return;
0314     }
0315 
0316     //text is available: select item for this text:
0317     bool changed = false;
0318     if (text.isEmpty() && 0 != currentIndex()) {
0319         setCurrentIndex(0);
0320         changed = true;
0321     } else {
0322         const int index = findText(text, Qt::MatchExactly);
0323         if (index >= 0 && index != currentIndex()) {
0324             setCurrentIndex(index);
0325             changed = true;
0326         }
0327     }
0328     if (changed) {
0329         emit dataSourceChanged();
0330     }
0331 }
0332 
0333 void KexiDataSourceComboBox::focusOutEvent(QFocusEvent *e)
0334 {
0335     KComboBox::focusOutEvent(e);
0336     slotReturnPressed(currentText());
0337 }
0338