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