summaryrefslogtreecommitdiffstats
path: root/kutils/ksettings
diff options
context:
space:
mode:
Diffstat (limited to 'kutils/ksettings')
-rw-r--r--kutils/ksettings/Makefile.am9
-rw-r--r--kutils/ksettings/README.dox276
-rw-r--r--kutils/ksettings/TODO13
-rw-r--r--kutils/ksettings/componentsdialog.cpp180
-rw-r--r--kutils/ksettings/componentsdialog.h88
-rw-r--r--kutils/ksettings/dialog.cpp642
-rw-r--r--kutils/ksettings/dialog.h224
-rw-r--r--kutils/ksettings/dispatcher.cpp157
-rw-r--r--kutils/ksettings/dispatcher.h133
-rw-r--r--kutils/ksettings/pluginpage.cpp88
-rw-r--r--kutils/ksettings/pluginpage.h120
11 files changed, 1930 insertions, 0 deletions
diff --git a/kutils/ksettings/Makefile.am b/kutils/ksettings/Makefile.am
new file mode 100644
index 000000000..39e7183b2
--- /dev/null
+++ b/kutils/ksettings/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES=-I$(srcdir)/.. $(all_includes)
+noinst_LTLIBRARIES = libksettings.la
+
+libksettings_la_SOURCES = dispatcher.cpp dialog.cpp pluginpage.cpp componentsdialog.cpp
+
+ksettingsincludedir = $(includedir)/ksettings
+ksettingsinclude_HEADERS = dispatcher.h dialog.h pluginpage.h componentsdialog.h
+
+METASOURCES = AUTO
diff --git a/kutils/ksettings/README.dox b/kutils/ksettings/README.dox
new file mode 100644
index 000000000..0660324c1
--- /dev/null
+++ b/kutils/ksettings/README.dox
@@ -0,0 +1,276 @@
+/**
+
+\namespace KSettings
+
+\short A collection of classes to create configuration dialogs that work over
+component boundaries
+
+<h2>How to use KSettings::Dialog in your application.</h2>
+
+<hr>
+<h3>1. Open the dialog from your app</h3>
+
+All you need to do is instanciate KSettings::Dialog and show() it. I recommend
+the following:
+
+create the 'Configure MyApp' StdAction like this:
+\code
+KStdAction::preferences( this, SLOT( showConfigDialog() ), actionCollection );
+\endcode
+
+and the slot looks like this:
+\code
+if( m_dlg == 0 )
+ m_dlg = new KSettings::Dialog( this );
+m_dlg->show();
+\endcode
+
+Of course you need to have the 'KSettings::Dialog * m_dlg' member var and
+initialize it to 0 in the ctor.
+
+If your application uses KParts that don't set 'X-KDE-ParentApp=&lt;the instance
+name of your application&gt;' then you need to use the second ctor of
+KSettings::Dialog:
+\code
+m_dlg = new KSettings::Dialog( QStringList::split( ';', "component1;component2" ) );
+\endcode
+
+The KSettings::Dialog object will be destructed automatically by the QObject
+mechanisms.
+
+
+<hr>
+<h3>2. Create pages for your dialog</h3>
+
+Every page is a KCM. This is what you need for creating a page:
+
+\code
+class MyAppConfig : public KCModule
+{
+ Q_OBJECT
+public:
+ MyAppConfig( QWidget *parent, const char *name = 0, const QStringList &args =
+ QStringList() );
+ ~MyAppConfig();
+
+ void load();
+ void save();
+ void defaults();
+}
+\endcode
+
+and in the cpp file:
+
+\code
+typedef KGenericFactory<MyAppConfig, QWidget> MyAppConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_myappconfig, MyAppConfigFactory(
+ "kcm_myappconfig" ) );
+
+MyAppConfig::MyAppConfig( QWidget *parent, const char *, const QStringList &args )
+ : KCModule( MyAppConfigFactory::instance(), parent, args )
+{
+ // create the pages GUI
+ load();
+}
+
+// implementations for the other methods
+\endcode
+
+For the KConfig object you can either use
+KGlobal::config() (I don't recommend it) or KSimpleConfig( "myapprc" ).
+I added a method to KSettings::Dispatcher that gives you the KConfig
+object for every registered instance name: \ref KSettings::Dispatcher::configForInstanceName
+
+
+<hr>
+<h3>3. The .desktop file for the page</h3>
+
+The .desktop file holds all the information for the dialog to find the page and
+insert it at the right place (with the right icon, name and comment).
+
+An example file:
+\verbatim
+[Desktop Entry]
+Encoding=UTF-8
+Icon=myapp
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=myappconfig
+X-KDE-FactoryName=MyAppConfigFactory
+X-KDE-ParentApp=myapp
+X-KDE-ParentComponents=myapp
+X-KDE-Weight=10
+
+Name=General
+Comment=General configuration of my app
+\endverbatim
+
+
+Some explanation for those keys:
+- You just keep 'Encoding', 'Type', 'ServiceTypes' and 'X-KDE-ModuleType' like
+ in the example. For very special needs you might add another ServiceType to
+ the list...
+- Icon is the icon that will be used in the listview/iconview for your page.
+- X-KDE-Library is the name of the library where the page is in. The library
+ always needs to be prefixed with kcm_ but you don't write the prefix in the
+ desktop file. For more docu on this look for the KCModule docu.
+- X-KDE-FactoryName is either the name of the Factory you used in the
+ KGenericFactory call or the suffix of the create_ function that you created.
+ Again for more info look for the KCModule docu.
+- X-KDE-ParentApp is the name of the application this config page belongs to. It
+ is used by the first two \ref KSettings::Dialog constructors. The Dialog will
+ use all modules that set X-KDE-ParentApp to
+ KGlobal::instance()->instanceName(). It
+ should be pretty easy to find out what name that is: look at the first
+ argument to the KAboutData ctor.
+- X-KDE-ParentComponents is a list of the components (plugin/KPart/whatever)
+ this config page belongs to. Normally there is only one component.
+ It is used for two things:
+ -# If you use KSettings::Dispatcher the dispatcher will notify all components
+ in this list after the save() method of your KCM has been called. The
+ components then can reload the configuration and apply the changes the user
+ did to the config.
+ -# If your component is used by another application (that is not =
+ X-KDE-ParentApp) then it may add the name of the component to the ctor of
+ KSettings::Dialog and the dialog will automatically include all config
+ pages that have the components name in their ParentComponents list.
+- X-KDE-Weight sets the order for the modules to be inserted into the dialog.
+ The higher the number (heavier) the lower the module will appear in the list.
+ (the default value is 100)
+- Name is the string that is shown in the listview/iconview right below the
+ icon.
+- Comment is the string that is shown on top of the config page for a short
+ description what you can do on this page.
+
+
+<hr>
+<h3>4. The .setdlg file for hierarchical (TreeList) page layouts</h3>
+
+If your config dialog should show a tree of pages in the config dialog you need
+to define that hierarchy with a .setdlg file.
+
+The file should be installed in apps/&lt;appname&gt;/&lt;appname&gt;.setdlg. If third party
+plugins need to merge in they will install their file to
+apps/&lt;appname&gt;/ksettingsdialog/&lt;pluginname&gt;.setdlg.
+
+A .setdlg file contains one or more blocks like the following:
+
+\verbatim
+[id]
+Name=
+Comment=
+Icon=
+Weight=
+Parent=
+\endverbatim
+
+- The group name (id) is the name you use in the .desktop file of the page:
+ If your page's .desktop file says "X-KDE-CfgDlgHierarchy=id" then it will be
+ inserted as a child of this entry.
+- \p Name: The name of the section. It will appear in the listview.
+- \p Comment: A description of what the modules in this section are. It will
+ appear in the place where the KCMs are placed when the user clicks on the item
+ in the listview.
+- \p Icon: An icon for the item.
+- \p Weight: Defines the position in the listview. See X-KDE-Weight above.
+- \p Parent: If this group should be a child of another group write the parent's
+ group id here.
+
+<hr>
+<h3>5. The Pluginselector</h3>
+
+There are two ways to use the KPluginSelector widget. One is to use the class
+directly and the second to use KSettings::PluginPage as baseclass for a config
+page that shows the KPluginSelector widget.
+
+I'll cover the second usage here and the calls to addPlugins are just the same
+for the first.
+
+To create a plugin page you need the following code:
+
+\code
+typedef KGenericFactory<MyAppPluginConfig, QWidget> MyAppPluginConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_myapppluginconfig, MyAppPluginConfigFactory( "kcm_myapppluginconfig" ) );
+
+MyAppPluginConfig( QWidget * parent, const char *, const QStringList & args )
+ : PluginPage( MyAppPluginConfigFactory::instance(), parent, args )
+{
+ pluginSelector()->addPlugins( ... );
+ pluginSelector()->addPlugins( ... );
+ .
+ .
+ .
+}
+\endcode
+
+pluginSelector() returns a pointer to the KPluginSelector widget of the page.
+There are three addPlugins methods available, two for adding KParts plugins and
+one for the rest.
+
+
+<hr>
+<h3>6. The .desktop files of plugin config pages</h3>
+
+this is the entry for the Makefile.am:
+
+\verbatim
+myappconfigpagedir = $(kde_servicesdir)/<appname>
+myappconfigpage_DATA = myappconfigpage.desktop
+\endverbatim
+
+
+And this is what the .desktop file looks like:
+
+\verbatim
+[Desktop Entry]
+Encoding=UTF-8
+Type=Service
+Icon=<iconname>
+ServiceTypes=KPluginInfo
+
+Name=MyPlugin
+Comment=My plugin is cool and does foo and bar.
+
+X-KDE-PluginInfo-Name=myplugin
+
+X-KDE-PluginInfo-Author=<your name>
+X-KDE-PluginInfo-Email=<your email>
+X-KDE-PluginInfo-Website=http://www.myplugin.org/
+X-KDE-PluginInfo-Category=CoolPlugins
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+X-KDE-PluginInfo-Depends=myotherplugin
+X-KDE-CfgDlgHierarchy=GroupID
+\endverbatim
+
+Explanation:
+mandatory entries:
+- leave \p Type and \p Encoding like in the example
+- \p Name
+- \p Comment
+- \p X-KDE-PluginInfo-Name is the "internal name" of the plugin.
+- You need to have \p KPluginInfo in \p ServiceTypes but of course you may have more
+ entries in there.
+
+optional entries:
+- \p Icon is the icon used for your plugin (it's shown in the pluginselector if you
+ set one).
+- \p X-KDE-PluginInfo-Author and \p X-KDE-PluginInfo-Email is some information about the author of the plugin.
+- \p X-KDE-PluginInfo-Website is the address for a webpage for this plugin.
+- \p X-KDE-PluginInfo-Category is used if your application has different categories of plugins.
+- \p X-KDE-PluginInfo-Version is the version of this plugin.
+- \p X-KDE-PluginInfo-License is the license of this plugin.
+- \p X-KDE-PluginInfo-EnabledByDefault tells the program whether the plugin
+ should be enabled on first startup or not.
+- \p X-KDE-PluginInfo-Depends can be used to tell the application that you need to have
+ myotherplugin enabled for your plugin to work.
+- \p X-KDE-CfgDlgHierarchy is used if you use a \p KSettings::Dialog::ConfigurableInline
+ KSettings::Dialog to put the plugin checkbox into the group with the GroupID
+ you set here.
+
+If you have questions contact Matthias Kretz <kretz@kde.org>.
+*/
+// vim: tw=80
diff --git a/kutils/ksettings/TODO b/kutils/ksettings/TODO
new file mode 100644
index 000000000..9950ddfd9
--- /dev/null
+++ b/kutils/ksettings/TODO
@@ -0,0 +1,13 @@
+- KPluginSelct.. listview should only show the name and comment, the rest is
+ shown in a tooltip (I don't know how to get the tooltip working in the
+ listview.)
+- Handle unsaved changes in KCMs that are hidden if the user deselects some
+ plugin KCMs (in KSettings::Dialog::Configurable mode). Ideas:
+ - don't allow changes to the plugin selection if there are unsaved changes in
+ the main dlg ("You have unsaved changes in the configuration dialog, please
+ save them first before changing the components selection.")
+ - automatically save changes of hidden modules when the dialog is deleted
+ - ask as soon as KCMultiDialog can detect that there are unsaved changes to
+ hidden KCMs (it might even be possible to show the KCMs for that)
+
+# vim: tw=80
diff --git a/kutils/ksettings/componentsdialog.cpp b/kutils/ksettings/componentsdialog.cpp
new file mode 100644
index 000000000..5093af985
--- /dev/null
+++ b/kutils/ksettings/componentsdialog.cpp
@@ -0,0 +1,180 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/componentsdialog.h"
+#include <klocale.h>
+#include <qlayout.h>
+#include <klistview.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <kplugininfo.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kseparator.h>
+
+namespace KSettings
+{
+
+class ComponentsDialog::ComponentsDialogPrivate
+{
+ public:
+ KListView * listview;
+ QFrame * infowidget;
+ QLabel * iconwidget;
+ QLabel * commentwidget;
+ QLabel * descriptionwidget;
+ QMap<QCheckListItem*, KPluginInfo*> plugininfomap;
+ QValueList<KPluginInfo*> plugininfolist;
+};
+
+ComponentsDialog::ComponentsDialog( QWidget * parent, const char * name )
+ : KDialogBase( parent, name, false, i18n( "Select Components" ) )
+, d( new ComponentsDialogPrivate )
+{
+ QWidget * page = new QWidget( this );
+ setMainWidget( page );
+ ( new QHBoxLayout( page, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->listview = new KListView( page );
+ d->listview->setMinimumSize( 200, 200 );
+ d->infowidget = new QFrame( page );
+ d->infowidget->setMinimumSize( 200, 200 );
+ ( new QVBoxLayout( d->infowidget, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->iconwidget = new QLabel( d->infowidget );
+ ( void )new KSeparator( d->infowidget );
+ d->commentwidget = new QLabel( d->infowidget );
+ d->commentwidget->setAlignment( Qt::WordBreak );
+ d->descriptionwidget = new QLabel( d->infowidget );
+ d->descriptionwidget->setAlignment( Qt::WordBreak );
+
+ d->listview->addColumn( QString::null );
+ d->listview->header()->hide();
+ d->listview->setRootIsDecorated( true );
+ d->listview->setSorting( -1 );
+ d->listview->setAcceptDrops( false );
+ d->listview->setSelectionModeExt( KListView::Single );
+ d->listview->setAllColumnsShowFocus( true );
+
+ connect( d->listview, SIGNAL( pressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( spacePressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( returnPressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( selectionChanged( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+}
+
+ComponentsDialog::~ComponentsDialog()
+{
+}
+
+void ComponentsDialog::addPluginInfo( KPluginInfo * info )
+{
+ d->plugininfolist.append( info );
+}
+
+void ComponentsDialog::setPluginInfos( const QMap<QString, KPluginInfo*> &
+ plugininfos )
+{
+ for( QMap<QString, KPluginInfo*>::ConstIterator it = plugininfos.begin();
+ it != plugininfos.end(); ++it )
+ {
+ d->plugininfolist.append( it.data() );
+ }
+}
+
+void ComponentsDialog::setPluginInfos( const QValueList<KPluginInfo *> &plugins )
+{
+ d->plugininfolist = plugins;
+}
+
+void ComponentsDialog::show()
+{
+ // clear the treelist
+ d->listview->clear();
+ d->plugininfomap.clear();
+
+ // construct the treelist
+ for( QValueList<KPluginInfo*>::ConstIterator it = d->plugininfolist.begin();
+ it != d->plugininfolist.end(); ++it )
+ {
+ ( *it )->load();
+ QCheckListItem * item = new QCheckListItem( d->listview, ( *it )->name(),
+ QCheckListItem::CheckBox );
+ if( ! ( *it )->icon().isEmpty() )
+ item->setPixmap( 0, SmallIcon( ( *it )->icon(), IconSize( KIcon::Small ) ) );
+ item->setOn( ( *it )->isPluginEnabled() );
+ d->plugininfomap[ item ] = ( *it );
+ }
+ KDialogBase::show();
+}
+
+void ComponentsDialog::executed( QListViewItem * item )
+{
+ kdDebug( 704 ) << k_funcinfo << endl;
+ if( item == 0 )
+ return;
+ if( item->rtti() != 1 ) // check for QCheckListItem
+ return;
+
+ QCheckListItem * citem = static_cast<QCheckListItem *>( item );
+ bool checked = citem->isOn();
+
+ kdDebug( 704 ) << "it's a " << ( checked ? "checked" : "unchecked" )
+ << " QCheckListItem" << endl;
+
+ KPluginInfo * info = d->plugininfomap[ citem ];
+ info->setPluginEnabled( checked );
+ //checkDependencies( info );
+ // show info about the component on the right
+ d->iconwidget->setPixmap( SmallIcon( info->icon(), KIcon::SizeLarge ) );
+ d->commentwidget->setText( info->comment() );
+ //d->descriptionwidget->setText( info->description() );
+}
+
+void ComponentsDialog::savePluginInfos()
+{
+ for( QValueList<KPluginInfo*>::ConstIterator it = d->plugininfolist.begin();
+ it != d->plugininfolist.end(); ++it )
+ {
+ if( ( *it )->config() )
+ {
+ ( *it )->save();
+ ( *it )->config()->sync();
+ }
+ }
+}
+
+void ComponentsDialog::slotOk()
+{
+ savePluginInfos();
+ KDialogBase::slotOk();
+}
+
+void ComponentsDialog::slotApply()
+{
+ savePluginInfos();
+ KDialogBase::slotApply();
+}
+
+} //namespace
+
+#include "componentsdialog.moc"
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/componentsdialog.h b/kutils/ksettings/componentsdialog.h
new file mode 100644
index 000000000..e5325f707
--- /dev/null
+++ b/kutils/ksettings/componentsdialog.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_COMPONENTSDIALOG_H
+#define KSETTINGS_COMPONENTSDIALOG_H
+
+#include <kdialogbase.h>
+
+class QString;
+class KPluginInfo;
+
+namespace KSettings
+{
+
+/**
+ @ingroup plugin
+ @ingroup settings
+ Dialog for selecting which plugins should be active for an application. Set
+ the list of available plugins with \ref setPluginInfos. The dialog will save the
+ configuration on clicking ok or apply to the applications config file. Connect
+ to the okClicked() and applyClicked() signals to be notified about
+ configuration changes.
+*/
+class KUTILS_EXPORT ComponentsDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ /**
+ Create Dialog.
+
+ @param parent parent widget
+ @param name name
+ */
+ ComponentsDialog( QWidget * parent = 0, const char * name = 0 );
+ ~ComponentsDialog();
+
+ /**
+ Add a plugin that the dialog offers for selection.
+ */
+ void addPluginInfo( KPluginInfo * );
+ /**
+ Set list of plugins the dialog offers for selection. (Overwrites a previous list)
+ */
+ void setPluginInfos( const QMap<QString, KPluginInfo*> & plugininfos );
+ /**
+ Set list of plugins the dialog offers for selection. (Overwrites a previous list)
+ */
+ void setPluginInfos( const QValueList<KPluginInfo *> &plugins );
+
+ /**
+ * reimplemented
+ */
+ void show();
+
+ protected slots:
+ void slotOk();
+ void slotApply();
+
+ private slots:
+ void executed( QListViewItem * );
+
+ private:
+ void savePluginInfos();
+
+ class ComponentsDialogPrivate;
+ ComponentsDialogPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_COMPONENTSDIALOG_H
diff --git a/kutils/ksettings/dialog.cpp b/kutils/ksettings/dialog.cpp
new file mode 100644
index 000000000..c98c68872
--- /dev/null
+++ b/kutils/ksettings/dialog.cpp
@@ -0,0 +1,642 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/dialog.h"
+
+
+#include <kcmultidialog.h>
+#include <klocale.h>
+#include <kservicegroup.h>
+#include <kdebug.h>
+#include <ktrader.h>
+#include <kplugininfo.h>
+#include "ksettings/dispatcher.h"
+#include "ksettings/componentsdialog.h"
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <qvbox.h>
+#include <qlabel.h>
+#include "kcmoduleinfo.h"
+
+namespace KSettings
+{
+
+struct GroupInfo
+{
+ QString id;
+ QString name;
+ QString comment;
+ QString icon;
+ int weight;
+ QString parentid;
+ QWidget * page;
+};
+
+// The TreeList can get really complicated. That's why a tree data structure
+// is necessary to make it suck less
+class PageNode
+{
+ private:
+ typedef QValueList<PageNode*> List;
+ enum Type { KCM, Group, Root };
+ union Value
+ {
+ KCModuleInfo * kcm;
+ GroupInfo * group;
+ };
+ Type m_type;
+ Value m_value;
+
+ Dialog * m_dialog;
+ List m_children;
+ PageNode * m_parent;
+ bool m_visible;
+ bool m_dirty;
+
+ protected:
+ PageNode( KCModuleInfo * info, PageNode * parent )
+ : m_type( KCM )
+ , m_parent( parent )
+ , m_visible( true )
+ , m_dirty( true )
+ {
+ m_value.kcm = info;
+ m_dialog = parent->m_dialog;
+ }
+
+ PageNode( GroupInfo & group, PageNode * parent )
+ : m_type( Group )
+ , m_parent( parent )
+ , m_visible( true )
+ , m_dirty( true )
+ {
+ m_value.group = new GroupInfo( group );
+ m_value.group->page = 0;
+ m_dialog = parent->m_dialog;
+ }
+
+ void bubbleSort( List::Iterator begin, List::Iterator end )
+ {
+ --end;
+ bool finished;
+ List::Iterator lastswapped = begin;
+ List::Iterator i;
+ List::Iterator j;
+ while( begin != end )
+ {
+ finished = true;
+ i = j = end;
+ do {
+ --j;
+ if( **i < **j )
+ {
+ finished = false;
+ qSwap( *i, *j );
+ lastswapped = j;
+ }
+ --i;
+ } while( j != begin );
+ if( finished )
+ return;
+ ++lastswapped;
+ begin = lastswapped;
+ }
+ }
+
+ public:
+ PageNode( Dialog * dialog )
+ : m_type( Root )
+ , m_dialog( dialog )
+ , m_parent( 0 )
+ , m_visible( true )
+ , m_dirty( true )
+ {}
+
+ ~PageNode()
+ {
+ if( KCM == m_type )
+ delete m_value.kcm;
+ else if( Group == m_type )
+ delete m_value.group;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ delete ( *it );
+ }
+
+ int weight() const
+ {
+ int w = ( KCM == m_type ) ? m_value.kcm->weight()
+ : m_value.group->weight;
+ kdDebug( 700 ) << k_funcinfo << name() << " " << w << endl;
+ return w;
+ }
+
+ bool operator<( const PageNode & rhs ) const
+ {
+ return weight() < rhs.weight();
+ }
+
+ bool isVisible()
+ {
+ if( m_dirty )
+ {
+ if( KCM == m_type )
+ m_visible = m_dialog->isPluginForKCMEnabled( m_value.kcm );
+ else
+ {
+ m_visible = false;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end;
+ ++it )
+ if( ( *it )->isVisible() )
+ {
+ m_visible = true;
+ break;
+ }
+ }
+ m_dirty = false;
+ }
+ kdDebug( 700 ) << k_funcinfo << "returns " << m_visible << endl;
+ return m_visible;
+ }
+
+ void makeDirty()
+ {
+ m_dirty = true;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->makeDirty();
+ }
+
+ QString name() const
+ {
+ if( Root == m_type )
+ return QString::fromAscii( "root node" );
+ return ( KCM == m_type ) ? m_value.kcm->moduleName()
+ : m_value.group->name;
+ }
+
+ QStringList parentNames() const
+ {
+ QStringList ret;
+ PageNode * node = m_parent;
+ while( node && node->m_type != Root )
+ {
+ ret.prepend( node->name() );
+ node = node->m_parent;
+ }
+ return ret;
+ }
+
+ void addToDialog( KCMultiDialog * dlg )
+ {
+ kdDebug( 700 ) << k_funcinfo << "for " << name() << endl;
+ if( ! isVisible() )
+ return;
+
+ if( KCM == m_type )
+ {
+ dlg->addModule( *m_value.kcm, parentNames() );
+ return;
+ }
+ if( Group == m_type && 0 == m_value.group->page )
+ {
+ QPixmap icon;
+ if( ! m_value.group->icon.isNull() )
+ icon = SmallIcon( m_value.group->icon,
+ IconSize( KIcon::Small ) );
+ QVBox * page = dlg->addVBoxPage( m_value.group->name,
+ QString::null, icon );
+ QLabel * comment = new QLabel( m_value.group->comment, page );
+ comment->setTextFormat( Qt::RichText );
+ m_value.group->page = page;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->addToDialog( dlg );
+ }
+
+ void removeFromDialog( KCMultiDialog * dlg )
+ {
+ kdDebug( 700 ) << k_funcinfo << "for " << name() << endl;
+ if( KCM == m_type )
+ return;
+ if( Root == m_type )
+ dlg->removeAllModules();
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->removeFromDialog( dlg );
+ if( Group == m_type )
+ {
+ delete m_value.group->page;
+ m_value.group->page = 0;
+ }
+ }
+
+ void sort()
+ {
+ kdDebug( 700 ) << k_funcinfo << name() << endl;
+ List::Iterator begin = m_children.begin();
+ List::Iterator end = m_children.end();
+ bubbleSort( begin, end );
+ for( List::Iterator it = begin ; it != end; ++it )
+ ( *it )->sort();
+ }
+
+ bool insert( GroupInfo & group )
+ {
+ if( group.parentid.isNull() )
+ {
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ else
+ kdFatal( 700 ) << "wrong PageNode insertion"
+ << kdBacktrace() << endl;
+ }
+ if( Group == m_type && group.parentid == m_value.group->id )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->insert( group ) )
+ return true;
+ // no parent with the right parentid
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ return false;
+ }
+
+ bool insert( KCModuleInfo * info, const QString & parentid )
+ {
+ if( parentid.isNull() )
+ {
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ else
+ kdFatal( 700 ) << "wrong PageNode insertion"
+ << kdBacktrace() << endl;
+ }
+ if( Group == m_type && parentid == m_value.group->id )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->insert( info, parentid ) )
+ return true;
+ // no parent with the right parentid
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ return false;
+ }
+
+ bool needTree()
+ {
+ List::ConstIterator end = m_children.end();
+ for( List::ConstIterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->m_children.count() > 0 )
+ return true;
+ return false;
+ }
+
+ bool singleChild()
+ {
+ return ( m_children.count() == 1 );
+ }
+};
+
+class Dialog::DialogPrivate
+{
+ public:
+ DialogPrivate( Dialog * parent )
+ : dlg( 0 )
+ , pagetree( parent )
+ {
+ }
+
+ bool staticlistview;
+ KCMultiDialog * dlg;
+ PageNode pagetree;
+ QWidget * parentwidget;
+ QStringList registeredComponents;
+ QValueList<KService::Ptr> services;
+ QMap<QString, KPluginInfo*> plugininfomap;
+};
+
+Dialog::Dialog( QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = true;
+ d->services = instanceServices();
+}
+
+Dialog::Dialog( ContentInListView content,
+ QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = ( content == Static );
+ d->services = instanceServices();
+}
+
+Dialog::Dialog( const QStringList & components,
+ QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = true;
+ d->services = instanceServices() + parentComponentsServices( components );
+}
+
+Dialog::Dialog( const QStringList & components,
+ ContentInListView content, QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = ( content == Static );
+ d->services = instanceServices() + parentComponentsServices( components );
+}
+
+Dialog::~Dialog()
+{
+ delete d;
+}
+
+void Dialog::addPluginInfos( const QValueList<KPluginInfo*> & plugininfos )
+{
+ for( QValueList<KPluginInfo*>::ConstIterator it = plugininfos.begin();
+ it != plugininfos.end(); ++it )
+ {
+ d->registeredComponents.append( ( *it )->pluginName() );
+ d->services += ( *it )->kcmServices();
+ d->plugininfomap[ ( *it )->pluginName() ] = *it;
+ }
+}
+
+void Dialog::show()
+{
+ if( 0 == d->dlg )
+ createDialogFromServices();
+ Dispatcher::self()->syncConfiguration();
+ return d->dlg->show();
+}
+
+KCMultiDialog * Dialog::dialog()
+{
+ if( 0 == d->dlg )
+ createDialogFromServices();
+ return d->dlg;
+}
+
+QValueList<KService::Ptr> Dialog::instanceServices() const
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+ QString instanceName = KGlobal::instance()->instanceName();
+ d->registeredComponents.append( instanceName );
+ kdDebug( 700 ) << "calling KServiceGroup::childGroup( " << instanceName
+ << " )" << endl;
+ KServiceGroup::Ptr service = KServiceGroup::childGroup( instanceName );
+
+ QValueList<KService::Ptr> ret;
+
+ if( service && service->isValid() )
+ {
+ kdDebug( 700 ) << "call was successfull" << endl;
+ KServiceGroup::List list = service->entries();
+ for( KServiceGroup::List::ConstIterator it = list.begin();
+ it != list.end(); ++it )
+ {
+ KSycocaEntry * p = *it;
+ if( p->isType( KST_KService ) )
+ {
+ kdDebug( 700 ) << "found service" << endl;
+ ret << static_cast<KService *>( p );
+ }
+ else
+ kdWarning( 700 ) << "KServiceGroup::childGroup returned"
+ " something else than a KService (kinda)" << endl;
+ }
+ }
+
+ return ret;
+}
+
+QValueList<KService::Ptr> Dialog::parentComponentsServices(
+ const QStringList & kcdparents ) const
+{
+ d->registeredComponents += kcdparents;
+ QString constraint = kcdparents.join(
+ "' in [X-KDE-ParentComponents]) or ('" );
+ constraint = "('" + constraint + "' in [X-KDE-ParentComponents])";
+
+ kdDebug( 700 ) << "constraint = " << constraint << endl;
+ return KTrader::self()->query( "KCModule", constraint );
+}
+
+bool Dialog::isPluginForKCMEnabled( KCModuleInfo * moduleinfo ) const
+{
+ // if the user of this class requested to hide disabled modules
+ // we check whether it should be enabled or not
+ bool enabled = true;
+ kdDebug( 700 ) << "check whether the " << moduleinfo->moduleName()
+ << " KCM should be shown" << endl;
+ // for all parent components
+ QStringList parentComponents = moduleinfo->service()->property(
+ "X-KDE-ParentComponents" ).toStringList();
+ for( QStringList::ConstIterator pcit = parentComponents.begin();
+ pcit != parentComponents.end(); ++pcit )
+ {
+ // if the parentComponent is not registered ignore it
+ if( d->registeredComponents.find( *pcit ) ==
+ d->registeredComponents.end() )
+ continue;
+
+ // we check if the parent component is a plugin
+ if( ! d->plugininfomap.contains( *pcit ) )
+ {
+ // if not the KCModule must be enabled
+ enabled = true;
+ // we're done for this KCModuleInfo
+ break;
+ }
+ // if it is a plugin we check whether the plugin is enabled
+ KPluginInfo * pinfo = d->plugininfomap[ *pcit ];
+ pinfo->load();
+ enabled = pinfo->isPluginEnabled();
+ kdDebug( 700 ) << "parent " << *pcit << " is "
+ << ( enabled ? "enabled" : "disabled" ) << endl;
+ // if it is enabled we're done for this KCModuleInfo
+ if( enabled )
+ break;
+ }
+ return enabled;
+}
+
+void Dialog::parseGroupFile( const QString & filename )
+{
+ KSimpleConfig file( filename );
+ QStringList groups = file.groupList();
+ for( QStringList::ConstIterator it = groups.begin(); it != groups.end();
+ ++it )
+ {
+ GroupInfo group;
+ QString id = *it;
+ file.setGroup( id.utf8() );
+ group.id = id;
+ group.name = file.readEntry( "Name" );
+ group.comment = file.readEntry( "Comment" );
+ group.weight = file.readNumEntry( "Weight", 100 );
+ group.parentid = file.readEntry( "Parent" );
+ group.icon = file.readEntry( "Icon" );
+ d->pagetree.insert( group );
+ }
+}
+
+void Dialog::createDialogFromServices()
+{
+ // read .setdlg files
+ QString setdlgpath = locate( "appdata",
+ KGlobal::instance()->instanceName() + ".setdlg" );
+ QStringList setdlgaddon = KGlobal::dirs()->findAllResources( "appdata",
+ "ksettingsdialog/*.setdlg" );
+ if( ! setdlgpath.isNull() )
+ parseGroupFile( setdlgpath );
+ if( setdlgaddon.size() > 0 )
+ for( QStringList::ConstIterator it = setdlgaddon.begin();
+ it != setdlgaddon.end(); ++it )
+ parseGroupFile( *it );
+
+ // now we process the KCModule services
+ for( QValueList<KService::Ptr>::ConstIterator it = d->services.begin();
+ it != d->services.end(); ++it )
+ {
+ // we create the KCModuleInfo
+ KCModuleInfo * info = new KCModuleInfo( *it );
+ QString parentid;
+ QVariant tmp = info->service()->property( "X-KDE-CfgDlgHierarchy",
+ QVariant::String );
+ if( tmp.isValid() )
+ parentid = tmp.toString();
+ d->pagetree.insert( info, parentid );
+ }
+
+ // At this point d->pagetree holds a nice structure of the pages we want
+ // to show. It's not going to change anymore so we can sort it now.
+ d->pagetree.sort();
+
+ int dialogface = KJanusWidget::IconList;
+ if( d->pagetree.needTree() )
+ dialogface = KJanusWidget::TreeList;
+ else if( d->pagetree.singleChild() )
+ dialogface = KJanusWidget::Plain;
+
+ kdDebug( 700 ) << "creating KCMultiDialog" << endl;
+ d->dlg = new KCMultiDialog( dialogface, i18n( "Configure" ),
+ d->parentwidget );
+
+ if( dialogface == KJanusWidget::TreeList )
+ d->dlg->setShowIconsInTreeList( true );
+
+ // TODO: Don't show the reset button until the issue with the
+ // KPluginSelector::load() method is solved.
+ // Problem:
+ // KCMultiDialog::show() call KCModule::load() to reset all KCMs
+ // (KPluginSelector::load() resets all plugin selections and all plugin
+ // KCMs).
+ // The reset button calls KCModule::load(), too but in this case we want the
+ // KPluginSelector to only reset the current visible plugin KCM and not
+ // touch the plugin selections.
+ // I have no idea how to check that in KPluginSelector::load()...
+ //d->dlg->showButton( KDialogBase::User1, true );
+
+ if( ! d->staticlistview )
+ d->dlg->addButtonBelowList( i18n( "Select Components..." ), this,
+ SLOT( configureTree() ) );
+
+ connect( d->dlg, SIGNAL( okClicked() ), Dispatcher::self(),
+ SLOT( syncConfiguration() ) );
+ connect( d->dlg, SIGNAL( applyClicked() ), Dispatcher::self(),
+ SLOT( syncConfiguration() ) );
+ connect( d->dlg, SIGNAL( configCommitted( const QCString & ) ),
+ Dispatcher::self(), SLOT( reparseConfiguration( const QCString & ) ) );
+
+ d->pagetree.addToDialog( d->dlg );
+
+ if( dialogface == KJanusWidget::TreeList )
+ d->dlg->unfoldTreeList();
+}
+
+void Dialog::configureTree()
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+ ComponentsDialog * subdlg = new ComponentsDialog( d->dlg );
+ subdlg->setPluginInfos( d->plugininfomap );
+ subdlg->show();
+ connect( subdlg, SIGNAL( okClicked() ), this, SLOT( updateTreeList() ) );
+ connect( subdlg, SIGNAL( applyClicked() ), this, SLOT( updateTreeList() ) );
+ connect( subdlg, SIGNAL( okClicked() ), this,
+ SIGNAL( pluginSelectionChanged() ) );
+ connect( subdlg, SIGNAL( applyClicked() ), this,
+ SIGNAL( pluginSelectionChanged() ) );
+ connect( subdlg, SIGNAL( finished() ), subdlg, SLOT( delayedDestruct() ) );
+}
+
+void Dialog::updateTreeList()
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+
+ d->pagetree.makeDirty();
+
+ // remove all pages from the dialog and then add them again. This is needed
+ // because KDialogBase/KJanusWidget can only append to the end of the list
+ // and we need to have a predefined order.
+
+ d->pagetree.removeFromDialog( d->dlg );
+ d->pagetree.addToDialog( d->dlg );
+
+ if( d->pagetree.needTree() )
+ d->dlg->unfoldTreeList( true );
+}
+
+} //namespace
+
+#include "dialog.moc"
+
+// vim: sw=4 ts=4 noet
diff --git a/kutils/ksettings/dialog.h b/kutils/ksettings/dialog.h
new file mode 100644
index 000000000..84f4619be
--- /dev/null
+++ b/kutils/ksettings/dialog.h
@@ -0,0 +1,224 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_DIALOG_H
+#define KSETTINGS_DIALOG_H
+
+#include <qobject.h>
+#include <kservice.h>
+
+template<class T> class QValueList;
+class KPluginInfo;
+class KCMultiDialog;
+class KCModuleInfo;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup main
+ * @ingroup settings
+ * @short Generic configuration dialog that even works over component boundaries
+ *
+ * For more information see \ref KSettings.
+ *
+ * This class aims to standardize the use of configuration dialogs in KDE
+ * applications. Especially when using KParts and/or Plugins you face problems
+ * creating a consistent config dialog.
+ *
+ * To show a configuration dialog you only have to call the show method and be
+ * done with it. A code example:
+ *
+ * You initialize \p m_cfgdlg with
+ * \code
+ * m_cfgdlg = new Dialog( Dialog::Static, this );
+ * \endcode
+ * If you use a KPart that was not especially designed for your app you can use
+ * the second constructor:
+ * \code
+ * QStringList kpartslist;
+ * for( all my kparts )
+ * kpartslist += m_mypart->instance().instanceName();
+ * m_cfgdlg = new Dialog( kpartslist, this );
+ * \endcode
+ * and the action for the config dialog is connected to the show slot:
+ * \code
+ * KStdAction::preferences( m_cfgdlg, SLOT( show() ), actionCollection() );
+ * \endcode
+ *
+ * If you need to be informed when the config was changed and applied in the
+ * dialog you might want to take a look at Dispatcher.
+ *
+ * For more information see \ref KSettings.
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT Dialog : public QObject
+{
+ friend class PageNode;
+ Q_OBJECT
+ public:
+ /**
+ * Tells the dialog whether the entries in the listview are all static
+ * or whether it should add a Configure... button to select which parts
+ * of the optional functionality should be active or not.
+ */
+ enum ContentInListView
+ {
+ /**
+ * Static listview, while running no entries are added or deleted
+ */
+ Static,
+ /**
+ * Configurable listview. The user can select what functionality he
+ * wants.
+ */
+ Configurable
+ };
+
+ /**
+ * Construct a new Preferences Dialog for the application. It uses all
+ * KCMs with X-KDE-ParentApp set to KGlobal::instance()->instanceName().
+ *
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( QWidget * parent = 0, const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog for the application. It uses all
+ * KCMs with X-KDE-ParentApp set to KGlobal::instance()->instanceName().
+ *
+ * @param content Select whether you want a static or configurable
+ * config dialog.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( ContentInListView content = Static, QWidget * parent = 0,
+ const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog with the pages for the selected
+ * instance names. For example if you want to have the configuration
+ * pages for the kviewviewer KPart you would pass a
+ * QStringList consisting of only the name of the part "kviewviewer".
+ *
+ * @param components A list of the names of the components that your
+ * config dialog should merge the config pages in.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( const QStringList & components, QWidget * parent = 0,
+ const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog with the pages for the selected
+ * instance names. For example if you want to have the configuration
+ * pages for the kviewviewer KPart you would pass a
+ * QStringList consisting of only the name of the part "kviewviewer".
+ *
+ * @param components A list of the names of the components that your
+ * config dialog should merge the config pages in.
+ * @param content Select whether you want a static or configurable
+ * config dialog.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( const QStringList & components, ContentInListView
+ content, QWidget * parent = 0, const char * name = 0 );
+
+ ~Dialog();
+
+ /**
+ * If you use a Configurable dialog you need to pass KPluginInfo
+ * objects that the dialog should configure.
+ */
+ void addPluginInfos( const QValueList<KPluginInfo*> & plugininfos );
+
+ KCMultiDialog * dialog();
+
+ public slots:
+ /**
+ * Show the config dialog. The slot immediatly returns since the dialog
+ * is non-modal.
+ */
+ void show();
+
+ signals:
+ /**
+ * If you use the dialog in Configurable mode and want to be notified
+ * when the user changes the plugin selections use this signal. It's
+ * emitted if the selection has changed and the user pressed Apply or
+ * Ok. In the slot you would then load and unload the plugins as
+ * requested.
+ */
+ void pluginSelectionChanged();
+
+ protected slots:
+ void configureTree();
+ void updateTreeList();
+
+ private:
+ /**
+ * @internal
+ * Check whether the plugin associated with this KCM is enabled.
+ */
+ bool isPluginForKCMEnabled( KCModuleInfo * ) const;
+
+ QValueList<KService::Ptr> instanceServices() const;
+ QValueList<KService::Ptr> parentComponentsServices(
+ const QStringList & ) const;
+ /**
+ * @internal
+ * Read the .setdlg file and add it to the groupmap
+ */
+ void parseGroupFile( const QString & );
+
+ /**
+ * @internal
+ * If this module is put into a TreeList hierarchy this will return a
+ * list of the names of the parent modules.
+ */
+ QStringList parentModuleNames( KCModuleInfo * );
+
+ /**
+ * @internal
+ * This method is called only once. The KCMultiDialog is not created
+ * until it's really needed. So if some method needs to access d->dlg it
+ * checks for 0 and if it's not created this method will do it.
+ */
+ void createDialogFromServices();
+
+ class DialogPrivate;
+ DialogPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_DIALOG_H
diff --git a/kutils/ksettings/dispatcher.cpp b/kutils/ksettings/dispatcher.cpp
new file mode 100644
index 000000000..4e54538d8
--- /dev/null
+++ b/kutils/ksettings/dispatcher.cpp
@@ -0,0 +1,157 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/dispatcher.h"
+
+#include <qsignal.h>
+
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <assert.h>
+
+namespace KSettings
+{
+
+//class Dispatcher::DispatcherPrivate
+//{
+//};
+
+static KStaticDeleter<Dispatcher> ksd_kpd;
+
+Dispatcher * Dispatcher::m_self = 0;
+
+Dispatcher * Dispatcher::self()
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ if( m_self == 0 )
+ ksd_kpd.setObject( m_self, new Dispatcher() );
+ return m_self;
+}
+
+Dispatcher::Dispatcher( QObject * parent, const char * name )
+ : QObject( parent, name )
+ //, d( 0 )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+}
+
+Dispatcher::~Dispatcher()
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ //delete d;
+}
+
+void Dispatcher::registerInstance( KInstance * instance, QObject * recv, const char * slot )
+{
+ assert( instance != 0 );
+ // keep the KInstance around and call
+ // instance->config()->reparseConfiguration when the app should reparse
+ QCString instanceName = instance->instanceName();
+ kdDebug( 701 ) << k_funcinfo << instanceName << endl;
+ m_instanceName[ recv ] = instanceName;
+ QSignal * sig;
+ if( m_instanceInfo.contains( instanceName ) )
+ {
+ sig = m_instanceInfo[ instanceName ].signal;
+ }
+ else
+ {
+ sig = new QSignal( this, "signal dispatcher" );
+ m_instanceInfo[ instanceName ].signal = sig;
+ m_instanceInfo[ instanceName ].instance = instance;
+ }
+ sig->connect( recv, slot );
+
+ ++m_instanceInfo[ instanceName ].count;
+ connect( recv, SIGNAL( destroyed( QObject * ) ), this, SLOT( unregisterInstance( QObject * ) ) );
+}
+
+KConfig * Dispatcher::configForInstanceName( const QCString & instanceName )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ if( m_instanceInfo.contains( instanceName ) )
+ {
+ KInstance * inst = m_instanceInfo[ instanceName ].instance;
+ if( inst )
+ return inst->config();
+ }
+ //if( fallback )
+ //return new KSimpleConfig( instanceName );
+ return 0;
+}
+
+QStrList Dispatcher::instanceNames() const
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ QStrList names;
+ for( QMap<QCString, InstanceInfo>::ConstIterator it = m_instanceInfo.begin(); it != m_instanceInfo.end(); ++it )
+ if( ( *it ).count > 0 )
+ names.append( it.key() );
+ return names;
+}
+
+void Dispatcher::reparseConfiguration( const QCString & instanceName )
+{
+ kdDebug( 701 ) << k_funcinfo << instanceName << endl;
+ // check if the instanceName is valid:
+ if( ! m_instanceInfo.contains( instanceName ) )
+ return;
+ // first we reparse the config of the instance so that the KConfig object
+ // will be up to date
+ m_instanceInfo[ instanceName ].instance->config()->reparseConfiguration();
+ QSignal * sig = m_instanceInfo[ instanceName ].signal;
+ if( sig )
+ {
+ kdDebug( 701 ) << "emit signal to instance" << endl;
+ sig->activate();
+ }
+}
+
+void Dispatcher::syncConfiguration()
+{
+ for( QMap<QCString, InstanceInfo>::ConstIterator it = m_instanceInfo.begin(); it != m_instanceInfo.end(); ++it )
+ {
+ ( *it ).instance->config()->sync();
+ }
+}
+
+void Dispatcher::unregisterInstance( QObject * obj )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ QCString name = m_instanceName[ obj ];
+ m_instanceName.remove( obj ); //obj will be destroyed when we return, so we better remove this entry
+ --m_instanceInfo[ name ].count;
+ if( m_instanceInfo[ name ].count == 0 )
+ {
+ delete m_instanceInfo[ name ].signal;
+ m_instanceInfo.remove( name );
+ }
+}
+
+//X KInstance * Dispatcher::instanceForName( const QCString & instanceName )
+//X {
+//X return m_instanceInfo[ instanceName ].instance;
+//X }
+
+} //namespace
+
+#include "dispatcher.moc"
+
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/dispatcher.h b/kutils/ksettings/dispatcher.h
new file mode 100644
index 000000000..5f6270bb5
--- /dev/null
+++ b/kutils/ksettings/dispatcher.h
@@ -0,0 +1,133 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_DISPATCHER_H
+#define KSETTINGS_DISPATCHER_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <kdelibs_export.h>
+
+class QCString;
+class QSignal;
+class QStrList;
+template<class T> class KStaticDeleter;
+class KInstance;
+class KConfig;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup settings
+ * @short Dispatch change notifications from the KCMs to the program.
+ *
+ * Since your program does not have direct control over the KCMs that get loaded
+ * into the KConfigureDialog you need a way to get notified. This is what you
+ * do:
+ * \code
+ * Dispatcher::self()->registerInstance( instance(), this, SLOT( loadSettings() ) );
+ * \endcode
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT Dispatcher : public QObject
+{
+ friend class KStaticDeleter<Dispatcher>;
+
+ Q_OBJECT
+ public:
+ /**
+ * Get a reference the the Dispatcher object.
+ */
+ static Dispatcher * self();
+
+ /**
+ * Register a slot to be called when the configuration for the instance
+ * has changed. @p instance is the KInstance object
+ * that is passed to KGenericFactory (if it is used). You can query
+ * it with KGenericFactory<YourClassName>::instance().
+ * instance->instanceName() is also the same name that is put into the
+ * .desktop file of the KCMs for the X-KDE-ParentComponents.
+ *
+ * @param instance The KInstance object
+ * @param recv The object that should receive the signal
+ * @param slot The slot to be called: SLOT( slotName() )
+ */
+ void registerInstance( KInstance * instance, QObject * recv, const char * slot );
+
+ /**
+ * @return the KConfig object that belongs to the instanceName
+ */
+ KConfig * configForInstanceName( const QCString & instanceName );
+
+ /**
+ * @return a list of all the instance names that are currently
+ * registered
+ */
+ QStrList instanceNames() const;
+
+//X /**
+//X * @return The KInstance object belonging to the instance name you pass
+//X * (only works for registered instances of course).
+//X */
+//X KInstance * instanceForName( const QCString & instanceName );
+
+ public slots:
+ /**
+ * Call this slot when the configuration belonging to the associated
+ * instance name has changed. The registered slot will be called.
+ *
+ * @param instanceName The value of X-KDE-ParentComponents.
+ */
+ void reparseConfiguration( const QCString & instanceName );
+
+ /**
+ * When this slot is called the KConfig objects of all the registered
+ * instances are sync()ed. This is usefull when some other KConfig
+ * objects will read/write from/to the same config file, so that you
+ * can first write out the current state of the KConfig objects.
+ */
+ void syncConfiguration();
+
+ private slots:
+ void unregisterInstance( QObject * );
+
+ private:
+ Dispatcher( QObject * parent = 0, const char * name = 0 );
+ ~Dispatcher();
+ static Dispatcher * m_self;
+
+ struct InstanceInfo {
+ KInstance * instance;
+ QSignal * signal;
+ int count;
+ };
+ QMap<QCString, InstanceInfo> m_instanceInfo;
+ QMap<QObject *, QCString> m_instanceName;
+
+ class DispatcherPrivate;
+ DispatcherPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_DISPATCHER_H
diff --git a/kutils/ksettings/pluginpage.cpp b/kutils/ksettings/pluginpage.cpp
new file mode 100644
index 000000000..2456ebf86
--- /dev/null
+++ b/kutils/ksettings/pluginpage.cpp
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/pluginpage.h"
+#include "kpluginselector.h"
+#include <qlayout.h>
+#include <kdialog.h>
+#include "ksettings/dispatcher.h"
+
+namespace KSettings
+{
+
+class PluginPage::PluginPagePrivate
+{
+ public:
+ PluginPagePrivate()
+ : selwid( 0 )
+ {
+ }
+
+ KPluginSelector * selwid;
+};
+
+ PluginPage::PluginPage( QWidget * parent, const char * name, const QStringList & args )
+ : KCModule( parent, name, args )
+ , d( new PluginPagePrivate )
+{
+ ( new QVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->selwid = new KPluginSelector( this );
+ connect( d->selwid, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
+}
+
+ PluginPage::PluginPage( KInstance * instance, QWidget * parent, const QStringList & args )
+ : KCModule( instance, parent, args )
+ , d( new PluginPagePrivate )
+{
+ ( new QVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->selwid = new KPluginSelector( this );
+ connect( d->selwid, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
+ connect( d->selwid, SIGNAL( configCommitted( const QCString & ) ),
+ Dispatcher::self(), SLOT( reparseConfiguration( const QCString & ) ) );
+}
+
+PluginPage::~PluginPage()
+{
+ delete d;
+}
+
+KPluginSelector * PluginPage::pluginSelector()
+{
+ return d->selwid;
+}
+
+void PluginPage::load()
+{
+ d->selwid->load();
+}
+
+void PluginPage::save()
+{
+ d->selwid->save();
+}
+
+void PluginPage::defaults()
+{
+ d->selwid->defaults();
+}
+
+} //namespace
+
+#include "pluginpage.moc"
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/pluginpage.h b/kutils/ksettings/pluginpage.h
new file mode 100644
index 000000000..fc6120f78
--- /dev/null
+++ b/kutils/ksettings/pluginpage.h
@@ -0,0 +1,120 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_PLUGINPAGE_H
+#define KSETTINGS_PLUGINPAGE_H
+
+#include <kcmodule.h>
+#include <kdelibs_export.h>
+
+class KPluginSelector;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup settings
+ * @ingroup plugin
+ * @short Convenience KCModule for creating a plugins config page.
+ *
+ * This class makes it very easy to create a plugins configuration page to your
+ * program. All you need to do is create a class that is derived from
+ * PluginPage and add the appropriate plugin infos to the KPluginSelector.
+ * This is done using the pluginSelector() method:
+ * \code
+ * typedef KGenericFactory<MyAppPluginConfig, QWidget> MyAppPluginConfigFactory;
+ * K_EXPORT_COMPONENT_FACTORY( kcm_myapppluginconfig, MyAppPluginConfigFactory( "kcm_myapppluginconfig" ) );
+ *
+ * MyAppPluginConfig( QWidget * parent, const char *, const QStringList & args )
+ * : PluginPage( MyAppPluginConfigFactory::instance(), parent, args )
+ * {
+ * pluginSelector()->addPlugins( KGlobal::instance()->instanceName(), i18n( "General Plugins" ), "General" );
+ * pluginSelector()->addPlugins( KGlobal::instance()->instanceName(), i18n( "Effects" ), "Effects" );
+ * }
+ * \endcode
+ *
+ * All that remains to be done is to create the appropriate .desktop file
+ * \verbatim
+ [Desktop Entry]
+ Encoding=UTF-8
+ Icon=plugin
+ Type=Service
+ ServiceTypes=KCModule
+
+ X-KDE-ModuleType=Library
+ X-KDE-Library=myapppluginconfig
+ X-KDE-FactoryName=MyAppPluginConfigFactory
+ X-KDE-ParentApp=myapp
+ X-KDE-ParentComponents=myapp
+
+ Name=Plugins
+ Comment=Select and configure your plugins:
+ \endverbatim
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT PluginPage : public KCModule
+{
+ Q_OBJECT
+ public:
+ /**
+ * Standart KCModule constructor. Automatically creates the the
+ * KPluginSelector widget.
+ */
+ PluginPage( QWidget * parent = 0, const char * name = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Standart KCModule constructor. Automatically creates the the
+ * KPluginSelector widget.
+ */
+ PluginPage( KInstance * instance, QWidget * parent = 0, const QStringList & args = QStringList() );
+
+ ~PluginPage();
+
+ /**
+ * @return a reference to the KPluginSelector.
+ */
+ KPluginSelector * pluginSelector();
+
+ /**
+ * Load the state of the plugins (selected or not) from the KPluginInfo
+ * objects. For KParts plugins everything should work automatically. For
+ * your own type of plugins you might need to reimplement the
+ * KPluginInfo::pluginLoaded() method. If that doesn't fit your needs
+ * you can also reimplement this method.
+ */
+ virtual void load();
+
+ /**
+ * Save the state of the plugins to KConfig objects
+ */
+ virtual void save();
+ virtual void defaults();
+
+ private:
+ class PluginPagePrivate;
+ PluginPagePrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+
+#endif // KSETTINGS_PLUGINPAGE_H