KUtils
dialog.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 2003 Matthias Kretz <kretz@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, USA. 00017 00018 */ 00019 00020 #include "dialog.h" 00021 #include "dialog_p.h" 00022 00023 #include "dispatcher.h" 00024 //#include "componentsdialog_p.h" 00025 00026 #include <klocale.h> 00027 #include <kservicegroup.h> 00028 #include <kdebug.h> 00029 #include <kservicetypetrader.h> 00030 #include <kconfig.h> 00031 #include <kstandarddirs.h> 00032 #include <kcomponentdata.h> 00033 #include <kiconloader.h> 00034 #include <QtCore/QFile> 00035 #include <QtGui/QCheckBox> 00036 #include <QtCore/QStack> 00037 00038 uint qHash(const KCModuleInfo &info) 00039 { 00040 return qHash(info.fileName()); 00041 } 00042 00043 namespace KSettings 00044 { 00045 00046 Dialog::Dialog(QWidget *parent) 00047 : KCMultiDialog(*new DialogPrivate, new KPageWidget, parent) 00048 { 00049 } 00050 00051 Dialog::Dialog(const QStringList &components, QWidget *parent) 00052 : KCMultiDialog(*new DialogPrivate, new KPageWidget, parent) 00053 { 00054 Q_D(Dialog); 00055 d->components = components; 00056 } 00057 00058 Dialog::~Dialog() 00059 { 00060 } 00061 00062 void Dialog::setAllowComponentSelection(bool selection) 00063 { 00064 d_func()->staticlistview = !selection; 00065 } 00066 00067 bool Dialog::allowComponentSelection() const 00068 { 00069 return !d_func()->staticlistview; 00070 } 00071 00072 void Dialog::setKCMArguments(const QStringList& arguments) 00073 { 00074 Q_D(Dialog); 00075 d->arguments = arguments; 00076 } 00077 00078 void Dialog::setComponentBlacklist(const QStringList& blacklist) 00079 { 00080 Q_D(Dialog); 00081 d->componentBlacklist = blacklist; 00082 } 00083 00084 void Dialog::addPluginInfos(const KPluginInfo::List &plugininfos) 00085 { 00086 Q_D(Dialog); 00087 for (KPluginInfo::List::ConstIterator it = plugininfos.begin(); 00088 it != plugininfos.end(); ++it ) { 00089 d->registeredComponents.append(it->pluginName()); 00090 if (it->kcmServices().isEmpty()) { 00091 // this plugin has no kcm services, still we want to show the disable/enable stuff 00092 // so add a dummy kcm 00093 KService::Ptr service = it->service(); 00094 d->kcmInfos << KCModuleInfo(service); 00095 continue; 00096 } 00097 foreach (const KService::Ptr &service, it->kcmServices()) { 00098 d->kcmInfos << KCModuleInfo(service); 00099 } 00100 } 00101 00102 // The plugin, when disabled, disables all the KCMs described by kcmServices(). 00103 // - Normally they are grouped using a .setdlg file so that the group parent can get a 00104 // checkbox to enable/disable the plugin. 00105 // - If the plugin does not belong to a group and has only one KCM the checkbox can be 00106 // used with this KCM. 00107 // - If the plugin belongs to a group but there are other modules in the group that do not 00108 // belong to this plugin we give a kError and show no checkbox 00109 // - If the plugin belongs to multiple groups we give a kError and show no checkbox 00110 d->plugininfos = plugininfos; 00111 } 00112 00113 KPluginInfo::List Dialog::pluginInfos() const 00114 { 00115 return d_func()->plugininfos; 00116 } 00117 00118 void Dialog::showEvent(QShowEvent *) 00119 { 00120 Q_D(Dialog); 00121 if (d->firstshow) { 00122 setUpdatesEnabled(false); 00123 d->kcmInfos += d->instanceServices(); 00124 if (!d->components.isEmpty()) { 00125 d->kcmInfos += d->parentComponentsServices(d->components); 00126 } 00127 d->createDialogFromServices(); 00128 d->firstshow = false; 00129 setUpdatesEnabled(true); 00130 } 00131 Dispatcher::syncConfiguration(); 00132 } 00133 00134 DialogPrivate::DialogPrivate() 00135 : staticlistview(true), firstshow(true), pluginStateDirty(0) 00136 { 00137 } 00138 00139 QSet<KCModuleInfo> DialogPrivate::instanceServices() 00140 { 00141 //kDebug(700) ; 00142 QString componentName = KGlobal::mainComponent().componentName(); 00143 registeredComponents.append(componentName); 00144 //kDebug(700) << "calling KServiceGroup::childGroup( " << componentName << " )"; 00145 KServiceGroup::Ptr service = KServiceGroup::childGroup( componentName ); 00146 00147 QSet<KCModuleInfo> ret; 00148 00149 if( service && service->isValid() ) 00150 { 00151 //kDebug(700) << "call was successful"; 00152 const KServiceGroup::List list = service->entries(); 00153 for( KServiceGroup::List::ConstIterator it = list.begin(); 00154 it != list.end(); ++it ) 00155 { 00156 KSycocaEntry::Ptr p = (*it); 00157 if( p->isType( KST_KService ) ) 00158 { 00159 //kDebug( 700 ) << "found service"; 00160 ret << KCModuleInfo(KService::Ptr::staticCast(p)); 00161 } 00162 else 00163 kWarning( 700 ) << "KServiceGroup::childGroup returned" 00164 " something else than a KService" << endl; 00165 } 00166 } 00167 00168 return ret; 00169 } 00170 00171 QSet<KCModuleInfo> DialogPrivate::parentComponentsServices(const QStringList &kcdparents) 00172 { 00173 registeredComponents += kcdparents; 00174 QString constraint = kcdparents.join("' in [X-KDE-ParentComponents]) or ('"); 00175 constraint = "('" + constraint + "' in [X-KDE-ParentComponents])"; 00176 00177 //kDebug(700) << "constraint = " << constraint; 00178 const QList<KService::Ptr> services = KServiceTypeTrader::self()->query("KCModule", constraint); 00179 QSet<KCModuleInfo> ret; 00180 foreach (const KService::Ptr &service, services) { 00181 ret << KCModuleInfo(service); 00182 } 00183 return ret; 00184 } 00185 00186 bool DialogPrivate::isPluginForKCMEnabled(const KCModuleInfo *moduleinfo, KPluginInfo &pinfo) const 00187 { 00188 // if the user of this class requested to hide disabled modules 00189 // we check whether it should be enabled or not 00190 bool enabled = true; 00191 //kDebug(700) << "check whether the '" << moduleinfo->moduleName() << "' KCM should be shown"; 00192 // for all parent components 00193 const QStringList parentComponents = moduleinfo->service()->property( 00194 "X-KDE-ParentComponents" ).toStringList(); 00195 for( QStringList::ConstIterator pcit = parentComponents.begin(); 00196 pcit != parentComponents.end(); ++pcit ) 00197 { 00198 // if the parentComponent is not registered ignore it 00199 if (!registeredComponents.contains(*pcit)) { 00200 continue; 00201 } 00202 00203 // we check if the parent component is a plugin 00204 // if not the KCModule must be enabled 00205 enabled = true; 00206 if (pinfo.pluginName() == *pcit) { 00207 // it is a plugin: we check whether the plugin is enabled 00208 pinfo.load(); 00209 enabled = pinfo.isPluginEnabled(); 00210 //kDebug(700) << "parent " << *pcit << " is " << (enabled ? "enabled" : "disabled"); 00211 } 00212 // if it is enabled we're done for this KCModuleInfo 00213 if (enabled) { 00214 return true; 00215 } 00216 } 00217 return enabled; 00218 } 00219 00220 bool DialogPrivate::isPluginImmutable(const KPluginInfo &pinfo) const 00221 { 00222 return pinfo.property("X-KDE-PluginInfo-Immutable").toBool(); 00223 } 00224 00225 KPageWidgetItem *DialogPrivate::createPageItem(KPageWidgetItem *parentItem, 00226 const QString &name, const QString &comment, 00227 const QString &iconName, int weight) 00228 { 00229 Q_Q(Dialog); 00230 QWidget * page = new QWidget( q ); 00231 00232 QCheckBox *checkBox = new QCheckBox(i18n("Enable component"), page); 00233 QLabel *iconLabel = new QLabel(page); 00234 QLabel *commentLabel = new QLabel(comment, page); 00235 commentLabel->setTextFormat(Qt::RichText); 00236 QVBoxLayout * layout = new QVBoxLayout(page); 00237 layout->addWidget(checkBox); 00238 layout->addWidget(iconLabel); 00239 layout->addWidget(commentLabel); 00240 layout->addStretch(); 00241 page->setLayout(layout); 00242 00243 KPageWidgetItem *item = new KPageWidgetItem(page, name); 00244 item->setIcon(KIcon(iconName)); 00245 iconLabel->setPixmap(item->icon().pixmap(128, 128)); 00246 item->setProperty("_k_weight", weight); 00247 checkBoxForItem.insert(item, checkBox); 00248 00249 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(q->pageWidget()->model()); 00250 Q_ASSERT(model); 00251 00252 if (parentItem) { 00253 const QModelIndex parentIndex = model->index(parentItem); 00254 const int siblingCount = model->rowCount(parentIndex); 00255 int row = 0; 00256 for (; row < siblingCount; ++row) { 00257 KPageWidgetItem *siblingItem = model->item(parentIndex.child(row, 0)); 00258 if (siblingItem->property("_k_weight").toInt() > weight) { 00259 // the item we found is heavier than the new module 00260 q->insertPage(siblingItem, item); 00261 break; 00262 } 00263 } 00264 if (row == siblingCount) { 00265 // the new module is either the first or the heaviest item 00266 q->addSubPage(parentItem, item); 00267 } 00268 } else { 00269 const int siblingCount = model->rowCount(); 00270 int row = 0; 00271 for (; row < siblingCount; ++row) { 00272 KPageWidgetItem *siblingItem = model->item(model->index(row, 0)); 00273 if (siblingItem->property("_k_weight").toInt() > weight) { 00274 // the item we found is heavier than the new module 00275 q->insertPage(siblingItem, item); 00276 break; 00277 } 00278 } 00279 if (row == siblingCount) { 00280 // the new module is either the first or the heaviest item 00281 q->addPage(item); 00282 } 00283 } 00284 00285 return (item); 00286 } 00287 00288 void DialogPrivate::parseGroupFile( const QString & filename ) 00289 { 00290 KConfig file( filename, KConfig::SimpleConfig ); 00291 const QStringList groups = file.groupList(); 00292 foreach (const QString &group, groups) { 00293 if (group.isEmpty()) { 00294 continue; 00295 } 00296 KConfigGroup conf(&file, group); 00297 00298 const QString parentId = conf.readEntry("Parent"); 00299 KPageWidgetItem *parentItem = pageItemForGroupId.value(parentId); 00300 KPageWidgetItem *item = createPageItem(parentItem, conf.readEntry("Name"), conf.readEntry("Comment"), 00301 conf.readEntry("Icon"), conf.readEntry("Weight", 100)); 00302 pageItemForGroupId.insert(group, item); 00303 } 00304 } 00305 00306 void DialogPrivate::createDialogFromServices() 00307 { 00308 Q_Q(Dialog); 00309 // read .setdlg files 00310 QString setdlgpath = KStandardDirs::locate( "appdata", 00311 KGlobal::mainComponent().componentName() + ".setdlg" ); 00312 const QStringList setdlgaddon = KGlobal::dirs()->findAllResources( "appdata", 00313 "ksettingsdialog/*.setdlg" ); 00314 if (!setdlgpath.isNull()) { 00315 parseGroupFile(setdlgpath); 00316 } 00317 if (setdlgaddon.size() > 0) { 00318 for (QStringList::ConstIterator it = setdlgaddon.begin(); it != setdlgaddon.end(); ++it) { 00319 parseGroupFile(*it); 00320 } 00321 } 00322 00323 //kDebug(700) << kcmInfos.count(); 00324 foreach (const KCModuleInfo &info, kcmInfos) { 00325 const QStringList parentComponents = info.service()->property("X-KDE-ParentComponents").toStringList(); 00326 bool blacklisted = false; 00327 foreach (const QString &parentComponent, parentComponents) { 00328 if (componentBlacklist.contains(parentComponent)) { 00329 blacklisted = true; 00330 break; 00331 } 00332 } 00333 if (blacklisted) { 00334 continue; 00335 } 00336 const QString parentId = info.service()->property("X-KDE-CfgDlgHierarchy", QVariant::String).toString(); 00337 KPageWidgetItem *parent = pageItemForGroupId.value(parentId); 00338 if (!parent) { 00339 // dummy kcm 00340 bool foundPlugin = false; 00341 foreach (const KPluginInfo &pinfo, plugininfos) { 00342 if (pinfo.service() == info.service()) { 00343 if (!pinfo.kcmServices().count()) { 00344 const KService::Ptr service = info.service(); 00345 // FIXME get weight from service or plugin info 00346 const int weight = 1000; 00347 KPageWidgetItem *item = createPageItem(0, service->name(), service->comment(), service->icon(), weight); 00348 connectItemCheckBox(item, pinfo, pinfo.isPluginEnabled()); 00349 foundPlugin = true; 00350 break; 00351 } 00352 } 00353 } 00354 if (foundPlugin) { 00355 continue; 00356 } 00357 } 00358 KPageWidgetItem *item = q->addModule(info, parent, arguments); 00359 kDebug(700) << "added KCM '" << info.moduleName() << "'"; 00360 foreach (KPluginInfo pinfo, plugininfos) { 00361 kDebug(700) << pinfo.pluginName(); 00362 if (pinfo.kcmServices().contains(info.service())) { 00363 const bool isEnabled = isPluginForKCMEnabled(&info, pinfo); 00364 kDebug(700) << "correct KPluginInfo for this KCM"; 00365 // this KCM belongs to a plugin 00366 if (parent && pinfo.kcmServices().count() >= 1) { 00367 item->setEnabled(isEnabled); 00368 const KPluginInfo &plugin = pluginForItem.value(parent); 00369 if (plugin.isValid()) { 00370 if (plugin != pinfo) { 00371 kError(700) << "A group contains more than one plugin: '" 00372 << plugin.pluginName() << "' and '" << pinfo.pluginName() 00373 << "'. Now it won't be possible to enable/disable the plugin." 00374 << endl; 00375 parent->setCheckable(false); 00376 q->disconnect(parent, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool))); 00377 } 00378 // else everything is fine 00379 } else { 00380 connectItemCheckBox(parent, pinfo, isEnabled); 00381 } 00382 } else { 00383 pluginForItem.insert(item, pinfo); 00384 item->setCheckable(!isPluginImmutable(pinfo)); 00385 item->setChecked(isEnabled); 00386 q->connect(item, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool))); 00387 } 00388 break; 00389 } 00390 } 00391 } 00392 // now that the KCMs are in, check for empty groups and remove them again 00393 { 00394 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(q->pageWidget()->model()); 00395 const QHash<QString, KPageWidgetItem *>::ConstIterator end = pageItemForGroupId.constEnd(); 00396 QHash<QString, KPageWidgetItem *>::ConstIterator it = pageItemForGroupId.constBegin(); 00397 for (; it != end; ++it) { 00398 const QModelIndex index = model->index(it.value()); 00399 KPluginInfo pinfo; 00400 foreach (const KPluginInfo &p, plugininfos) { 00401 if (p.name()==it.key()) { 00402 pinfo = p; 00403 break; 00404 } 00405 } 00406 bool allowEmpty = false; 00407 if (pinfo.isValid()) { 00408 allowEmpty = pinfo.property("X-KDE-PluginInfo-AllowEmptySettings").toBool(); 00409 } 00410 00411 if (!index.child(0, 0).isValid()) { 00412 // no children, and it's not allowed => remove this item 00413 if (!allowEmpty) { 00414 q->removePage(it.value()); 00415 } else { 00416 connectItemCheckBox(it.value(), pinfo, pinfo.isPluginEnabled()); 00417 } 00418 } 00419 } 00420 } 00421 00422 // TODO: Don't show the reset button until the issue with the 00423 // KPluginSelector::load() method is solved. 00424 // Problem: 00425 // KCMultiDialog::show() call KCModule::load() to reset all KCMs 00426 // (KPluginSelector::load() resets all plugin selections and all plugin 00427 // KCMs). 00428 // The reset button calls KCModule::load(), too but in this case we want the 00429 // KPluginSelector to only reset the current visible plugin KCM and not 00430 // touch the plugin selections. 00431 // I have no idea how to check that in KPluginSelector::load()... 00432 //q->showButton(KDialog::User1, true); 00433 00434 QObject::connect(q, SIGNAL(okClicked()), q, SLOT(_k_syncConfiguration())); 00435 QObject::connect(q, SIGNAL(applyClicked()), q, SLOT(_k_syncConfiguration())); 00436 QObject::connect(q, SIGNAL(configCommitted(const QByteArray &)), q, 00437 SLOT(_k_reparseConfiguration(const QByteArray &))); 00438 } 00439 00440 void DialogPrivate::connectItemCheckBox(KPageWidgetItem *item, const KPluginInfo &pinfo, bool isEnabled) 00441 { 00442 Q_Q(Dialog); 00443 QCheckBox *checkBox = checkBoxForItem.value(item); 00444 Q_ASSERT(checkBox); 00445 pluginForItem.insert(item, pinfo); 00446 item->setCheckable(!isPluginImmutable(pinfo)); 00447 item->setChecked(isEnabled); 00448 checkBox->setVisible(!isPluginImmutable(pinfo)); 00449 checkBox->setChecked(isEnabled); 00450 q->connect(item, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool))); 00451 q->connect(item, SIGNAL(toggled(bool)), checkBox, SLOT(setChecked(bool))); 00452 q->connect(checkBox, SIGNAL(clicked(bool)), item, SLOT(setChecked(bool))); 00453 } 00454 00455 void DialogPrivate::_k_syncConfiguration() 00456 { 00457 Q_Q(Dialog); 00458 const QHash<KPageWidgetItem *, KPluginInfo>::Iterator endIt = pluginForItem.end(); 00459 QHash<KPageWidgetItem *, KPluginInfo>::Iterator it = pluginForItem.begin(); 00460 for (; it != endIt; ++it) { 00461 KPageWidgetItem *item = it.key(); 00462 KPluginInfo pinfo = it.value(); 00463 pinfo.setPluginEnabled(item->isChecked()); 00464 pinfo.save(); 00465 } 00466 if (pluginStateDirty > 0) { 00467 emit q->pluginSelectionChanged(); 00468 pluginStateDirty = 0; 00469 } 00470 Dispatcher::syncConfiguration(); 00471 } 00472 00473 void DialogPrivate::_k_reparseConfiguration(const QByteArray &a) 00474 { 00475 Dispatcher::reparseConfiguration(a); 00476 } 00477 00478 /* 00479 void DialogPrivate::_k_configureTree() 00480 { 00481 kDebug( 700 ) ; 00482 QObject::connect(subdlg, SIGNAL(okClicked()), q, SLOT(_k_updateTreeList())); 00483 QObject::connect(subdlg, SIGNAL(applyClicked()), q, SLOT(_k_updateTreeList())); 00484 QObject::connect(subdlg, SIGNAL(okClicked()), q, SIGNAL(pluginSelectionChanged())); 00485 QObject::connect(subdlg, SIGNAL(applyClicked()), q, SIGNAL(pluginSelectionChanged())); 00486 } 00487 */ 00488 00489 void DialogPrivate::_k_clientChanged() 00490 { 00491 if (pluginStateDirty > 0) { 00492 Q_Q(Dialog); 00493 q->enableButton(KDialog::Apply, true); 00494 } else { 00495 KCMultiDialogPrivate::_k_clientChanged(); 00496 } 00497 } 00498 00499 void DialogPrivate::_k_updateEnabledState(bool enabled) 00500 { 00501 Q_Q(Dialog); 00502 KPageWidgetItem *item = qobject_cast<KPageWidgetItem *>(q->sender()); 00503 if (!item) { 00504 kWarning(700) << "invalid sender"; 00505 return; 00506 } 00507 00508 // iterate over all child KPageWidgetItem objects and check whether they need to be enabled/disabled 00509 const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(q->pageWidget()->model()); 00510 Q_ASSERT(model); 00511 QModelIndex index = model->index(item); 00512 if (!index.isValid()) { 00513 kWarning(700) << "could not find item in model"; 00514 return; 00515 } 00516 00517 const KPluginInfo &pinfo = pluginForItem.value(item); 00518 if (!pinfo.isValid()) { 00519 kWarning(700) << "could not find KPluginInfo in item"; 00520 return; 00521 } 00522 if (pinfo.isPluginEnabled() != enabled) { 00523 ++pluginStateDirty; 00524 } else { 00525 --pluginStateDirty; 00526 } 00527 if (pluginStateDirty < 2) { 00528 _k_clientChanged(); 00529 } 00530 00531 //kDebug(700) ; 00532 00533 QModelIndex firstborn = index.child(0, 0); 00534 if (firstborn.isValid()) { 00535 //kDebug(700) << "iterating over children"; 00536 // change all children 00537 index = firstborn; 00538 QStack<QModelIndex> stack; 00539 while (index.isValid()) { 00540 //kDebug(700) << index; 00541 KPageWidgetItem *item = model->item(index); 00542 //kDebug(700) << "item->setEnabled(" << enabled << ')'; 00543 item->setEnabled(enabled); 00544 firstborn = index.child(0, 0); 00545 if (firstborn.isValid()) { 00546 stack.push(index); 00547 index = firstborn; 00548 } else { 00549 index = index.sibling(index.row() + 1, 0); 00550 while (!index.isValid() && !stack.isEmpty()) { 00551 index = stack.pop(); 00552 index = index.sibling(index.row() + 1, 0); 00553 } 00554 } 00555 } 00556 } 00557 } 00558 00559 } //namespace 00560 00561 #include "dialog.moc" 00562 00563 // vim: ts=4
KDE 4.6 API Reference