diff options
Diffstat (limited to 'kutils/ksettings')
-rw-r--r-- | kutils/ksettings/Makefile.am | 9 | ||||
-rw-r--r-- | kutils/ksettings/README.dox | 276 | ||||
-rw-r--r-- | kutils/ksettings/TODO | 13 | ||||
-rw-r--r-- | kutils/ksettings/componentsdialog.cpp | 180 | ||||
-rw-r--r-- | kutils/ksettings/componentsdialog.h | 88 | ||||
-rw-r--r-- | kutils/ksettings/dialog.cpp | 642 | ||||
-rw-r--r-- | kutils/ksettings/dialog.h | 224 | ||||
-rw-r--r-- | kutils/ksettings/dispatcher.cpp | 157 | ||||
-rw-r--r-- | kutils/ksettings/dispatcher.h | 133 | ||||
-rw-r--r-- | kutils/ksettings/pluginpage.cpp | 88 | ||||
-rw-r--r-- | kutils/ksettings/pluginpage.h | 120 |
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=<the instance +name of your application>' 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/<appname>/<appname>.setdlg. If third party +plugins need to merge in they will install their file to +apps/<appname>/ksettingsdialog/<pluginname>.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 |