diff options
Diffstat (limited to 'tdeutils/ksettings')
-rw-r--r-- | tdeutils/ksettings/CMakeLists.txt | 39 | ||||
-rw-r--r-- | tdeutils/ksettings/Makefile.am | 9 | ||||
-rw-r--r-- | tdeutils/ksettings/README.dox | 276 | ||||
-rw-r--r-- | tdeutils/ksettings/TODO | 13 | ||||
-rw-r--r-- | tdeutils/ksettings/componentsdialog.cpp | 180 | ||||
-rw-r--r-- | tdeutils/ksettings/componentsdialog.h | 88 | ||||
-rw-r--r-- | tdeutils/ksettings/dialog.cpp | 642 | ||||
-rw-r--r-- | tdeutils/ksettings/dialog.h | 224 | ||||
-rw-r--r-- | tdeutils/ksettings/dispatcher.cpp | 157 | ||||
-rw-r--r-- | tdeutils/ksettings/dispatcher.h | 133 | ||||
-rw-r--r-- | tdeutils/ksettings/pluginpage.cpp | 88 | ||||
-rw-r--r-- | tdeutils/ksettings/pluginpage.h | 120 |
12 files changed, 1969 insertions, 0 deletions
diff --git a/tdeutils/ksettings/CMakeLists.txt b/tdeutils/ksettings/CMakeLists.txt new file mode 100644 index 000000000..7a1d5034d --- /dev/null +++ b/tdeutils/ksettings/CMakeLists.txt @@ -0,0 +1,39 @@ +################################################# +# +# (C) 2010 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${TQT_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_BINARY_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/tdeutils + ${CMAKE_SOURCE_DIR}/tdecore + ${CMAKE_SOURCE_DIR}/tdeui + ${CMAKE_SOURCE_DIR}/kio/kio +) + +##### headers ################################### + +install( FILES + dispatcher.h dialog.h pluginpage.h componentsdialog.h + DESTINATION ${INCLUDE_INSTALL_DIR}/ksettings ) + + +##### ksettings ################################# + +set( target ksettings ) + +set( ${target}_SRCS + dispatcher.cpp dialog.cpp pluginpage.cpp componentsdialog.cpp +) + +tde_add_library( ${target} STATIC_PIC AUTOMOC + SOURCES ${${target}_SRCS} +) diff --git a/tdeutils/ksettings/Makefile.am b/tdeutils/ksettings/Makefile.am new file mode 100644 index 000000000..39e7183b2 --- /dev/null +++ b/tdeutils/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/tdeutils/ksettings/README.dox b/tdeutils/ksettings/README.dox new file mode 100644 index 000000000..88268f671 --- /dev/null +++ b/tdeutils/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-TDE-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 TDECModule +{ + 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 ) + : TDECModule( MyAppConfigFactory::instance(), parent, args ) +{ + // create the pages GUI + load(); +} + +// implementations for the other methods +\endcode + +For the TDEConfig object you can either use +TDEGlobal::config() (I don't recommend it) or KSimpleConfig( "myapprc" ). +I added a method to KSettings::Dispatcher that gives you the TDEConfig +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=TDECModule + +X-TDE-ModuleType=Library +X-TDE-Library=myappconfig +X-TDE-FactoryName=MyAppConfigFactory +X-TDE-ParentApp=myapp +X-TDE-ParentComponents=myapp +X-TDE-Weight=10 + +Name=General +Comment=General configuration of my app +\endverbatim + + +Some explanation for those keys: +- You just keep 'Encoding', 'Type', 'ServiceTypes' and 'X-TDE-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-TDE-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 TDECModule docu. +- X-TDE-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 TDECModule docu. +- X-TDE-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-TDE-ParentApp to + TDEGlobal::instance()->instanceName(). It + should be pretty easy to find out what name that is: look at the first + argument to the TDEAboutData ctor. +- X-TDE-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-TDE-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-TDE-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-TDE-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-TDE-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-TDE-PluginInfo-Name=myplugin + +X-TDE-PluginInfo-Author=<your name> +X-TDE-PluginInfo-Email=<your email> +X-TDE-PluginInfo-Website=http://www.myplugin.org/ +X-TDE-PluginInfo-Category=CoolPlugins +X-TDE-PluginInfo-Version=0.1 +X-TDE-PluginInfo-License=GPL +X-TDE-PluginInfo-EnabledByDefault=true +X-TDE-PluginInfo-Depends=myotherplugin +X-TDE-CfgDlgHierarchy=GroupID +\endverbatim + +Explanation: +mandatory entries: +- leave \p Type and \p Encoding like in the example +- \p Name +- \p Comment +- \p X-TDE-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-TDE-PluginInfo-Author and \p X-TDE-PluginInfo-Email is some information about the author of the plugin. +- \p X-TDE-PluginInfo-Website is the address for a webpage for this plugin. +- \p X-TDE-PluginInfo-Category is used if your application has different categories of plugins. +- \p X-TDE-PluginInfo-Version is the version of this plugin. +- \p X-TDE-PluginInfo-License is the license of this plugin. +- \p X-TDE-PluginInfo-EnabledByDefault tells the program whether the plugin + should be enabled on first startup or not. +- \p X-TDE-PluginInfo-Depends can be used to tell the application that you need to have + myotherplugin enabled for your plugin to work. +- \p X-TDE-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/tdeutils/ksettings/TODO b/tdeutils/ksettings/TODO new file mode 100644 index 000000000..9950ddfd9 --- /dev/null +++ b/tdeutils/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/tdeutils/ksettings/componentsdialog.cpp b/tdeutils/ksettings/componentsdialog.cpp new file mode 100644 index 000000000..097d1f6d7 --- /dev/null +++ b/tdeutils/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 <tqlayout.h> +#include <klistview.h> +#include <tqlabel.h> +#include <tqheader.h> +#include <kplugininfo.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <kconfig.h> +#include <kseparator.h> + +namespace KSettings +{ + +class ComponentsDialog::ComponentsDialogPrivate +{ + public: + KListView * listview; + TQFrame * infowidget; + TQLabel * iconwidget; + TQLabel * commentwidget; + TQLabel * descriptionwidget; + TQMap<TQCheckListItem*, KPluginInfo*> plugininfomap; + TQValueList<KPluginInfo*> plugininfolist; +}; + +ComponentsDialog::ComponentsDialog( TQWidget * parent, const char * name ) + : KDialogBase( parent, name, false, i18n( "Select Components" ) ) +, d( new ComponentsDialogPrivate ) +{ + TQWidget * page = new TQWidget( this ); + setMainWidget( page ); + ( new TQHBoxLayout( page, 0, KDialog::spacingHint() ) )->setAutoAdd( true ); + d->listview = new KListView( page ); + d->listview->setMinimumSize( 200, 200 ); + d->infowidget = new TQFrame( page ); + d->infowidget->setMinimumSize( 200, 200 ); + ( new TQVBoxLayout( d->infowidget, 0, KDialog::spacingHint() ) )->setAutoAdd( true ); + d->iconwidget = new TQLabel( d->infowidget ); + ( void )new KSeparator( d->infowidget ); + d->commentwidget = new TQLabel( d->infowidget ); + d->commentwidget->setAlignment( TQt::WordBreak ); + d->descriptionwidget = new TQLabel( d->infowidget ); + d->descriptionwidget->setAlignment( TQt::WordBreak ); + + d->listview->addColumn( TQString::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, TQT_SIGNAL( pressed( TQListViewItem * ) ), this, + TQT_SLOT( executed( TQListViewItem * ) ) ); + connect( d->listview, TQT_SIGNAL( spacePressed( TQListViewItem * ) ), this, + TQT_SLOT( executed( TQListViewItem * ) ) ); + connect( d->listview, TQT_SIGNAL( returnPressed( TQListViewItem * ) ), this, + TQT_SLOT( executed( TQListViewItem * ) ) ); + connect( d->listview, TQT_SIGNAL( selectionChanged( TQListViewItem * ) ), this, + TQT_SLOT( executed( TQListViewItem * ) ) ); +} + +ComponentsDialog::~ComponentsDialog() +{ +} + +void ComponentsDialog::addPluginInfo( KPluginInfo * info ) +{ + d->plugininfolist.append( info ); +} + +void ComponentsDialog::setPluginInfos( const TQMap<TQString, KPluginInfo*> & + plugininfos ) +{ + for( TQMap<TQString, KPluginInfo*>::ConstIterator it = plugininfos.begin(); + it != plugininfos.end(); ++it ) + { + d->plugininfolist.append( it.data() ); + } +} + +void ComponentsDialog::setPluginInfos( const TQValueList<KPluginInfo *> &plugins ) +{ + d->plugininfolist = plugins; +} + +void ComponentsDialog::show() +{ + // clear the treelist + d->listview->clear(); + d->plugininfomap.clear(); + + // construct the treelist + for( TQValueList<KPluginInfo*>::ConstIterator it = d->plugininfolist.begin(); + it != d->plugininfolist.end(); ++it ) + { + ( *it )->load(); + TQCheckListItem * item = new TQCheckListItem( d->listview, ( *it )->name(), + TQCheckListItem::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( TQListViewItem * item ) +{ + kdDebug( 704 ) << k_funcinfo << endl; + if( item == 0 ) + return; + if( item->rtti() != 1 ) // check for QCheckListItem + return; + + TQCheckListItem * citem = static_cast<TQCheckListItem *>( item ); + bool checked = citem->isOn(); + + kdDebug( 704 ) << "it's a " << ( checked ? "checked" : "unchecked" ) + << " TQCheckListItem" << 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( TQValueList<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/tdeutils/ksettings/componentsdialog.h b/tdeutils/ksettings/componentsdialog.h new file mode 100644 index 000000000..e3f8b60a0 --- /dev/null +++ b/tdeutils/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 TQString; +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( TQWidget * 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 TQMap<TQString, KPluginInfo*> & plugininfos ); + /** + Set list of plugins the dialog offers for selection. (Overwrites a previous list) + */ + void setPluginInfos( const TQValueList<KPluginInfo *> &plugins ); + + /** + * reimplemented + */ + void show(); + + protected slots: + void slotOk(); + void slotApply(); + + private slots: + void executed( TQListViewItem * ); + + private: + void savePluginInfos(); + + class ComponentsDialogPrivate; + ComponentsDialogPrivate * d; +}; + +} + +// vim: sw=4 sts=4 et +#endif // KSETTINGS_COMPONENTSDIALOG_H diff --git a/tdeutils/ksettings/dialog.cpp b/tdeutils/ksettings/dialog.cpp new file mode 100644 index 000000000..e3bfa579f --- /dev/null +++ b/tdeutils/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 <tqvbox.h> +#include <tqlabel.h> +#include "tdecmoduleinfo.h" + +namespace KSettings +{ + +struct GroupInfo +{ + TQString id; + TQString name; + TQString comment; + TQString icon; + int weight; + TQString parentid; + TQWidget * 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 TQValueList<PageNode*> List; + enum Type { KCM, Group, Root }; + union Value + { + TDECModuleInfo * 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( TDECModuleInfo * 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; + tqSwap( *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(); + } + + TQString name() const + { + if( Root == m_type ) + return TQString::fromAscii( "root node" ); + return ( KCM == m_type ) ? m_value.kcm->moduleName() + : m_value.group->name; + } + + TQStringList parentNames() const + { + TQStringList 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 ) + { + TQPixmap icon; + if( ! m_value.group->icon.isNull() ) + icon = SmallIcon( m_value.group->icon, + IconSize( KIcon::Small ) ); + TQVBox * page = dlg->addVBoxPage( m_value.group->name, + TQString::null, icon ); + TQLabel * comment = new TQLabel( m_value.group->comment, page ); + comment->setTextFormat( TQt::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( TDECModuleInfo * info, const TQString & 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; + TQWidget * parentwidget; + TQStringList registeredComponents; + TQValueList<KService::Ptr> services; + TQMap<TQString, KPluginInfo*> plugininfomap; +}; + +Dialog::Dialog( TQWidget * parent, const char * name ) + : TQObject( parent, name ) + , d( new DialogPrivate( this ) ) +{ + d->parentwidget = parent; + d->staticlistview = true; + d->services = instanceServices(); +} + +Dialog::Dialog( ContentInListView content, + TQWidget * parent, const char * name ) + : TQObject( parent, name ) + , d( new DialogPrivate( this ) ) +{ + d->parentwidget = parent; + d->staticlistview = ( content == Static ); + d->services = instanceServices(); +} + +Dialog::Dialog( const TQStringList & components, + TQWidget * parent, const char * name ) + : TQObject( parent, name ) + , d( new DialogPrivate( this ) ) +{ + d->parentwidget = parent; + d->staticlistview = true; + d->services = instanceServices() + parentComponentsServices( components ); +} + +Dialog::Dialog( const TQStringList & components, + ContentInListView content, TQWidget * parent, const char * name ) + : TQObject( 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 TQValueList<KPluginInfo*> & plugininfos ) +{ + for( TQValueList<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; +} + +TQValueList<KService::Ptr> Dialog::instanceServices() const +{ + kdDebug( 700 ) << k_funcinfo << endl; + TQString instanceName = TDEGlobal::instance()->instanceName(); + d->registeredComponents.append( instanceName ); + kdDebug( 700 ) << "calling KServiceGroup::childGroup( " << instanceName + << " )" << endl; + KServiceGroup::Ptr service = KServiceGroup::childGroup( instanceName ); + + TQValueList<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; +} + +TQValueList<KService::Ptr> Dialog::parentComponentsServices( + const TQStringList & kcdparents ) const +{ + d->registeredComponents += kcdparents; + TQString constraint = kcdparents.join( + "' in [X-TDE-ParentComponents]) or ('" ); + constraint = "('" + constraint + "' in [X-TDE-ParentComponents])"; + + kdDebug( 700 ) << "constraint = " << constraint << endl; + return KTrader::self()->query( "TDECModule", constraint ); +} + +bool Dialog::isPluginForKCMEnabled( TDECModuleInfo * 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 + TQStringList parentComponents = moduleinfo->service()->property( + "X-TDE-ParentComponents" ).toStringList(); + for( TQStringList::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 TDECModule must be enabled + enabled = true; + // we're done for this TDECModuleInfo + 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 TDECModuleInfo + if( enabled ) + break; + } + return enabled; +} + +void Dialog::parseGroupFile( const TQString & filename ) +{ + KSimpleConfig file( filename ); + TQStringList groups = file.groupList(); + for( TQStringList::ConstIterator it = groups.begin(); it != groups.end(); + ++it ) + { + GroupInfo group; + TQString 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 + TQString setdlgpath = locate( "appdata", + TDEGlobal::instance()->instanceName() + ".setdlg" ); + TQStringList setdlgaddon = TDEGlobal::dirs()->findAllResources( "appdata", + "ksettingsdialog/*.setdlg" ); + if( ! setdlgpath.isNull() ) + parseGroupFile( setdlgpath ); + if( setdlgaddon.size() > 0 ) + for( TQStringList::ConstIterator it = setdlgaddon.begin(); + it != setdlgaddon.end(); ++it ) + parseGroupFile( *it ); + + // now we process the TDECModule services + for( TQValueList<KService::Ptr>::ConstIterator it = d->services.begin(); + it != d->services.end(); ++it ) + { + // we create the TDECModuleInfo + TDECModuleInfo * info = new TDECModuleInfo( *it ); + TQString parentid; + TQVariant tmp = info->service()->property( "X-TDE-CfgDlgHierarchy", + TQVariant::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 TDECModule::load() to reset all KCMs + // (KPluginSelector::load() resets all plugin selections and all plugin + // KCMs). + // The reset button calls TDECModule::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, + TQT_SLOT( configureTree() ) ); + + connect( d->dlg, TQT_SIGNAL( okClicked() ), Dispatcher::self(), + TQT_SLOT( syncConfiguration() ) ); + connect( d->dlg, TQT_SIGNAL( applyClicked() ), Dispatcher::self(), + TQT_SLOT( syncConfiguration() ) ); + connect( d->dlg, TQT_SIGNAL( configCommitted( const TQCString & ) ), + Dispatcher::self(), TQT_SLOT( reparseConfiguration( const TQCString & ) ) ); + + 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, TQT_SIGNAL( okClicked() ), this, TQT_SLOT( updateTreeList() ) ); + connect( subdlg, TQT_SIGNAL( applyClicked() ), this, TQT_SLOT( updateTreeList() ) ); + connect( subdlg, TQT_SIGNAL( okClicked() ), this, + TQT_SIGNAL( pluginSelectionChanged() ) ); + connect( subdlg, TQT_SIGNAL( applyClicked() ), this, + TQT_SIGNAL( pluginSelectionChanged() ) ); + connect( subdlg, TQT_SIGNAL( finished() ), subdlg, TQT_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/tdeutils/ksettings/dialog.h b/tdeutils/ksettings/dialog.h new file mode 100644 index 000000000..1a3c741a6 --- /dev/null +++ b/tdeutils/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 <tqobject.h> +#include <kservice.h> + +template<class T> class TQValueList; +class KPluginInfo; +class KCMultiDialog; +class TDECModuleInfo; + +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 + * TQStringList tdepartslist; + * for( all my tdeparts ) + * tdepartslist += m_mypart->instance().instanceName(); + * m_cfgdlg = new Dialog( tdepartslist, this ); + * \endcode + * and the action for the config dialog is connected to the show slot: + * \code + * KStdAction::preferences( m_cfgdlg, TQT_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 TQObject +{ + 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-TDE-ParentApp set to TDEGlobal::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( TQWidget * parent = 0, const char * name = 0 ); + + /** + * Construct a new Preferences Dialog for the application. It uses all + * KCMs with X-TDE-ParentApp set to TDEGlobal::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, TQWidget * 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 + * TQStringList 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 TQStringList & components, TQWidget * 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 + * TQStringList 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 TQStringList & components, ContentInListView + content, TQWidget * 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 TQValueList<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( TDECModuleInfo * ) const; + + TQValueList<KService::Ptr> instanceServices() const; + TQValueList<KService::Ptr> parentComponentsServices( + const TQStringList & ) const; + /** + * @internal + * Read the .setdlg file and add it to the groupmap + */ + void parseGroupFile( const TQString & ); + + /** + * @internal + * If this module is put into a TreeList hierarchy this will return a + * list of the names of the parent modules. + */ + TQStringList parentModuleNames( TDECModuleInfo * ); + + /** + * @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/tdeutils/ksettings/dispatcher.cpp b/tdeutils/ksettings/dispatcher.cpp new file mode 100644 index 000000000..590ca7e3c --- /dev/null +++ b/tdeutils/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 <tqsignal.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( TQObject * parent, const char * name ) + : TQObject( parent, name ) + //, d( 0 ) +{ + kdDebug( 701 ) << k_funcinfo << endl; +} + +Dispatcher::~Dispatcher() +{ + kdDebug( 701 ) << k_funcinfo << endl; + //delete d; +} + +void Dispatcher::registerInstance( TDEInstance * instance, TQObject * recv, const char * slot ) +{ + assert( instance != 0 ); + // keep the TDEInstance around and call + // instance->config()->reparseConfiguration when the app should reparse + TQCString instanceName = instance->instanceName(); + kdDebug( 701 ) << k_funcinfo << instanceName << endl; + m_instanceName[ recv ] = instanceName; + TQSignal * sig; + if( m_instanceInfo.contains( instanceName ) ) + { + sig = m_instanceInfo[ instanceName ].signal; + } + else + { + sig = new TQSignal( this, "signal dispatcher" ); + m_instanceInfo[ instanceName ].signal = sig; + m_instanceInfo[ instanceName ].instance = instance; + } + sig->connect( recv, slot ); + + ++m_instanceInfo[ instanceName ].count; + connect( recv, TQT_SIGNAL( destroyed( TQObject * ) ), this, TQT_SLOT( unregisterInstance( TQObject * ) ) ); +} + +TDEConfig * Dispatcher::configForInstanceName( const TQCString & instanceName ) +{ + kdDebug( 701 ) << k_funcinfo << endl; + if( m_instanceInfo.contains( instanceName ) ) + { + TDEInstance * inst = m_instanceInfo[ instanceName ].instance; + if( inst ) + return inst->config(); + } + //if( fallback ) + //return new KSimpleConfig( instanceName ); + return 0; +} + +TQStrList Dispatcher::instanceNames() const +{ + kdDebug( 701 ) << k_funcinfo << endl; + TQStrList names; + for( TQMap<TQCString, 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 TQCString & 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 TDEConfig object + // will be up to date + m_instanceInfo[ instanceName ].instance->config()->reparseConfiguration(); + TQSignal * sig = m_instanceInfo[ instanceName ].signal; + if( sig ) + { + kdDebug( 701 ) << "emit signal to instance" << endl; + sig->activate(); + } +} + +void Dispatcher::syncConfiguration() +{ + for( TQMap<TQCString, InstanceInfo>::ConstIterator it = m_instanceInfo.begin(); it != m_instanceInfo.end(); ++it ) + { + ( *it ).instance->config()->sync(); + } +} + +void Dispatcher::unregisterInstance( TQObject * obj ) +{ + kdDebug( 701 ) << k_funcinfo << endl; + TQCString 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 TDEInstance * Dispatcher::instanceForName( const TQCString & instanceName ) +//X { +//X return m_instanceInfo[ instanceName ].instance; +//X } + +} //namespace + +#include "dispatcher.moc" + +// vim: sw=4 sts=4 et diff --git a/tdeutils/ksettings/dispatcher.h b/tdeutils/ksettings/dispatcher.h new file mode 100644 index 000000000..fa0c9e6c9 --- /dev/null +++ b/tdeutils/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 <tqobject.h> +#include <tqmap.h> +#include <tdelibs_export.h> + +class TQCString; +class TQSignal; +class TQStrList; +template<class T> class KStaticDeleter; +class TDEInstance; +class TDEConfig; + +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 TDEConfigureDialog you need a way to get notified. This is what you + * do: + * \code + * Dispatcher::self()->registerInstance( instance(), this, TQT_SLOT( loadSettings() ) ); + * \endcode + * + * @author Matthias Kretz <kretz@kde.org> + * @since 3.2 + */ +class KUTILS_EXPORT Dispatcher : public TQObject +{ + 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 TDEInstance 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-TDE-ParentComponents. + * + * @param instance The TDEInstance object + * @param recv The object that should receive the signal + * @param slot The slot to be called: TQT_SLOT( slotName() ) + */ + void registerInstance( TDEInstance * instance, TQObject * recv, const char * slot ); + + /** + * @return the TDEConfig object that belongs to the instanceName + */ + TDEConfig * configForInstanceName( const TQCString & instanceName ); + + /** + * @return a list of all the instance names that are currently + * registered + */ + TQStrList instanceNames() const; + +//X /** +//X * @return The TDEInstance object belonging to the instance name you pass +//X * (only works for registered instances of course). +//X */ +//X TDEInstance * instanceForName( const TQCString & 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-TDE-ParentComponents. + */ + void reparseConfiguration( const TQCString & instanceName ); + + /** + * When this slot is called the TDEConfig objects of all the registered + * instances are sync()ed. This is usefull when some other TDEConfig + * objects will read/write from/to the same config file, so that you + * can first write out the current state of the TDEConfig objects. + */ + void syncConfiguration(); + + private slots: + void unregisterInstance( TQObject * ); + + private: + Dispatcher( TQObject * parent = 0, const char * name = 0 ); + ~Dispatcher(); + static Dispatcher * m_self; + + struct InstanceInfo { + TDEInstance * instance; + TQSignal * signal; + int count; + }; + TQMap<TQCString, InstanceInfo> m_instanceInfo; + TQMap<TQObject *, TQCString> m_instanceName; + + class DispatcherPrivate; + DispatcherPrivate * d; +}; + +} + +// vim: sw=4 sts=4 et +#endif // KSETTINGS_DISPATCHER_H diff --git a/tdeutils/ksettings/pluginpage.cpp b/tdeutils/ksettings/pluginpage.cpp new file mode 100644 index 000000000..c7208dc69 --- /dev/null +++ b/tdeutils/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 <tqlayout.h> +#include <kdialog.h> +#include "ksettings/dispatcher.h" + +namespace KSettings +{ + +class PluginPage::PluginPagePrivate +{ + public: + PluginPagePrivate() + : selwid( 0 ) + { + } + + KPluginSelector * selwid; +}; + + PluginPage::PluginPage( TQWidget * parent, const char * name, const TQStringList & args ) + : TDECModule( parent, name, args ) + , d( new PluginPagePrivate ) +{ + ( new TQVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true ); + d->selwid = new KPluginSelector( this ); + connect( d->selwid, TQT_SIGNAL( changed( bool ) ), this, TQT_SIGNAL( changed( bool ) ) ); +} + + PluginPage::PluginPage( TDEInstance * instance, TQWidget * parent, const TQStringList & args ) + : TDECModule( instance, parent, args ) + , d( new PluginPagePrivate ) +{ + ( new TQVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true ); + d->selwid = new KPluginSelector( this ); + connect( d->selwid, TQT_SIGNAL( changed( bool ) ), this, TQT_SIGNAL( changed( bool ) ) ); + connect( d->selwid, TQT_SIGNAL( configCommitted( const TQCString & ) ), + Dispatcher::self(), TQT_SLOT( reparseConfiguration( const TQCString & ) ) ); +} + +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/tdeutils/ksettings/pluginpage.h b/tdeutils/ksettings/pluginpage.h new file mode 100644 index 000000000..7f5c7e952 --- /dev/null +++ b/tdeutils/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 <tdecmodule.h> +#include <tdelibs_export.h> + +class KPluginSelector; + +namespace KSettings +{ + +/** + * @ingroup settings + * @ingroup plugin + * @short Convenience TDECModule 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, TQWidget> MyAppPluginConfigFactory; + * K_EXPORT_COMPONENT_FACTORY( kcm_myapppluginconfig, MyAppPluginConfigFactory( "kcm_myapppluginconfig" ) ); + * + * MyAppPluginConfig( TQWidget * parent, const char *, const TQStringList & args ) + * : PluginPage( MyAppPluginConfigFactory::instance(), parent, args ) + * { + * pluginSelector()->addPlugins( TDEGlobal::instance()->instanceName(), i18n( "General Plugins" ), "General" ); + * pluginSelector()->addPlugins( TDEGlobal::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=TDECModule + + X-TDE-ModuleType=Library + X-TDE-Library=myapppluginconfig + X-TDE-FactoryName=MyAppPluginConfigFactory + X-TDE-ParentApp=myapp + X-TDE-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 TDECModule +{ + Q_OBJECT + public: + /** + * Standart TDECModule constructor. Automatically creates the the + * KPluginSelector widget. + */ + PluginPage( TQWidget * parent = 0, const char * name = 0, const TQStringList & args = TQStringList() ); + + /** + * Standart TDECModule constructor. Automatically creates the the + * KPluginSelector widget. + */ + PluginPage( TDEInstance * instance, TQWidget * parent = 0, const TQStringList & args = TQStringList() ); + + ~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 TDEConfig objects + */ + virtual void save(); + virtual void defaults(); + + private: + class PluginPagePrivate; + PluginPagePrivate * d; +}; + +} + +// vim: sw=4 sts=4 et + +#endif // KSETTINGS_PLUGINPAGE_H |