• Skip to content
  • Skip to link menu
KDE 4.4 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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     Q_Q(Dialog);
00291     KConfig file( filename, KConfig::SimpleConfig );
00292     const QStringList groups = file.groupList();
00293     foreach (const QString &group, groups) {
00294         if (group.isEmpty()) {
00295             continue;
00296         }
00297         KConfigGroup conf(&file, group);
00298 
00299         const QString parentId = conf.readEntry("Parent");
00300         KPageWidgetItem *parentItem = pageItemForGroupId.value(parentId);
00301         KPageWidgetItem *item = createPageItem(parentItem, conf.readEntry("Name"), conf.readEntry("Comment"),
00302                                                conf.readEntry("Icon"), conf.readEntry("Weight", 100));
00303         pageItemForGroupId.insert(group, item);
00304     }
00305 }
00306 
00307 void DialogPrivate::createDialogFromServices()
00308 {
00309     Q_Q(Dialog);
00310     // read .setdlg files
00311     QString setdlgpath = KStandardDirs::locate( "appdata",
00312                                                     KGlobal::mainComponent().componentName() + ".setdlg" );
00313     const QStringList setdlgaddon = KGlobal::dirs()->findAllResources( "appdata",
00314             "ksettingsdialog/*.setdlg" );
00315     if (!setdlgpath.isNull()) {
00316         parseGroupFile(setdlgpath);
00317     }
00318     if (setdlgaddon.size() > 0) {
00319         for (QStringList::ConstIterator it = setdlgaddon.begin(); it != setdlgaddon.end(); ++it) {
00320             parseGroupFile(*it);
00321         }
00322     }
00323 
00324     //kDebug(700) << kcmInfos.count();
00325     foreach (const KCModuleInfo &info, kcmInfos) {
00326         const QStringList parentComponents = info.service()->property("X-KDE-ParentComponents").toStringList();
00327         bool blacklisted = false;
00328         foreach (const QString &parentComponent, parentComponents) {
00329             if (componentBlacklist.contains(parentComponent)) {
00330                 blacklisted = true;
00331                 break;
00332             }
00333         }
00334         if (blacklisted) {
00335             continue;
00336         }
00337         const QString parentId = info.service()->property("X-KDE-CfgDlgHierarchy", QVariant::String).toString();
00338         KPageWidgetItem *parent = pageItemForGroupId.value(parentId);
00339         if (!parent) {
00340             // dummy kcm
00341             bool foundPlugin = false;
00342             foreach (KPluginInfo pinfo, plugininfos) {
00343                 if (pinfo.service() == info.service()) {
00344                     if (!pinfo.kcmServices().count()) {
00345                         const KService::Ptr service = info.service();
00346                         // FIXME get weight from service or plugin info
00347                         const int weight = 1000;
00348                         KPageWidgetItem *item = createPageItem(0, service->name(), service->comment(), service->icon(), weight);
00349                         connectItemCheckBox(item, pinfo, pinfo.isPluginEnabled());
00350                         foundPlugin = true;
00351                         break;
00352                     }
00353                 }
00354             }
00355             if (foundPlugin) {
00356                 continue;
00357             }
00358         }
00359         KPageWidgetItem *item = q->addModule(info, parent, arguments);
00360         kDebug(700) << "added KCM '" << info.moduleName() << "'";
00361         foreach (KPluginInfo pinfo, plugininfos) {
00362             kDebug(700) << pinfo.pluginName();
00363             if (pinfo.kcmServices().contains(info.service())) {
00364                 const bool isEnabled = isPluginForKCMEnabled(&info, pinfo);
00365                 kDebug(700) << "correct KPluginInfo for this KCM";
00366                 // this KCM belongs to a plugin
00367                 if (parent && pinfo.kcmServices().count() >= 1) {
00368                     item->setEnabled(isEnabled);
00369                     const KPluginInfo &plugin = pluginForItem.value(parent);
00370                     if (plugin.isValid()) {
00371                         if (plugin != pinfo) {
00372                             kError(700) << "A group contains more than one plugin: '"
00373                                 << plugin.pluginName() << "' and '" << pinfo.pluginName()
00374                                 << "'. Now it won't be possible to enable/disable the plugin."
00375                                 << endl;
00376                             parent->setCheckable(false);
00377                             q->disconnect(parent, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool)));
00378                         }
00379                         // else everything is fine
00380                     } else {
00381                         connectItemCheckBox(parent, pinfo, isEnabled);
00382                     }
00383                 } else {
00384                     pluginForItem.insert(item, pinfo);
00385                     item->setCheckable(!isPluginImmutable(pinfo));
00386                     item->setChecked(isEnabled);
00387                     q->connect(item, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool)));
00388                 }
00389                 break;
00390             }
00391         }
00392     }
00393     // now that the KCMs are in, check for empty groups and remove them again
00394     {
00395         const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(q->pageWidget()->model());
00396         const QHash<QString, KPageWidgetItem *>::ConstIterator end = pageItemForGroupId.constEnd();
00397         QHash<QString, KPageWidgetItem *>::ConstIterator it = pageItemForGroupId.constBegin();
00398         for (; it != end; ++it) {
00399             const QModelIndex index = model->index(it.value());
00400             KPluginInfo pinfo;
00401             foreach (const KPluginInfo &p, plugininfos) {
00402                 if (p.name()==it.key()) {
00403                     pinfo = p;
00404                     break;
00405                 }
00406             }
00407             bool allowEmpty = false;
00408             if (pinfo.isValid()) {
00409                 allowEmpty = pinfo.property("X-KDE-PluginInfo-AllowEmptySettings").toBool();
00410             }
00411 
00412             if (!index.child(0, 0).isValid()) {
00413                 // no children, and it's not allowed => remove this item
00414                 if (!allowEmpty) {
00415                     q->removePage(it.value());
00416                 } else {
00417                     connectItemCheckBox(it.value(), pinfo, pinfo.isPluginEnabled());
00418                 }
00419             }
00420         }
00421     }
00422 
00423     // TODO: Don't show the reset button until the issue with the
00424     // KPluginSelector::load() method is solved.
00425     // Problem:
00426     // KCMultiDialog::show() call KCModule::load() to reset all KCMs
00427     // (KPluginSelector::load() resets all plugin selections and all plugin
00428     // KCMs).
00429     // The reset button calls KCModule::load(), too but in this case we want the
00430     // KPluginSelector to only reset the current visible plugin KCM and not
00431     // touch the plugin selections.
00432     // I have no idea how to check that in KPluginSelector::load()...
00433     //q->showButton(KDialog::User1, true);
00434 
00435     QObject::connect(q, SIGNAL(okClicked()), q, SLOT(_k_syncConfiguration()));
00436     QObject::connect(q, SIGNAL(applyClicked()), q, SLOT(_k_syncConfiguration()));
00437     QObject::connect(q, SIGNAL(configCommitted(const QByteArray &)), q,
00438             SLOT(_k_reparseConfiguration(const QByteArray &)));
00439 }
00440 
00441 void DialogPrivate::connectItemCheckBox(KPageWidgetItem *item, const KPluginInfo &pinfo, bool isEnabled)
00442 {
00443     Q_Q(Dialog);
00444     QCheckBox *checkBox = checkBoxForItem.value(item);
00445     Q_ASSERT(checkBox);
00446     pluginForItem.insert(item, pinfo);
00447     item->setCheckable(!isPluginImmutable(pinfo));
00448     item->setChecked(isEnabled);
00449     checkBox->setVisible(!isPluginImmutable(pinfo));
00450     checkBox->setChecked(isEnabled);
00451     q->connect(item, SIGNAL(toggled(bool)), q, SLOT(_k_updateEnabledState(bool)));
00452     q->connect(item, SIGNAL(toggled(bool)), checkBox, SLOT(setChecked(bool)));
00453     q->connect(checkBox, SIGNAL(clicked(bool)), item, SLOT(setChecked(bool)));
00454 }
00455 
00456 void DialogPrivate::_k_syncConfiguration()
00457 {
00458     Q_Q(Dialog);
00459     const QHash<KPageWidgetItem *, KPluginInfo>::Iterator endIt = pluginForItem.end();
00460     QHash<KPageWidgetItem *, KPluginInfo>::Iterator it = pluginForItem.begin();
00461     for (; it != endIt; ++it) {
00462         KPageWidgetItem *item = it.key();
00463         KPluginInfo pinfo = it.value();
00464         pinfo.setPluginEnabled(item->isChecked());
00465         pinfo.save();
00466     }
00467     if (pluginStateDirty > 0) {
00468       emit q->pluginSelectionChanged();
00469       pluginStateDirty = 0;
00470     }
00471     Dispatcher::syncConfiguration();
00472 }
00473 
00474 void DialogPrivate::_k_reparseConfiguration(const QByteArray &a)
00475 {
00476     Dispatcher::reparseConfiguration(a);
00477 }
00478 
00479 /*
00480 void DialogPrivate::_k_configureTree()
00481 {
00482     kDebug( 700 ) ;
00483     QObject::connect(subdlg, SIGNAL(okClicked()), q, SLOT(_k_updateTreeList()));
00484     QObject::connect(subdlg, SIGNAL(applyClicked()), q, SLOT(_k_updateTreeList()));
00485     QObject::connect(subdlg, SIGNAL(okClicked()), q, SIGNAL(pluginSelectionChanged()));
00486     QObject::connect(subdlg, SIGNAL(applyClicked()), q, SIGNAL(pluginSelectionChanged()));
00487 }
00488 */
00489 
00490 void DialogPrivate::_k_clientChanged()
00491 {
00492     if (pluginStateDirty > 0) {
00493         Q_Q(Dialog);
00494         q->enableButton(KDialog::Apply, true);
00495     } else {
00496         KCMultiDialogPrivate::_k_clientChanged();
00497     }
00498 }
00499 
00500 void DialogPrivate::_k_updateEnabledState(bool enabled)
00501 {
00502     Q_Q(Dialog);
00503     KPageWidgetItem *item = qobject_cast<KPageWidgetItem *>(q->sender());
00504     if (!item) {
00505         kWarning(700) << "invalid sender";
00506         return;
00507     }
00508 
00509     // iterate over all child KPageWidgetItem objects and check whether they need to be enabled/disabled
00510     const KPageWidgetModel *model = qobject_cast<const KPageWidgetModel *>(q->pageWidget()->model());
00511     Q_ASSERT(model);
00512     QModelIndex index = model->index(item);
00513     if (!index.isValid()) {
00514         kWarning(700) << "could not find item in model";
00515         return;
00516     }
00517 
00518     const KPluginInfo &pinfo = pluginForItem.value(item);
00519     if (!pinfo.isValid()) {
00520         kWarning(700) << "could not find KPluginInfo in item";
00521         return;
00522     }
00523     if (pinfo.isPluginEnabled() != enabled) {
00524         ++pluginStateDirty;
00525     } else {
00526         --pluginStateDirty;
00527     }
00528     if (pluginStateDirty < 2) {
00529         _k_clientChanged();
00530     }
00531 
00532     //kDebug(700) ;
00533 
00534     QModelIndex firstborn = index.child(0, 0);
00535     if (firstborn.isValid()) {
00536         //kDebug(700) << "iterating over children";
00537         // change all children
00538         index = firstborn;
00539         QStack<QModelIndex> stack;
00540         while (index.isValid()) {
00541             //kDebug(700) << index;
00542             KPageWidgetItem *item = model->item(index);
00543             //kDebug(700) << "item->setEnabled(" << enabled << ')';
00544             item->setEnabled(enabled);
00545             firstborn = index.child(0, 0);
00546             if (firstborn.isValid()) {
00547                 stack.push(index);
00548                 index = firstborn;
00549             } else {
00550                 index = index.sibling(index.row() + 1, 0);
00551                 while (!index.isValid() && !stack.isEmpty()) {
00552                     index = stack.pop();
00553                     index = index.sibling(index.row() + 1, 0);
00554                 }
00555             }
00556         }
00557     }
00558 }
00559 
00560 } //namespace
00561 
00562 #include "dialog.moc"
00563 
00564 // vim: ts=4

KUtils

Skip menu "KUtils"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  •     Sodep
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.9-20090814
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal