summaryrefslogtreecommitdiffstats
path: root/kutils
diff options
context:
space:
mode:
Diffstat (limited to 'kutils')
-rw-r--r--kutils/Mainpage.dox24
-rw-r--r--kutils/Makefile.am31
-rw-r--r--kutils/TODO25
-rw-r--r--kutils/groups.dox41
-rw-r--r--kutils/kcmodulecontainer.cpp258
-rw-r--r--kutils/kcmodulecontainer.h207
-rw-r--r--kutils/kcmoduleinfo.cpp240
-rw-r--r--kutils/kcmoduleinfo.h296
-rw-r--r--kutils/kcmoduleloader.cpp288
-rw-r--r--kutils/kcmoduleloader.h220
-rw-r--r--kutils/kcmoduleproxy.cpp649
-rw-r--r--kutils/kcmoduleproxy.h357
-rw-r--r--kutils/kcmoduleproxyIface.h121
-rw-r--r--kutils/kcmoduleproxyIfaceImpl.cpp129
-rw-r--r--kutils/kcmoduleproxyIfaceImpl.h97
-rw-r--r--kutils/kcmultidialog.cpp411
-rw-r--r--kutils/kcmultidialog.h284
-rw-r--r--kutils/kdeglobals.kcfg564
-rw-r--r--kutils/kdeglobals.kcfgc5
-rw-r--r--kutils/kfind.cpp710
-rw-r--r--kutils/kfind.h426
-rw-r--r--kutils/kfinddialog.cpp544
-rw-r--r--kutils/kfinddialog.h315
-rw-r--r--kutils/kmultitabbar.cpp997
-rw-r--r--kutils/kmultitabbar.h316
-rw-r--r--kutils/kmultitabbar_p.h67
-rw-r--r--kutils/kplugininfo.cpp359
-rw-r--r--kutils/kplugininfo.desktop107
-rw-r--r--kutils/kplugininfo.h319
-rw-r--r--kutils/kpluginselector.cpp727
-rw-r--r--kutils/kpluginselector.h218
-rw-r--r--kutils/kpluginselector_p.h188
-rw-r--r--kutils/kreplace.cpp328
-rw-r--r--kutils/kreplace.h269
-rw-r--r--kutils/kreplacedialog.cpp160
-rw-r--r--kutils/kreplacedialog.h160
-rw-r--r--kutils/ksettings/Makefile.am9
-rw-r--r--kutils/ksettings/README.dox276
-rw-r--r--kutils/ksettings/TODO13
-rw-r--r--kutils/ksettings/componentsdialog.cpp180
-rw-r--r--kutils/ksettings/componentsdialog.h88
-rw-r--r--kutils/ksettings/dialog.cpp642
-rw-r--r--kutils/ksettings/dialog.h224
-rw-r--r--kutils/ksettings/dispatcher.cpp157
-rw-r--r--kutils/ksettings/dispatcher.h133
-rw-r--r--kutils/ksettings/pluginpage.cpp88
-rw-r--r--kutils/ksettings/pluginpage.h120
-rw-r--r--kutils/tests/Makefile.am16
-rw-r--r--kutils/tests/kfindtest.cpp252
-rw-r--r--kutils/tests/kfindtest.h59
-rw-r--r--kutils/tests/kreplacetest.cpp342
-rw-r--r--kutils/tests/kreplacetest.h52
52 files changed, 13108 insertions, 0 deletions
diff --git a/kutils/Mainpage.dox b/kutils/Mainpage.dox
new file mode 100644
index 000000000..f649fcc3d
--- /dev/null
+++ b/kutils/Mainpage.dox
@@ -0,0 +1,24 @@
+/** @mainpage Some random bits and pieces
+
+Some utility classes for KSettings, KCModule and KPlugin.
+
+@authors
+Matthias Kretz \<kretz@kde.org\><br>
+Rafael Fernández López \<ereslibre@kde.org\><br>
+Matthias Elter \<elter@kde.org\><br>
+Daniel Molkentin \<molkentin@kde.org\><br>
+Frans Englich \<frans.englich@telia.com\><br>
+Tobias Koenig \<tokoe@kde.org\><br>
+Matthias Hoelzer-Kluepfel \<hoelzer@kde.org\>
+
+@maintainers
+[Unknown/None]
+
+@licenses
+@lgpl
+
+*/
+
+// DOXYGEN_REFERENCES = kdecore kdeui kio
+// DOXYGEN_SET_PROJECT_NAME = KUtils
+// vim:ts=4:sw=4:expandtab:filetype=doxygen
diff --git a/kutils/Makefile.am b/kutils/Makefile.am
new file mode 100644
index 000000000..2206d798f
--- /dev/null
+++ b/kutils/Makefile.am
@@ -0,0 +1,31 @@
+SUBDIRS = ksettings .
+INCLUDES = -I$(top_srcdir)/interfaces/kregexpeditor $(all_includes)
+
+lib_LTLIBRARIES = libkutils.la
+libkutils_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 3:0:2
+# Needs KTrader
+libkutils_la_LIBADD = ../kio/libkio.la ksettings/libksettings.la
+
+libkutils_la_SOURCES = kfind.cpp kfinddialog.cpp kreplace.cpp \
+ kreplacedialog.cpp kcmoduleinfo.cpp kcmoduleloader.cpp \
+ kcmultidialog.cpp kmultitabbar.cpp kplugininfo.cpp \
+ kcmoduleproxy.cpp kpluginselector.cpp \
+ kcmodulecontainer.cpp kcmoduleproxyIface.skel \
+ kcmoduleproxyIfaceImpl.cpp
+
+include_HEADERS = kfind.h kfinddialog.h kreplace.h kreplacedialog.h \
+ kcmoduleinfo.h kcmoduleloader.h kcmultidialog.h \
+ kmultitabbar.h kplugininfo.h kcmoduleproxy.h \
+ kpluginselector.h kcmodulecontainer.h
+
+noinst_HEADERS = kmultitabbar_p.h kpluginselector_p.h \
+ kcmoduleproxyIface.h kcmoduleproxyIfaceImpl.h
+
+kde_servicetypes_DATA = kplugininfo.desktop
+
+METASOURCES = AUTO
+
+DOXYGEN_REFERENCES = kdecore kdeui kio
+include ../admin/Doxyfile.am
+
+# vim: et
diff --git a/kutils/TODO b/kutils/TODO
new file mode 100644
index 000000000..82f28e129
--- /dev/null
+++ b/kutils/TODO
@@ -0,0 +1,25 @@
+
+This is a list of random changes to do in KDE 4 with the various KCM* classes.
+
+* KCModuleinfo;
+ - Make it inherit KService; saves a lot of code, simplifies ctors/overloads in surrounding classes
+ - Implement docPath in KService
+ - Get rid of loadAll() and do lazy loading in each getter
+
+* Merge KCModuleProxy and its DCOP Object class(MI)
+
+* Get rid of `QStringlist arguments` in KCModule and all the helper classes(simplify API)
+
+* Move stuff to d pointers.. Too much hackish solutions otherwise
+
+* Get rid of bool fallback argument in helper classes(never fails, simplify API)
+
+* Re-implement the QScrollView(d->view) in KCModuleProxy; the protection against violation of HIG-clause. It needs kde-core-devel approval.
+
+* The API in KCModule, KCModuleProxy, and the various desktop entries, concerning root loading are butt ugly. It needs cleaning and simplification.
+
+* There's big confusion regarding /when/ the changed(bool) signal should be emitted, abd by /whom/. This can't be fixed properly in 3.x since it means breaking behavior. In KDE 4 it must be made clear if KCModuleProxy should take care of emitting appropriate signals for load()/save()/defaults, or if modules manually should handle it(is there a reason?).
+
+* Root modules in KCModuleProxy doesn't support D&D, or it is perhaps implemented in QXEmbed? Anyway, this is what the XEmbed Protocol Specification says:
+
+Solving the drag-and-drop problem, however, is quite easy, since the XDND protocol was carefully designed in a way that makes it possible to support embedded windows. Basically, the embedder has to operate as drag-and-drop proxy for the client. Any XDND messages like XdndEnter, Xdnd,Leave, etc. simply have to be passed through. A toolkit's XDND implementation has to take this situation in consideration.
diff --git a/kutils/groups.dox b/kutils/groups.dox
new file mode 100644
index 000000000..95d926d73
--- /dev/null
+++ b/kutils/groups.dox
@@ -0,0 +1,41 @@
+/**
+@defgroup main Main classes
+*/
+
+/**
+@defgroup internal Internal classes
+*/
+
+/**
+@defgroup findreplace Find and Replace classes
+
+These classes implement a generic find/replace function, along with
+extensible dialogs.
+*/
+
+/**
+@defgroup plugin Plugin handling classes
+
+These classes provide an easy and generic way for handling plugins.
+*/
+
+/**
+@defgroup settings Settings UI classes
+
+These classes provide widgets to build settings dialogs. See KSettings
+namespace description for an overview.
+*/
+
+/**
+@defgroup kcmodule KControl module classes
+
+Thwse classes provide the KControl module API.
+*/
+
+/**
+@defgroup multitabbar KMultiTabBar related classes
+
+These classes implement the tab bar often found on the border of KDE
+applications' main windows (such as Kate or KDevelop).
+*/
+
diff --git a/kutils/kcmodulecontainer.cpp b/kutils/kcmodulecontainer.cpp
new file mode 100644
index 000000000..000189e0d
--- /dev/null
+++ b/kutils/kcmodulecontainer.cpp
@@ -0,0 +1,258 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 <qlayout.h>
+#include <qpixmap.h>
+#include <qstringlist.h>
+#include <qtabwidget.h>
+#include <qtooltip.h>
+#include <qvaluelist.h>
+
+#include <kcmodule.h>
+#include <kcmoduleinfo.h>
+#include <kcmoduleloader.h>
+#include <kcmoduleproxy.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <kservice.h>
+#include <kstdguiitem.h>
+
+#include "kcmodulecontainer.h"
+#include "kcmodulecontainer.moc"
+
+/***********************************************************************/
+class KCModuleContainer::KCModuleContainerPrivate
+{
+ public:
+ KCModuleContainerPrivate( const QStringList& mods )
+ : modules( mods )
+ , tabWidget( 0 )
+ , buttons( 0 )
+ , hasRootKCM( false )
+ , btnRootMode( 0 )
+ , btnLayout( 0 )
+ , topLayout( 0 )
+ {}
+
+ QStringList modules;
+ QTabWidget *tabWidget;
+ int buttons;
+ bool hasRootKCM: 1;
+ KPushButton *btnRootMode;
+ QHBoxLayout *btnLayout;
+ QVBoxLayout *topLayout;
+
+
+};
+/***********************************************************************/
+
+
+
+
+
+/***********************************************************************/
+KCModuleContainer::KCModuleContainer( QWidget* parent, const char* name,
+ const QString& mods )
+ : KCModule( parent, name )
+{
+ d = new KCModuleContainerPrivate( QStringList::split( ",", QString(mods).remove( " " )) );
+ init();
+}
+
+KCModuleContainer::KCModuleContainer( QWidget* parent, const char* name,
+ const QStringList& mods )
+ : KCModule( parent, name ), d( new KCModuleContainerPrivate( mods ) )
+{
+ init();
+}
+
+void KCModuleContainer::init()
+{
+ d->topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint(), "topLayout" );
+ d->tabWidget = new QTabWidget(this, "tabWidget");
+ d->tabWidget->setMargin(KDialog::marginHint());
+ connect( d->tabWidget, SIGNAL( currentChanged( QWidget* ) ), SLOT( tabSwitched( QWidget* ) ));
+ d->topLayout->addWidget( d->tabWidget );
+
+ if ( !d->modules.isEmpty() )
+ {
+ /* Add our modules */
+ for ( QStringList::Iterator it = d->modules.begin(); it != d->modules.end(); ++it )
+ addModule( (*it) );
+
+ finalize();
+ }
+
+}
+
+void KCModuleContainer::finalize()
+{
+ setButtons( d->buttons );
+ if ( d->hasRootKCM ) /* Add a root mode button */
+ {
+ if(!d->btnLayout) /* It could already be added */
+ {
+ d->btnLayout = new QHBoxLayout(this, 0, 0, "btnLayout");
+ d->btnRootMode = new KPushButton(KStdGuiItem::adminMode(), this, "btnRootMode");
+
+ d->btnLayout->addWidget( d->btnRootMode );
+ d->btnLayout->addStretch();
+ d->topLayout->addLayout( d->btnLayout );
+ }
+ }
+}
+
+void KCModuleContainer::addModule( const QString& module )
+{
+ /* In case it doesn't exist we just silently drop it.
+ * This allows people to easily extend containers.
+ * For example, KCM monitor gamma can be in kdegraphics.
+ */
+ if ( !KService::serviceByDesktopName( module ) )
+ {
+ kdDebug(713) << "KCModuleContainer: module '" <<
+ module << "' was not found and thus not loaded" << endl;
+ return;
+ }
+
+ if( !KCModuleLoader::testModule( module ))
+ return;
+
+ KCModuleProxy* proxy = new KCModuleProxy( module, false, d->tabWidget, module.latin1());
+ allModules.append( proxy );
+
+ d->tabWidget->addTab( proxy, QIconSet(KGlobal::iconLoader()->loadIcon(
+ proxy->moduleInfo().icon(), KIcon::Desktop)),
+ /* QT eats ampersands for dinner. But not this time. */
+ proxy->moduleInfo().moduleName().replace( "&", "&&" ));
+
+ d->tabWidget->setTabToolTip( proxy, proxy->moduleInfo().comment() );
+
+ connect( proxy, SIGNAL(changed(KCModuleProxy *)), SLOT(moduleChanged(KCModuleProxy *)));
+
+ /* Collect our buttons - we go for the common deliminator */
+ d->buttons = d->buttons | proxy->realModule()->buttons();
+
+ /* If we should add an Administrator Mode button */
+ if ( proxy->moduleInfo().needsRootPrivileges() )
+ d->hasRootKCM=true;
+
+
+}
+
+void KCModuleContainer::tabSwitched( QWidget * module )
+{
+ if ( !d->hasRootKCM )
+ return;
+
+ /* Not like this. Not like this. */
+ disconnect( d->btnRootMode, 0, 0, 0 );
+ /* Welcome to the real world huh baby? */
+
+ KCModuleProxy* mod = (KCModuleProxy *) module;
+
+ if ( mod->moduleInfo().needsRootPrivileges() && !mod->rootMode() )
+ {
+ d->btnRootMode->setEnabled( true );
+ connect( d->btnRootMode, SIGNAL( clicked() ),
+ SLOT( runAsRoot() ));
+ connect( mod, SIGNAL( childClosed() ),
+ SLOT ( rootExited() ));
+ }
+ else
+ d->btnRootMode->setEnabled( false );
+
+ setQuickHelp( mod->quickHelp() );
+ setAboutData( const_cast<KAboutData*>(mod->aboutData()) );
+
+}
+
+void KCModuleContainer::runAsRoot()
+{
+ if ( d->tabWidget->currentPage() )
+ ( (KCModuleProxy *) d->tabWidget->currentPage() )->runAsRoot();
+ d->btnRootMode->setEnabled( false );
+}
+
+void KCModuleContainer::rootExited()
+{
+ connect( d->btnRootMode, SIGNAL( clicked() ), SLOT( runAsRoot() ));
+ d->btnRootMode->setEnabled( true );
+}
+
+void KCModuleContainer::save()
+{
+ ModuleList list = changedModules;
+ ModuleList::iterator it;
+ for ( it = list.begin() ; it !=list.end() ; ++it )
+ {
+ (*it)->save();
+ }
+
+ emit changed( false );
+
+}
+
+void KCModuleContainer::load()
+{
+ ModuleList list = allModules;
+ ModuleList::iterator it;
+ for ( it = list.begin() ; it !=list.end() ; ++it )
+ {
+ (*it)->load();
+ }
+
+ emit changed( false );
+}
+
+void KCModuleContainer::defaults()
+{
+ ModuleList list = allModules;
+ ModuleList::iterator it;
+ for ( it = list.begin() ; it !=list.end() ; ++it )
+ {
+ (*it)->defaults();
+ }
+
+ emit changed( true );
+}
+
+
+void KCModuleContainer::moduleChanged(KCModuleProxy * proxy)
+{
+ changedModules.append( proxy );
+ if( changedModules.isEmpty() )
+ return;
+
+ emit changed(true);
+}
+
+KCModuleContainer::~KCModuleContainer()
+{
+ delete d;
+}
+
+/***********************************************************************/
+
+
+
+
diff --git a/kutils/kcmodulecontainer.h b/kutils/kcmodulecontainer.h
new file mode 100644
index 000000000..6b23a16c9
--- /dev/null
+++ b/kutils/kcmodulecontainer.h
@@ -0,0 +1,207 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 KCMODULECONTAINER_H
+#define KCMODULECONTAINER_H
+
+#include <qvaluelist.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include <kcmodule.h>
+#include <kcmoduleloader.h>
+
+class QTabWidget;
+class QWidget;
+class QVBoxLayout;
+
+class KCModuleProxy;
+
+/**
+ * @ingroup kcmodule
+ * @brief KCModuleContainer is a convenience class encapsulating several KCModules.
+ *
+ * The KCModuleContainer class is a convenience class for organizing a multiple set
+ * of KCModule. KCModuleContainer is a sub class of KCModule and builds an interface mainly
+ * consisting of a tab widget where each tab contains one of the modules specified via one of the
+ * constructors. KCModuleContainer can handle modules which requires root permissions. What you
+ * most likely want is the KCMODULECONTAINER macro. \n
+ * Sometimes it is of interest to detect in runtime whether a module should be loaded or not. This
+ * can be achieved by sub classing KCModuleContainer, doing the probing/testing checks and then manually
+ * call addModule for each module which should be displayed. When all calls to addModule is done, call
+ * finalize() which performs some necessary final steps.
+ *
+ * @author Frans Englich <frans.englich@telia.com>
+ * @since 3.4
+ */
+class KUTILS_EXPORT KCModuleContainer : public KCModule
+{
+ Q_OBJECT
+ public:
+ /**
+ * Creates a KCModuleContainer with tabs, each one containing one of the
+ * specified modules in @p mods.
+ *
+ * @param parent the parent QWidget.
+ * @param name the module's name.
+ * @param mods The list of KCModules to be loaded. The name of each
+ * KCModule is its service name, that is the name of the desktop file without
+ * the ".desktop" part
+ *
+ */
+ KCModuleContainer( QWidget* parent, const char* name, const QStringList& mods );
+
+ /**
+ * This is a convenience function, instead of building a QStringList you
+ * can specify the modules in a comma separated QString. For example;
+ * \code
+ * KCModuleContainer* cont = KCModuleContainer( this, "kcm_misc", QString("kcm_energy, kcm_keyboard ,kcm_useraccount, kcm_mouse") );
+ * \endcode
+ * The other constructor takes its modules in a QStringlist which also can be constructed from a
+ * string and thus you will have to be explicit on the data type.
+ *
+ * What you probably want is the KCMODULECONTAINER macro which builds an KCModule
+ * for you, taking the modules you want as argument.
+ *
+ * @param parent The parent widget
+ * @param name The service name
+ * @param mods The modules to load
+ * @return The KCModule containing the requested modules.
+ */
+ KCModuleContainer( QWidget *parent, const char* name, const QString& mods = QString() );
+
+ /**
+ * Adds the specified module to the tab widget. Setting the tab icon, text,
+ * tool tip, connecting the signals is what it does.
+ *
+ * @param module the name of the module to add. The name is the desktop file's name
+ * without the ".desktop" part.
+ */
+ void addModule( const QString& module );
+
+ /**
+ * Default destructor.
+ */
+ virtual ~KCModuleContainer();
+
+ /**
+ * Reimplemented for internal purposes.
+ * @internal
+ */
+ void save();
+
+ /**
+ * Reimplemented for internal purposes.
+ * @internal
+ */
+ void load();
+
+ /**
+ * Reimplemented for internal purposes.
+ * @internal
+ */
+ void defaults();
+
+ protected:
+
+ /**
+ * Sets this KCM's buttons and adds a AdminMode button
+ * if necessary. If KCModuleContainer is subclassed finalize()
+ * should be called in the constructor after all calls to addModule
+ * have been done. Call it once.
+ */
+ void finalize();
+
+ typedef QValueList<KCModuleProxy*> ModuleList;
+
+ /**
+ * A list containing KCModuleProxy objects which
+ * have changed and must be saved.
+ */
+ ModuleList changedModules;
+
+ /**
+ * A list of all modules which are encapsulated.
+ */
+ ModuleList allModules; // KDE 4 put in the Private class and abstract with getter
+
+ private slots:
+
+ /**
+ * Enables/disables the Admin Mode button, as appropriate.
+ */
+ void tabSwitched( QWidget * module );
+
+ void moduleChanged(KCModuleProxy *proxy);
+
+ /**
+ * Called when the user clicks our custom root button.
+ */
+ void runAsRoot();
+
+ /**
+ * Enables the admin mode button
+ */
+ void rootExited();
+
+ private:
+
+ void init();
+
+ class KCModuleContainerPrivate;
+ KCModuleContainerPrivate *d;
+
+};
+
+/**
+ * @ingroup kcmodule
+ * This macro creates an factory declaration which when run creates an KCModule with specified
+ * modules. For example:
+ * \code
+ * KCMODULECONTAINER( "kcm_fonts, kcm_keyboard,kcm_fonts", misc_modules)
+ * \endcode
+ * would create a KCModule with three tabs, each containing one of the specified KCMs. Each
+ * use of the macro must be accompanied by a desktop file where the factory name equals
+ * the second argument in the macro(in this example, misc_modules). \n
+ * The module container takes care of testing the contained modules when being shown, as well
+ * as when the module itself is asked whether it should be shown.
+ *
+ * @param modules the modules to put in the container
+ * @param factoryName what factory name the module should have
+ */
+#define KCMODULECONTAINER( modules, factoryName ) \
+extern "C" \
+{ \
+ KCModule *create_## factoryName(QWidget *parent, const char *name) \
+ { \
+ return new KCModuleContainer( parent, name, QString( modules ) ); \
+ } \
+ \
+ bool test_## factoryName() \
+ { \
+ QStringList modList = QStringList::split( ",", QString(modules).remove( " " )); \
+ for ( QStringList::Iterator it = modList.begin(); it != modList.end(); ++it ) \
+ if ( KCModuleLoader::testModule( *it ) ) \
+ return true; \
+ return false; \
+ } \
+}
+
+#endif // KCMODULECONTAINER_H
+
diff --git a/kutils/kcmoduleinfo.cpp b/kutils/kcmoduleinfo.cpp
new file mode 100644
index 000000000..ababaadba
--- /dev/null
+++ b/kutils/kcmoduleinfo.cpp
@@ -0,0 +1,240 @@
+/*
+ Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+ Copyright (c) 2000 Matthias Elter <elter@kde.org>
+ Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (c) 2003 Matthias Kretz <kretz@kde.org>
+
+ This file is part of the KDE project
+
+ 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 <qvariant.h>
+
+#include <kdesktopfile.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+
+#include "kcmoduleinfo.h"
+
+class KCModuleInfo::KCModuleInfoPrivate
+{
+ public:
+ KCModuleInfoPrivate() :
+ testModule( false )
+ {}
+ ~KCModuleInfoPrivate()
+ { }
+
+ QString factoryName;
+ bool testModule;
+
+};
+
+KCModuleInfo::KCModuleInfo()
+{
+ _allLoaded = false;
+ d = new KCModuleInfoPrivate;
+}
+
+KCModuleInfo::KCModuleInfo(const QString& desktopFile)
+{
+ init( KService::serviceByStorageId(desktopFile) );
+}
+
+KCModuleInfo::KCModuleInfo( KService::Ptr moduleInfo )
+{
+ init(moduleInfo);
+}
+
+KCModuleInfo::KCModuleInfo( const KCModuleInfo &rhs )
+{
+ d = new KCModuleInfoPrivate;
+ ( *this ) = rhs;
+}
+
+// this re-implementation exists to ensure that other code always calls
+// our re-implementation, so in case we add data to the d pointer in the future
+// we can be sure that we get called when we are copied.
+KCModuleInfo &KCModuleInfo::operator=( const KCModuleInfo &rhs )
+{
+ _keywords = rhs._keywords;
+ _name = rhs._name;
+ _icon = rhs._icon;
+ _lib = rhs._lib;
+ _handle = rhs._handle;
+ _fileName = rhs._fileName;
+ _doc = rhs._doc;
+ _comment = rhs._comment;
+ _needsRootPrivileges = rhs._needsRootPrivileges;
+ _isHiddenByDefault = rhs._isHiddenByDefault;
+ _allLoaded = rhs._allLoaded;
+ _service = rhs._service;
+
+ *d = *(rhs.d);
+
+ return *this;
+}
+
+QString KCModuleInfo::factoryName() const
+{
+ if( d->factoryName.isEmpty() )
+ {
+ d->factoryName = _service->property("X-KDE-FactoryName", QVariant::String).toString();
+ if ( d->factoryName.isEmpty() )
+ d->factoryName = library();
+ }
+
+ return d->factoryName;
+}
+
+bool KCModuleInfo::operator==( const KCModuleInfo & rhs ) const
+{
+ return ( ( _name == rhs._name ) && ( _lib == rhs._lib ) && ( _fileName == rhs._fileName ) );
+}
+
+bool KCModuleInfo::operator!=( const KCModuleInfo & rhs ) const
+{
+ return ! operator==( rhs );
+}
+
+KCModuleInfo::~KCModuleInfo()
+{
+ delete d;
+}
+
+void KCModuleInfo::init(KService::Ptr s)
+{
+ _allLoaded = false;
+ d = new KCModuleInfoPrivate;
+
+ if ( s )
+ _service = s;
+ else
+ {
+ kdDebug(712) << "Could not find the service." << endl;
+ return;
+ }
+
+ // set the modules simple attributes
+ setName(_service->name());
+ setComment(_service->comment());
+ setIcon(_service->icon());
+
+ _fileName = ( _service->desktopEntryPath() );
+
+ // library and factory
+ setLibrary(_service->library());
+
+ // get the keyword list
+ setKeywords(_service->keywords());
+}
+
+void
+KCModuleInfo::loadAll()
+{
+ if( !_service ) /* We have a bogus service. All get functions will return empty/zero values */
+ return;
+
+ _allLoaded = true;
+
+ // library and factory
+ setHandle(_service->property("X-KDE-FactoryName", QVariant::String).toString());
+
+ QVariant tmp;
+
+ // read weight
+ tmp = _service->property( "X-KDE-Weight", QVariant::Int );
+ setWeight( tmp.isValid() ? tmp.toInt() : 100 );
+
+ // does the module need super user privileges?
+ tmp = _service->property( "X-KDE-RootOnly", QVariant::Bool );
+ setNeedsRootPrivileges( tmp.isValid() ? tmp.toBool() : false );
+
+ // does the module need to be shown to root only?
+ // Deprecated ! KDE 4
+ tmp = _service->property( "X-KDE-IsHiddenByDefault", QVariant::Bool );
+ setIsHiddenByDefault( tmp.isValid() ? tmp.toBool() : false );
+
+ // get the documentation path
+ setDocPath( _service->property( "DocPath", QVariant::String ).toString() );
+
+ tmp = _service->property( "X-KDE-Test-Module", QVariant::Bool );
+ setNeedsTest( tmp.isValid() ? tmp.asBool() : false );
+}
+
+QString
+KCModuleInfo::docPath() const
+{
+ if (!_allLoaded)
+ const_cast<KCModuleInfo*>(this)->loadAll();
+
+ return _doc;
+}
+
+QString
+KCModuleInfo::handle() const
+{
+ if (!_allLoaded)
+ const_cast<KCModuleInfo*>(this)->loadAll();
+
+ if (_handle.isEmpty())
+ return _lib;
+
+ return _handle;
+}
+
+int
+KCModuleInfo::weight() const
+{
+ if (!_allLoaded)
+ const_cast<KCModuleInfo*>(this)->loadAll();
+
+ return _weight;
+}
+
+bool
+KCModuleInfo::needsRootPrivileges() const
+{
+ if (!_allLoaded)
+ const_cast<KCModuleInfo*>(this)->loadAll();
+
+ return _needsRootPrivileges;
+}
+
+bool
+KCModuleInfo::isHiddenByDefault() const
+{
+ if (!_allLoaded)
+ const_cast<KCModuleInfo*>(this)->loadAll();
+
+ return _isHiddenByDefault;
+}
+
+bool KCModuleInfo::needsTest() const
+{
+ return d->testModule;
+}
+
+void KCModuleInfo::setNeedsTest( bool val )
+{
+ d->testModule = val;
+}
+
+
+
+// vim: ts=2 sw=2 et
diff --git a/kutils/kcmoduleinfo.h b/kutils/kcmoduleinfo.h
new file mode 100644
index 000000000..7ccaceb94
--- /dev/null
+++ b/kutils/kcmoduleinfo.h
@@ -0,0 +1,296 @@
+/*
+ Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+ Copyright (c) 2000 Matthias Elter <elter@kde.org>
+ Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (c) 2003 Matthias Kretz <kretz@kde.org>
+
+ This file is part of the KDE project
+
+ 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 KCMODULEINFO_H
+#define KCMODULEINFO_H
+
+#include <kservice.h>
+
+class QPixmap;
+class QString;
+class QStringList;
+
+/**
+ * @ingroup kcmodule
+ * A class that provides information about a KCModule
+ *
+ * KCModuleInfo provides various technical information, such as icon, library
+ * etc. about a KCModule.n
+ * @note Any values set with the set* functions is not
+ * written back with KCModuleInfo it only reads value from the desktop file.
+ *
+ * @internal
+ * @author Matthias Hoelzer-Kluepfel <mhk@kde.org>
+ * @author Matthias Elter <elter@kde.org>
+ * @author Daniel Molkentin <molkentin@kde.org>
+ * @since 3.2
+ *
+ */
+class KUTILS_EXPORT KCModuleInfo
+{
+
+public:
+
+ /**
+ * Constructs a KCModuleInfo.
+ * @note a KCModuleInfo object will have to be manually deleted, it is not
+ * done automatically for you.
+ * @param desktopFile the desktop file representing the module, or
+ * the name of the module.
+ */
+ KCModuleInfo(const QString& desktopFile);
+
+ /**
+ * Same as above but takes a KService::Ptr as argument.
+ *
+ * @note @p moduleInfo must be a valid pointer.
+ *
+ * @param moduleInfo specifies the module
+ */
+ KCModuleInfo( KService::Ptr moduleInfo );
+
+
+ /**
+ * Same as above but takes a KCModuleInfo as argument.
+ *
+ * @param rhs specifies the module
+ */
+ KCModuleInfo( const KCModuleInfo &rhs );
+
+ /**
+ * Same as above but creates an empty KCModuleInfo.
+ * You should not normally call this.
+ * @since 3.4
+ */
+ KCModuleInfo();
+
+ /**
+ * Assignment operator
+ */
+ KCModuleInfo &operator=( const KCModuleInfo &rhs );
+
+ /**
+ * Equal operator
+ *
+ * @return true if @p rhs equals itself
+ */
+
+ bool operator==( const KCModuleInfo &rhs ) const;
+
+ /**
+ * @return true if @p rhs is not equal itself
+ */
+ bool operator!=( const KCModuleInfo &rhs ) const;
+
+ /**
+ * Default destructor.
+ */
+ ~KCModuleInfo();
+
+ /**
+ * @return the filename of the .desktop file that describes the KCM
+ */
+ QString fileName() const { return _fileName; }
+
+ /**
+ * @return the keywords associated with this KCM.
+ */
+ const QStringList &keywords() const { return _keywords; }
+
+ /**
+ * Returns the module's factory name, if it's set. If not, the library
+ * name is returned.
+ * @returns the module's factory name
+ * @since 3.4
+ */
+ QString factoryName() const;
+
+ /**
+ * @return the module\'s (translated) name
+ */
+ QString moduleName() const { return _name; }
+ // changed from name() to avoid ambiguity with QObject::name() on multiple inheritance
+
+ /**
+ * @return a KSharedPtr to KService created from the modules .desktop file
+ */
+ KService::Ptr service() const { return _service; }
+
+ /**
+ * @return the module's (translated) comment field
+ */
+ QString comment() const { return _comment; }
+
+ /**
+ * @return the module's icon name
+ */
+ QString icon() const { return _icon; }
+
+ /**
+ * @return the path of the module's documentation
+ */
+ QString docPath() const;
+
+ /**
+ * @return the library name
+ */
+ QString library() const { return _lib; }
+
+ /**
+ * @return a handle (usually the contents of the FactoryName field)
+ */
+ QString handle() const;
+
+ /**
+ * @return the weight of the module which determines the order of the pages in
+ * the KCMultiDialog. It's set by the X-KDE-Weight field.
+ */
+ int weight() const;
+
+ /**
+ * @return whether the module might require root permissions
+ */
+ bool needsRootPrivileges() const;
+
+ /**
+ * @deprecated
+ * @return the isHiddenByDefault attribute.
+ */
+ bool isHiddenByDefault() const KDE_DEPRECATED;
+
+
+ /**
+ * @returns true if the module should be conditionally
+ * loaded.
+ * @since 3.4
+ */
+ bool needsTest() const;
+
+
+protected:
+
+ /**
+ * Sets the object's keywords.
+ * @param keyword the new keywords
+ */
+ void setKeywords(const QStringList &keyword) { _keywords = keyword; }
+
+ /**
+ * Sets the object's name.
+ * @param name the new name
+ */
+ void setName(const QString &name) { _name = name; }
+
+ /**
+ * Sets the object's name.
+ * @param comment the new comment
+ */
+ void setComment(const QString &comment) { _comment = comment; }
+
+ /**
+ * Sets the object's icon.
+ * @param icon the name of the new icon
+ */
+ void setIcon(const QString &icon) { _icon = icon; }
+
+ /**
+ * Set the object's library
+ * @param lib the name of the new library without any extensions or prefixs.
+ */
+ void setLibrary(const QString &lib) { _lib = lib; }
+
+ /**
+ * Sets the factory name
+ * @param handle The new factory name
+ */
+ void setHandle(const QString &handle) { _handle = handle; }
+
+ /**
+ * Sets the object's weight property which determines in what
+ * order modules will be displayed. Default is 100.
+ *
+ * @param weight the new weight
+ */
+ void setWeight(int weight) { _weight = weight; }
+
+
+ /**
+ * Sets if the module should be tested for loading.
+ * @param val the value to set
+ * @since 3.4
+ */
+ void setNeedsTest( bool val );
+
+ /**
+ * Toggles whether the represented module needs root privileges.
+ * Use with caution.
+ * @param needsRootPrivileges if module needs root privilges
+ */
+ void setNeedsRootPrivileges(bool needsRootPrivileges)
+ { _needsRootPrivileges = needsRootPrivileges; }
+
+ /**
+ * @deprecated
+ */
+ void setIsHiddenByDefault(bool isHiddenByDefault)
+ { _isHiddenByDefault = isHiddenByDefault; }
+
+ /**
+ * Sets the object's documentation path
+ * @param p the new documentation path
+ */
+ void setDocPath(const QString &p) { _doc = p; }
+
+ /**
+ * Reads the service entries specific for KCModule from the desktop file.
+ * The usual desktop entries are read in init.
+ */
+ void loadAll();
+
+private:
+
+ /**
+ * Reads the service entries. Called by the constructors.
+ */
+ void init(KService::Ptr s);
+
+private:
+
+ // KDE4 These needs to be moved to KCModuleInfoPrivate
+ QStringList _keywords;
+ QString _name, _icon, _lib, _handle, _fileName, _doc, _comment;
+ bool _needsRootPrivileges : 1;
+ bool _isHiddenByDefault : 1;
+ bool _allLoaded : 1;
+ int _weight;
+
+ KService::Ptr _service;
+
+ class KCModuleInfoPrivate;
+ KCModuleInfoPrivate *d;
+
+};
+
+#endif // KCMODULEINFO_H
+
+// vim: ts=2 sw=2 et
diff --git a/kutils/kcmoduleloader.cpp b/kutils/kcmoduleloader.cpp
new file mode 100644
index 000000000..24a5fa8b3
--- /dev/null
+++ b/kutils/kcmoduleloader.cpp
@@ -0,0 +1,288 @@
+/*
+ Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+ Copyright (c) 2000 Matthias Elter <elter@kde.org>
+ Copyright (c) 2003,2004 Matthias Kretz <kretz@kde.org>
+ Copyright (c) 2004 Frans Englich <frans.englich@telia.com>
+
+ This file is part of the KDE project
+
+ 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 <qfile.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kparts/componentfactory.h>
+
+#include "kcmoduleloader.h"
+
+
+/***************************************************************/
+/**
+ * When something goes wrong in loading the module, this one
+ * jumps in as a "dummy" module.
+ */
+class KCMError : public KCModule
+{
+ public:
+ KCMError( const QString& msg, const QString& details, QWidget* parent )
+ : KCModule( parent, "KCMError" )
+ {
+ QVBoxLayout* topLayout = new QVBoxLayout( this );
+ topLayout->addWidget( new QLabel( msg, this ) );
+ topLayout->addWidget( new QLabel( details, this ) );
+ }
+};
+/***************************************************************/
+
+
+
+
+KCModule* KCModuleLoader::load(const KCModuleInfo &mod, const QString &libname,
+ KLibLoader *loader, ErrorReporting report, QWidget * parent,
+ const char * name, const QStringList & args )
+{
+ // attempt to load modules with ComponentFactory, only if the symbol init_<lib> exists
+ // (this is because some modules, e.g. kcmkio with multiple modules in the library,
+ // cannot be ported to KGenericFactory)
+ KLibrary *lib = loader->library(QFile::encodeName(libname.arg(mod.library())));
+ if (lib) {
+ QString initSym("init_");
+ initSym += libname.arg(mod.library());
+
+ if ( lib->hasSymbol(QFile::encodeName(initSym)) )
+ {
+ KLibFactory *factory = lib->factory();
+ if ( factory )
+ {
+ KCModule *module = KParts::ComponentFactory::createInstanceFromFactory<KCModule>( factory, parent, name ? name : mod.handle().latin1(), args );
+ if (module)
+ return module;
+ }
+ // else do a fallback
+ kdDebug(1208) << "Unable to load module using ComponentFactory. Falling back to old loader." << endl;
+ }
+
+ // get the create_ function
+ QString factory("create_%1");
+ void *create = lib->symbol(QFile::encodeName(factory.arg(mod.handle())));
+
+ if (create)
+ {
+ // create the module
+ KCModule* (*func)(QWidget *, const char *);
+ func = (KCModule* (*)(QWidget *, const char *)) create;
+ return func( parent, name ? name : mod.handle().latin1() );
+ }
+ else
+ {
+ QString libFileName = lib->fileName();
+ lib->unload();
+ return reportError( report, i18n("<qt>There was an error when loading the module '%1'.<br><br>"
+ "The desktop file (%2) as well as the library (%3) was found but "
+ "yet the module could not be loaded properly. Most likely "
+ "the factory declaration was wrong, or the "
+ "create_* function was missing.</qt>")
+ .arg( mod.moduleName() )
+ .arg( mod.fileName() )
+ .arg( libFileName ),
+ QString::null, parent );
+ }
+
+ lib->unload();
+ }
+ return reportError( report, i18n("The specified library %1 could not be found.")
+ .arg( mod.library() ), QString::null, parent );
+ return 0;
+}
+
+KCModule* KCModuleLoader::loadModule(const KCModuleInfo &mod, bool withfallback, QWidget * parent, const char * name, const QStringList & args )
+{
+ return loadModule( mod, None, withfallback, parent, name, args );
+}
+
+KCModule* KCModuleLoader::loadModule(const KCModuleInfo &mod, ErrorReporting report, bool withfallback, QWidget * parent, const char * name, const QStringList & args )
+{
+ /*
+ * Simple libraries as modules are the easiest case:
+ * We just have to load the library and get the module
+ * from the factory.
+ */
+
+ if ( !mod.service() )
+ {
+ return reportError( report,
+ i18n("The module %1 could not be found.")
+ .arg( mod.moduleName() ), i18n("<qt><p>The diagnostics is:<br>The desktop file %1 could not be found.</qt>").arg(mod.fileName()), parent );
+ }
+
+ if (!mod.library().isEmpty())
+ {
+ // get the library loader instance
+
+ KLibLoader *loader = KLibLoader::self();
+
+ KCModule *module = load(mod, "kcm_%1", loader, report, parent, name, args );
+ /*
+ * Only try to load libkcm_* if it exists, otherwise KLibLoader::lastErrorMessage would say
+ * "libkcm_foo not found" instead of the real problem with loading kcm_foo.
+ */
+ if (!KLibLoader::findLibrary( QCString( "libkcm_" ) + QFile::encodeName( mod.library() ) ).isEmpty() )
+ module = load(mod, "libkcm_%1", loader, report, parent, name, args );
+ if (module)
+ return module;
+ return reportError( report,
+ i18n("The module %1 could not be loaded.")
+ .arg( mod.moduleName() ), QString::null, parent );
+ }
+
+ /*
+ * Ok, we could not load the library.
+ * Try to run it as an executable.
+ * This must not be done when calling from kcmshell, or you'll
+ * have infinite recursion
+ * (startService calls kcmshell which calls modloader which calls startService...)
+ *
+ */
+ if(withfallback)
+ {
+ KApplication::startServiceByDesktopPath(mod.fileName(), QString::null);
+ }
+ else
+ {
+ return reportError( report,
+ i18n("The module %1 is not a valid configuration module.")
+ .arg( mod.moduleName() ), i18n("<qt><p>The diagnostics is:<br>The desktop file %1 does not specify a library.</qt>").arg(mod.fileName()), parent );
+ }
+
+ return 0;
+}
+
+KCModule* KCModuleLoader::loadModule(const QString &module, QWidget *parent,
+ const char *name, const QStringList & args)
+{
+ return loadModule(KCModuleInfo(module), None, false, parent, name, args);
+}
+
+KCModule* KCModuleLoader::loadModule(const QString &module, ErrorReporting
+ report, QWidget *parent, const char *name, const QStringList & args)
+{
+ return loadModule(KCModuleInfo(module), report, false, parent, name, args);
+}
+
+void KCModuleLoader::unloadModule(const KCModuleInfo &mod)
+{
+ // get the library loader instance
+ KLibLoader *loader = KLibLoader::self();
+
+ // try to unload the library
+ QString libname("libkcm_%1");
+ loader->unloadLibrary(QFile::encodeName(libname.arg(mod.library())));
+
+ libname = "kcm_%1";
+ loader->unloadLibrary(QFile::encodeName(libname.arg(mod.library())));
+}
+
+void KCModuleLoader::showLastLoaderError(QWidget *parent)
+{
+ KMessageBox::detailedError(parent,
+ i18n("There was an error loading the module."),i18n("<qt><p>The diagnostics is:<br>%1"
+ "<p>Possible reasons:</p><ul><li>An error occurred during your last "
+ "KDE upgrade leaving an orphaned control module<li>You have old third party "
+ "modules lying around.</ul><p>Check these points carefully and try to remove "
+ "the module mentioned in the error message. If this fails, consider contacting "
+ "your distributor or packager.</p></qt>")
+ .arg(KLibLoader::self()->lastErrorMessage()));
+
+}
+
+bool KCModuleLoader::testModule( const QString& module )
+{
+ return testModule( KCModuleInfo( module ) );
+}
+
+bool KCModuleLoader::testModule( const KCModuleInfo& module )
+{
+ if (!module.service())
+ {
+ kdDebug(1208) << "Module '" << module.fileName() << "' not found." << endl;
+ return true;
+ }
+
+ bool doLoad = module.service()->property( "X-KDE-Test-Module", QVariant::Bool ).toBool();
+ if( !doLoad )
+ {
+ return true;
+ }
+ else
+ {
+ /**
+ * If something fails we return true - we can't risk functionality becoming
+ * unavailable because of a buggy test. Furthermore, the error needs to
+ * show so it is discovered. KCModuleProxy will detect the error and load
+ * a corresponding KCMError.
+ * */
+ KLibLoader* loader = KLibLoader::self();
+ KLibrary* library = loader->library( QFile::encodeName((QString("kcm_%1").arg(module.library()))) );
+ if( library )
+ {
+ void *test_func = library->symbol( QString("test_%1").arg(module.factoryName()).utf8() );
+ if( test_func )
+ {
+ bool (*func)() = (bool(*)())test_func;
+ if( func() )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ kdDebug(1208) << "The test function for module '" << module.fileName() << "' could not be found." << endl;
+ return true;
+ }
+ }
+ kdDebug(1208) << "The library '" << module.library() << "' could not be found." << endl;
+ return true;
+ }
+}
+
+KCModule* KCModuleLoader::reportError( ErrorReporting report, const QString & text,
+ QString details, QWidget * parent )
+{
+ if( details.isNull() )
+ details = i18n("<qt><p>The diagnostics is:<br>%1"
+ "<p>Possible reasons:</p><ul><li>An error occurred during your last "
+ "KDE upgrade leaving an orphaned control module<li>You have old third party "
+ "modules lying around.</ul><p>Check these points carefully and try to remove "
+ "the module mentioned in the error message. If this fails, consider contacting "
+ "your distributor or packager.</p></qt>").arg(KLibLoader::self()->lastErrorMessage());
+ if( report & Dialog )
+ KMessageBox::detailedError( parent, text, details );
+ if( report & Inline )
+ return new KCMError( text, details, parent );
+ return 0;
+}
+
+// vim: ts=2 sw=2 et
+
diff --git a/kutils/kcmoduleloader.h b/kutils/kcmoduleloader.h
new file mode 100644
index 000000000..8aa6aac69
--- /dev/null
+++ b/kutils/kcmoduleloader.h
@@ -0,0 +1,220 @@
+/*
+ Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org>
+ Copyright (c) 2002-2003 Daniel Molkentin <molkentin@kde.org>
+
+ This file is part of the KDE project
+
+ 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 MODLOADER_H
+#define MODLOADER_H
+
+#include <kcmodule.h>
+#include <kcmoduleinfo.h>
+
+class QWidget;
+class KLibLoader;
+
+/**
+ * @ingroup kcmodule
+ * @brief Loads a KControl Module.
+ *
+ * KCModuleLoader tries in several ways
+ * to locate and load a KCModule. If loading fails a
+ * zero pointer is returned. \n
+ * It is very unlikely KCModuleLoader is what you want
+ * and @ref KCModuleProxy suits your needs.
+ *
+ * @author Matthias Hoelzer-Kluepfel <mhk@kde.org>
+ * @author Frans Englich <frans.englich@telia.com>
+ * @since 3.2
+ * @internal
+**/
+class KUTILS_EXPORT KCModuleLoader
+{
+ public:
+
+ /**
+ * Loads a @ref KCModule. If loading fails a zero pointer is returned.
+ * @param module what module to load
+ * @param withFallback if true and loading failed a separate window
+ * with the module may appear and a zero pointer is a returned
+ * @param parent The parent widget
+ * @param name The widget's name
+ * @param args A list of arguments for the module to load
+ *
+ * @return a pointer to the loaded @ref KCModule
+ *
+ * @deprecated use the function which explicitly states the error reporting
+ * method
+ */
+ static KCModule *loadModule(const KCModuleInfo &module, bool withFallback=true,
+ QWidget * parent = 0, const char * name = 0,
+ const QStringList & args = QStringList() ) KDE_DEPRECATED;
+
+ /**
+ * Loads a @ref KCModule. If loading fails a zero pointer is returned.
+ * @param module what module to load
+ * with the module may appear and a zero pointer is a returned
+ * @param parent The parent widget
+ * @param name The widget's name
+ * @param args A list of arguments for the module to load
+ *
+ * @deprecated use the function which explicitly states the error reporting
+ * method
+ */
+ static KCModule *loadModule(const QString &module, QWidget *parent = 0,
+ const char *name = 0, const QStringList & args = QStringList()) KDE_DEPRECATED;
+
+ /**
+ * Determines the way errors are reported
+ */
+ enum ErrorReporting {
+ /**
+ * no error reporting is done
+ * */
+ None = 0,
+ /**
+ * the error report is shown instead of the
+ * KCModule that should have * been loaded
+ */
+ Inline = 1,
+ /**
+ * shows a dialog with the error report
+ */
+ Dialog = 2,
+ /**
+ * does both Inline and Dialog
+ */
+ Both = 3
+ };
+
+ /**
+ * Loads a @ref KCModule. If loading fails a zero pointer is returned.
+ * @param module what module to load
+ * @param report see ErrorReporting
+ * @param withFallback if true and loading failed a separate window
+ * with the module may appear and a zero pointer is a returned
+ * @param parent The parent widget
+ * @param name The widget's name
+ * @param args A list of arguments for the module to load
+ *
+ * @return a pointer to the loaded @ref KCModule
+ * @since 3.4
+ */
+ static KCModule *loadModule(const KCModuleInfo &module, ErrorReporting
+ report, bool withFallback=true, QWidget * parent = 0,
+ const char * name = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Loads a @ref KCModule. If loading fails a zero pointer is returned.
+ * @param module what module to load
+ * @param report see ErrorReporting
+ * with the module may appear and a zero pointer is a returned
+ * @param parent The parent widget
+ * @param name The widget's name
+ * @param args A list of arguments for the module to load
+ *
+ * @return a pointer to the loaded @ref KCModule
+ * @since 3.4
+ */
+ static KCModule *loadModule(const QString &module, ErrorReporting
+ report, QWidget *parent = 0, const char *name = 0,
+ const QStringList & args = QStringList());
+
+ /**
+ * Unloads the module's library
+ * @param mod What module to unload for
+ */
+ static void unloadModule(const KCModuleInfo &mod);
+
+ /**
+ * Display a message box explaining an error occured and possible
+ * reasons to why.
+ *
+ * @deprecated Use a constructor with ErrorReporting set to Dialog to show a
+ * message box like this function did.
+ */
+ static void showLastLoaderError(QWidget *parent) KDE_DEPRECATED;
+
+
+ /**
+ * Checks whether an KCModule should be shown by running its
+ * test function. If it is unsure whether a module should be shown, it should
+ * be made available, leaving the decision to the user.
+ * If false is returned, the module should not be loaded in any interface.
+ *
+ * A module declares it needs to be tested by having "X-KDE-Test-Module=true" in
+ * its desktop file. When that line exists, the following code must be available
+ * in the module's library:
+ *
+ * \code
+ * extern "C"
+ * {
+ * bool test_moduleName()
+ * {
+ * // Code testing for hardware/software presence.
+ * return true; // and the modue will be loaded.
+ * }
+ *
+ * }
+ * \endcode
+ *
+ * where moduleName is the library name for the module.
+ *
+ * @param module the module to check
+ * @returns true if the module should be loaded
+ * @since 3.4
+ */
+ static bool testModule( const QString& module );
+
+ /**
+ * Convenience function, essentially the same as above.
+ *
+ * @param module the module to check
+ * @returns true if the module should be loaded
+ * @since 3.4
+ */
+ static bool testModule( const KCModuleInfo& module );
+
+ /**
+ * Returns a KCModule containing the messages @p report and @p text.
+ *
+ * @param report the type of error reporting, see ErrorReporting
+ * @param text the main message
+ * @param details any additional details
+ * @param parent The parent widget
+ *
+ * @since 3.4
+ * @internal
+ */
+ static KCModule* reportError( ErrorReporting report, const QString & text,
+ QString details, QWidget * parent );
+
+ private:
+
+ /**
+ * Internal loader called by the public loaders.
+ * @internal
+ */
+ static KCModule* load(const KCModuleInfo &mod, const QString &libname,
+ KLibLoader *loader, ErrorReporting report, QWidget * parent = 0,
+ const char * name = 0, const QStringList & args = QStringList() );
+
+};
+
+// vim: ts=2 sw=2 et
+#endif // MODLOADER_H
+
diff --git a/kutils/kcmoduleproxy.cpp b/kutils/kcmoduleproxy.cpp
new file mode 100644
index 000000000..3822db841
--- /dev/null
+++ b/kutils/kcmoduleproxy.cpp
@@ -0,0 +1,649 @@
+/* This file is part of the KDE project
+ Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+ 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 <qapplication.h>
+#include <qcursor.h>
+#include <qdatastream.h>
+#include <qevent.h>
+#include <qfileinfo.h>
+#include <qframe.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpoint.h>
+#include <qscrollview.h>
+#include <qtextstream.h>
+#include <qvbox.h>
+#include <qwhatsthis.h>
+#include <qwidget.h>
+
+#include <dcopclient.h>
+#include <qxembed.h>
+
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmodule.h>
+#include <kcmoduleinfo.h>
+#include <kcmoduleloader.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kprocess.h>
+#include <kservice.h>
+#include <kstandarddirs.h>
+#include <kuser.h>
+
+#include <X11/Xlib.h>
+
+#include "kcmoduleproxy.h"
+#include "kcmoduleproxyIface.h"
+#include "kcmoduleproxyIfaceImpl.h"
+
+/***************************************************************/
+class KCModuleProxy::KCModuleProxyPrivate
+{
+ public:
+ KCModuleProxyPrivate( const KCModuleInfo & info )
+ : args( 0 )
+ , kcm( 0 )
+ //, view( 0 )
+ , embedWidget( 0 )
+ , rootProcess ( 0 )
+ , embedFrame ( 0 )
+ , dcopObject( 0 )
+ , dcopClient( 0 )
+ , topLayout( 0 )
+ , rootCommunicator( 0 )
+ , rootInfo( 0 )
+ , modInfo( info )
+ , withFallback( false )
+ , changed( false )
+ , rootMode( false )
+ , bogusOccupier( false )
+ , isInitialized( false )
+ {}
+
+ ~KCModuleProxyPrivate()
+ {
+ delete rootInfo; // Delete before embedWidget!
+ delete embedWidget; // Delete before embedFrame!
+ delete embedFrame;
+ delete dcopClient;
+ delete dcopObject;
+ delete rootCommunicator;
+ delete rootProcess;
+ delete kcm;
+ }
+
+ QStringList args;
+ KCModule *kcm;
+ QXEmbed *embedWidget;
+ KProcess *rootProcess;
+ QVBox *embedFrame;
+ KCModuleProxyIfaceImpl *dcopObject;
+ DCOPClient *dcopClient;
+ QVBoxLayout *topLayout; /* Contains QScrollView view, and root stuff */
+ KCModuleProxyRootCommunicatorImpl *rootCommunicator;
+ QLabel *rootInfo;
+ QCString dcopName;
+ KCModuleInfo modInfo;
+ bool withFallback;
+ bool changed;
+ bool rootMode;
+ bool bogusOccupier;
+ bool isInitialized;
+};
+/***************************************************************/
+
+
+
+/*
+ TODO:
+
+ - How KCModuleProxy behaves wrt memory leaks and behavior, when exiting
+ from root mode is not tested, because no code make use of it. It needs
+ work, if it should be used.
+
+ - Should write a document which outlines test cases, to avoid
+ regressions. This class is a hazard.
+
+ - Two Layout problems in runAsRoot:
+ * lblBusy doesn't show
+ * d->kcm/d->rootInfo doesn't get it right when the user
+ presses cancel in the kdesu dialog
+
+ - Resizing horizontally is contrained; minimum size is set somewhere.
+ It appears to be somehow derived from the module's size.
+
+ - Prettify: set icon in KCMultiDialog.
+
+ - Perhaps it's possible to link against kdesu such that
+ the dialog is in process?
+
+ */
+/***************************************************************/
+KCModule * KCModuleProxy::realModule() const
+{
+
+ /*
+ * Note, don't call any function that calls realModule() since
+ * that leads to an infinite loop.
+ */
+
+ kdDebug(711) << k_funcinfo << endl;
+
+ /* Already loaded */
+ if( d->kcm )
+ return d->kcm;
+
+ /* /We/ have no kcm, but kcmshell running with root prevs does.. */
+ if( d->rootMode )
+ return 0;
+
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+
+ KCModuleProxy * that = const_cast<KCModuleProxy*>( this );
+
+ if( !d->isInitialized )
+ {
+ d->dcopName = moduleInfo().handle().prepend("KCModuleProxy-").utf8();
+ d->topLayout = new QVBoxLayout( that, 0, 0, "topLayout" );
+
+ d->isInitialized = true;
+ }
+
+ if( !d->dcopClient )
+ d->dcopClient = new DCOPClient();
+
+ if( !d->dcopClient->isRegistered() )
+ d->dcopClient->registerAs( d->dcopName, false );
+
+ d->dcopClient->setAcceptCalls( true );
+
+ if( d->dcopClient->appId() == d->dcopName || d->bogusOccupier )
+ { /* We got the name we requested, because no one was before us,
+ * or, it was an random application which had picked that name */
+ kdDebug(711) << "Module not already loaded, loading module" << endl;
+
+ d->dcopObject = new KCModuleProxyIfaceImpl( d->dcopName, that );
+
+ d->kcm = KCModuleLoader::loadModule( moduleInfo(), KCModuleLoader::Inline, d->withFallback,
+ that, name(), d->args );
+
+ connect( d->kcm, SIGNAL( changed( bool ) ),
+ SLOT(moduleChanged(bool)) );
+ connect( d->kcm, SIGNAL( destroyed() ),
+ SLOT( moduleDestroyed() ) );
+ connect( d->kcm, SIGNAL(quickHelpChanged()),
+ SIGNAL(quickHelpChanged()));
+ QWhatsThis::add( that, d->kcm->quickHelp() );
+
+ d->topLayout->addWidget( d->kcm );
+
+ if ( !d->rootInfo && /* If it's already done */
+ moduleInfo().needsRootPrivileges() /* root, anyone? */ &&
+ !KUser().isSuperUser() ) /* Not necessary if we're root */
+ {
+
+ d->rootInfo = new QLabel( that, "rootInfo" );
+ d->topLayout->insertWidget( 0, d->rootInfo );
+
+ d->rootInfo->setFrameShape(QFrame::Box);
+ d->rootInfo->setFrameShadow(QFrame::Raised);
+
+ const QString msg = d->kcm->rootOnlyMsg();
+ if( msg.isEmpty() )
+ d->rootInfo->setText(i18n(
+ "<b>Changes in this section requires root access.</b><br />"
+ "Click the \"Administrator Mode\" button to "
+ "allow modifications."));
+ else
+ d->rootInfo->setText(msg);
+
+ QWhatsThis::add( d->rootInfo, i18n(
+ "This section requires special permissions, probably "
+ "for system-wide changes; therefore, it is "
+ "required that you provide the root password to be "
+ "able to change the module's properties. If "
+ "you do not provide the password, the module will be "
+ "disabled."));
+ }
+ }
+ else
+ {
+ kdDebug(711) << "Module already loaded, loading KCMError" << endl;
+
+ d->dcopClient->detach();
+ /* Re-register as anonymous */
+ d->dcopClient->attach();
+
+ d->dcopClient->setNotifications( true );
+ connect( d->dcopClient, SIGNAL( applicationRemoved( const QCString& )),
+ SLOT( applicationRemoved( const QCString& )));
+
+ /* Figure out the name of where the module is already loaded */
+ QByteArray replyData, data;
+ QCString replyType;
+ QString result;
+ QDataStream arg, stream( replyData, IO_ReadOnly );
+
+ if( d->dcopClient->call( d->dcopName, d->dcopName, "applicationName()",
+ data, replyType, replyData ))
+ {
+ stream >> result;
+
+ d->kcm = KCModuleLoader::reportError( KCModuleLoader::Inline,
+ i18n( "Argument is application name", "This configuration section is "
+ "already opened in %1" ).arg( result ), " ", that );
+
+ d->topLayout->addWidget( d->kcm );
+ }
+ else
+ {
+ kdDebug(711) << "Calling KCModuleProxy's DCOP interface for fetching the name failed." << endl;
+ d->bogusOccupier = true;
+ QApplication::restoreOverrideCursor();
+ return realModule();
+ }
+ }
+
+ QApplication::restoreOverrideCursor();
+
+ return d->kcm;
+}
+
+void KCModuleProxy::applicationRemoved( const QCString& app )
+{
+ if( app == d->dcopName )
+ {
+ /* Violence: Get rid of KCMError & CO, so that
+ * realModule() attempts to reload the module */
+ delete d->kcm;
+ d->kcm = 0;
+ d->dcopClient->setNotifications( false );
+ realModule();
+ d->kcm->show();
+ }
+}
+
+void KCModuleProxy::showEvent( QShowEvent * ev )
+{
+
+ kdDebug(711) << k_funcinfo << endl;
+ ( void )realModule();
+
+ /* We have no kcm, if we're in root mode */
+ if( d->kcm )
+ d->kcm->show();
+
+ QWidget::showEvent( ev );
+
+}
+
+void KCModuleProxy::runAsRoot()
+{
+ if ( !moduleInfo().needsRootPrivileges() )
+ return;
+
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+
+ delete d->rootProcess;
+ delete d->embedWidget;
+ delete d->embedFrame;
+
+ d->embedFrame = new QVBox( this, "embedFrame" );
+ d->embedFrame->setFrameStyle( QFrame::Box | QFrame::Raised );
+
+ QPalette pal( red );
+ pal.setColor( QColorGroup::Background,
+ colorGroup().background() );
+ d->embedFrame->setPalette( pal );
+ d->embedFrame->setLineWidth( 2 );
+ d->embedFrame->setMidLineWidth( 2 );
+ d->topLayout->addWidget(d->embedFrame,1);
+
+ d->embedWidget = new QXEmbed( d->embedFrame, "embedWidget" );
+
+ d->embedFrame->show();
+
+ QLabel *lblBusy = new QLabel(i18n("<big>Loading...</big>"), d->embedWidget, "lblBusy" );
+ lblBusy->setTextFormat(RichText);
+ lblBusy->setAlignment(AlignCenter);
+ lblBusy->setGeometry(0,0, d->kcm->width(), d->kcm->height());
+ lblBusy->show();
+
+ deleteClient();
+ /* The DCOP registration is now gone, and it will occur again when kcmshell soon
+ * registers. Here's a race condition in other words, but how likely is that?
+ *
+ * - It's a user initiated action, which means the user have to do weird stuff, very
+ * quick.
+ * - If the user _do_ manage to fsck up, the code will recover gracefully, see realModule().
+ *
+ * So no worry. At the end of this function, communication with
+ * the DCOP object is established.
+ */
+
+ /* Prepare the process to run the kcmshell */
+ QString cmd = moduleInfo().service()->exec().stripWhiteSpace();
+ if (cmd.left(5) == "kdesu")
+ {
+ cmd = cmd.remove(0,5).stripWhiteSpace();
+
+ /* Remove all kdesu switches */
+ while( cmd.length() > 1 && cmd[ 0 ] == '-' )
+ cmd = cmd.remove( 0, cmd.find( ' ' ) ).stripWhiteSpace();
+ }
+
+ if (cmd.left(8) == "kcmshell")
+ cmd = cmd.remove(0,8).stripWhiteSpace();
+
+ /* Run the process */
+ QString kdesu = KStandardDirs::findExe("kdesu");
+ if (!kdesu.isEmpty())
+ {
+
+ d->rootProcess = new KProcess;
+
+ *d->rootProcess << kdesu;
+ *d->rootProcess << "--nonewdcop" << "-n" << "-d" << QString( "-i%1" ).arg(moduleInfo().icon());
+
+ *d->rootProcess << QString("%1 %2 --embed-proxy %3 --lang %4").arg(locate("exe", "kcmshell"))
+ .arg(cmd).arg(d->embedWidget->winId()).arg(KGlobal::locale()->language());
+
+ connect(d->rootProcess, SIGNAL(processExited(KProcess*)), SLOT(rootExited()));
+
+ if ( !d->rootProcess->start( KProcess::NotifyOnExit ))
+ {
+ d->rootMode = false;
+ rootExited();
+ }
+ else
+ {
+ d->rootMode = true;
+ kapp->dcopClient();
+ d->rootCommunicator = new KCModuleProxyRootCommunicatorImpl( d->dcopName + "-RootCommunicator", this );
+ }
+
+ delete lblBusy;
+ QApplication::restoreOverrideCursor();
+ return;
+ }
+
+ /* Clean up in case of failure */
+ delete d->embedWidget;
+ d->embedWidget = 0;
+ delete d->embedFrame;
+ d->embedFrame = 0;
+
+ QApplication::restoreOverrideCursor();
+}
+
+void KCModuleProxy::rootExited()
+{
+ kdDebug(711) << k_funcinfo << endl;
+
+ if ( d->embedWidget->embeddedWinId() )
+ XDestroyWindow(qt_xdisplay(), d->embedWidget->embeddedWinId());
+
+ delete d->embedWidget;
+ d->embedWidget = 0;
+
+ delete d->rootProcess;
+ d->rootProcess = 0;
+
+ delete d->embedFrame;
+ d->embedFrame=0;
+
+ delete d->rootCommunicator;
+ d->rootCommunicator = 0;
+
+ /* Such that the "ordinary" module loads again */
+ d->rootMode = false;
+
+ d->topLayout->invalidate();
+
+ QShowEvent ev;
+ showEvent( &ev );
+
+ moduleChanged( false );
+ emit childClosed();
+}
+
+KCModuleProxy::~KCModuleProxy()
+{
+ deleteClient();
+ KCModuleLoader::unloadModule(moduleInfo());
+
+ delete d;
+}
+
+void KCModuleProxy::deleteClient()
+{
+ if( d->embedWidget )
+ XKillClient(qt_xdisplay(), d->embedWidget->embeddedWinId());
+
+
+ delete d->kcm;
+ d->kcm = 0;
+
+ delete d->dcopObject;
+ d->dcopObject = 0;
+
+ if( d->dcopClient && !d->dcopClient->detach() )
+ kdDebug(711) << "Unregistering from DCOP failed." << endl;
+
+ delete d->dcopClient;
+ d->dcopClient = 0;
+
+ kapp->syncX();
+
+}
+
+void KCModuleProxy::moduleChanged( bool c )
+{
+ if( d->changed == c )
+ return;
+
+ d->changed = c;
+ emit changed( c );
+ emit changed( this );
+}
+
+void KCModuleProxy::moduleDestroyed()
+{
+ d->kcm = 0;
+}
+
+KCModuleProxy::KCModuleProxy( const KService::Ptr & service, bool withFallback,
+ QWidget * parent, const char * name, const QStringList & args)
+ : QWidget( parent, name )
+{
+ init( KCModuleInfo( service ));
+ d->args = args;
+ d->withFallback = withFallback;
+}
+
+KCModuleProxy::KCModuleProxy( const KCModuleInfo & info, bool withFallback,
+ QWidget * parent, const char * name, const QStringList & args )
+ : QWidget( parent, name )
+{
+ init( info );
+ d->args = args;
+ d->withFallback = withFallback;
+}
+
+KCModuleProxy::KCModuleProxy( const QString& serviceName, bool withFallback,
+ QWidget * parent, const char * name,
+ const QStringList & args)
+ : QWidget( parent, name )
+{
+ init( KCModuleInfo( serviceName ));
+ d->args = args;
+ d->withFallback = withFallback;
+}
+
+void KCModuleProxy::init( const KCModuleInfo& info )
+{
+ kdDebug(711) << k_funcinfo << endl;
+
+ d = new KCModuleProxyPrivate( info );
+
+ /* This is all we do for now; all the heavy work is
+ * done in realModule(). It's called when the module
+ * _actually_ is needed, in for example showEvent().
+ * The module is loaded "on demand" -- lazy loading.
+ */
+
+}
+
+void KCModuleProxy::load()
+{
+
+ if( d->rootMode )
+ callRootModule( "load()" );
+ else if( realModule() )
+ {
+ d->kcm->load();
+ moduleChanged( false );
+ }
+}
+
+void KCModuleProxy::save()
+{
+ if( d->rootMode )
+ callRootModule( "save()" );
+ else if( d->changed && realModule() )
+ {
+ d->kcm->save();
+ moduleChanged( false );
+ }
+}
+
+void KCModuleProxy::callRootModule( const QCString& function )
+{
+ QByteArray sendData, replyData;
+ QCString replyType;
+
+ /* Note, we don't use d->dcopClient here, because it's used for
+ * the loaded module(and it's not "us" when this function is called) */
+ if( !kapp->dcopClient()->call( d->dcopName, d->dcopName, function, sendData,
+ replyType, replyData, true, -1 ))
+ kdDebug(711) << "Calling function '" << function << "' failed." << endl;
+
+}
+
+void KCModuleProxy::defaults()
+{
+ if( d->rootMode )
+ callRootModule( "defaults()" );
+ if( realModule() )
+ d->kcm->defaults();
+}
+
+QString KCModuleProxy::quickHelp() const
+{
+
+ if( !d->rootMode )
+ return realModule() ? realModule()->quickHelp() : QString::null;
+ else
+ {
+ QByteArray data, replyData;
+ QCString replyType;
+
+ if (kapp->dcopClient()->call(d->dcopName, d->dcopName, "quickHelp()",
+ data, replyType, replyData))
+ kdDebug(711) << "Calling DCOP function bool changed() failed." << endl;
+ else
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ if (replyType == "QString")
+ {
+ QString result;
+ reply >> result;
+ return result;
+ }
+ else
+ kdDebug(711) << "DCOP function changed() returned mumbo jumbo." << endl;
+ }
+ return QString::null;
+ }
+}
+
+const KAboutData * KCModuleProxy::aboutData() const
+{
+ if( !d->rootMode )
+ return realModule() ? realModule()->aboutData() : 0;
+ else
+ /* This needs fixing, perhaps cache a KAboutData copy
+ * while in root mode? */
+ return 0;
+
+
+}
+
+int KCModuleProxy::buttons() const
+{
+ return realModule() ? realModule()->buttons() :
+ KCModule::Help | KCModule::Default | KCModule::Apply ;
+}
+
+QString KCModuleProxy::rootOnlyMsg() const
+{
+ return realModule() ? realModule()->rootOnlyMsg() : QString::null;
+}
+
+bool KCModuleProxy::useRootOnlyMsg() const
+{
+ return realModule() ? realModule()->useRootOnlyMsg() : true;
+}
+
+KInstance * KCModuleProxy::instance() const
+{
+ return realModule() ? realModule()->instance() : 0;
+}
+
+bool KCModuleProxy::changed() const
+{
+ return d->changed;
+}
+
+const KCModuleInfo& KCModuleProxy::moduleInfo() const
+{
+ return d->modInfo;
+}
+
+bool KCModuleProxy::rootMode() const
+{
+ return d->rootMode;
+}
+
+QCString KCModuleProxy::dcopName() const
+{
+ return d->dcopName;
+}
+
+void KCModuleProxy::emitQuickHelpChanged()
+{
+ emit quickHelpChanged();
+}
+
+/***************************************************************/
+#include "kcmoduleproxy.moc"
+
+// vim: sw=4 ts=4 noet
diff --git a/kutils/kcmoduleproxy.h b/kutils/kcmoduleproxy.h
new file mode 100644
index 000000000..b785bbaca
--- /dev/null
+++ b/kutils/kcmoduleproxy.h
@@ -0,0 +1,357 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+ Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+
+ 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 KCMODULEPROXY_H
+#define KCMODULEPROXY_H
+
+#include <qwidget.h>
+#include <qstringlist.h>
+
+#include <kservice.h>
+#include <kdelibs_export.h>
+
+class KAboutData;
+class KCModule;
+class KCModuleInfo;
+class KInstance;
+class KProcess;
+
+/**
+ * @ingroup kcmodule
+ *
+ * @brief Encapsulates a KCModule for embedding.
+ *
+ * KCModuleProxy is a wrapper for KCModule intended for cases where
+ * modules are to be displayed. It ensures layout is consistent, handles
+ * root/administrator modules and in general takes care of the details
+ * needed for making a module available in an interface. A KCModuleProxy
+ * can be treated as a QWidget, without worrying about the details specific
+ * for modules such as library loading. KCModuleProxy is not a sub class of KCModule
+ * but its API closely resembles KCModule's.\n
+ * Usually, an instance is created by passing one of the constructors a KService::Ptr,
+ * KCModuleInfo or simply the name of the module and then added to the layout as any
+ * other widget. \n
+ * When the user have changed the module, changed( bool ) as well as changed ( KCModuleProxy * )
+ * is emitted. KCModuleProxy does not take care of prompting for saving - if the object is deleted while
+ * changes is not saved the changes will be lost. changed() returns true if changes are unsaved. \n
+ * \n
+ * KCModuleProxy does not take care of authorization of KCModules. \n
+ * KCModuleProxy do lazy loading, meaning the library will not be loaded or
+ * any other initialization done before its show() function is called. This means
+ * modules will only be loaded when they are actually needed as well as it is possible to
+ * load many KCModuleProxy without any speed penalty.
+ *
+ * KCModuleProxy should be used in all cases where modules are embedded in order to
+ * promote code efficiency and usability consistency.
+ *
+ * @author Frans Englich <frans.englich@telia.com>
+ * @author Matthias Kretz <kretz@kde.org>
+ *
+ */
+class KUTILS_EXPORT KCModuleProxy : public QWidget
+{
+Q_OBJECT
+
+ friend class KCModuleProxyRootCommunicatorImpl;
+
+public:
+
+ /**
+ * Constructs a KCModuleProxy from a KCModuleInfo class.
+ *
+ * @param info The KCModuleInfo to construct the module from.
+ * @param withFallback If set to true and loading of the module fails,
+ * a alternative will be tried, resulting in the module appearing in its
+ * own window, if at all.
+ * The embedded module will be load()ed.
+ * @param parent the parent QWidget.
+ * @param name the module's name.
+ * @param args This is used in the implementation and is internal.
+ * Use the default.
+ */
+ KCModuleProxy( const KCModuleInfo & info, bool withFallback = true,
+ QWidget * parent = 0, const char * name = 0,
+ const QStringList & args = QStringList() );
+
+ /**
+ * Constructs a KCModuleProxy from a module's service name, which is
+ * equivalent to the desktop file for the kcm without the ".desktop" part.
+ * Otherwise equal to the one above.
+ *
+ * @param serviceName The module's service name to construct from.
+ * @param withFallback If set to true and loading of the module fails,
+ * a alternative will be tried, resulting in the module appearing in its
+ * own window, if at all.
+ * The embedded module will be load()ed.
+ * @param parent the parent QWidget.
+ * @param name the module's name.
+ * @param args This is used in the implementation and is internal.
+ * Use the default.
+ */
+ KCModuleProxy( const QString& serviceName, bool withFallback = true,
+ QWidget * parent = 0, const char * name = 0,
+ const QStringList & args = QStringList() );
+
+ /**
+ * Constructs a KCModuleProxy from KService. Otherwise equal to the one above.
+ *
+ * @param service The KService to construct from.
+ * @param withFallback If set to true and loading of the module fails,
+ * a alternative will be tried, resulting in the module appearing in its
+ * own window, if at all.
+ * The embedded module will be load()ed.
+ * @param parent the parent QWidget.
+ * @param name the module's name.
+ * @param args This is used in the implementation and is internal.
+ * Use the default.
+ */
+ KCModuleProxy( const KService::Ptr& service, bool withFallback = true,
+ QWidget * parent = 0, const char * name = 0,
+ const QStringList & args = QStringList() );
+
+ /**
+ * Default destructor
+ */
+ ~KCModuleProxy();
+
+ /**
+ * Calling it will cause the contained module to
+ * run its load() routine.
+ */
+ void load();
+
+ /**
+ * Calling it will cause the contained module to
+ * run its save() routine.
+ *
+ * If the module was not modified, it will not be asked
+ * to save.
+ */
+ void save();
+
+ /**
+ * @return the module's quickHelp();
+ */
+ QString quickHelp() const;
+
+ /**
+ * @return the module's aboutData()
+ */
+ const KAboutData * aboutData() const;
+
+ /**
+ * @return what buttons the module
+ * needs
+ */
+ int buttons() const;
+
+ /**
+ * @return The module's custom root
+ * message, if it has one
+ * @deprecated
+ */
+ QString rootOnlyMsg() const;
+ //KDE4 remove. There's a limit for convenience functions,
+ // this one's available via moduleInfo()-> and realModule()->
+
+ /**
+ * @return If the module is a root module.
+ * @deprecated
+ */
+ bool useRootOnlyMsg() const;
+ //KDE4 remove. There's a limit for convenience functions,
+ // this one's available via moduleInfo()-> and realModule()->
+
+ /**
+ * Returns the embedded KCModule's KInstance.
+ * @return The module's KInstance.
+ * @deprecated
+ */
+ KInstance * instance() const;
+ //KDE4 remove. There's a limit for convenience functions,
+ // this one's available via realModule()
+
+ /**
+ * @return true if the module is modified
+ * and needs to be saved.
+ */
+ bool changed() const;
+
+ /**
+ * Returns whether the module is running in root mode. A module is in root mode
+ * when runAsRoot() has been called. A session under root user will never reach
+ * root mode.
+ *
+ * @note realModule() will return null when the module is running in root mode.
+ *
+ * @return true if the module is running with root privileges
+ * @since 3.4
+ */
+ bool rootMode() const;
+
+ /**
+ * Access to the actual module. However, if the module is
+ * running in root mode, see rootMode(), this function returns
+ * a NULL pointer, since the module is in another process. It may also
+ * return NULL if anything goes wrong.
+ *
+ * @return the encapsulated module.
+ */
+ KCModule* realModule() const;
+
+ /**
+ * @return a KCModuleInfo for the encapsulated
+ * module
+ */
+ const KCModuleInfo& moduleInfo() const;
+
+ /**
+ * Returns the DCOP the module's DCOPClient
+ * and DCOPObject has(they are identical).
+ *
+ * @since 3.4
+ */
+ QCString dcopName() const;
+
+public slots:
+
+ /**
+ * Calling this will cause the module to be run in
+ * "administrator mode".
+ *
+ * @since 3.4
+ */
+ void runAsRoot();
+
+ /**
+ * Calling it will cause the contained module to
+ * load its default values.
+ */
+ void defaults();
+
+ /**
+ * Calling this, results in deleting the contained
+ * module, and unregistering from DCOP. A similar result is achieved
+ * by deleting the KCModuleProxy itself.
+ *
+ * @since 3.4
+ */
+ void deleteClient();
+
+signals:
+
+ /*
+ * This signal is emitted when the contained module is changed.
+ */
+ void changed( bool state );
+
+ /**
+ * This is emitted in the same situations as in the one above. Practical
+ * when several KCModuleProxys are loaded.
+ *
+ * @since 3.4
+ */
+ void changed( KCModuleProxy* mod );
+
+ /**
+ * When a module running with root privileges and exits, returns to normal mode, the
+ * childClosed() signal is emitted.
+ *
+ * @since 3.4
+ */
+ void childClosed();
+
+ /*
+ * This signal is relayed from the encapsulated module, and
+ * is equivalent to the module's own quickHelpChanged() signal.
+ *
+ * @since 3.4
+ */
+ void quickHelpChanged();
+
+protected:
+
+ /**
+ * Reimplemented for internal purposes. Makes sure the encapsulated
+ * module is loaded before the show event is taken care of.
+ */
+ void showEvent( QShowEvent * );
+
+ /**
+ * Internal intialization function, called by the constructors.
+ *
+ * @internal
+ * @since 3.4
+ */
+ void init( const KCModuleInfo& info );
+
+
+ /**
+ * Emits the quickHelpChanged signal.
+ * @since 3.4
+ */
+ void emitQuickHelpChanged();
+
+private slots:
+
+ /**
+ * Calls the function @p function of the root module's KCModuleProxy
+ * DCOP interface.
+ *
+ * @param function the function signature of the function to call.
+ * @since 3.4
+ */
+ void callRootModule( const QCString& function );
+
+ /**
+ * This is called when the module exits from root mode. It zeroes
+ * pointers, deletes the embed window, and so forth.
+ *
+ * @since 3.4
+ */
+ void rootExited();
+
+ /**
+ * Makes sure the proper variables is set and signals are emitted.
+ */
+ void moduleChanged( bool );
+
+ /**
+ * Zeroes d->kcm
+ */
+ void moduleDestroyed();
+
+ /**
+ * Gets called by DCOP when an application closes.
+ * Is used to (try to) reload a KCM which previously
+ * was loaded.
+ *
+ * @since 3.4
+ */
+ void applicationRemoved( const QCString& app );
+
+private:
+
+ class KCModuleProxyPrivate;
+ KCModuleProxyPrivate * d;
+};
+
+#endif // KCMODULEPROXY_H
+// vim: sw=4 ts=4 noet
diff --git a/kutils/kcmoduleproxyIface.h b/kutils/kcmoduleproxyIface.h
new file mode 100644
index 000000000..8159a843f
--- /dev/null
+++ b/kutils/kcmoduleproxyIface.h
@@ -0,0 +1,121 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 __KCMODULEPROXYIFACE_H__
+#define __KCMODULEPROXYIFACE_H__
+
+#include <dcopobject.h>
+
+/**
+ * @ingroup kcmodule
+ * @brief DCOP Interface for KCModule.
+ *
+ * A module (which is loaded via KCModuleProxy) does always have
+ * this DCOP interface, whether it's in root mode or not.
+ *
+ * @since 3.4
+ * @internal
+ * @author Frans Englich <frans.englich@telia.com>
+ */
+class KCModuleProxyIface : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+
+ /**
+ * Return the caption of the host application which the module
+ * is part in. This is the application name, or similar.
+ *
+ * @returns the host's name
+ */
+ virtual QString applicationName() = 0;
+
+ /**
+ * Save settings.
+ */
+ virtual void save() = 0;
+
+ /**
+ * Load settings.
+ */
+ virtual void load() = 0;
+
+ /**
+ * Load defaults.
+ */
+ virtual void defaults() = 0;
+
+ /**
+ * Returns the module's quick help.
+ */
+ virtual QString quickHelp() = 0;
+
+ /**
+ * @returns true if the module has unsaved
+ * data, typically.
+ */
+ virtual bool changed() = 0;
+
+k_dcop_signals:
+
+ /**
+ * Emitted when the state of the module changes. @p c
+ * is true when the content is changed, otherwise false.
+ *
+ * @param c true if the module is modified, false if its not.
+ * @param module a string identifying the module which was changed. This
+ * is typically "KCModuleProx-X" where X is the module's name.
+ */
+ virtual void changed( bool c );
+
+ virtual void quickHelpChanged();
+
+};
+
+/**
+ * @ingroup kcmodule
+ * @brief DCOP interface for communicating with the real module running in root mode.
+ *
+ * When a KCModuleProxy tells kcmshell to load "itself" and embed into the
+ * KCModuleProxy, this DCOP interface is used to communicate to the real KCModuleProxy, since
+ * the KCModuleProxy which told kcmshell to load itself, is nothing but a shell.
+ *
+ * Currently is only the changed signal routed, but it's possible to proxy
+ * the rest of the KCModuleProxy API, if it turns out necessary.
+ *
+ * @since 3.4
+ * @internal
+ * @author Frans Englich <frans.englich@telia.com>
+ */
+class KCModuleProxyRootDispatcher : virtual public DCOPObject
+{
+ K_DCOP
+
+k_dcop:
+
+ /**
+ * KCModuleProxyIface::changed() gets connected to this.
+ */
+ virtual void changed( bool c ) = 0;
+
+ virtual void quickHelpChanged() = 0;
+};
+
+#endif // __KCMODULEPROXYIFACE_H__
diff --git a/kutils/kcmoduleproxyIfaceImpl.cpp b/kutils/kcmoduleproxyIfaceImpl.cpp
new file mode 100644
index 000000000..5d1ae15fe
--- /dev/null
+++ b/kutils/kcmoduleproxyIfaceImpl.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+ *
+ * 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 <qcstring.h>
+#include <qdatastream.h>
+
+#include <dcopclient.h>
+
+#include <kapplication.h>
+#include <kcmoduleproxy.h>
+#include <kdebug.h>
+
+#include "kcmoduleproxyIfaceImpl.h"
+
+
+#include <qmessagebox.h>
+
+KCModuleProxyIfaceImpl::KCModuleProxyIfaceImpl( const QCString& name,
+ KCModuleProxy* const client )
+ : DCOPObject( name ), QObject( 0, name ),
+ p( const_cast<KCModuleProxy *>( client ))
+{
+ connect( p, SIGNAL( changed(bool)),
+ SLOT( changedRelay(bool)));
+ connect( p, SIGNAL( quickHelpChanged()),
+ SLOT( quickHelpRelay()));
+}
+
+void KCModuleProxyIfaceImpl::save()
+{
+ kdDebug(711) << k_funcinfo << endl;
+ p->save();
+}
+
+void KCModuleProxyIfaceImpl::load()
+{
+ kdDebug(711) << k_funcinfo << endl;
+ p->load();
+}
+
+void KCModuleProxyIfaceImpl::defaults()
+{
+ kdDebug(711) << k_funcinfo << endl;
+ p->defaults();
+}
+
+QString KCModuleProxyIfaceImpl::applicationName()
+{
+ return kapp->caption();
+}
+
+QString KCModuleProxyIfaceImpl::quickHelp()
+{
+ return p->quickHelp();
+}
+
+bool KCModuleProxyIfaceImpl::changed()
+{
+ return p->changed();
+}
+
+void KCModuleProxyIfaceImpl::changedRelay( bool c )
+{
+ QByteArray data;
+ QDataStream stream(data, IO_WriteOnly);
+ stream << c;
+ emitDCOPSignal( "changed(bool)", data );
+}
+
+void KCModuleProxyIfaceImpl::quickHelpRelay()
+{
+ QByteArray data;
+ emitDCOPSignal( "quickHelpChanged()", data );
+}
+
+/***************************************************************/
+
+
+
+
+/***************************************************************/
+KCModuleProxyRootCommunicatorImpl::KCModuleProxyRootCommunicatorImpl
+ ( const QCString& name, KCModuleProxy* const client )
+ : DCOPObject( name ), QObject( 0, name ),
+ p( const_cast<KCModuleProxy *>( client ))
+{
+ /*
+ * Connect kcmshell's KCModuleProxy's change signal
+ * to us, such that we act as a proxy for
+ * KCModuleProxy's API.
+ */
+
+ /* Note, we don't use KCModuleProxy::d->dcopClient */
+ kapp->dcopClient()->connectDCOPSignal( 0, p->dcopName(),
+ "changed(bool)", objId(), "changed(bool)", false );
+
+ kapp->dcopClient()->connectDCOPSignal( 0, p->dcopName(),
+ "quickHelpChanged()", objId(), "quickHelpChanged()", false );
+}
+
+/* Reimplementations of DCOP members */
+void KCModuleProxyRootCommunicatorImpl::changed( bool c )
+{
+ kdDebug(711) << k_funcinfo << endl;
+ p->moduleChanged( c );
+}
+
+void KCModuleProxyRootCommunicatorImpl::quickHelpChanged()
+{
+ kdDebug(711) << k_funcinfo << endl;
+ p->emitQuickHelpChanged();
+}
+
+#include "kcmoduleproxyIfaceImpl.moc"
diff --git a/kutils/kcmoduleproxyIfaceImpl.h b/kutils/kcmoduleproxyIfaceImpl.h
new file mode 100644
index 000000000..435c856ab
--- /dev/null
+++ b/kutils/kcmoduleproxyIfaceImpl.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
+ *
+ * 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 __KCMODULEPROXYIFACEIMPL_H__
+#define __KCMODULEPROXYIFACEIMPL_H__
+
+#include "kcmoduleproxyIface.h"
+
+class KCModuleProxy;
+
+/***************************************************************/
+/** @internal
+ @ingroup internal */
+class KCModuleProxyIfaceImpl: public QObject, virtual public KCModuleProxyIface
+{
+ /* KDE4 Merge KCModuleProxyIfaceImpl with KCModuleProxy(MI)
+ * if it doesn't break what DCOPClient it binds to.
+ * Update: This is probably not possible, since we don't want the DCOPObject when
+ * we're running in root mode. */
+
+ Q_OBJECT
+
+public:
+
+ /* Reimplementations of DCOP members */
+ KCModuleProxyIfaceImpl( const QCString& name, KCModuleProxy* const client );
+
+ virtual void save();
+
+ virtual void load();
+
+ virtual void defaults();
+
+ virtual QString applicationName();
+
+ virtual QString quickHelp();
+
+ virtual bool changed();
+public slots:
+
+ /**
+ * Emits the changed(bool) DCOP signal.
+ */
+ void changedRelay( bool c );
+
+ /**
+ * Simply relays KCModuleProxy's signal with the same name.
+ */
+ void quickHelpRelay();
+
+
+private:
+
+ KCModuleProxy* p;
+};
+/***************************************************************/
+
+
+
+
+/***************************************************************/
+/** @internal
+ @ingroup internal */
+class KCModuleProxyRootCommunicatorImpl: public QObject,
+ virtual public KCModuleProxyRootDispatcher
+{
+ Q_OBJECT
+
+public:
+ KCModuleProxyRootCommunicatorImpl( const QCString& name, KCModuleProxy* const client );
+
+ /* Reimplementations of DCOP members */
+ virtual void changed( bool c );
+
+ virtual void quickHelpChanged();
+
+KCModuleProxy* p;
+};
+/***************************************************************/
+
+#endif // __KCMODULEPROXYIFACEIMPL_H__
+
diff --git a/kutils/kcmultidialog.cpp b/kutils/kcmultidialog.cpp
new file mode 100644
index 000000000..f64a9e139
--- /dev/null
+++ b/kutils/kcmultidialog.cpp
@@ -0,0 +1,411 @@
+/*
+ Copyright (c) 2000 Matthias Elter <elter@kde.org>
+ Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
+ Copyright (c) 2003 Matthias Kretz <kretz@kde.org>
+ Copyright (c) 2004 Frans Englich <frans.erglich.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 <qcursor.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kprocess.h>
+#include <krun.h>
+#include <kstdguiitem.h>
+#include <kuser.h>
+
+#include "kcmoduleloader.h"
+#include "kcmoduleproxy.h"
+#include "kcmultidialog.h"
+#include "kcmultidialog.moc"
+
+class KCMultiDialog::KCMultiDialogPrivate
+{
+ public:
+ KCMultiDialogPrivate()
+ : hasRootKCM( false ), currentModule( 0 )
+ {}
+
+ bool hasRootKCM;
+ KCModuleProxy* currentModule;
+};
+
+
+KCMultiDialog::KCMultiDialog(QWidget *parent, const char *name, bool modal)
+ : KDialogBase(IconList, i18n("Configure"), Help | Default |Cancel | Apply |
+ Ok | User1 | User2, Ok, parent, name, modal, true,
+ KStdGuiItem::reset(), KStdGuiItem::adminMode())
+ , dialogface( IconList ), d( new KCMultiDialogPrivate )
+{
+ init();
+}
+
+KCMultiDialog::KCMultiDialog( int dialogFace, const QString & caption, QWidget * parent, const char * name, bool modal )
+ : KDialogBase( dialogFace, caption, Help | Default | Cancel | Apply | Ok |
+ User1 | User2, Ok, parent, name, modal, true,
+ KStdGuiItem::reset(), KStdGuiItem::adminMode())
+ , dialogface( dialogFace ), d( new KCMultiDialogPrivate )
+{
+ init();
+}
+
+KCMultiDialog::KCMultiDialog( int dialogFace, const KGuiItem &user2,
+ const KGuiItem &user3, int buttonMask, const QString &caption,
+ QWidget *parent, const char *name, bool modal )
+ : KDialogBase( dialogFace, caption, buttonMask | Help | Default | Cancel |
+ Apply | Ok | User1, Ok, parent, name, modal, true,
+ KStdGuiItem::reset(), user2, user3 )
+ , dialogface( dialogFace ), d( new KCMultiDialogPrivate )
+{
+ kdDebug( 710 ) << "Root modules will not work with this constructor. See the API documentation." << endl;
+ init();
+ if ( buttonMask & User2 )
+ showButton( User2, true );
+}
+
+inline void KCMultiDialog::init()
+{
+ connect( this, SIGNAL( finished()), SLOT( dialogClosed()));
+ showButton( User1, false );
+ showButton( User2, false );
+ enableButton(Apply, false);
+ connect(this, SIGNAL(aboutToShowPage(QWidget *)), this, SLOT(slotAboutToShow(QWidget *)));
+ setInitialSize(QSize(640,480));
+ moduleParentComponents.setAutoDelete( true );
+
+}
+
+KCMultiDialog::~KCMultiDialog()
+{
+ OrphanMap::Iterator end2 = m_orphanModules.end();
+ for( OrphanMap::Iterator it = m_orphanModules.begin(); it != end2; ++it )
+ delete ( *it );
+ delete d;
+}
+
+void KCMultiDialog::slotDefault()
+{
+ int curPageIndex = activePageIndex();
+
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ if( pageIndex( ( QWidget * )( *it ).kcm->parent() ) == curPageIndex )
+ {
+ ( *it ).kcm->defaults();
+ clientChanged( true );
+ return;
+ }
+}
+
+void KCMultiDialog::slotUser1()
+{
+ int curPageIndex = activePageIndex();
+
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ if( pageIndex( ( QWidget * )( *it ).kcm->parent() ) == curPageIndex )
+ {
+ ( *it ).kcm->load();
+ clientChanged( false );
+ return;
+ }
+}
+
+void KCMultiDialog::apply()
+{
+ QStringList updatedModules;
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ {
+ KCModuleProxy * m = ( *it ).kcm;
+ if( m->changed() )
+ {
+ m->save();
+ QStringList * names = moduleParentComponents[ m ];
+ kdDebug(710) << k_funcinfo << *names << " saved and added to the list" << endl;
+ for( QStringList::ConstIterator it = names->begin(); it != names->end(); ++it )
+ if( updatedModules.find( *it ) == updatedModules.end() )
+ updatedModules.append( *it );
+ }
+ }
+ for( QStringList::const_iterator it = updatedModules.begin(); it != updatedModules.end(); ++it )
+ {
+ kdDebug(710) << k_funcinfo << *it << " " << ( *it ).latin1() << endl;
+ emit configCommitted( ( *it ).latin1() );
+ }
+ emit configCommitted();
+}
+
+void KCMultiDialog::slotApply()
+{
+ QPushButton *button = actionButton(Apply);
+ if (button)
+ button->setFocus();
+ emit applyClicked();
+ apply();
+}
+
+
+void KCMultiDialog::slotOk()
+{
+ QPushButton *button = actionButton(Ok);
+ if (button)
+ button->setFocus();
+ emit okClicked();
+ apply();
+ accept();
+}
+
+void KCMultiDialog::slotHelp()
+{
+ QString docPath;
+
+ int curPageIndex = activePageIndex();
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ if( pageIndex( ( QWidget * )( *it ).kcm->parent() ) == curPageIndex )
+ {
+ docPath = ( *it ).kcm->moduleInfo().docPath();
+ break;
+ }
+
+ KURL url( KURL("help:/"), docPath );
+
+ if (url.protocol() == "help" || url.protocol() == "man" || url.protocol() == "info") {
+ KProcess process;
+ process << "khelpcenter"
+ << url.url();
+ process.start(KProcess::DontCare);
+ process.detach();
+ } else {
+ new KRun(url);
+ }
+}
+
+void KCMultiDialog::clientChanged(bool state)
+{
+ kdDebug( 710 ) << k_funcinfo << state << endl;
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ if( ( *it ).kcm->changed() )
+ {
+ enableButton( Apply, true );
+ return;
+ }
+ enableButton( Apply, false );
+}
+
+void KCMultiDialog::addModule(const QString& path, bool withfallback)
+{
+ QString complete = path;
+
+ if( !path.endsWith( ".desktop" ))
+ complete += ".desktop";
+
+ KService::Ptr service = KService::serviceByStorageId( complete );
+
+ addModule( KCModuleInfo( service ), QStringList(), withfallback);
+}
+
+void KCMultiDialog::addModule(const KCModuleInfo& moduleinfo,
+ QStringList parentmodulenames, bool withfallback)
+{
+ kdDebug(710) << "KCMultiDialog::addModule "
+ << moduleinfo.moduleName() << endl;
+
+ if( !moduleinfo.service() )
+ return;
+
+ if ( !kapp->authorizeControlModule( moduleinfo.service()->menuId() ))
+ return;
+
+ if( !KCModuleLoader::testModule( moduleinfo ))
+ return;
+
+ QFrame* page = 0;
+ if (!moduleinfo.service()->noDisplay())
+ switch( dialogface )
+ {
+ case TreeList:
+ parentmodulenames += moduleinfo.moduleName();
+ page = addHBoxPage( parentmodulenames, moduleinfo.comment(),
+ SmallIcon( moduleinfo.icon(),
+ IconSize( KIcon::Small ) ) );
+ break;
+ case IconList:
+ page = addHBoxPage( moduleinfo.moduleName(),
+ moduleinfo.comment(), DesktopIcon( moduleinfo.icon(),
+ KIcon::SizeMedium ) );
+ break;
+ case Plain:
+ page = plainPage();
+ ( new QHBoxLayout( page ) )->setAutoAdd( true );
+ break;
+ default:
+ kdError( 710 ) << "unsupported dialog face for KCMultiDialog"
+ << endl;
+ break;
+ }
+ if(!page) {
+ KCModuleLoader::unloadModule(moduleinfo);
+ return;
+ }
+ KCModuleProxy * module;
+ if( m_orphanModules.contains( moduleinfo.service() ) )
+ {
+ // the KCModule already exists - it was removed from the dialog in
+ // removeAllModules
+ module = m_orphanModules[ moduleinfo.service() ];
+ m_orphanModules.remove( moduleinfo.service() );
+ kdDebug( 710 ) << "Use KCModule from the list of orphans for " <<
+ moduleinfo.moduleName() << ": " << module << endl;
+
+ module->reparent( page, 0, QPoint( 0, 0 ), true );
+
+ if( module->changed() )
+ clientChanged( true );
+
+ if( activePageIndex() == -1 )
+ showPage( pageIndex( page ) );
+ }
+ else
+ {
+ module = new KCModuleProxy( moduleinfo, withfallback, page );
+ QStringList parentComponents = moduleinfo.service()->property(
+ "X-KDE-ParentComponents" ).toStringList();
+ moduleParentComponents.insert( module,
+ new QStringList( parentComponents ) );
+
+ connect(module, SIGNAL(changed(bool)), this, SLOT(clientChanged(bool)));
+
+ if( m_modules.count() == 0 )
+ aboutToShowPage( page );
+ }
+ CreatedModule cm;
+ cm.kcm = module;
+ cm.service = moduleinfo.service();
+ m_modules.append( cm );
+ if ( moduleinfo.needsRootPrivileges() &&
+ !d->hasRootKCM &&
+ !KUser().isSuperUser() ) /* If we're embedded, it's true */
+ {
+ d->hasRootKCM = true;
+ showButton( User2, true );
+ if( plainPage() ) // returns 0 if we're not a Plain dialog
+ slotAboutToShow( page ); // Won't be called otherwise, necessary for adminMode button
+ }
+}
+
+void KCMultiDialog::removeAllModules()
+{
+ kdDebug( 710 ) << k_funcinfo << endl;
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ {
+ kdDebug( 710 ) << "remove 2" << endl;
+ KCModuleProxy * kcm = ( *it ).kcm;
+ QObject * page = kcm->parent();
+ kcm->hide();
+ if( page )
+ {
+ // I hate this
+ kcm->reparent( 0, QPoint( 0, 0 ), false );
+ delete page;
+ }
+ m_orphanModules[ ( *it ).service ] = kcm;
+ kdDebug( 710 ) << "added KCModule to the list of orphans: " <<
+ kcm << endl;
+ }
+ m_modules.clear();
+ // all modules are gone, none can be changed
+ clientChanged( false );
+}
+
+void KCMultiDialog::show()
+{ /* KDE 4 Remove..? */
+ KDialogBase::show();
+}
+
+void KCMultiDialog::slotAboutToShow(QWidget *page)
+{
+ kdDebug(710) << k_funcinfo << endl;
+
+ QObject * obj = page->child( 0, "KCModuleProxy" );
+ if( ! obj )
+ return;
+
+ KCModuleProxy * module = ( KCModuleProxy* )obj->qt_cast(
+ "KCModuleProxy" );
+ if( ! module )
+ return;
+ d->currentModule = module;
+
+ enableButton( KDialogBase::Help,
+ d->currentModule->buttons() & KCModule::Help );
+ enableButton( KDialogBase::Default,
+ d->currentModule->buttons() & KCModule::Default );
+
+ disconnect( this, SIGNAL(user2Clicked()), 0, 0 );
+
+ if (d->currentModule->moduleInfo().needsRootPrivileges())
+ {
+ if ( !d->currentModule->rootMode() )
+ { /* Enable the Admin Mode button */
+ enableButton( User2, true );
+ connect( this, SIGNAL(user2Clicked()), d->currentModule, SLOT( runAsRoot() ));
+ connect( this, SIGNAL(user2Clicked()), SLOT( disableRModeButton() ));
+ }
+ else
+ enableButton( User2, false);
+ }
+}
+
+void KCMultiDialog::rootExit()
+{
+ enableButton( User2, true);
+}
+
+void KCMultiDialog::disableRModeButton()
+{
+ enableButton( User2, false );
+ connect ( d->currentModule, SIGNAL( childClosed() ), SLOT( rootExit() ));
+}
+
+void KCMultiDialog::dialogClosed()
+{
+ kdDebug(710) << k_funcinfo << endl;
+
+ /* If we don't delete them, the DCOP registration stays, and trying to load the KCMs
+ * in other situations will lead to "module already loaded in Foo," while to the user
+ * doesn't appear so(the dialog is hidden) */
+ ModuleList::Iterator end = m_modules.end();
+ for( ModuleList::Iterator it = m_modules.begin(); it != end; ++it )
+ ( *it ).kcm->deleteClient();
+}
+
+
+// vim: sw=4 et sts=4
diff --git a/kutils/kcmultidialog.h b/kutils/kcmultidialog.h
new file mode 100644
index 000000000..8461c5643
--- /dev/null
+++ b/kutils/kcmultidialog.h
@@ -0,0 +1,284 @@
+/*
+ Copyright (c) 2000 Matthias Elter <elter@kde.org>
+ Copyright (c) 2003 Daniel Molkentin <molkentin@kde.org>
+ 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 as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 KCMULTIDIALOG_H
+#define KCMULTIDIALOG_H
+
+#include <qptrdict.h>
+
+#include <kdialogbase.h>
+#include <klocale.h>
+#include <kservice.h>
+
+class KCModuleProxy;
+class KCModuleInfo;
+
+/**
+ * @ingroup kcmodule
+ * @short A method that offers a KDialogBase containing arbitrary
+ * KControl Modules.
+ *
+ * @author Matthias Elter <elter@kde.org>, Daniel Molkentin <molkentin@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT KCMultiDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a new KCMultiDialog
+ *
+ * @param parent The parent widget
+ * @param name The widget name
+ * @param modal If you pass true here, the dialog will be modal
+ **/
+ KCMultiDialog( QWidget *parent=0, const char *name=0, bool modal=false );
+
+ /**
+ * Construct a personalized KCMultiDialog.
+ *
+ * @param dialogFace You can use TreeList, Tabbed, Plain, Swallow or
+ * IconList.
+ * @param caption The dialog caption. Do not specify the application name
+ * here. The class will take care of that.
+ * @param parent Parent of the dialog.
+ * @param name Dialog name (for internal use only).
+ * @param modal Controls dialog modality. If @p false, the rest of the
+ * program interface (example: other dialogs) is accessible while
+ * the dialog is open.
+ */
+ KCMultiDialog( int dialogFace, const QString & caption, QWidget * parent = 0,
+ const char * name = 0, bool modal = false );
+
+
+ /**
+ * Constructor for the predefined layout mode where you specify the
+ * kind of layout (face) and also add buttons. Note that the User1 button
+ * of KDialogBase is already used to provide a "Reset" button so only
+ * two more buttons are available to users of KCMultiDialog. When clicked
+ * they trigger slotUser2() and slotUser3().
+ *
+ * @note If any root modules are added to the dialog when this constructor is
+ * used, it will not be able to run them with root privileges. Since that will
+ * render them useless, it is a good idea to use another constructor. In KDE 4
+ * the argument @p user3 will be removed.
+ *
+ * @param dialogFace You can use TreeList, Tabbed, Plain, Swallow or
+ * IconList.
+ * @param user2 User button2 text item.
+ * @param user3 User button3 text item.
+ * @param buttonMask Specifies which buttons will be visible. If zero
+ * (0) no extra buttons will be added. You can only use the User2 and
+ * User3 buttons. The User1 button is already used internally. See
+ * KDialogBase for more information on this.
+ * @param caption The dialog caption. Do not specify the application name
+ * here. The class will take care of that.
+ * @param parent Parent of the dialog.
+ * @param name Dialog name (for internal use only).
+ * @param modal Controls dialog modality. If @p false, the rest of the
+ * program interface (example: other dialogs) is accessible while
+ * the dialog is open.
+ */
+ KCMultiDialog( int dialogFace, const KGuiItem &user2,
+ const KGuiItem &user3=KGuiItem(), int buttonMask=User2,
+ const QString &caption=i18n("Configure"), QWidget *parent=0,
+ const char *name=0, bool modal=false ) KDE_DEPRECATED;
+ // KDE4 remove the user3 argument, and instead initialize it to KStdGuiItem::adminMode.
+
+ /**
+ * Destructor
+ **/
+ virtual ~KCMultiDialog();
+
+ /**
+ * Add a module.
+ *
+ * @param module Specify the name of the module that is to be added
+ * to the list of modules the dialog will show.
+ *
+ * @param withfallback Try harder to load the module. Might result
+ * in the module appearing outside the dialog.
+ **/
+ void addModule(const QString& module, bool withfallback=true);
+
+ /**
+ * Add a module.
+ *
+ * @param moduleinfo Pass a KCModuleInfo object which will be
+ * used for creating the module. It will be added
+ * to the list of modules the dialog will show.
+ *
+ * @param parentmodulenames The names of the modules that should appear as
+ * parents in the TreeList. Look at the
+ * KDialogBase::addPage documentation for more info
+ * on this.
+ *
+ * @param withfallback Try harder to load the module. Might result
+ * in the module appearing outside the dialog.
+ **/
+ void addModule(const KCModuleInfo& moduleinfo, QStringList
+ parentmodulenames = QStringList(), bool withfallback=false);
+
+ /**
+ * Remove all modules from the dialog.
+ */
+ void removeAllModules();
+
+ /**
+ * @internal
+ * Re-implemented for internal reasons.
+ */
+ void show();
+
+signals:
+ /**
+ * Emitted after all KCModules have been told to save their configuration.
+ *
+ * The applyClicked and okClicked signals are emitted before the
+ * configuration is saved.
+ */
+ void configCommitted();
+
+ /**
+ * Emitted after the KCModules have been told to save their configuration.
+ * It is emitted once for every instance the KCMs that were changed belong
+ * to.
+ *
+ * You can make use of this if you have more than one component in your
+ * application. instanceName tells you the instance that has to reload its
+ * configuration.
+ *
+ * The applyClicked and okClicked signals are emitted before the
+ * configuration is saved.
+ *
+ * @param instanceName The name of the instance that needs to reload its
+ * configuration.
+ */
+ void configCommitted( const QCString & instanceName );
+
+protected slots:
+ /**
+ * This slot is called when the user presses the "Default" Button.
+ * You can reimplement it if needed.
+ *
+ * @note Make sure you call the original implementation.
+ **/
+ virtual void slotDefault();
+
+ /**
+ * This slot is called when the user presses the "Reset" Button.
+ * You can reimplement it if needed.
+ *
+ * @note Make sure you call the original implementation.
+ */
+ virtual void slotUser1();
+
+ /**
+ * This slot is called when the user presses the "Apply" Button.
+ * You can reimplement it if needed.
+ *
+ * @note Make sure you call the original implementation.
+ **/
+ virtual void slotApply();
+
+ /**
+ * This slot is called when the user presses the "OK" Button.
+ * You can reimplement it if needed.
+ *
+ * @note Make sure you call the original implementation.
+ **/
+ virtual void slotOk();
+
+ /**
+ * This slot is called when the user presses the "Help" Button.
+ * It reads the DocPath field of the currently selected KControl
+ * module's .desktop file to find the path to the documentation,
+ * which it then attempts to load.
+ *
+ * You can reimplement this slot if needed.
+ *
+ * @note Make sure you call the original implementation.
+ **/
+ virtual void slotHelp();
+
+private slots:
+
+ void slotAboutToShow(QWidget *);
+
+ void clientChanged(bool state);
+
+ /**
+ * Called when entering root mode, and disables
+ * the Admin Mode button such that the user doesn't do it
+ * twice.
+ *
+ * @since 3.4
+ */
+ void disableRModeButton();
+
+ /**
+ * Called when the current module exits from root
+ * mode. Enables the Administrator Mode button, again.
+ *
+ * @since 3.4
+ */
+ void rootExit();
+
+ /**
+ *
+ * Called when the dialog is hidden. It unregisters the modules,
+ * such that they don't hinder the same modules to be opened in
+ * another application.
+ *
+ * @since 3.4
+ */
+ void dialogClosed();
+
+private:
+
+ void init();
+ void apply();
+
+ struct CreatedModule
+ {
+ KCModuleProxy * kcm;
+ KService::Ptr service;
+ /* KDE 4 Move to Private class */
+ };
+ typedef QValueList<CreatedModule> ModuleList;
+ ModuleList m_modules;
+
+ typedef QMap<KService::Ptr, KCModuleProxy*> OrphanMap;
+ OrphanMap m_orphanModules;
+
+ QPtrDict<QStringList> moduleParentComponents;
+ QString _docPath;
+ int dialogface;
+
+ class KCMultiDialogPrivate;
+ KCMultiDialogPrivate *d;
+};
+
+#endif //KCMULTIDIALOG_H
+
+// vim: sw=4 sts=4 et
diff --git a/kutils/kdeglobals.kcfg b/kutils/kdeglobals.kcfg
new file mode 100644
index 000000000..0941cebb7
--- /dev/null
+++ b/kutils/kdeglobals.kcfg
@@ -0,0 +1,564 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile name="kdeglobals" />
+ <group name="DesktopIcons" >
+ <entry key="GridXSpacing" type="Int" >
+ <label>Distance between desktop icons</label>
+ <whatsthis>The distance between icons specified in pixels.</whatsthis>
+ <default>50</default>
+ </entry>
+ </group>
+ <group name="General" >
+ <entry key="alternateBackground" type="String" />
+ <entry key="background" type="String" />
+ <entry key="buttonBackground" type="String" />
+ <entry key="buttonForeground" type="String" />
+ <entry key="foreground" type="String" />
+ <entry key="selectBackground" type="String" />
+ <entry key="selectForeground" type="String" />
+ <entry key="widgetStyle" type="String" >
+ <label>Widget style to use</label>
+ <whatsthis>The name of the widget style, for example &quot;keramik&quot; or &quot;plastik&quot;. Without quotes.</whatsthis>
+ <default>keramik</default>
+ </entry>
+ <entry key="windowBackground" type="String" />
+ <entry key="windowForeground" type="String" />
+ <entry key="UseSystemBell" type="Bool" >
+ <label>Use the PC speaker</label>
+ <whatsthis>If the ordinary PC speaker should be used instead of KDE's own notifications system.</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry key="TerminalApplication" type="String" >
+ <label>What terminal application to use</label>
+ <whatsthis>Whenever a terminal application is launched this terminal emulator program will be used.
+</whatsthis>
+ <default>konsole</default>
+ </entry>
+ <entry key="fixed" type="Font" >
+ <label>Fixed width font</label>
+ <whatsthis>This font is used when a fixed font is needed. A fixed font has a constant width.
+</whatsthis>
+ </entry>
+ <entry key="font" type="Font" >
+ <label>System wide font</label>
+ </entry>
+ <entry key="menuFont" type="Font" >
+ <label>Font for menus</label>
+ <whatsthis>What font to use for menus in applications.</whatsthis>
+ </entry>
+ <entry key="linkColor" type="Color" >
+ <label>Color for links</label>
+ <whatsthis>What color links which are yet not clicked on should have.</whatsthis>
+ </entry>
+ <entry key="visitedLinkColor" type="Color" >
+ <label>Color for visited links</label>
+ </entry>
+ <entry key="taskbarFont" type="Font" >
+ <label>Font for the taskbar</label>
+ <whatsthis>What font to use for the panel at the bottom of the screen, where the currently running applications are.</whatsthis>
+ </entry>
+ <entry key="toolBarFont" type="Font" >
+ <label>Fonts for toolbars</label>
+ </entry>
+ </group>
+ <group name="Global Shortcuts" >
+ <entry key="Activate Window Demanding Attention" type="String" name="Activate Window Demanding Attention" >
+ <default>Alt+Ctrl+A</default>
+ </entry>
+
+ <entry key="Desktop Screenshot" type="String" name="Desktop Screenshot" >
+ <label>Shortcut for taking screenshot</label>
+ <default>Ctrl+Print</default>
+ </entry>
+
+ <entry key="Enable/Disable Clipboard Actions" type="String" name="Enable/Disable Clipboard Actions" >
+ <label>Shortcut for toggling Clipboard Actions on and off</label>
+ <default>Alt+Ctrl+X</default>
+ </entry>
+
+ <entry key="Halt without Confirmation" type="String" name="Halt without Confirmation" >
+ <label>Shortcut for shutting down the computer without confirmation</label>
+ <default>Alt+Ctrl+Shift+PageDown</default>
+ </entry>
+
+ <entry key="Kill Window" type="String" name="Kill Window" >
+ <default>Alt+Ctrl+Escape</default>
+ </entry>
+
+ <entry key="Lock Screen" type="String" >
+ <default>Alt+Ctrl+L</default>
+ </entry>
+ <entry key="Logout" type="String" >
+ <default>Alt+Ctrl+Delete</default>
+ </entry>
+ <entry key="Logout without Confirmation" type="String" >
+ <default>Alt+Ctrl+Shift+Delete</default>
+ </entry>
+ <entry key="Manually Invoke Action on Current Clipboard" type="String" >
+ <default>Alt+Ctrl+R</default>
+ </entry>
+ <entry key="Mouse Emulation" type="String" >
+ <default>Alt+F12</default>
+ </entry>
+ <entry key="Popup Launch Menu" type="String" >
+ <default>Alt+F1</default>
+ </entry>
+ <entry key="Reboot without Confirmation" type="String" name="Reboot without Confirmation" >
+ <default>Alt+Ctrl+Shift+PageUp</default>
+ </entry>
+ <entry key="Run Command" type="String" name="Run Command">
+ <default>Alt+F2</default>
+ </entry>
+ <entry key="Show Klipper Popup-Menu" type="String" >
+ <default>Alt+Ctrl+V</default>
+ </entry>
+ <entry key="Show Taskmanager" type="String" >
+ <default>Ctrl+Escape</default>
+ </entry>
+ <entry key="Show Window List" type="String" >
+ <default>Alt+F5</default>
+ </entry>
+ <entry key="Switch One Desktop Down" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Switch One Desktop Up" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Switch One Desktop to the Left" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Switch One Desktop to the Right" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Switch to Desktop 1" type="String" >
+ <default>Ctrl+F1</default>
+ </entry>
+ <entry key="Switch to Desktop 10" type="String" >
+ <default>Ctrl+F10</default>
+ </entry>
+ <entry key="Switch to Desktop 11" type="String" >
+ <default>Ctrl+F11</default>
+ </entry>
+ <entry key="Switch to Desktop 12" type="String" >
+ <default>Ctrl+F12</default>
+ </entry>
+ <entry key="Switch to Desktop 13" type="String" >
+ <default>Ctrl+Shift+F1</default>
+ </entry>
+ <entry key="Switch to Desktop 14" type="String" >
+ <default>Ctrl+Shift+F2</default>
+ </entry>
+ <entry key="Switch to Desktop 15" type="String" >
+ <default>Ctrl+Shift+F3</default>
+ </entry>
+ <entry key="Switch to Desktop 16" type="String" >
+ <default>Ctrl+Shift+F4</default>
+ </entry>
+ <entry key="Switch to Desktop 2" type="String" >
+ <default>Ctrl+F2</default>
+ </entry>
+ <entry key="Switch to Desktop 3" type="String" >
+ <default>Ctrl+F3</default>
+ </entry>
+ <entry key="Switch to Desktop 4" type="String" >
+ <default>Ctrl+F4</default>
+ </entry>
+ <entry key="Switch to Desktop 5" type="String" >
+ <default>Ctrl+F5</default>
+ </entry>
+ <entry key="Switch to Desktop 6" type="String" >
+ <default>Ctrl+F6</default>
+ </entry>
+ <entry key="Switch to Desktop 7" type="String" >
+ <default>Ctrl+F7</default>
+ </entry>
+ <entry key="Switch to Desktop 8" type="String" >
+ <default>Ctrl+F8</default>
+ </entry>
+ <entry key="Switch to Desktop 9" type="String" >
+ <default>Ctrl+F9</default>
+ </entry>
+ <entry key="Switch to Next Desktop" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Switch to Next Keyboard Layout" type="String" >
+ <default>Ctrl+Alt+K</default>
+ </entry>
+ <entry key="Switch to Previous Desktop" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Toggle Showing Desktop" type="String" >
+ <default>Alt+Ctrl+D</default>
+ </entry>
+ <entry key="Toggle Window Raise/Lower" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Walk Through Desktop List" type="String" >
+ <default>Ctrl+Tab</default>
+ </entry>
+ <entry key="Walk Through Desktop List (Reverse)" type="String" >
+ <default>Ctrl+Shift+Tab</default>
+ </entry>
+ <entry key="Walk Through Desktops" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Walk Through Desktops (Reverse)" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Walk Through Windows" type="String" >
+ <default>Alt+Tab</default>
+ </entry>
+ <entry key="Walk Through Windows (Reverse)" type="String" >
+ <default>Alt+Shift+Tab</default>
+ </entry>
+ <entry key="Window Above Other Windows" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Below Other Windows" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Close" type="String" >
+ <default>Alt+F4</default>
+ </entry>
+ <entry key="Window Fullscreen" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Grow Horizontal" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Grow Vertical" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Lower" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Maximize" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Maximize Horizontal" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Maximize Vertical" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Minimize" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Move" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window No Border" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window On All Desktops" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Operations Menu" type="String" >
+ <default>Alt+F3</default>
+ </entry>
+ <entry key="Window Pack Down" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Pack Left" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Pack Right" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Pack Up" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Raise" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Resize" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Screenshot" type="String" >
+ <default>Alt+Print</default>
+ </entry>
+ <entry key="Window Shade" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Shrink Horizontal" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window Shrink Vertical" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 1" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 10" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 11" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 12" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 13" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 14" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 15" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 16" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 2" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 3" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 4" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 5" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 6" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 7" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 8" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Desktop 9" type="String" >
+ <default></default>
+ </entry>
+ <entry key="Window to Next Desktop" type="String" >
+ <default></default>
+ </entry>
+
+ <entry key="Window to Previous Desktop" type="String" >
+ <default></default>
+ </entry>
+
+ </group>
+ <group name="KDE" >
+ <entry key="AutoSelectDelay" type="String" />
+ <entry key="DoubleClickInterval" type="String" />
+ <entry key="EffectAnimateCombo" type="String" />
+ <entry key="EffectAnimateMenu" type="String" />
+ <entry key="EffectAnimateTooltip" type="String" />
+ <entry key="EffectFadeMenu" type="String" />
+ <entry key="EffectFadeTooltip" type="String" />
+ <entry key="EffectNoTooltip" type="String" />
+ <entry key="EffectsEnabled" type="String" />
+ <entry key="InsertTearOffHandle" type="String" />
+ <entry key="ShowIconsOnPushButtons" type="String" />
+ <entry key="SingleClick" type="String" />
+ <entry key="StartDragDist" type="String" />
+ <entry key="StartDragTime" type="String" />
+ <entry key="VisualActivate" type="String" />
+ <entry key="WheelScrollLines" type="String" />
+ <entry key="colorScheme" type="String" />
+ <entry key="contrast" type="String" />
+ </group>
+ <group name="KFileDialog Settings" >
+ <entry key="Automatically select filename extension" type="String" name="Automatically select filename extension">
+ <default>true</default>
+ </entry>
+ <entry key="Height 768" type="String" name="Height 768" />
+ <entry key="LocationCombo Completionmode" type="String" name="LocationCombo Completionmode" />
+ <entry key="PathCombo Completionmode" type="String" name="PathCombo Completionmode" />
+
+ <entry key="Separate Directories" type="Bool" name="Separate Directories" >
+ <default>false</default>
+ </entry>
+
+ <entry key="Sort by" type="String" name="Sort by" />
+
+ <entry key="Sort case insensitively" type="Bool" name="Sort case insensitively">
+ <default>true</default>
+ </entry>
+
+ <entry key="Sort directories first" type="Bool" name="Sort directories first" >
+ <label>Show directories first</label>
+ <whatsthis>If directories should be placed at the top when displaying files</whatsthis>
+ <default>true</default>
+ </entry>
+
+ <entry key="View Style" type="String" name="View Style" />
+ <entry key="Width 1024" type="String" name="Width 1024" />
+
+ <entry key="Recent URLs" type="Path" name="Recent URLs" >
+ <label>The recent URLs recently visited</label>
+ <whatsthis>Used for auto completion in file dialogs for example.</whatsthis>
+ </entry>
+
+ <entry key="Show Preview" type="Bool" name="Show Preview" >
+ <label>Show file preview in file dialog</label>
+ <default>false</default>
+ </entry>
+
+ <entry key="Show hidden files" type="Bool" name="Show hidden files" >
+ <label>Show hidden files</label>
+ <whatsthis>Determines if files starting with a dot(convention for hidden files) should be shown</whatsthis>
+ <default>false</default>
+ </entry>
+
+ <entry key="Show Speedbar" type="Bool" name="Show Speedbar" >
+ <label>Show speedbar</label>
+ <whatsthis>Whether the shortcut icons to the left in the file dialog should be shown</whatsthis>
+ <default>true</default>
+ </entry>
+
+ <entry key="Sort reversed" type="Bool" name="Sort reversed" >
+ <default>false</default>
+ </entry>
+
+ </group>
+ <group name="KSpell" >
+ <entry key="KSpell_Client" type="String" />
+ <entry key="KSpell_DictFromList" type="String" />
+ <entry key="KSpell_Dictionary" type="String" />
+ <entry key="KSpell_Encoding" type="String" />
+ <entry key="KSpell_NoRootAffix" type="String" />
+ <entry key="KSpell_RunTogether" type="String" />
+ </group>
+ <group name="Locale" >
+ <entry key="Country" type="String" >
+ <label>What country</label>
+ <whatsthis>Used to determine how to display numbers, currency and time/date for example</whatsthis>
+ <default>C</default>
+ </entry>
+ <entry key="Language" type="String" >
+ <label>What language to display text in</label>
+ </entry>
+ <entry key="PositiveSign" type="String" >
+ <label>Character used for indicating positive numbers</label>
+ <whatsthis>Most countries have no character for this</whatsthis>
+ </entry>
+ </group>
+ <group name="Paths" >
+ <entry key="Trash" type="Path" >
+ <label>Path for the trash can</label>
+ <default>$HOME/Desktop/Trash</default>
+ </entry>
+ <entry key="Autostart" type="Path" >
+ <label>Path to the autostart directory</label>
+ <whatsthis>Path to the directory containing executables to be run on session login</whatsthis>
+ <default>$KDEHOME/Autostart</default>
+ </entry>
+ <entry key="Desktop" type="Path" >
+ <label>Path to the desktop directory</label>
+ <whatsthis>In this directory the files on the dekstop are stored</whatsthis>
+ <default>$HOME/Desktop</default>
+ </entry>
+ <entry key="Documents" type="Path" >
+ <label>Path to documents folder</label>
+ <whatsthis></whatsthis>
+ <default>$HOME</default>
+ </entry>
+ </group>
+ <group name="PreviewSettings" >
+ <entry key="BoostSize" type="Bool" />
+ <entry key="MaximumSize" type="Bool" />
+ <entry key="UseFileThumbnails" type="Bool" />
+ <entry key="ar" type="Bool" />
+ <entry key="audiocd" type="Bool" />
+ <entry key="devices" type="Bool" />
+ <entry key="file" type="Bool" />
+ <entry key="fish" type="Bool" />
+ <entry key="floppy" type="Bool" />
+ <entry key="fonts" type="Bool" />
+ <entry key="ftp" type="Bool" />
+ <entry key="imap" type="Bool" />
+ <entry key="imaps" type="Bool" />
+ <entry key="kdeapi" type="Bool" />
+ <entry key="lan" type="Bool" />
+ <entry key="mac" type="Bool" />
+ <entry key="man" type="Bool" />
+ <entry key="nfs" type="Bool" />
+ <entry key="nntp" type="Bool" />
+ <entry key="perldoc" type="Bool" />
+ <entry key="pop3" type="Bool" />
+ <entry key="pop3s" type="Bool" />
+ <entry key="print" type="Bool" />
+ <entry key="printdb" type="Bool" />
+ <entry key="programs" type="Bool" />
+ <entry key="pydoc" type="Bool" />
+ <entry key="rlan" type="Bool" />
+ <entry key="settings" type="Bool" />
+ <entry key="sftp" type="Bool" />
+ <entry key="sieve" type="Bool" />
+ <entry key="smb" type="Bool" />
+ <entry key="smtp" type="Bool" />
+ <entry key="smtps" type="Bool" />
+ <entry key="system" type="Bool" />
+ <entry key="tar" type="Bool" />
+ <entry key="webdav" type="Bool" />
+ <entry key="webdavs" type="Bool" />
+ <entry key="zip" type="Bool" />
+ </group>
+ <group name="Socks" >
+ <entry key="SOCKS_enable" type="Bool" >
+ <label>Enable SOCKS support</label>
+ <whatsthis>Whether SOCKS version 4 and 5 should be enabled in KDE's sub systems</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry key="SOCKS_lib" type="Path" >
+ <label>Path to custom SOCKS library</label>
+ </entry>
+ <entry key="SOCKS_method" type="String" />
+ </group>
+ <group name="Toolbar style" >
+ <entry key="Highlighting" type="Bool">
+ <label>Highlight toolbar buttons on mouse over</label>
+ <default>true</default>
+ </entry>
+ <entry key="IconText" type="Bool">
+ <label>Show text on toolbar icons </label>
+ <whatsthis>Whether text should be shown in addition to icons on toolbar icons</whatsthis>
+ <default>false</default>
+ </entry>
+ <entry key="TransparentMoving" type="Bool" >
+ <label>Transparent toolbars when moved</label>
+ <whatsthis>Whether toolbars should be visible when moved</whatsthis>
+ <default>false</default>
+ </entry>
+ </group>
+ <group name="WM" >
+ <entry key="activeBackground" type="String" />
+ <entry key="activeBlend" type="String" />
+ <entry key="activeFont" type="String" />
+ <entry key="activeForeground" type="String" />
+ <entry key="activeTitleBtnBg" type="String" />
+ <entry key="frame" type="String" />
+ <entry key="handle" type="String" />
+ <entry key="inactiveBackground" type="String" />
+ <entry key="inactiveBlend" type="String" />
+ <entry key="inactiveForeground" type="String" />
+ <entry key="inactiveFrame" type="String" />
+ <entry key="inactiveHandle" type="String" />
+ <entry key="inactiveTitleBtnBg" type="String" />
+ </group>
+ <group name="Passwords">
+ <entry name="EchoMode" type="Enum">
+ <label>Password echo type</label>
+ <choices>
+ <choice name="OneStar"/>
+ <choice name="ThreeStars"/>
+ <choice name="NoEcho"/>
+ </choices>
+ <default>OneStar</default>
+ </entry>
+ </group>
+ <group name="KKeyDialog Settings">
+ <entry name="Dialog Size" type="IntList"><!-- Should this be taken care of globally, for dialogs? -->
+ <label>The size of the dialog</label>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kutils/kdeglobals.kcfgc b/kutils/kdeglobals.kcfgc
new file mode 100644
index 000000000..fee74fc45
--- /dev/null
+++ b/kutils/kdeglobals.kcfgc
@@ -0,0 +1,5 @@
+File=kdeglobals.kcfg
+ClassName=KDEGlobals
+Singleton=true
+Mutators=true
+
diff --git a/kutils/kfind.cpp b/kutils/kfind.cpp
new file mode 100644
index 000000000..a6eb8dd46
--- /dev/null
+++ b/kutils/kfind.cpp
@@ -0,0 +1,710 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl>
+ This file is part of the KDE project
+
+ 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 "kfind.h"
+#include "kfinddialog.h"
+#include <kapplication.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qstylesheet.h>
+#include <qguardedptr.h>
+#include <qptrvector.h>
+#include <kdebug.h>
+
+//#define DEBUG_FIND
+
+#define INDEX_NOMATCH -1
+
+class KFindNextDialog : public KDialogBase
+{
+public:
+ KFindNextDialog(const QString &pattern, QWidget *parent);
+};
+
+// Create the dialog.
+KFindNextDialog::KFindNextDialog(const QString &pattern, QWidget *parent) :
+ KDialogBase(parent, 0, false, // non-modal!
+ i18n("Find Next"),
+ User1 | Close,
+ User1,
+ false,
+ KStdGuiItem::find())
+{
+ setMainWidget( new QLabel( i18n("<qt>Find next occurrence of '<b>%1</b>'?</qt>").arg(pattern), this ) );
+}
+
+////
+
+struct KFind::Private
+{
+ Private() :
+ findDialog(0),
+ patternChanged(false),
+ matchedPattern(""),
+ incrementalPath(29, true),
+ emptyMatch(0),
+ currentId(0),
+ customIds(false)
+ {
+ incrementalPath.setAutoDelete(true);
+ data.setAutoDelete(true);
+ }
+
+ ~Private()
+ {
+ delete emptyMatch;
+ emptyMatch = 0;
+ }
+
+ struct Match
+ {
+ Match(int dataId, int index, int matchedLength) :
+ dataId(dataId),
+ index(index),
+ matchedLength(matchedLength)
+ { }
+
+ int dataId;
+ int index;
+ int matchedLength;
+ };
+
+ struct Data
+ {
+ Data() : id(-1), dirty(false) { }
+ Data(int id, const QString &text, bool dirty = false) :
+ id(id),
+ text(text),
+ dirty(dirty)
+ { }
+
+ int id;
+ QString text;
+ bool dirty;
+ };
+
+ QGuardedPtr<QWidget> findDialog;
+ bool patternChanged;
+ QString matchedPattern;
+ QDict<Match> incrementalPath;
+ Match * emptyMatch;
+ QPtrVector<Data> data;
+ int currentId;
+ bool customIds;
+};
+
+////
+
+KFind::KFind( const QString &pattern, long options, QWidget *parent )
+ : QObject( parent )
+{
+ d = new KFind::Private;
+ m_options = options;
+ init( pattern );
+}
+
+KFind::KFind( const QString &pattern, long options, QWidget *parent, QWidget *findDialog )
+ : QObject( parent )
+{
+ d = new KFind::Private;
+ d->findDialog = findDialog;
+ m_options = options;
+ init( pattern );
+}
+
+void KFind::init( const QString& pattern )
+{
+ m_matches = 0;
+ m_pattern = pattern;
+ m_dialog = 0;
+ m_dialogClosed = false;
+ m_index = INDEX_NOMATCH;
+ m_lastResult = NoMatch;
+ if (m_options & KFindDialog::RegularExpression)
+ m_regExp = new QRegExp(pattern, m_options & KFindDialog::CaseSensitive);
+ else {
+ m_regExp = 0;
+ }
+}
+
+KFind::~KFind()
+{
+ delete m_dialog;
+ delete d;
+}
+
+bool KFind::needData() const
+{
+ // always true when m_text is empty.
+ if (m_options & KFindDialog::FindBackwards)
+ // m_index==-1 and m_lastResult==Match means we haven't answered nomatch yet
+ // This is important in the "replace with a prompt" case.
+ return ( m_index < 0 && m_lastResult != Match );
+ else
+ // "index over length" test removed: we want to get a nomatch before we set data again
+ // This is important in the "replace with a prompt" case.
+ return m_index == INDEX_NOMATCH;
+}
+
+void KFind::setData( const QString& data, int startPos )
+{
+ setData( -1, data, startPos );
+}
+
+void KFind::setData( int id, const QString& data, int startPos )
+{
+ // cache the data for incremental find
+ if ( m_options & KFindDialog::FindIncremental )
+ {
+ if ( id != -1 )
+ d->customIds = true;
+ else
+ id = d->currentId + 1;
+
+ if ( id >= (int) d->data.size() )
+ d->data.resize( id + 100 );
+
+ d->data.insert( id, new Private::Data(id, data, true) );
+ }
+
+ if ( !(m_options & KFindDialog::FindIncremental) || needData() )
+ {
+ m_text = data;
+
+ if ( startPos != -1 )
+ m_index = startPos;
+ else if (m_options & KFindDialog::FindBackwards)
+ m_index = m_text.length();
+ else
+ m_index = 0;
+#ifdef DEBUG_FIND
+ kdDebug() << "setData: '" << m_text << "' m_index=" << m_index << endl;
+#endif
+ Q_ASSERT( m_index != INDEX_NOMATCH );
+ m_lastResult = NoMatch;
+
+ d->currentId = id;
+ }
+}
+
+KDialogBase* KFind::findNextDialog( bool create )
+{
+ if ( !m_dialog && create )
+ {
+ m_dialog = new KFindNextDialog( m_pattern, parentWidget() );
+ connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotFindNext() ) );
+ connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) );
+ }
+ return m_dialog;
+}
+
+KFind::Result KFind::find()
+{
+ Q_ASSERT( m_index != INDEX_NOMATCH || d->patternChanged );
+
+ if ( m_lastResult == Match && !d->patternChanged )
+ {
+ // Move on before looking for the next match, _if_ we just found a match
+ if (m_options & KFindDialog::FindBackwards) {
+ m_index--;
+ if ( m_index == -1 ) // don't call KFind::find with -1, it has a special meaning
+ {
+ m_lastResult = NoMatch;
+ return NoMatch;
+ }
+ } else
+ m_index++;
+ }
+ d->patternChanged = false;
+
+ if ( m_options & KFindDialog::FindIncremental )
+ {
+ // if the current pattern is shorter than the matchedPattern we can
+ // probably look up the match in the incrementalPath
+ if ( m_pattern.length() < d->matchedPattern.length() )
+ {
+ Private::Match *match = m_pattern.isEmpty() ? d->emptyMatch : d->incrementalPath[m_pattern];
+ QString previousPattern = d->matchedPattern;
+ d->matchedPattern = m_pattern;
+ if ( match != 0 )
+ {
+ bool clean = true;
+
+ // find the first result backwards on the path that isn't dirty
+ while ( d->data[match->dataId]->dirty == true &&
+ !m_pattern.isEmpty() )
+ {
+ m_pattern.truncate( m_pattern.length() - 1 );
+
+ match = d->incrementalPath[m_pattern];
+
+ clean = false;
+ }
+
+ // remove all matches that lie after the current match
+ while ( m_pattern.length() < previousPattern.length() )
+ {
+ d->incrementalPath.remove(previousPattern);
+ previousPattern.truncate(previousPattern.length() - 1);
+ }
+
+ // set the current text, index, etc. to the found match
+ m_text = d->data[match->dataId]->text;
+ m_index = match->index;
+ m_matchedLength = match->matchedLength;
+ d->currentId = match->dataId;
+
+ // if the result is clean we can return it now
+ if ( clean )
+ {
+ if ( d->customIds )
+ emit highlight(d->currentId, m_index, m_matchedLength);
+ else
+ emit highlight(m_text, m_index, m_matchedLength);
+
+ m_lastResult = Match;
+ d->matchedPattern = m_pattern;
+ return Match;
+ }
+ }
+ // if we couldn't look up the match, the new pattern isn't a
+ // substring of the matchedPattern, so we start a new search
+ else
+ {
+ startNewIncrementalSearch();
+ }
+ }
+ // if the new pattern is longer than the matchedPattern we might be
+ // able to proceed from the last search
+ else if ( m_pattern.length() > d->matchedPattern.length() )
+ {
+ // continue from the previous pattern
+ if ( m_pattern.startsWith(d->matchedPattern) )
+ {
+ // we can't proceed from the previous position if the previous
+ // position already failed
+ if ( m_index == INDEX_NOMATCH )
+ return NoMatch;
+
+ QString temp = m_pattern;
+ m_pattern.truncate(d->matchedPattern.length() + 1);
+ d->matchedPattern = temp;
+ }
+ // start a new search
+ else
+ {
+ startNewIncrementalSearch();
+ }
+ }
+ // if the new pattern is as long as the matchedPattern, we reset if
+ // they are not equal
+ else if ( m_pattern != d->matchedPattern )
+ {
+ startNewIncrementalSearch();
+ }
+ }
+
+#ifdef DEBUG_FIND
+ kdDebug() << k_funcinfo << "m_index=" << m_index << endl;
+#endif
+ do
+ {
+ // if we have multiple data blocks in our cache, walk through these
+ // blocks till we either searched all blocks or we find a match
+ do
+ {
+ // Find the next candidate match.
+ if ( m_options & KFindDialog::RegularExpression )
+ m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength);
+ else
+ m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength);
+
+ if ( m_options & KFindDialog::FindIncremental )
+ d->data[d->currentId]->dirty = false;
+
+ if ( m_index == -1 && d->currentId < (int) d->data.count() - 1 )
+ {
+ m_text = d->data[++d->currentId]->text;
+
+ if ( m_options & KFindDialog::FindBackwards )
+ m_index = m_text.length();
+ else
+ m_index = 0;
+ }
+ else
+ break;
+ } while ( !(m_options & KFindDialog::RegularExpression) );
+
+ if ( m_index != -1 )
+ {
+ // Flexibility: the app can add more rules to validate a possible match
+ if ( validateMatch( m_text, m_index, m_matchedLength ) )
+ {
+ bool done = true;
+
+ if ( m_options & KFindDialog::FindIncremental )
+ {
+ if ( m_pattern.isEmpty() ) {
+ delete d->emptyMatch;
+ d->emptyMatch = new Private::Match( d->currentId, m_index, m_matchedLength );
+ } else
+ d->incrementalPath.replace(m_pattern, new Private::Match(d->currentId, m_index, m_matchedLength));
+
+ if ( m_pattern.length() < d->matchedPattern.length() )
+ {
+ m_pattern += d->matchedPattern.mid(m_pattern.length(), 1);
+ done = false;
+ }
+ }
+
+ if ( done )
+ {
+ m_matches++;
+ // Tell the world about the match we found, in case someone wants to
+ // highlight it.
+ if ( d->customIds )
+ emit highlight(d->currentId, m_index, m_matchedLength);
+ else
+ emit highlight(m_text, m_index, m_matchedLength);
+
+ if ( !m_dialogClosed )
+ findNextDialog(true)->show();
+
+#ifdef DEBUG_FIND
+ kdDebug() << k_funcinfo << "Match. Next m_index=" << m_index << endl;
+#endif
+ m_lastResult = Match;
+ return Match;
+ }
+ }
+ else // Skip match
+ {
+ if (m_options & KFindDialog::FindBackwards)
+ m_index--;
+ else
+ m_index++;
+ }
+ }
+ else
+ {
+ if ( m_options & KFindDialog::FindIncremental )
+ {
+ QString temp = m_pattern;
+ temp.truncate(temp.length() - 1);
+ m_pattern = d->matchedPattern;
+ d->matchedPattern = temp;
+ }
+
+ m_index = INDEX_NOMATCH;
+ }
+ }
+ while (m_index != INDEX_NOMATCH);
+
+#ifdef DEBUG_FIND
+ kdDebug() << k_funcinfo << "NoMatch. m_index=" << m_index << endl;
+#endif
+ m_lastResult = NoMatch;
+ return NoMatch;
+}
+
+void KFind::startNewIncrementalSearch()
+{
+ Private::Match *match = d->emptyMatch;
+ if(match == 0)
+ {
+ m_text = QString::null;
+ m_index = 0;
+ d->currentId = 0;
+ }
+ else
+ {
+ m_text = d->data[match->dataId]->text;
+ m_index = match->index;
+ d->currentId = match->dataId;
+ }
+ m_matchedLength = 0;
+ d->incrementalPath.clear();
+ delete d->emptyMatch;
+ d->emptyMatch = 0;
+ d->matchedPattern = m_pattern;
+ m_pattern = QString::null;
+}
+
+// static
+int KFind::find(const QString &text, const QString &pattern, int index, long options, int *matchedLength)
+{
+ // Handle regular expressions in the appropriate way.
+ if (options & KFindDialog::RegularExpression)
+ {
+ QRegExp regExp(pattern, options & KFindDialog::CaseSensitive);
+
+ return find(text, regExp, index, options, matchedLength);
+ }
+
+ bool caseSensitive = (options & KFindDialog::CaseSensitive);
+
+ if (options & KFindDialog::WholeWordsOnly)
+ {
+ if (options & KFindDialog::FindBackwards)
+ {
+ // Backward search, until the beginning of the line...
+ while (index >= 0)
+ {
+ // ...find the next match.
+ index = text.findRev(pattern, index, caseSensitive);
+ if (index == -1)
+ break;
+
+ // Is the match delimited correctly?
+ *matchedLength = pattern.length();
+ if (isWholeWords(text, index, *matchedLength))
+ break;
+ index--;
+ }
+ }
+ else
+ {
+ // Forward search, until the end of the line...
+ while (index < (int)text.length())
+ {
+ // ...find the next match.
+ index = text.find(pattern, index, caseSensitive);
+ if (index == -1)
+ break;
+
+ // Is the match delimited correctly?
+ *matchedLength = pattern.length();
+ if (isWholeWords(text, index, *matchedLength))
+ break;
+ index++;
+ }
+ if (index >= (int)text.length()) // end of line
+ index = -1; // not found
+ }
+ }
+ else
+ {
+ // Non-whole-word search.
+ if (options & KFindDialog::FindBackwards)
+ {
+ index = text.findRev(pattern, index, caseSensitive);
+ }
+ else
+ {
+ index = text.find(pattern, index, caseSensitive);
+ }
+ if (index != -1)
+ {
+ *matchedLength = pattern.length();
+ }
+ }
+ return index;
+}
+
+// static
+int KFind::find(const QString &text, const QRegExp &pattern, int index, long options, int *matchedLength)
+{
+ if (options & KFindDialog::WholeWordsOnly)
+ {
+ if (options & KFindDialog::FindBackwards)
+ {
+ // Backward search, until the beginning of the line...
+ while (index >= 0)
+ {
+ // ...find the next match.
+ index = text.findRev(pattern, index);
+ if (index == -1)
+ break;
+
+ // Is the match delimited correctly?
+ //pattern.match(text, index, matchedLength, false);
+ /*int pos =*/ pattern.search( text.mid(index) );
+ *matchedLength = pattern.matchedLength();
+ if (isWholeWords(text, index, *matchedLength))
+ break;
+ index--;
+ }
+ }
+ else
+ {
+ // Forward search, until the end of the line...
+ while (index < (int)text.length())
+ {
+ // ...find the next match.
+ index = text.find(pattern, index);
+ if (index == -1)
+ break;
+
+ // Is the match delimited correctly?
+ //pattern.match(text, index, matchedLength, false);
+ /*int pos =*/ pattern.search( text.mid(index) );
+ *matchedLength = pattern.matchedLength();
+ if (isWholeWords(text, index, *matchedLength))
+ break;
+ index++;
+ }
+ if (index >= (int)text.length()) // end of line
+ index = -1; // not found
+ }
+ }
+ else
+ {
+ // Non-whole-word search.
+ if (options & KFindDialog::FindBackwards)
+ {
+ index = text.findRev(pattern, index);
+ }
+ else
+ {
+ index = text.find(pattern, index);
+ }
+ if (index != -1)
+ {
+ //pattern.match(text, index, matchedLength, false);
+ /*int pos =*/ pattern.search( text.mid(index) );
+ *matchedLength = pattern.matchedLength();
+ }
+ }
+ return index;
+}
+
+bool KFind::isInWord(QChar ch)
+{
+ return ch.isLetter() || ch.isDigit() || ch == '_';
+}
+
+bool KFind::isWholeWords(const QString &text, int starts, int matchedLength)
+{
+ if ((starts == 0) || (!isInWord(text[starts - 1])))
+ {
+ int ends = starts + matchedLength;
+
+ if ((ends == (int)text.length()) || (!isInWord(text[ends])))
+ return true;
+ }
+ return false;
+}
+
+void KFind::slotFindNext()
+{
+ emit findNext();
+}
+
+void KFind::slotDialogClosed()
+{
+ emit dialogClosed();
+ m_dialogClosed = true;
+}
+
+void KFind::displayFinalDialog() const
+{
+ QString message;
+ if ( numMatches() )
+ message = i18n( "1 match found.", "%n matches found.", numMatches() );
+ else
+ message = i18n("<qt>No matches found for '<b>%1</b>'.</qt>").arg(QStyleSheet::escape(m_pattern));
+ KMessageBox::information(dialogsParent(), message);
+}
+
+bool KFind::shouldRestart( bool forceAsking, bool showNumMatches ) const
+{
+ // Only ask if we did a "find from cursor", otherwise it's pointless.
+ // Well, unless the user can modify the document during a search operation,
+ // hence the force boolean.
+ if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0 )
+ {
+ displayFinalDialog();
+ return false;
+ }
+ QString message;
+ if ( showNumMatches )
+ {
+ if ( numMatches() )
+ message = i18n( "1 match found.", "%n matches found.", numMatches() );
+ else
+ message = i18n("No matches found for '<b>%1</b>'.").arg(QStyleSheet::escape(m_pattern));
+ }
+ else
+ {
+ if ( m_options & KFindDialog::FindBackwards )
+ message = i18n( "Beginning of document reached." );
+ else
+ message = i18n( "End of document reached." );
+ }
+
+ message += "<br><br>"; // can't be in the i18n() of the first if() because of the plural form.
+ // Hope this word puzzle is ok, it's a different sentence
+ message +=
+ ( m_options & KFindDialog::FindBackwards ) ?
+ i18n("Continue from the end?")
+ : i18n("Continue from the beginning?");
+
+ int ret = KMessageBox::questionYesNo( dialogsParent(), QString("<qt>")+message+QString("</qt>"),
+ QString::null, KStdGuiItem::cont(), KStdGuiItem::stop() );
+ bool yes = ( ret == KMessageBox::Yes );
+ if ( yes )
+ const_cast<KFind*>(this)->m_options &= ~KFindDialog::FromCursor; // clear FromCursor option
+ return yes;
+}
+
+void KFind::setOptions( long options )
+{
+ m_options = options;
+
+ delete m_regExp;
+ if (m_options & KFindDialog::RegularExpression)
+ m_regExp = new QRegExp(m_pattern, m_options & KFindDialog::CaseSensitive);
+ else
+ m_regExp = 0;
+}
+
+void KFind::closeFindNextDialog()
+{
+ delete m_dialog;
+ m_dialog = 0L;
+ m_dialogClosed = true;
+}
+
+int KFind::index() const
+{
+ return m_index;
+}
+
+void KFind::setPattern( const QString& pattern )
+{
+ if ( m_options & KFindDialog::FindIncremental && m_pattern != pattern )
+ d->patternChanged = true;
+
+ m_pattern = pattern;
+ setOptions( options() ); // rebuild m_regExp if necessary
+}
+
+QWidget* KFind::dialogsParent() const
+{
+ // If the find dialog is still up, it should get the focus when closing a message box
+ // Otherwise, maybe the "find next?" dialog is up
+ // Otherwise, the "view" is the parent.
+ return d->findDialog ? (QWidget*)d->findDialog : ( m_dialog ? m_dialog : parentWidget() );
+}
+
+#include "kfind.moc"
diff --git a/kutils/kfind.h b/kutils/kfind.h
new file mode 100644
index 000000000..a2159f6b1
--- /dev/null
+++ b/kutils/kfind.h
@@ -0,0 +1,426 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 KFIND_H
+#define KFIND_H
+
+#include <kdialogbase.h>
+#include <qrect.h>
+
+/**
+ * @ingroup main
+ * @ingroup findreplace
+ * @brief A generic implementation of the "find" function.
+ *
+ * @author S.R.Haque <srhaque@iee.org>, David Faure <faure@kde.org>,
+ * Arend van Beelen jr. <arend@auton.nl>
+ *
+ * \b Detail:
+ *
+ * This class includes prompt handling etc. Also provides some
+ * static functions which can be used to create custom behavior
+ * instead of using the class directly.
+ *
+ * \b Example:
+ *
+ * To use the class to implement a complete find feature:
+ *
+ * In the slot connected to the find action, after using KFindDialog:
+ * \code
+ *
+ * // This creates a find-next-prompt dialog if needed.
+ * m_find = new KFind(pattern, options, this);
+ *
+ * // Connect highlight signal to code which handles highlighting
+ * // of found text.
+ * connect( m_find, SIGNAL( highlight( const QString &, int, int ) ),
+ * this, SLOT( slotHighlight( const QString &, int, int ) ) );
+ * // Connect findNext signal - called when pressing the button in the dialog
+ * connect( m_find, SIGNAL( findNext() ),
+ * this, SLOT( slotFindNext() ) );
+ * \endcode
+ *
+ * If you are using a non-modal find dialog (the recommended new way
+ * in KDE-3.2), you should call right away m_find->closeFindNextDialog().
+ *
+ * Then initialize the variables determining the "current position"
+ * (to the cursor, if the option FromCursor is set,
+ * to the beginning of the selection if the option SelectedText is set,
+ * and to the beginning of the document otherwise).
+ * Initialize the "end of search" variables as well (end of doc or end of selection).
+ * Swap begin and end if FindBackwards.
+ * Finally, call slotFindNext();
+ *
+ * \code
+ * void slotFindNext()
+ * {
+ * KFind::Result res = KFind::NoMatch;
+ * while ( res == KFind::NoMatch && <position not at end> ) {
+ * if ( m_find->needData() )
+ * m_find->setData( <current text fragment> );
+ *
+ * // Let KFind inspect the text fragment, and display a dialog if a match is found
+ * res = m_find->find();
+ *
+ * if ( res == KFind::NoMatch ) {
+ * <Move to the next text fragment, honoring the FindBackwards setting for the direction>
+ * }
+ * }
+ *
+ * if ( res == KFind::NoMatch ) // i.e. at end
+ * <Call either m_find->displayFinalDialog(); delete m_find; m_find = 0L;
+ * or if ( m_find->shouldRestart() ) { reinit (w/o FromCursor) and call slotFindNext(); }
+ * else { m_find->closeFindNextDialog(); }>
+ * }
+ * \endcode
+ *
+ * Don't forget to delete m_find in the destructor of your class,
+ * unless you gave it a parent widget on construction.
+ *
+ * This implementation allows to have a "Find Next" action, which resumes the
+ * search, even if the user closed the "Find Next" dialog.
+ *
+ * A "Find Previous" action can simply switch temporarily the value of
+ * FindBackwards and call slotFindNext() - and reset the value afterwards.
+ */
+class KUTILS_EXPORT KFind :
+ public QObject
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Only use this constructor if you don't use KFindDialog, or if
+ * you use it as a modal dialog.
+ * @param pattern The pattern to look for.
+ * @param options Options for the find dialog. @see KFindDialog.
+ * @param parent The parent widget.
+ */
+ KFind(const QString &pattern, long options, QWidget *parent);
+
+ /**
+ * This is the recommended constructor if you also use KFindDialog (non-modal).
+ * You should pass the pointer to it here, so that when a message box
+ * appears it has the right parent. Don't worry about deletion, KFind
+ * will notice if the find dialog is closed.
+ * @param pattern The pattern to look for.
+ * @param options Options for the find dialog. @see KFindDialog.
+ * @param parent The parent widget.
+ * @param findDialog A pointer to the KFindDialog object.
+ */
+ KFind(const QString &pattern, long options, QWidget *parent, QWidget* findDialog);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KFind();
+
+ /**
+ * Result enum. Holds information if the find was successful.
+ */
+ enum Result {
+ NoMatch, ///< No match was found.
+ Match ///< A match was found.
+ };
+
+ /**
+ * @return @c true if the application must supply a new text fragment
+ * It also means the last call returned "NoMatch". But by storing this here
+ * the application doesn't have to store it in a member variable (between
+ * calls to slotFindNext()).
+ */
+ bool needData() const;
+
+ /**
+ * Call this when needData returns @c true, before calling find().
+ * @param data the text fragment (line)
+ * @param startPos if set, the index at which the search should start.
+ * This is only necessary for the very first call to setData usually,
+ * for the 'find in selection' feature. A value of -1 (the default value)
+ * means "process all the data", i.e. either 0 or data.length()-1 depending
+ * on FindBackwards.
+ */
+ void setData( const QString& data, int startPos = -1 );
+
+ /**
+ * Call this when needData returns @c true, before calling find(). The use of
+ * ID's is especially useful if you're using the FindIncremental option.
+ * @param id the id of the text fragment
+ * @param data the text fragment (line)
+ * @param startPos if set, the index at which the search should start.
+ * This is only necessary for the very first call to setData usually,
+ * for the 'find in selection' feature. A value of -1 (the default value)
+ * means "process all the data", i.e. either 0 or data.length()-1 depending
+ * on FindBackwards.
+ *
+ * @since 3.3
+ */
+ void setData( int id, const QString& data, int startPos = -1 );
+
+ /**
+ * Walk the text fragment (e.g. text-processor line, kspread cell) looking for matches.
+ * For each match, emits the highlight() signal and displays the find-again dialog
+ * proceeding.
+ * @return Whether or not there has been a match.
+ */
+ Result find();
+
+ /**
+ * Return the current options.
+ *
+ * Warning: this is usually the same value as the one passed to the constructor,
+ * but options might change _during_ the replace operation:
+ * e.g. the "All" button resets the PromptOnReplace flag.
+ *
+ * @return The current options. @see KFindDialog.
+ */
+ long options() const { return m_options; }
+
+ /**
+ * Set new options. Usually this is used for setting or clearing the
+ * FindBackwards options.
+ *
+ * @see KFindDialog.
+ */
+ virtual void setOptions( long options );
+
+ /**
+ * @return the pattern we're currently looking for
+ */
+ QString pattern() const { return m_pattern; }
+
+ /**
+ * Change the pattern we're looking for
+ * @param pattern The new pattern.
+ */
+ void setPattern( const QString& pattern );
+
+ /**
+ * Return the number of matches found (i.e. the number of times
+ * the highlight signal was emitted).
+ * If 0, can be used in a dialog box to tell the user "no match was found".
+ * The final dialog does so already, unless you used setDisplayFinalDialog(false).
+ * @return The number of matches.
+ */
+ int numMatches() const { return m_matches; }
+
+ /**
+ * Call this to reset the numMatches count
+ * (and the numReplacements count for a KReplace).
+ * Can be useful if reusing the same KReplace for different operations,
+ * or when restarting from the beginning of the document.
+ */
+ virtual void resetCounts() { m_matches = 0; }
+
+ /**
+ * Virtual method, which allows applications to add extra checks for
+ * validating a candidate match. It's only necessary to reimplement this
+ * if the find dialog extension has been used to provide additional
+ * criterias.
+ *
+ * @param text The current text fragment
+ * @param index The starting index where the candidate match was found
+ * @param matchedlength The length of the candidate match
+ */
+ virtual bool validateMatch( const QString & text, int index, int matchedlength ) {
+ Q_UNUSED(text); Q_UNUSED(index); Q_UNUSED(matchedlength); return true; }
+
+ /**
+ * Returns @c true if we should restart the search from scratch.
+ * Can ask the user, or return @c false (if we already searched the whole document).
+ *
+ * @param forceAsking set to @c true if the user modified the document during the
+ * search. In that case it makes sense to restart the search again.
+ *
+ * @param showNumMatches set to @c true if the dialog should show the number of
+ * matches. Set to @c false if the application provides a "find previous" action,
+ * in which case the match count will be erroneous when hitting the end,
+ * and we could even be hitting the beginning of the document (so not all
+ * matches have even been seen).
+ *
+ * @return @c true, if the search should be restarted.
+ */
+ virtual bool shouldRestart( bool forceAsking = false, bool showNumMatches = true ) const;
+
+ /**
+ * Search the given string, and returns whether a match was found. If one is,
+ * the length of the string matched is also returned.
+ *
+ * A performance optimised version of the function is provided for use
+ * with regular expressions.
+ *
+ * @param text The string to search.
+ * @param pattern The pattern to look for.
+ * @param index The starting index into the string.
+ * @param options The options to use.
+ * @param matchedlength The length of the string that was matched
+ * @return The index at which a match was found, or -1 if no match was found.
+ */
+ static int find( const QString &text, const QString &pattern, int index, long options, int *matchedlength );
+
+ /**
+ * Search the given regular expression, and returns whether a match was found. If one is,
+ * the length of the matched string is also returned.
+ *
+ * Another version of the function is provided for use with strings.
+ *
+ * @param text The string to search.
+ * @param pattern The regular expression pattern to look for.
+ * @param index The starting index into the string.
+ * @param options The options to use.
+ * @param matchedlength The length of the string that was matched
+ * @return The index at which a match was found, or -1 if no match was found.
+ */
+ static int find( const QString &text, const QRegExp &pattern, int index, long options, int *matchedlength );
+
+ /**
+ * Displays the final dialog saying "no match was found", if that was the case.
+ * Call either this or shouldRestart().
+ */
+ virtual void displayFinalDialog() const;
+
+ /**
+ * Return (or create) the dialog that shows the "find next?" prompt.
+ * Usually you don't need to call this.
+ * One case where it can be useful, is when the user selects the "Find"
+ * menu item while a find operation is under way. In that case, the
+ * program may want to call setActiveWindow() on that dialog.
+ * @return The find next dialog.
+ */
+ KDialogBase* findNextDialog( bool create = false );
+
+ /**
+ * Close the "find next?" dialog. The application should do this when
+ * the last match was hit. If the application deletes the KFind, then
+ * "find previous" won't be possible anymore.
+ *
+ * IMPORTANT: you should also call this if you are using a non-modal
+ * find dialog, to tell KFind not to pop up its own dialog.
+ */
+ void closeFindNextDialog();
+
+ /**
+ * @return the current matching index ( or -1 ).
+ * Same as the matchingIndex parameter passed to highlight.
+ * You usually don't need to use this, except maybe when updating the current data,
+ * so you need to call setData( newData, index() ).
+ * @since 3.2
+ */
+ int index() const;
+
+signals:
+
+ /**
+ * Connect to this signal to implement highlighting of found text during the find
+ * operation.
+ *
+ * If you've set data with setData(id, text), use the signal highlight(id,
+ * matchingIndex, matchedLength)
+ *
+ * @warning If you're using the FindIncremental option, the text argument
+ * passed by this signal is not necessarily the data last set through
+ * setData(), but can also be an earlier set data block.
+ *
+ * @param text The found text.
+ * @param matchingIndex The index of the found text's occurrence.
+ * @param matchedLength The length of the matched text.
+ * @see setData()
+ */
+ void highlight(const QString &text, int matchingIndex, int matchedLength);
+
+ /**
+ * Connect to this signal to implement highlighting of found text during the find
+ * operation.
+ *
+ * Use this signal if you've set your data with setData(id, text), otherwise
+ * use the signal with highlight(text, matchingIndex, matchedLength).
+ *
+ * @warning If you're using the FindIncremental option, the id argument
+ * passed by this signal is not necessarily the id of the data last set
+ * through setData(), but can also be of an earlier set data block.
+ *
+ * @param id The ID of the text fragment, as used in setData().
+ * @param matchingIndex The index of the found text's occurrence.
+ * @param matchedLength The length of the matched text.
+ * @see setData()
+ *
+ * @since 3.3
+ */
+ void highlight(int id, int matchingIndex, int matchedLength);
+
+ // ## TODO docu
+ // findprevious will also emit findNext, after temporarily switching the value
+ // of FindBackwards
+ void findNext();
+
+ /**
+ * Emitted when the options have changed.
+ * This can happen e.g. with "Replace All", or if our 'find next' dialog
+ * gets a "find previous" one day.
+ */
+ void optionsChanged();
+
+ /**
+ * Emitted when the 'find next' dialog is being closed.
+ * Some apps might want to remove the highlighted text when this happens.
+ * Apps without support for "Find Next" can also do m_find->deleteLater()
+ * to terminate the find operation.
+ */
+ void dialogClosed();
+
+protected:
+
+ QWidget* parentWidget() const { return (QWidget *)parent(); }
+ QWidget* dialogsParent() const;
+
+protected slots:
+
+ void slotFindNext();
+ void slotDialogClosed();
+
+private:
+ void init( const QString& pattern );
+ void startNewIncrementalSearch();
+
+ static bool isInWord( QChar ch );
+ static bool isWholeWords( const QString &text, int starts, int matchedLength );
+
+ friend class KReplace;
+
+
+ QString m_pattern;
+ QRegExp *m_regExp;
+ KDialogBase* m_dialog;
+ long m_options;
+ unsigned m_matches;
+
+ QString m_text; // the text set by setData
+ int m_index;
+ int m_matchedLength;
+ bool m_dialogClosed;
+ bool m_lastResult;
+
+ // Binary compatible extensibility.
+ struct Private;
+ Private *d;
+};
+
+#endif
diff --git a/kutils/kfinddialog.cpp b/kutils/kfinddialog.cpp
new file mode 100644
index 000000000..7a18111a3
--- /dev/null
+++ b/kutils/kfinddialog.cpp
@@ -0,0 +1,544 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 "kfinddialog.h"
+#include <qcheckbox.h>
+#include <qcursor.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpopupmenu.h>
+#include <qpushbutton.h>
+#include <qregexp.h>
+#include <kcombobox.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <assert.h>
+#include <qwhatsthis.h>
+
+#include <kregexpeditorinterface.h>
+#include <kparts/componentfactory.h>
+
+class KFindDialog::KFindDialogPrivate
+{
+public:
+ KFindDialogPrivate() : m_regexpDialog(0),
+ m_regexpDialogQueryDone(false),
+ m_enabled(WholeWordsOnly | FromCursor | SelectedText | CaseSensitive | FindBackwards | RegularExpression), m_initialShowDone(false) {}
+ QDialog* m_regexpDialog;
+ bool m_regexpDialogQueryDone;
+ long m_enabled; // uses Options to define which search options are enabled
+ bool m_initialShowDone;
+ QStringList findStrings;
+ QString pattern;
+};
+
+KFindDialog::KFindDialog(QWidget *parent, const char *name, long options, const QStringList &findStrings, bool hasSelection) :
+ KDialogBase(parent, name, true, i18n("Find Text"), Ok | Cancel, Ok),
+ m_findExtension (0),
+ m_replaceExtension (0)
+{
+ d = new KFindDialogPrivate;
+ init(false, findStrings, hasSelection);
+ setOptions(options);
+ setButtonCancel( KStdGuiItem::close() );
+}
+
+KFindDialog::KFindDialog(bool modal, QWidget *parent, const char *name, long options, const QStringList &findStrings, bool hasSelection) :
+ KDialogBase(parent, name, modal, i18n("Find Text"), Ok | Cancel, Ok),
+ m_findExtension (0),
+ m_replaceExtension (0)
+{
+ d = new KFindDialogPrivate;
+ init(false, findStrings, hasSelection);
+ setOptions(options);
+ setButtonCancel( KStdGuiItem::close() );
+}
+
+KFindDialog::KFindDialog(QWidget *parent, const char *name, bool /*forReplace*/) :
+ KDialogBase(parent, name, true, i18n("Replace Text"), Ok | Cancel, Ok),
+ m_findExtension (0),
+ m_replaceExtension (0)
+{
+ d = new KFindDialogPrivate;
+ setButtonCancel( KStdGuiItem::close() );
+}
+
+KFindDialog::~KFindDialog()
+{
+ delete d;
+}
+
+QWidget *KFindDialog::findExtension()
+{
+ if (!m_findExtension)
+ {
+ m_findExtension = new QWidget(m_findGrp);
+ m_findLayout->addMultiCellWidget(m_findExtension, 3, 3, 0, 1);
+ }
+
+ return m_findExtension;
+}
+
+QStringList KFindDialog::findHistory() const
+{
+ return m_find->historyItems();
+}
+
+void KFindDialog::init(bool forReplace, const QStringList &findStrings, bool hasSelection)
+{
+ QVBoxLayout *topLayout;
+ QGridLayout *optionsLayout;
+
+ // Create common parts of dialog.
+ QWidget *page = new QWidget(this);
+ setMainWidget(page);
+
+ topLayout = new QVBoxLayout(page);
+ topLayout->setSpacing( KDialog::spacingHint() );
+ topLayout->setMargin( 0 );
+
+ m_findGrp = new QGroupBox(0, Qt::Vertical, i18n("Find"), page);
+ m_findGrp->layout()->setSpacing( KDialog::spacingHint() );
+ // m_findGrp->layout()->setMargin( KDialog::marginHint() );
+ m_findLayout = new QGridLayout(m_findGrp->layout());
+ m_findLayout->setSpacing( KDialog::spacingHint() );
+ // m_findLayout->setMargin( KDialog::marginHint() );
+
+ m_findLabel = new QLabel(i18n("&Text to find:"), m_findGrp);
+ m_find = new KHistoryCombo(true, m_findGrp);
+ m_find->setMaxCount(10);
+ m_find->setDuplicatesEnabled(false);
+ m_regExp = new QCheckBox(i18n("Regular e&xpression"), m_findGrp);
+ m_regExpItem = new QPushButton(i18n("&Edit..."), m_findGrp);
+ m_regExpItem->setEnabled(false);
+
+ m_findLayout->addWidget(m_findLabel, 0, 0);
+ m_findLayout->addMultiCellWidget(m_find, 1, 1, 0, 1);
+ m_findLayout->addWidget(m_regExp, 2, 0);
+ m_findLayout->addWidget(m_regExpItem, 2, 1);
+ topLayout->addWidget(m_findGrp);
+
+ m_replaceGrp = new QGroupBox(0, Qt::Vertical, i18n("Replace With"), page);
+ m_replaceGrp->layout()->setSpacing( KDialog::spacingHint() );
+ // m_replaceGrp->layout()->setMargin( KDialog::marginHint() );
+ m_replaceLayout = new QGridLayout(m_replaceGrp->layout());
+ m_replaceLayout->setSpacing( KDialog::spacingHint() );
+// m_replaceLayout->setMargin( KDialog::marginHint() );
+
+ m_replaceLabel = new QLabel(i18n("Replace&ment text:"), m_replaceGrp);
+ m_replace = new KHistoryCombo(true, m_replaceGrp);
+ m_replace->setMaxCount(10);
+ m_replace->setDuplicatesEnabled(false);
+ m_backRef = new QCheckBox(i18n("Use p&laceholders"), m_replaceGrp);
+ m_backRefItem = new QPushButton(i18n("Insert Place&holder"), m_replaceGrp);
+ m_backRefItem->setEnabled(false);
+
+ m_replaceLayout->addWidget(m_replaceLabel, 0, 0);
+ m_replaceLayout->addMultiCellWidget(m_replace, 1, 1, 0, 1);
+ m_replaceLayout->addWidget(m_backRef, 2, 0);
+ m_replaceLayout->addWidget(m_backRefItem, 2, 1);
+ topLayout->addWidget(m_replaceGrp);
+
+ m_optionGrp = new QGroupBox(0, Qt::Vertical, i18n("Options"), page);
+ m_optionGrp->layout()->setSpacing(KDialog::spacingHint());
+ // m_optionGrp->layout()->setMargin(KDialog::marginHint());
+ optionsLayout = new QGridLayout(m_optionGrp->layout());
+ optionsLayout->setSpacing( KDialog::spacingHint() );
+ // optionsLayout->setMargin( KDialog::marginHint() );
+
+ m_caseSensitive = new QCheckBox(i18n("C&ase sensitive"), m_optionGrp);
+ m_wholeWordsOnly = new QCheckBox(i18n("&Whole words only"), m_optionGrp);
+ m_fromCursor = new QCheckBox(i18n("From c&ursor"), m_optionGrp);
+ m_findBackwards = new QCheckBox(i18n("Find &backwards"), m_optionGrp);
+ m_selectedText = new QCheckBox(i18n("&Selected text"), m_optionGrp);
+ setHasSelection( hasSelection );
+ // If we have a selection, we make 'find in selection' default
+ // and if we don't, then the option has to be unchecked, obviously.
+ m_selectedText->setChecked( hasSelection );
+ slotSelectedTextToggled( hasSelection );
+
+ m_promptOnReplace = new QCheckBox(i18n("&Prompt on replace"), m_optionGrp);
+ m_promptOnReplace->setChecked( true );
+
+ optionsLayout->addWidget(m_caseSensitive, 0, 0);
+ optionsLayout->addWidget(m_wholeWordsOnly, 1, 0);
+ optionsLayout->addWidget(m_fromCursor, 2, 0);
+ optionsLayout->addWidget(m_findBackwards, 0, 1);
+ optionsLayout->addWidget(m_selectedText, 1, 1);
+ optionsLayout->addWidget(m_promptOnReplace, 2, 1);
+ topLayout->addWidget(m_optionGrp);
+
+ // We delay creation of these until needed.
+ m_patterns = 0L;
+ m_placeholders = 0L;
+
+ // signals and slots connections
+ connect(m_selectedText, SIGNAL(toggled(bool)), this, SLOT(slotSelectedTextToggled(bool)));
+ connect(m_regExp, SIGNAL(toggled(bool)), m_regExpItem, SLOT(setEnabled(bool)));
+ connect(m_backRef, SIGNAL(toggled(bool)), m_backRefItem, SLOT(setEnabled(bool)));
+ connect(m_regExpItem, SIGNAL(clicked()), this, SLOT(showPatterns()));
+ connect(m_backRefItem, SIGNAL(clicked()), this, SLOT(showPlaceholders()));
+
+ connect(m_find, SIGNAL(textChanged ( const QString & )),this, SLOT(textSearchChanged( const QString & )));
+
+ // tab order
+ setTabOrder(m_find, m_regExp);
+ setTabOrder(m_regExp, m_regExpItem);
+ setTabOrder(m_regExpItem, m_replace);
+ setTabOrder(m_replace, m_backRef);
+ setTabOrder(m_backRef, m_backRefItem);
+ setTabOrder(m_backRefItem, m_caseSensitive);
+ setTabOrder(m_caseSensitive, m_wholeWordsOnly);
+ setTabOrder(m_wholeWordsOnly, m_fromCursor);
+ setTabOrder(m_fromCursor, m_findBackwards);
+ setTabOrder(m_findBackwards, m_selectedText);
+ setTabOrder(m_selectedText, m_promptOnReplace);
+
+ // buddies
+ m_findLabel->setBuddy(m_find);
+ m_replaceLabel->setBuddy(m_replace);
+
+ if (!forReplace)
+ {
+ m_promptOnReplace->hide();
+ m_replaceGrp->hide();
+ }
+
+ d->findStrings = findStrings;
+ m_find->setFocus();
+ enableButtonOK( !pattern().isEmpty() );
+ if (forReplace)
+ {
+ setButtonOK(KGuiItem( i18n("&Replace"), QString::null,
+ i18n("Start replace"),
+ i18n("<qt>If you press the <b>Replace</b> button, the text you entered "
+ "above is searched for within the document and any occurrence is "
+ "replaced with the replacement text.</qt>")));
+ }
+ else
+ {
+ setButtonOK(KGuiItem( i18n("&Find"), "find",
+ i18n("Start searching"),
+ i18n("<qt>If you press the <b>Find</b> button, the text you entered "
+ "above is searched for within the document.</qt>")));
+ }
+
+ // QWhatsthis texts
+ QWhatsThis::add ( m_find, i18n(
+ "Enter a pattern to search for, or select a previous pattern from "
+ "the list.") );
+ QWhatsThis::add ( m_regExp, i18n(
+ "If enabled, search for a regular expression.") );
+ QWhatsThis::add ( m_regExpItem, i18n(
+ "Click here to edit your regular expression using a graphical editor.") );
+ QWhatsThis::add ( m_replace, i18n(
+ "Enter a replacement string, or select a previous one from the list.") );
+ QWhatsThis::add( m_backRef, i18n(
+ "<qt>If enabled, any occurrence of <code><b>\\N</b></code>, where "
+ "<code><b>N</b></code> is a integer number, will be replaced with "
+ "the corresponding capture (\"parenthesized substring\") from the "
+ "pattern.<p>To include (a literal <code><b>\\N</b></code> in your "
+ "replacement, put an extra backslash in front of it, like "
+ "<code><b>\\\\N</b></code>.</qt>") );
+ QWhatsThis::add ( m_backRefItem, i18n(
+ "Click for a menu of available captures.") );
+ QWhatsThis::add ( m_wholeWordsOnly, i18n(
+ "Require word boundaries in both ends of a match to succeed.") );
+ QWhatsThis::add ( m_fromCursor, i18n(
+ "Start searching at the current cursor location rather than at the top.") );
+ QWhatsThis::add ( m_selectedText, i18n(
+ "Only search within the current selection.") );
+ QWhatsThis::add ( m_caseSensitive, i18n(
+ "Perform a case sensitive search: entering the pattern "
+ "'Joe' will not match 'joe' or 'JOE', only 'Joe'.") );
+ QWhatsThis::add ( m_findBackwards, i18n(
+ "Search backwards.") );
+ QWhatsThis::add ( m_promptOnReplace, i18n(
+ "Ask before replacing each match found.") );
+}
+
+void KFindDialog::textSearchChanged( const QString & text)
+{
+ enableButtonOK( !text.isEmpty() );
+}
+
+void KFindDialog::showEvent( QShowEvent *e )
+{
+ if ( !d->m_initialShowDone )
+ {
+ d->m_initialShowDone = true; // only once
+ kdDebug() << "showEvent\n";
+ if (!d->findStrings.isEmpty())
+ setFindHistory(d->findStrings);
+ d->findStrings = QStringList();
+ if (!d->pattern.isEmpty()) {
+ m_find->lineEdit()->setText( d->pattern );
+ m_find->lineEdit()->selectAll();
+ d->pattern = QString::null;
+ }
+ }
+ KDialogBase::showEvent(e);
+}
+
+long KFindDialog::options() const
+{
+ long options = 0;
+
+ if (m_caseSensitive->isChecked())
+ options |= CaseSensitive;
+ if (m_wholeWordsOnly->isChecked())
+ options |= WholeWordsOnly;
+ if (m_fromCursor->isChecked())
+ options |= FromCursor;
+ if (m_findBackwards->isChecked())
+ options |= FindBackwards;
+ if (m_selectedText->isChecked())
+ options |= SelectedText;
+ if (m_regExp->isChecked())
+ options |= RegularExpression;
+ return options;
+}
+
+QString KFindDialog::pattern() const
+{
+ return m_find->currentText();
+}
+
+void KFindDialog::setPattern (const QString &pattern)
+{
+ m_find->lineEdit()->setText( pattern );
+ m_find->lineEdit()->selectAll();
+ d->pattern = pattern;
+ kdDebug() << "setPattern " << pattern<<endl;
+}
+
+void KFindDialog::setFindHistory(const QStringList &strings)
+{
+ if (strings.count() > 0)
+ {
+ m_find->setHistoryItems(strings, true);
+ m_find->lineEdit()->setText( strings.first() );
+ m_find->lineEdit()->selectAll();
+ }
+ else
+ m_find->clearHistory();
+}
+
+void KFindDialog::setHasSelection(bool hasSelection)
+{
+ if (hasSelection) d->m_enabled |= SelectedText;
+ else d->m_enabled &= ~SelectedText;
+ m_selectedText->setEnabled( hasSelection );
+ if ( !hasSelection )
+ {
+ m_selectedText->setChecked( false );
+ slotSelectedTextToggled( hasSelection );
+ }
+}
+
+void KFindDialog::slotSelectedTextToggled(bool selec)
+{
+ // From cursor doesn't make sense if we have a selection
+ m_fromCursor->setEnabled( !selec && (d->m_enabled & FromCursor) );
+ if ( selec ) // uncheck if disabled
+ m_fromCursor->setChecked( false );
+}
+
+void KFindDialog::setHasCursor(bool hasCursor)
+{
+ if (hasCursor) d->m_enabled |= FromCursor;
+ else d->m_enabled &= ~FromCursor;
+ m_fromCursor->setEnabled( hasCursor );
+ m_fromCursor->setChecked( hasCursor && (options() & FromCursor) );
+}
+
+void KFindDialog::setSupportsBackwardsFind( bool supports )
+{
+ // ########## Shouldn't this hide the checkbox instead?
+ if (supports) d->m_enabled |= FindBackwards;
+ else d->m_enabled &= ~FindBackwards;
+ m_findBackwards->setEnabled( supports );
+ m_findBackwards->setChecked( supports && (options() & FindBackwards) );
+}
+
+void KFindDialog::setSupportsCaseSensitiveFind( bool supports )
+{
+ // ########## This should hide the checkbox instead
+ if (supports) d->m_enabled |= CaseSensitive;
+ else d->m_enabled &= ~CaseSensitive;
+ m_caseSensitive->setEnabled( supports );
+ m_caseSensitive->setChecked( supports && (options() & CaseSensitive) );
+}
+
+void KFindDialog::setSupportsWholeWordsFind( bool supports )
+{
+ // ########## This should hide the checkbox instead
+ if (supports) d->m_enabled |= WholeWordsOnly;
+ else d->m_enabled &= ~WholeWordsOnly;
+ m_wholeWordsOnly->setEnabled( supports );
+ m_wholeWordsOnly->setChecked( supports && (options() & WholeWordsOnly) );
+}
+
+void KFindDialog::setSupportsRegularExpressionFind( bool supports )
+{
+ // ########## This should hide the checkbox instead
+ if (supports) d->m_enabled |= RegularExpression;
+ else d->m_enabled &= ~RegularExpression;
+ m_regExp->setEnabled( supports );
+ m_regExp->setChecked( supports && (options() & RegularExpression) );
+}
+
+void KFindDialog::setOptions(long options)
+{
+ m_caseSensitive->setChecked((d->m_enabled & CaseSensitive) && (options & CaseSensitive));
+ m_wholeWordsOnly->setChecked((d->m_enabled & WholeWordsOnly) && (options & WholeWordsOnly));
+ m_fromCursor->setChecked((d->m_enabled & FromCursor) && (options & FromCursor));
+ m_findBackwards->setChecked((d->m_enabled & FindBackwards) && (options & FindBackwards));
+ m_selectedText->setChecked((d->m_enabled & SelectedText) && (options & SelectedText));
+ m_regExp->setChecked((d->m_enabled & RegularExpression) && (options & RegularExpression));
+}
+
+// Create a popup menu with a list of regular expression terms, to help the user
+// compose a regular expression search pattern.
+void KFindDialog::showPatterns()
+{
+ if ( !d->m_regexpDialogQueryDone )
+ {
+ d->m_regexpDialog = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString::null, this );
+ d->m_regexpDialogQueryDone = true;
+ }
+
+ if ( d->m_regexpDialog )
+ {
+ KRegExpEditorInterface *iface = static_cast<KRegExpEditorInterface *>( d->m_regexpDialog->qt_cast( "KRegExpEditorInterface" ) );
+ assert( iface );
+
+ iface->setRegExp( pattern() );
+ if ( d->m_regexpDialog->exec() == QDialog::Accepted )
+ setPattern( iface->regExp() );
+ }
+ else // No complete regexp-editor available, bring up the old popupmenu
+ {
+ typedef struct
+ {
+ const char *description;
+ const char *regExp;
+ int cursorAdjustment;
+ } term;
+ static const term items[] =
+ {
+ { I18N_NOOP("Any Character"), ".", 0 },
+ { I18N_NOOP("Start of Line"), "^", 0 },
+ { I18N_NOOP("End of Line"), "$", 0 },
+ { I18N_NOOP("Set of Characters"), "[]", -1 },
+ { I18N_NOOP("Repeats, Zero or More Times"), "*", 0 },
+ { I18N_NOOP("Repeats, One or More Times"), "+", 0 },
+ { I18N_NOOP("Optional"), "?", 0 },
+ { I18N_NOOP("Escape"), "\\", 0 },
+ { I18N_NOOP("TAB"), "\\t", 0 },
+ { I18N_NOOP("Newline"), "\\n", 0 },
+ { I18N_NOOP("Carriage Return"), "\\r", 0 },
+ { I18N_NOOP("White Space"), "\\s", 0 },
+ { I18N_NOOP("Digit"), "\\d", 0 },
+ };
+ int i;
+
+ // Populate the popup menu.
+ if (!m_patterns)
+ {
+ m_patterns = new QPopupMenu(this);
+ for (i = 0; (unsigned)i < sizeof(items) / sizeof(items[0]); i++)
+ {
+ m_patterns->insertItem(i18n(items[i].description), i, i);
+ }
+ }
+
+ // Insert the selection into the edit control.
+ i = m_patterns->exec(m_regExpItem->mapToGlobal(m_regExpItem->rect().bottomLeft()));
+ if (i != -1)
+ {
+ QLineEdit *editor = m_find->lineEdit();
+
+ editor->insert(items[i].regExp);
+ editor->setCursorPosition(editor->cursorPosition() + items[i].cursorAdjustment);
+ }
+ }
+}
+
+// Create a popup menu with a list of backreference terms, to help the user
+// compose a regular expression replacement pattern.
+void KFindDialog::showPlaceholders()
+{
+ // Populate the popup menu.
+ if (!m_placeholders)
+ {
+ m_placeholders = new QPopupMenu(this);
+ connect( m_placeholders, SIGNAL(aboutToShow()), this, SLOT(slotPlaceholdersAboutToShow()) );
+ }
+
+ // Insert the selection into the edit control.
+ int i = m_placeholders->exec(m_backRefItem->mapToGlobal(m_backRefItem->rect().bottomLeft()));
+ if (i != -1)
+ {
+ QLineEdit *editor = m_replace->lineEdit();
+ editor->insert( QString("\\%1").arg( i ) );
+ }
+}
+
+void KFindDialog::slotPlaceholdersAboutToShow()
+{
+ m_placeholders->clear();
+ m_placeholders->insertItem( i18n("Complete Match"), 0 );
+
+ QRegExp r( pattern() );
+ uint n = r.numCaptures();
+ for ( uint i=0; i < n; i++ )
+ m_placeholders->insertItem( i18n("Captured Text (%1)").arg( i+1 ), i+1 );
+}
+
+void KFindDialog::slotOk()
+{
+ // Nothing to find?
+ if (pattern().isEmpty())
+ {
+ KMessageBox::error(this, i18n("You must enter some text to search for."));
+ return;
+ }
+
+ if (m_regExp->isChecked())
+ {
+ // Check for a valid regular expression.
+ QRegExp regExp(pattern());
+
+ if (!regExp.isValid())
+ {
+ KMessageBox::error(this, i18n("Invalid regular expression."));
+ return;
+ }
+ }
+ m_find->addToHistory(pattern());
+ emit okClicked();
+ if ( testWFlags( WShowModal ) )
+ accept();
+}
+// kate: space-indent on; indent-width 4; replace-tabs on;
+#include "kfinddialog.moc"
diff --git a/kutils/kfinddialog.h b/kutils/kfinddialog.h
new file mode 100644
index 000000000..ed63d5551
--- /dev/null
+++ b/kutils/kfinddialog.h
@@ -0,0 +1,315 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 KFINDDIALOG_H
+#define KFINDDIALOG_H
+
+#include <kdialogbase.h>
+class KHistoryCombo;
+class QPushButton;
+class QPopupMenu;
+class QGridLayout;
+class QLabel;
+class QGroupBox;
+class QCheckBox;
+
+/**
+ * @ingroup main
+ * @ingroup findreplace
+ * @brief A generic "find" dialog.
+ *
+ * @author S.R.Haque <srhaque@iee.org>
+ *
+ * \b Detail:
+ *
+ * This widget inherits from KDialogBase and implements
+ * the following additional functionalities: a find string
+ * object and an area for a user-defined widget to extend the dialog.
+ *
+ * \b Example:
+ *
+ * To use the basic modal find dialog, and then run the search:
+ *
+ * \code
+ * KFindDialog dlg(....)
+ * if ( dlg.exec() != QDialog::Accepted )
+ * return;
+ *
+ * // proceed with KFind from here
+ * \endcode
+ *
+ * To create a non-modal find dialog:
+ * \code
+ * if ( m_findDia )
+ * KWin::setActiveWindow( m_findDia->winId() );
+ * else
+ * {
+ * m_findDia = new KFindDialog(false,...);
+ * connect( m_findDia, SIGNAL(okClicked()), this, SLOT(findTextNext()) );
+ * }
+ * \endcode
+ * Don't forget to delete and reset m_findDia when closed.
+ * (But do NOT delete your KFind object at that point, it's needed for "Find Next")
+ *
+ * To use your own extensions: see findExtension().
+ *
+ */
+class KUTILS_EXPORT KFindDialog:
+ public KDialogBase
+{
+ Q_OBJECT
+
+public:
+
+ // Options.
+
+ // KDE4: move to KFind
+ /**
+ * Options for the search
+ */
+ enum Options
+ {
+ WholeWordsOnly = 1, ///< Match whole words only.
+ FromCursor = 2, ///< Start from current cursor position.
+ SelectedText = 4, ///< Only search selected area.
+ CaseSensitive = 8, ///< Consider case when matching.
+ FindBackwards = 16, ///< Go backwards.
+ RegularExpression = 32, ///< Interpret the pattern as a regular expression.
+ FindIncremental = 64, ///< Find incremental.
+ // Note that KReplaceDialog uses 256 and 512
+ // User extensions can use boolean options above this value.
+ MinimumUserOption = 65536 ///< The first flag which can be used by extensions.
+ };
+
+ /**
+ * Construct a modal find dialog
+ *
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param options A bitfield of the Options to be checked.
+ * @param findStrings The find history, see findHistory()
+ * @param hasSelection Whether a selection exists
+ */
+ KFindDialog( QWidget *parent = 0, const char *name = 0, long options = 0,
+ const QStringList &findStrings = QStringList(), bool hasSelection = false );
+ // KDE4: fix ambiguity with private constructor
+ // Maybe remove options (there's setOptions) and findStrings (setFindHistory) and hasSelection (setHasSelection)
+
+ /**
+ * Construct a non-modal find dialog
+ *
+ * @param modal set to @c false to get a non-modal dialog
+ * @param parent The parent object of this widget.
+ * @param name The name of this widget.
+ * @param options A bitfield of the Options to be checked.
+ * @param findStrings The find history, see findHistory()
+ * @param hasSelection Whether a selection exists
+ */
+ KFindDialog( bool modal, QWidget *parent = 0, const char *name = 0, long options = 0,
+ const QStringList &findStrings = QStringList(), bool hasSelection = false );
+ // KDE4: consider simplifying
+
+ /**
+ * Destructor.
+ */
+ virtual ~KFindDialog();
+
+ /**
+ * Provide the list of @p strings to be displayed as the history
+ * of find strings. @p strings might get truncated if it is
+ * too long.
+ *
+ * @param history The find history.
+ * @see findHistory
+ */
+ void setFindHistory( const QStringList &history );
+
+ /**
+ * Returns the list of history items.
+ *
+ * @return The find history.
+ * @see setFindHistory
+ */
+ QStringList findHistory() const;
+
+ /**
+ * Enable/disable the 'search in selection' option, depending
+ * on whether there actually is a selection.
+ *
+ * @param hasSelection @c true if a selection exists
+ */
+ void setHasSelection( bool hasSelection );
+
+ /**
+ * Hide/show the 'from cursor' option, depending
+ * on whether the application implements a cursor.
+ *
+ * @param hasCursor @c true if the application features a cursor
+ * This is assumed to be the case by default.
+ */
+ void setHasCursor( bool hasCursor );
+
+ /**
+ * Enable/disable the 'Find backwards' option, depending
+ * on whether the application supports it.
+ *
+ * @param supports @c true if the application supports backwards find
+ * This is assumed to be the case by default.
+ * @since 3.4
+ */
+ void setSupportsBackwardsFind( bool supports );
+
+ /**
+ * Enable/disable the 'Case sensitive' option, depending
+ * on whether the application supports it.
+ *
+ * @param supports @c true if the application supports case sensitive find
+ * This is assumed to be the case by default.
+ * @since 3.4
+ */
+ void setSupportsCaseSensitiveFind( bool supports );
+
+ /**
+ * Enable/disable the 'Whole words only' option, depending
+ * on whether the application supports it.
+ *
+ * @param supports @c true if the application supports whole words only find
+ * This is assumed to be the case by default.
+ * @since 3.4
+ */
+ void setSupportsWholeWordsFind( bool supports );
+
+ /**
+ * Enable/disable the 'Regular expression' option, depending
+ * on whether the application supports it.
+ *
+ * @param supports @c true if the application supports regular expression find
+ * This is assumed to be the case by default.
+ * @since 3.4
+ */
+ void setSupportsRegularExpressionFind( bool supports );
+
+ /**
+ * Set the options which are checked.
+ *
+ * @param options The setting of the Options.
+ * @see Options
+ */
+ void setOptions( long options );
+
+ /**
+ * Returns the state of the options. Disabled options may be returned in
+ * an indeterminate state.
+ *
+ * @return The options.
+ * @see Options, setOptions
+ */
+ long options() const;
+
+ /**
+ * Returns the pattern to find.
+ * @return The search text.
+ */
+ QString pattern() const;
+
+ /**
+ * Sets the pattern to find.
+ * @param pattern The new search pattern.
+ */
+ void setPattern ( const QString &pattern );
+
+ /**
+ * Returns an empty widget which the user may fill with additional UI
+ * elements as required. The widget occupies the width of the dialog,
+ * and is positioned immediately below the regular expression support
+ * widgets for the pattern string.
+ * @return An extensible QWidget.
+ */
+ QWidget *findExtension();
+
+protected slots:
+
+ void slotOk();
+ void slotSelectedTextToggled(bool);
+ void showPatterns();
+ void showPlaceholders();
+ void textSearchChanged( const QString &);
+
+protected:
+ virtual void showEvent ( QShowEvent * );
+
+private slots:
+ /**
+ * connected to the aboutToShow of the placeholders menu,
+ * updates it according to the text in the pattern.
+ */
+ void slotPlaceholdersAboutToShow();
+
+private:
+
+ QGroupBox *m_findGrp;
+ QLabel *m_findLabel;
+ KHistoryCombo *m_find;
+ QCheckBox *m_regExp;
+ QPushButton *m_regExpItem;
+ QGridLayout *m_findLayout;
+ QWidget *m_findExtension;
+
+ QGroupBox *m_optionGrp;
+ QCheckBox *m_wholeWordsOnly;
+ QCheckBox *m_fromCursor;
+ QCheckBox *m_selectedText;
+ QCheckBox *m_caseSensitive;
+ QCheckBox *m_findBackwards;
+
+ QPopupMenu *m_patterns;
+
+ // Our dirty little secret is that we also implement the "replace" dialog. But we
+ // keep that fact hidden from all but our friends.
+
+ friend class KReplaceDialog;
+
+ /**
+ * Construct a find dialog with a parent object and a name. This version of the
+ * constructor is for use by friends only!
+ *
+ * @param forReplace Is this a replace dialog?
+ */
+ KFindDialog( QWidget *parent, const char *name, bool forReplace );
+ void init( bool forReplace, const QStringList &findStrings, bool hasSelection );
+
+ QGroupBox *m_replaceGrp;
+ QLabel *m_replaceLabel;
+ KHistoryCombo *m_replace;
+ QCheckBox* m_backRef;
+ QPushButton* m_backRefItem;
+ QGridLayout *m_replaceLayout;
+ QWidget *m_replaceExtension;
+
+ QCheckBox* m_promptOnReplace;
+
+ QPopupMenu *m_placeholders;
+
+ // Binary compatible extensibility.
+ class KFindDialogPrivate;
+ KFindDialogPrivate *d;
+};
+
+#endif // KFINDDIALOG_H
diff --git a/kutils/kmultitabbar.cpp b/kutils/kmultitabbar.cpp
new file mode 100644
index 000000000..18a123717
--- /dev/null
+++ b/kutils/kmultitabbar.cpp
@@ -0,0 +1,997 @@
+/***************************************************************************
+ kmultitabbar.cpp - description
+ -------------------
+ begin : 2001
+ copyright : (C) 2001,2002,2003 by Joseph Wenninger <jowenn@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 as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 "kmultitabbar.h"
+#include "kmultitabbar.moc"
+#include "kmultitabbar_p.h"
+#include "kmultitabbar_p.moc"
+#include <qbutton.h>
+#include <qpopupmenu.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qfontmetrics.h>
+#include <qstyle.h>
+
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <qapplication.h>
+#include <math.h>
+
+#define NEARBYINT(i) ((int(float(i) + 0.5)))
+
+class KMultiTabBarTabPrivate {
+public:
+ QPixmap pix;
+};
+
+
+KMultiTabBarInternal::KMultiTabBarInternal(QWidget *parent, KMultiTabBar::KMultiTabBarMode bm):QScrollView(parent)
+{
+ m_expandedTabSize=-1;
+ m_showActiveTabTexts=false;
+ m_tabs.setAutoDelete(true);
+ m_barMode=bm;
+ setHScrollBarMode(AlwaysOff);
+ setVScrollBarMode(AlwaysOff);
+ if (bm==KMultiTabBar::Vertical)
+ {
+ box=new QWidget(viewport());
+ mainLayout=new QVBoxLayout(box);
+ mainLayout->setAutoAdd(true);
+ box->setFixedWidth(24);
+ setFixedWidth(24);
+ }
+ else
+ {
+ box=new QWidget(viewport());
+ mainLayout=new QHBoxLayout(box);
+ mainLayout->setAutoAdd(true);
+ box->setFixedHeight(24);
+ setFixedHeight(24);
+ }
+ addChild(box);
+ setFrameStyle(NoFrame);
+ viewport()->setBackgroundMode(Qt::PaletteBackground);
+/* box->setPaletteBackgroundColor(Qt::red);
+ setPaletteBackgroundColor(Qt::green);*/
+}
+
+void KMultiTabBarInternal::setStyle(enum KMultiTabBar::KMultiTabBarStyle style)
+{
+ m_style=style;
+ for (uint i=0;i<m_tabs.count();i++)
+ m_tabs.at(i)->setStyle(m_style);
+
+ if ( (m_style==KMultiTabBar::KDEV3) ||
+ (m_style==KMultiTabBar::KDEV3ICON ) ) {
+ delete mainLayout;
+ mainLayout=0;
+ resizeEvent(0);
+ } else if (mainLayout==0) {
+ if (m_barMode==KMultiTabBar::Vertical)
+ {
+ box=new QWidget(viewport());
+ mainLayout=new QVBoxLayout(box);
+ box->setFixedWidth(24);
+ setFixedWidth(24);
+ }
+ else
+ {
+ box=new QWidget(viewport());
+ mainLayout=new QHBoxLayout(box);
+ box->setFixedHeight(24);
+ setFixedHeight(24);
+ }
+ addChild(box);
+ for (uint i=0;i<m_tabs.count();i++)
+ mainLayout->add(m_tabs.at(i));
+ mainLayout->setAutoAdd(true);
+
+ }
+ viewport()->repaint();
+}
+
+void KMultiTabBarInternal::drawContents ( QPainter * paint, int clipx, int clipy, int clipw, int cliph )
+{
+ QScrollView::drawContents (paint , clipx, clipy, clipw, cliph );
+
+ if (m_position==KMultiTabBar::Right)
+ {
+
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,viewport()->height());
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(1,0,1,viewport()->height());
+
+
+ }
+ else
+ if (m_position==KMultiTabBar::Left)
+ {
+ paint->setPen(colorGroup().light());
+ paint->drawLine(23,0,23,viewport()->height());
+ paint->drawLine(22,0,22,viewport()->height());
+
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,viewport()->height());
+ }
+ else
+ if (m_position==KMultiTabBar::Bottom)
+ {
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,viewport()->width(),0);
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(0,1,viewport()->width(),1);
+ }
+ else
+ {
+ paint->setPen(colorGroup().light());
+ paint->drawLine(0,23,viewport()->width(),23);
+ paint->drawLine(0,22,viewport()->width(),22);
+
+/* paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,viewport()->height());*/
+
+ }
+
+
+}
+
+void KMultiTabBarInternal::contentsMousePressEvent(QMouseEvent *ev)
+{
+ ev->ignore();
+}
+
+void KMultiTabBarInternal::mousePressEvent(QMouseEvent *ev)
+{
+ ev->ignore();
+}
+
+
+#define CALCDIFF(m_tabs,diff,i) if (m_lines>(int)lines) {\
+ /*kdDebug()<<"i="<<i<<" tabCount="<<tabCount<<" space="<<space<<endl;*/ \
+ uint ulen=0;\
+ diff=0; \
+ for (uint i2=i;i2<tabCount;i2++) {\
+ uint l1=m_tabs.at(i2)->neededSize();\
+ if ((ulen+l1)>space){\
+ if (ulen==0) diff=0;\
+ else diff=((float)(space-ulen))/(i2-i);\
+ break;\
+ }\
+ ulen+=l1;\
+ }\
+ } else {diff=0; }
+
+
+void KMultiTabBarInternal::resizeEvent(QResizeEvent *ev) {
+/* kdDebug()<<"KMultiTabBarInternal::resizeEvent"<<endl;
+ kdDebug()<<"KMultiTabBarInternal::resizeEvent - box geometry"<<box->geometry()<<endl;
+ kdDebug()<<"KMultiTabBarInternal::resizeEvent - geometry"<<geometry()<<endl;*/
+ if (ev) QScrollView::resizeEvent(ev);
+
+ if ( (m_style==KMultiTabBar::KDEV3) ||
+ (m_style==KMultiTabBar::KDEV3ICON) ){
+ box->setGeometry(0,0,width(),height());
+ int lines=1;
+ uint space;
+ float tmp=0;
+ if ((m_position==KMultiTabBar::Bottom) || (m_position==KMultiTabBar::Top))
+ space=width();
+ else
+ space=height();
+
+ int cnt=0;
+//CALCULATE LINES
+ const uint tabCount=m_tabs.count();
+ for (uint i=0;i<tabCount;i++) {
+ cnt++;
+ tmp+=m_tabs.at(i)->neededSize();
+ if (tmp>space) {
+ if (cnt>1)i--;
+ else if (i==(tabCount-1)) break;
+ cnt=0;
+ tmp=0;
+ lines++;
+ }
+ }
+//SET SIZE & PLACE
+ float diff=0;
+ cnt=0;
+
+ if ((m_position==KMultiTabBar::Bottom) || (m_position==KMultiTabBar::Top)) {
+
+ setFixedHeight(lines*24);
+ box->setFixedHeight(lines*24);
+ m_lines=height()/24-1;
+ lines=0;
+ CALCDIFF(m_tabs,diff,0)
+ tmp=-diff;
+
+ //kdDebug()<<"m_lines recalculated="<<m_lines<<endl;
+ for (uint i=0;i<tabCount;i++) {
+ KMultiTabBarTab *tab=m_tabs.at(i);
+ cnt++;
+ tmp+=tab->neededSize()+diff;
+ if (tmp>space) {
+ //kdDebug()<<"about to start new line"<<endl;
+ if (cnt>1) {
+ CALCDIFF(m_tabs,diff,i)
+ i--;
+ }
+ else {
+ //kdDebug()<<"placing line on old line"<<endl;
+ kdDebug()<<"diff="<<diff<<endl;
+ tab->removeEventFilter(this);
+ tab->move(NEARBYINT(tmp-tab->neededSize()),lines*24);
+// tab->setFixedWidth(tab->neededSize()+diff);
+ tab->setFixedWidth(NEARBYINT(tmp+diff)-tab->x());;
+ tab->installEventFilter(this);
+ CALCDIFF(m_tabs,diff,(i+1))
+
+ }
+ tmp=-diff;
+ cnt=0;
+ lines++;
+ //kdDebug()<<"starting new line:"<<lines<<endl;
+
+ } else {
+ //kdDebug()<<"Placing line on line:"<<lines<<" pos: (x/y)=("<<tmp-m_tabs.at(i)->neededSize()<<"/"<<lines*24<<")"<<endl;
+ //kdDebug()<<"diff="<<diff<<endl;
+ tab->removeEventFilter(this);
+ tab->move(NEARBYINT(tmp-tab->neededSize()),lines*24);
+ tab->setFixedWidth(NEARBYINT(tmp+diff)-tab->x());;
+
+ //tab->setFixedWidth(tab->neededSize()+diff);
+ tab->installEventFilter(this);
+
+ }
+ }
+ }
+ else {
+ setFixedWidth(lines*24);
+ box->setFixedWidth(lines*24);
+ m_lines=lines=width()/24;
+ lines=0;
+ CALCDIFF(m_tabs,diff,0)
+ tmp=-diff;
+
+ for (uint i=0;i<tabCount;i++) {
+ KMultiTabBarTab *tab=m_tabs.at(i);
+ cnt++;
+ tmp+=tab->neededSize()+diff;
+ if (tmp>space) {
+ if (cnt>1) {
+ CALCDIFF(m_tabs,diff,i);
+ tmp=-diff;
+ i--;
+ }
+ else {
+ tab->removeEventFilter(this);
+ tab->move(lines*24,NEARBYINT(tmp-tab->neededSize()));
+ tab->setFixedHeight(NEARBYINT(tmp+diff)-tab->y());;
+ tab->installEventFilter(this);
+ }
+ cnt=0;
+ tmp=-diff;
+ lines++;
+ } else {
+ tab->removeEventFilter(this);
+ tab->move(lines*24,NEARBYINT(tmp-tab->neededSize()));
+ tab->setFixedHeight(NEARBYINT(tmp+diff)-tab->y());;
+ tab->installEventFilter(this);
+ }
+ }
+ }
+
+
+ //kdDebug()<<"needed lines:"<<m_lines<<endl;
+ } else {
+ int size=0; /*move the calculation into another function and call it only on add tab and tab click events*/
+ for (int i=0;i<(int)m_tabs.count();i++)
+ size+=(m_barMode==KMultiTabBar::Vertical?m_tabs.at(i)->height():m_tabs.at(i)->width());
+ if ((m_position==KMultiTabBar::Bottom) || (m_position==KMultiTabBar::Top))
+ box->setGeometry(0,0,size,height());
+ else box->setGeometry(0,0,width(),size);
+
+ }
+}
+
+
+void KMultiTabBarInternal::showActiveTabTexts(bool show)
+{
+ m_showActiveTabTexts=show;
+}
+
+
+KMultiTabBarTab* KMultiTabBarInternal::tab(int id) const
+{
+ for (QPtrListIterator<KMultiTabBarTab> it(m_tabs);it.current();++it){
+ if (it.current()->id()==id) return it.current();
+ }
+ return 0;
+}
+
+bool KMultiTabBarInternal::eventFilter(QObject *, QEvent *e) {
+ if (e->type()==QEvent::Resize) resizeEvent(0);
+ return false;
+}
+
+int KMultiTabBarInternal::appendTab(const QPixmap &pic ,int id,const QString& text)
+{
+ KMultiTabBarTab *tab;
+ m_tabs.append(tab= new KMultiTabBarTab(pic,text,id,box,m_position,m_style));
+ tab->installEventFilter(this);
+ tab->showActiveTabText(m_showActiveTabTexts);
+
+ if (m_style==KMultiTabBar::KONQSBC)
+ {
+ if (m_expandedTabSize<tab->neededSize()) {
+ m_expandedTabSize=tab->neededSize();
+ for (uint i=0;i<m_tabs.count();i++)
+ m_tabs.at(i)->setSize(m_expandedTabSize);
+
+ } else tab->setSize(m_expandedTabSize);
+ } else tab->updateState();
+ tab->show();
+ resizeEvent(0);
+ return 0;
+}
+
+void KMultiTabBarInternal::removeTab(int id)
+{
+ for (uint pos=0;pos<m_tabs.count();pos++)
+ {
+ if (m_tabs.at(pos)->id()==id)
+ {
+ m_tabs.remove(pos);
+ resizeEvent(0);
+ break;
+ }
+ }
+}
+
+void KMultiTabBarInternal::setPosition(enum KMultiTabBar::KMultiTabBarPosition pos)
+{
+ m_position=pos;
+ for (uint i=0;i<m_tabs.count();i++)
+ m_tabs.at(i)->setTabsPosition(m_position);
+ viewport()->repaint();
+}
+
+KMultiTabBarButton::KMultiTabBarButton(const QPixmap& pic,const QString& text, QPopupMenu *popup,
+ int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style)
+ :QPushButton(QIconSet(),text,parent),m_style(style)
+{
+ setIconSet(pic);
+ setText(text);
+ m_position=pos;
+ if (popup) setPopup(popup);
+ setFlat(true);
+ setFixedHeight(24);
+ setFixedWidth(24);
+ m_id=id;
+ QToolTip::add(this,text);
+ connect(this,SIGNAL(clicked()),this,SLOT(slotClicked()));
+}
+
+KMultiTabBarButton::KMultiTabBarButton(const QString& text, QPopupMenu *popup,
+ int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style)
+ :QPushButton(QIconSet(),text,parent),m_style(style)
+{
+ setText(text);
+ m_position=pos;
+ if (popup) setPopup(popup);
+ setFlat(true);
+ setFixedHeight(24);
+ setFixedWidth(24);
+ m_id=id;
+ QToolTip::add(this,text);
+ connect(this,SIGNAL(clicked()),this,SLOT(slotClicked()));
+}
+
+KMultiTabBarButton::~KMultiTabBarButton() {
+}
+
+int KMultiTabBarButton::id() const{
+ return m_id;
+}
+
+void KMultiTabBarButton::setText(const QString& text)
+{
+ QPushButton::setText(text);
+ m_text=text;
+ QToolTip::add(this,text);
+}
+
+void KMultiTabBarButton::slotClicked()
+{
+ emit clicked(m_id);
+}
+
+void KMultiTabBarButton::setPosition(KMultiTabBar::KMultiTabBarPosition pos)
+{
+ m_position=pos;
+ repaint();
+}
+
+void KMultiTabBarButton::setStyle(KMultiTabBar::KMultiTabBarStyle style)
+{
+ m_style=style;
+ repaint();
+}
+
+void KMultiTabBarButton::hideEvent( QHideEvent* he) {
+ QPushButton::hideEvent(he);
+ KMultiTabBar *tb=dynamic_cast<KMultiTabBar*>(parentWidget());
+ if (tb) tb->updateSeparator();
+}
+
+void KMultiTabBarButton::showEvent( QShowEvent* he) {
+ QPushButton::showEvent(he);
+ KMultiTabBar *tb=dynamic_cast<KMultiTabBar*>(parentWidget());
+ if (tb) tb->updateSeparator();
+}
+
+
+QSize KMultiTabBarButton::sizeHint() const
+{
+ constPolish();
+
+ int w = 0, h = 0;
+
+ // calculate contents size...
+#ifndef QT_NO_ICONSET
+ if ( iconSet() && !iconSet()->isNull() ) {
+ int iw = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4;
+ int ih = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).height();
+ w += iw;
+ h = QMAX( h, ih );
+ }
+#endif
+ if ( isMenuButton() )
+ w += style().pixelMetric(QStyle::PM_MenuButtonIndicator, this);
+
+ if ( pixmap() ) {
+ QPixmap *pm = (QPixmap *)pixmap();
+ w += pm->width();
+ h += pm->height();
+ } else {
+ QString s( text() );
+ bool empty = s.isEmpty();
+ if ( empty )
+ s = QString::fromLatin1("XXXX");
+ QFontMetrics fm = fontMetrics();
+ QSize sz = fm.size( ShowPrefix, s );
+ if(!empty || !w)
+ w += sz.width();
+ if(!empty || !h)
+ h = QMAX(h, sz.height());
+ }
+
+ return (style().sizeFromContents(QStyle::CT_ToolButton, this, QSize(w, h)).
+ expandedTo(QApplication::globalStrut()));
+}
+
+
+KMultiTabBarTab::KMultiTabBarTab(const QPixmap& pic, const QString& text,
+ int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos,
+ KMultiTabBar::KMultiTabBarStyle style)
+ :KMultiTabBarButton(text,0,id,parent,pos,style),
+ m_showActiveTabText(false)
+{
+ d=new KMultiTabBarTabPrivate();
+ setIcon(pic);
+ m_expandedSize=24;
+ setToggleButton(true);
+}
+
+KMultiTabBarTab::~KMultiTabBarTab() {
+ delete d;
+}
+
+
+void KMultiTabBarTab::setTabsPosition(KMultiTabBar::KMultiTabBarPosition pos)
+{
+ if ((pos!=m_position) && ((pos==KMultiTabBar::Left) || (pos==KMultiTabBar::Right))) {
+ if (!d->pix.isNull()) {
+ QWMatrix temp;// (1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F);
+ temp.rotate(180);
+ d->pix=d->pix.xForm(temp);
+ setIconSet(d->pix);
+ }
+ }
+
+ setPosition(pos);
+// repaint();
+}
+
+void KMultiTabBarTab::setIcon(const QString& icon)
+{
+ QPixmap pic=SmallIcon(icon);
+ setIcon(pic);
+}
+
+void KMultiTabBarTab::setIcon(const QPixmap& icon)
+{
+
+ if (m_style!=KMultiTabBar::KDEV3) {
+ if ((m_position==KMultiTabBar::Left) || (m_position==KMultiTabBar::Right)) {
+ QWMatrix rotateMatrix;
+ if (m_position==KMultiTabBar::Left)
+ rotateMatrix.rotate(90);
+ else
+ rotateMatrix.rotate(-90);
+ QPixmap pic=icon.xForm(rotateMatrix); //TODO FIX THIS, THIS SHOWS WINDOW
+ d->pix=pic;
+ setIconSet(pic);
+ } else setIconSet(icon);
+ }
+}
+
+void KMultiTabBarTab::slotClicked()
+{
+ updateState();
+ KMultiTabBarButton::slotClicked();
+}
+
+void KMultiTabBarTab::setState(bool b)
+{
+ setOn(b);
+ updateState();
+}
+
+void KMultiTabBarTab::updateState()
+{
+
+ if (m_style!=KMultiTabBar::KONQSBC) {
+ if ((m_style==KMultiTabBar::KDEV3) || (m_style==KMultiTabBar::KDEV3ICON) || (isOn())) {
+ QPushButton::setText(m_text);
+ } else {
+ kdDebug()<<"KMultiTabBarTab::updateState(): setting text to an empty QString***************"<<endl;
+ QPushButton::setText(QString::null);
+ }
+
+ if ((m_position==KMultiTabBar::Right || m_position==KMultiTabBar::Left)) {
+ setFixedWidth(24);
+ if ((m_style==KMultiTabBar::KDEV3) || (m_style==KMultiTabBar::KDEV3ICON) || (isOn())) {
+ setFixedHeight(KMultiTabBarButton::sizeHint().width());
+ } else setFixedHeight(36);
+ } else {
+ setFixedHeight(24);
+ if ((m_style==KMultiTabBar::KDEV3) || (m_style==KMultiTabBar::KDEV3ICON) || (isOn())) {
+ setFixedWidth(KMultiTabBarButton::sizeHint().width());
+ } else setFixedWidth(36);
+ }
+ } else {
+ if ((!isOn()) || (!m_showActiveTabText))
+ {
+ setFixedWidth(24);
+ setFixedHeight(24);
+ return;
+ }
+ if ((m_position==KMultiTabBar::Right || m_position==KMultiTabBar::Left))
+ setFixedHeight(m_expandedSize);
+ else
+ setFixedWidth(m_expandedSize);
+ }
+ QApplication::sendPostedEvents(0,QEvent::Paint | QEvent::Move | QEvent::Resize | QEvent::LayoutHint);
+ QApplication::flush();
+}
+
+int KMultiTabBarTab::neededSize()
+{
+ return (((m_style!=KMultiTabBar::KDEV3)?24:0)+QFontMetrics(QFont()).width(m_text)+6);
+}
+
+void KMultiTabBarTab::setSize(int size)
+{
+ m_expandedSize=size;
+ updateState();
+}
+
+void KMultiTabBarTab::showActiveTabText(bool show)
+{
+ m_showActiveTabText=show;
+}
+
+void KMultiTabBarTab::drawButtonLabel(QPainter *p) {
+ drawButton(p);
+}
+void KMultiTabBarTab::drawButton(QPainter *paint)
+{
+ if (m_style!=KMultiTabBar::KONQSBC) drawButtonStyled(paint);
+ else drawButtonClassic(paint);
+}
+
+void KMultiTabBarTab::drawButtonStyled(QPainter *paint) {
+
+ QSize sh;
+ const int width = 36; // rotated
+ const int height = 24;
+ if ((m_style==KMultiTabBar::KDEV3) || (m_style==KMultiTabBar::KDEV3ICON) || (isOn())) {
+ if ((m_position==KMultiTabBar::Left) || (m_position==KMultiTabBar::Right))
+ sh=QSize(this->height(),this->width());//KMultiTabBarButton::sizeHint();
+ else sh=QSize(this->width(),this->height());
+ }
+ else
+ sh=QSize(width,height);
+
+ QPixmap pixmap( sh.width(),height); ///,sh.height());
+ pixmap.fill(eraseColor());
+ QPainter painter(&pixmap);
+
+
+ QStyle::SFlags st=QStyle::Style_Default;
+
+ st|=QStyle::Style_Enabled;
+
+ if (isOn()) st|=QStyle::Style_On;
+
+ style().drawControl(QStyle::CE_PushButton,&painter,this, QRect(0,0,pixmap.width(),pixmap.height()), colorGroup(),st);
+ style().drawControl(QStyle::CE_PushButtonLabel,&painter,this, QRect(0,0,pixmap.width(),pixmap.height()), colorGroup(),st);
+
+ switch (m_position) {
+ case KMultiTabBar::Left:
+ paint->rotate(-90);
+ paint->drawPixmap(1-pixmap.width(),0,pixmap);
+ break;
+ case KMultiTabBar::Right:
+ paint->rotate(90);
+ paint->drawPixmap(0,1-pixmap.height(),pixmap);
+ break;
+
+ default:
+ paint->drawPixmap(0,0,pixmap);
+ break;
+ }
+// style().drawControl(QStyle::CE_PushButtonLabel,painter,this, QRect(0,0,pixmap.width(),pixmap.height()),
+// colorGroup(),QStyle::Style_Enabled);
+
+
+}
+
+void KMultiTabBarTab::drawButtonClassic(QPainter *paint)
+{
+ QPixmap pixmap;
+ if ( iconSet())
+ pixmap = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal );
+ paint->fillRect(0, 0, 24, 24, colorGroup().background());
+
+ if (!isOn())
+ {
+
+ if (m_position==KMultiTabBar::Right)
+ {
+ paint->fillRect(0,0,21,21,QBrush(colorGroup().background()));
+
+ paint->setPen(colorGroup().background().dark(150));
+ paint->drawLine(0,22,23,22);
+
+ paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap);
+
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,23);
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(1,0,1,23);
+
+ }
+ else
+ if ((m_position==KMultiTabBar::Bottom) || (m_position==KMultiTabBar::Top))
+ {
+ paint->fillRect(0,1,23,22,QBrush(colorGroup().background()));
+
+ paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap);
+
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(23,0,23,23);
+
+
+ paint->setPen(colorGroup().light());
+ paint->drawLine(0,22,23,22);
+ paint->drawLine(0,23,23,23);
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,23,0);
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(0,1,23,1);
+
+ }
+ else
+ {
+ paint->setPen(colorGroup().background().dark(120));
+ paint->drawLine(0,23,23,23);
+ paint->fillRect(0,0,23,21,QBrush(colorGroup().background()));
+ paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap);
+
+ paint->setPen(colorGroup().light());
+ paint->drawLine(23,0,23,23);
+ paint->drawLine(22,0,22,23);
+
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,0,0,23);
+
+ }
+
+
+ }
+ else
+ {
+ if (m_position==KMultiTabBar::Right)
+ {
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,height()-1,23,height()-1);
+ paint->drawLine(0,height()-2,23,height()-2);
+ paint->drawLine(23,0,23,height()-1);
+ paint->drawLine(22,0,22,height()-1);
+ paint->fillRect(0,0,21,height()-3,QBrush(colorGroup().light()));
+ paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap);
+
+ if (m_showActiveTabText)
+ {
+ if (height()<25+4) return;
+
+ QPixmap tpixmap(height()-25-3, width()-2);
+ QPainter painter(&tpixmap);
+
+ painter.fillRect(0,0,tpixmap.width(),tpixmap.height(),QBrush(colorGroup().light()));
+
+ painter.setPen(colorGroup().text());
+ painter.drawText(0,+width()/2+QFontMetrics(QFont()).height()/2,m_text);
+
+ paint->rotate(90);
+ kdDebug()<<"tpixmap.width:"<<tpixmap.width()<<endl;
+ paint->drawPixmap(25,-tpixmap.height()+1,tpixmap);
+ }
+
+ }
+ else
+ if (m_position==KMultiTabBar::Top)
+ {
+ paint->fillRect(0,0,width()-1,23,QBrush(colorGroup().light()));
+ paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap);
+ if (m_showActiveTabText)
+ {
+ paint->setPen(colorGroup().text());
+ paint->drawText(25,height()/2+QFontMetrics(QFont()).height()/2,m_text);
+ }
+ }
+ else
+ if (m_position==KMultiTabBar::Bottom)
+ {
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,23,width()-1,23);
+ paint->drawLine(0,22,width()-1,22);
+ paint->fillRect(0,0,width()-1,21,QBrush(colorGroup().light()));
+ paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap);
+ if (m_showActiveTabText)
+ {
+ paint->setPen(colorGroup().text());
+ paint->drawText(25,height()/2+QFontMetrics(QFont()).height()/2,m_text);
+ }
+
+ }
+ else
+ {
+
+
+ paint->setPen(colorGroup().shadow());
+ paint->drawLine(0,height()-1,23,height()-1);
+ paint->drawLine(0,height()-2,23,height()-2);
+ paint->fillRect(0,0,23,height()-3,QBrush(colorGroup().light()));
+ paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap);
+ if (m_showActiveTabText)
+ {
+
+ if (height()<25+4) return;
+
+ QPixmap tpixmap(height()-25-3, width()-2);
+ QPainter painter(&tpixmap);
+
+ painter.fillRect(0,0,tpixmap.width(),tpixmap.height(),QBrush(colorGroup().light()));
+
+ painter.setPen(colorGroup().text());
+ painter.drawText(tpixmap.width()-QFontMetrics(QFont()).width(m_text),+width()/2+QFontMetrics(QFont()).height()/2,m_text);
+
+ paint->rotate(-90);
+ kdDebug()<<"tpixmap.width:"<<tpixmap.width()<<endl;
+
+ paint->drawPixmap(-24-tpixmap.width(),2,tpixmap);
+
+ }
+
+ }
+
+ }
+}
+
+
+
+
+
+
+
+KMultiTabBar::KMultiTabBar(KMultiTabBarMode bm, QWidget *parent,const char *name):QWidget(parent,name)
+{
+ m_buttons.setAutoDelete(false);
+ if (bm==Vertical)
+ {
+ m_l=new QVBoxLayout(this);
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding, true);
+// setFixedWidth(24);
+ }
+ else
+ {
+ m_l=new QHBoxLayout(this);
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed, true);
+// setFixedHeight(24);
+ }
+ m_l->setMargin(0);
+ m_l->setAutoAdd(false);
+
+ m_internal=new KMultiTabBarInternal(this,bm);
+ setPosition((bm==KMultiTabBar::Vertical)?KMultiTabBar::Right:KMultiTabBar::Bottom);
+ setStyle(VSNET);
+ // setStyle(KDEV3);
+ //setStyle(KONQSBC);
+ m_l->insertWidget(0,m_internal);
+ m_l->insertWidget(0,m_btnTabSep=new QFrame(this));
+ m_btnTabSep->setFixedHeight(4);
+ m_btnTabSep->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ m_btnTabSep->setLineWidth(2);
+ m_btnTabSep->hide();
+
+ updateGeometry();
+}
+
+KMultiTabBar::~KMultiTabBar() {
+}
+
+/*int KMultiTabBar::insertButton(QPixmap pic,int id ,const QString&)
+{
+ (new KToolbarButton(pic,id,m_internal))->show();
+ return 0;
+}*/
+
+int KMultiTabBar::appendButton(const QPixmap &pic ,int id,QPopupMenu *popup,const QString&)
+{
+ KMultiTabBarButton *btn;
+ m_buttons.append(btn= new KMultiTabBarButton(pic,QString::null,
+ popup,id,this,m_position,m_internal->m_style));
+ m_l->insertWidget(0,btn);
+ btn->show();
+ m_btnTabSep->show();
+ return 0;
+}
+
+void KMultiTabBar::updateSeparator() {
+ bool hideSep=true;
+ for (QPtrListIterator<KMultiTabBarButton> it(m_buttons);it.current();++it){
+ if (it.current()->isVisibleTo(this)) {
+ hideSep=false;
+ break;
+ }
+ }
+ if (hideSep) m_btnTabSep->hide();
+ else m_btnTabSep->show();
+
+}
+
+int KMultiTabBar::appendTab(const QPixmap &pic ,int id ,const QString& text)
+{
+ m_internal->appendTab(pic,id,text);
+ return 0;
+}
+
+KMultiTabBarButton* KMultiTabBar::button(int id) const
+{
+ for (QPtrListIterator<KMultiTabBarButton> it(m_buttons);it.current();++it){
+ if (it.current()->id()==id) return it.current();
+ }
+ return 0;
+}
+
+KMultiTabBarTab* KMultiTabBar::tab(int id) const
+{
+ return m_internal->tab(id);
+}
+
+
+
+void KMultiTabBar::removeButton(int id)
+{
+ for (uint pos=0;pos<m_buttons.count();pos++)
+ {
+ if (m_buttons.at(pos)->id()==id)
+ {
+ m_buttons.take(pos)->deleteLater();
+ break;
+ }
+ }
+ if (m_buttons.count()==0) m_btnTabSep->hide();
+}
+
+void KMultiTabBar::removeTab(int id)
+{
+ m_internal->removeTab(id);
+}
+
+void KMultiTabBar::setTab(int id,bool state)
+{
+ KMultiTabBarTab *ttab=tab(id);
+ if (ttab)
+ {
+ ttab->setState(state);
+ }
+}
+
+bool KMultiTabBar::isTabRaised(int id) const
+{
+ KMultiTabBarTab *ttab=tab(id);
+ if (ttab)
+ {
+ return ttab->isOn();
+ }
+
+ return false;
+}
+
+
+void KMultiTabBar::showActiveTabTexts(bool show)
+{
+ m_internal->showActiveTabTexts(show);
+}
+
+void KMultiTabBar::setStyle(KMultiTabBarStyle style)
+{
+ m_internal->setStyle(style);
+}
+
+KMultiTabBar::KMultiTabBarStyle KMultiTabBar::tabStyle() const
+{
+ return m_internal->m_style;
+}
+
+void KMultiTabBar::setPosition(KMultiTabBarPosition pos)
+{
+ m_position=pos;
+ m_internal->setPosition(pos);
+ for (uint i=0;i<m_buttons.count();i++)
+ m_buttons.at(i)->setPosition(pos);
+}
+
+KMultiTabBar::KMultiTabBarPosition KMultiTabBar::position() const
+{
+ return m_position;
+}
+void KMultiTabBar::fontChange(const QFont& /* oldFont */)
+{
+ for (uint i=0;i<tabs()->count();i++)
+ tabs()->at(i)->resize();
+ repaint();
+}
+
+QPtrList<KMultiTabBarTab>* KMultiTabBar::tabs() {return m_internal->tabs();}
+QPtrList<KMultiTabBarButton>* KMultiTabBar::buttons() {return &m_buttons;}
+
diff --git a/kutils/kmultitabbar.h b/kutils/kmultitabbar.h
new file mode 100644
index 000000000..8d9a95449
--- /dev/null
+++ b/kutils/kmultitabbar.h
@@ -0,0 +1,316 @@
+/***************************************************************************
+ kmultitabbar.h - description
+ -------------------
+ begin : 2001
+ copyright : (C) 2001,2002,2003 by Joseph Wenninger <jowenn@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 as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 _KMultitabbar_h_
+#define _KMultitabbar_h_
+
+#include <qscrollview.h>
+#include <qvbox.h>
+#include <qhbox.h>
+#include <qlayout.h>
+#include <qstring.h>
+#include <qptrlist.h>
+#include <qpushbutton.h>
+
+#include <kdelibs_export.h>
+
+class QPixmap;
+class QPainter;
+class QFrame;
+
+class KMultiTabBarPrivate;
+class KMultiTabBarTabPrivate;
+class KMultiTabBarButtonPrivate;
+class KMultiTabBarInternal;
+
+/**
+ * @ingroup main
+ * @ingroup multitabbar
+ * A Widget for horizontal and vertical tabs.
+ * It is possible to add normal buttons to the top/left
+ * The handling if only one tab at a time or multiple tabs
+ * should be raisable is left to the "user".
+ *@author Joseph Wenninger
+ */
+class KUTILS_EXPORT KMultiTabBar: public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * The tab bar's orientation. Also constraints the bar's position.
+ */
+ enum KMultiTabBarMode {
+ Horizontal, ///< Horizontal orientation (i.e. on top or bottom)
+ Vertical ///< Vertical orientation (i.e. on the left or right hand side)
+ };
+
+ /**
+ * The tab bar's position
+ */
+ enum KMultiTabBarPosition {
+ Left, ///< Left hand side
+ Right, ///< Right hand side
+ Top, ///< On top
+ Bottom ///< On bottom
+ };
+
+ /**
+ * The list of available styles for KMultiTabBar
+ */
+ enum KMultiTabBarStyle {
+ VSNET=0, ///< Visual Studio .Net like (only show the text of active tabs)
+ KDEV3=1, ///< KDevelop 3 like (always show the text)
+ KONQSBC=2, ///< Konqueror's classic sidebar style (unthemed) (currently disabled)
+ KDEV3ICON=3, ///< KDevelop 3 like with icons
+ STYLELAST=0xffff ///< Last style
+ };
+
+ /**
+ * Constructor.
+ * @param bm The tab bar's orientation
+ * @param parent The parent widget
+ * @param name The widget's name
+ */
+ KMultiTabBar(KMultiTabBarMode bm,QWidget *parent=0,const char *name=0);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KMultiTabBar();
+
+ /**
+ * append a new button to the button area. The button can later on be accessed with button(ID)
+ * eg for connecting signals to it
+ * @param pic a pixmap for the button
+ * @param id an arbitraty ID value. It will be emitted in the clicked signal for identifying the button
+ * if more than one button is connected to a signals.
+ * @param popup A popup menu which should be displayed if the button is clicked
+ * @param not_used_yet will be used for a popup text in the future
+ */
+ int appendButton(const QPixmap &pic,int id=-1,QPopupMenu* popup=0,const QString& not_used_yet=QString::null);
+ /**
+ * remove a button with the given ID
+ */
+ void removeButton(int id);
+ /**
+ * append a new tab to the tab area. It can be accessed lateron with tabb(id);
+ * @param pic a bitmap for the tab
+ * @param id an arbitrary ID which can be used later on to identify the tab
+ * @param text if a mode with text is used it will be the tab text, otherwise a mouse over hint
+ * @return Always zero. Can be safely ignored.
+ */
+ int appendTab(const QPixmap &pic,int id=-1,const QString& text=QString::null);
+ /**
+ * remove a tab with a given ID
+ * @param id The ID of the tab to remove
+ */
+ void removeTab(int id);
+ /**
+ * set a tab to "raised"
+ * @param id The ID of the tab to manipulate
+ * @param state true == activated/raised, false == not active
+ */
+ void setTab(int id ,bool state);
+ /**
+ * return the state of a tab, identified by it's ID
+ * @param id The ID of the tab to raise
+ */
+ bool isTabRaised(int id) const;
+ /**
+ * get a pointer to a button within the button area identified by its ID
+ * @param id The id of the tab
+ */
+ class KMultiTabBarButton *button(int id) const;
+
+ /**
+ * get a pointer to a tab within the tab area, identified by its ID
+ */
+ class KMultiTabBarTab *tab(int id) const;
+ /**
+ * set the real position of the widget.
+ * @param pos if the mode is horizontal, only use top, bottom, if it is vertical use left or right
+ */
+ void setPosition(KMultiTabBarPosition pos);
+ /**
+ * get the tabbar position.
+ * @return The tab bar's position
+ */
+ KMultiTabBarPosition position() const;
+ /**
+ * set the display style of the tabs
+ * @param style The new display style
+ */
+ void setStyle(KMultiTabBarStyle style);
+ /**
+ * get the display style of the tabs
+ * @return display style
+ */
+ KMultiTabBarStyle tabStyle() const;
+ /**
+ * Returns the list of pointers to the tabs of type KMultiTabBarTab.
+ * @return The list of tabs.
+ * @warning be careful, don't delete tabs yourself and don't delete the list itself
+ */
+ QPtrList<KMultiTabBarTab>* tabs();
+ /**
+ * Returns the list of pointers to the tab buttons of type KMultiTabBarButton.
+ * @return The list of tab buttons.
+ * @warning be careful, don't delete buttons yourself and don't delete the list itself
+ */
+ QPtrList<KMultiTabBarButton>* buttons();
+
+ /**
+ * might vanish, not sure yet
+ */
+ void showActiveTabTexts(bool show=true);
+protected:
+ friend class KMultiTabBarButton;
+ virtual void fontChange( const QFont& );
+ void updateSeparator();
+private:
+ class KMultiTabBarInternal *m_internal;
+ QBoxLayout *m_l;
+ QFrame *m_btnTabSep;
+ QPtrList<KMultiTabBarButton> m_buttons;
+ KMultiTabBarPosition m_position;
+ KMultiTabBarPrivate *d;
+};
+
+/**
+ * @ingroup multitabbar
+ * This class represents a tab bar button in a KMultiTabBarWidget.
+ * This class should never be created except with the appendButton call of KMultiTabBar
+ */
+class KUTILS_EXPORT KMultiTabBarButton: public QPushButton
+{
+ Q_OBJECT
+public:
+ /** @internal */
+ KMultiTabBarButton(const QPixmap& pic,const QString&, QPopupMenu *popup,
+ int id,QWidget *parent, KMultiTabBar::KMultiTabBarPosition pos, KMultiTabBar::KMultiTabBarStyle style);
+ /** @internal */
+ KMultiTabBarButton(const QString&, QPopupMenu *popup,
+ int id,QWidget *parent, KMultiTabBar::KMultiTabBarPosition pos, KMultiTabBar::KMultiTabBarStyle style);
+ /**
+ * Destructor
+ */
+ virtual ~KMultiTabBarButton();
+ /**
+ * Returns the tab's ID
+ * @return The tab's ID
+ */
+ int id() const;
+
+public slots:
+ /**
+ * this is used internaly, but can be used by the user, if (s)he wants to
+ * It the according call of KMultiTabBar is invoked though this modifications will be overwritten
+ */
+ void setPosition(KMultiTabBar::KMultiTabBarPosition);
+ /**
+ * this is used internaly, but can be used by the user, if (s)he wants to
+ * It the according call of KMultiTabBar is invoked though this modifications will be overwritten
+ */
+ void setStyle(KMultiTabBar::KMultiTabBarStyle);
+
+ /**
+ * modify the text of the button
+ */
+ void setText(const QString &);
+
+ QSize sizeHint() const;
+
+protected:
+ KMultiTabBar::KMultiTabBarPosition m_position;
+ KMultiTabBar::KMultiTabBarStyle m_style;
+ QString m_text;
+ virtual void hideEvent( class QHideEvent*);
+ virtual void showEvent( class QShowEvent*);
+private:
+ int m_id;
+ KMultiTabBarButtonPrivate *d;
+signals:
+ /**
+ * this is emitted if the button is clicked
+ * @param id the ID identifying the button
+ */
+ void clicked(int id);
+protected slots:
+ virtual void slotClicked();
+};
+
+/**
+ * @ingroup multitabbar
+ * This class represents a tab bar's tab in a KMultiTabBarWidget.
+ * This class should never be created except with the appendTab call of KMultiTabBar
+ */
+class KUTILS_EXPORT KMultiTabBarTab: public KMultiTabBarButton
+{
+ Q_OBJECT
+public:
+ /** @internal */
+ KMultiTabBarTab(const QPixmap& pic,const QString&,int id,QWidget *parent,
+ KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style);
+ /**
+ * Destructor.
+ */
+ virtual ~KMultiTabBarTab();
+ /**
+ * set the active state of the tab
+ * @param state @c true if the tab should become active, @c false otherwise
+ */
+ void setState(bool state);
+ /**
+ * choose if the text should always be displayed
+ * this is only used in classic mode if at all
+ * @param show Whether or not to show the text
+ */
+ void showActiveTabText(bool show);
+ /**
+ * Resized the tab to the needed size.
+ */
+ void resize(){ setSize( neededSize() ); }
+private:
+ bool m_showActiveTabText;
+ int m_expandedSize;
+ KMultiTabBarTabPrivate *d;
+protected:
+ friend class KMultiTabBarInternal;
+ void setSize(int);
+ int neededSize();
+ void updateState();
+ virtual void drawButton(QPainter *);
+ virtual void drawButtonLabel(QPainter *);
+ void drawButtonStyled(QPainter *);
+ void drawButtonClassic(QPainter *);
+protected slots:
+ virtual void slotClicked();
+ void setTabsPosition(KMultiTabBar::KMultiTabBarPosition);
+
+public slots:
+ virtual void setIcon(const QString&);
+ virtual void setIcon(const QPixmap&);
+};
+
+#endif
diff --git a/kutils/kmultitabbar_p.h b/kutils/kmultitabbar_p.h
new file mode 100644
index 000000000..f47cc5385
--- /dev/null
+++ b/kutils/kmultitabbar_p.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ kmultitabbar_p.h - description
+ -------------------
+ begin : 2003
+ copyright : (C) 2003 by Joseph Wenninger <jowenn@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 as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ 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 K_MULTI_TAB_BAR_P_H
+#define K_MULTI_TAB_BAR_P_H
+#include <qscrollview.h>
+#include <kmultitabbar.h>
+
+class KMultiTabBarInternal: public QScrollView
+{
+ Q_OBJECT
+public:
+ KMultiTabBarInternal(QWidget *parent,KMultiTabBar::KMultiTabBarMode bm);
+ int appendTab(const QPixmap &,int=-1,const QString& =QString::null);
+ KMultiTabBarTab *tab(int) const;
+ void removeTab(int);
+ void setPosition(enum KMultiTabBar::KMultiTabBarPosition pos);
+ void setStyle(enum KMultiTabBar::KMultiTabBarStyle style);
+ void showActiveTabTexts(bool show);
+ QPtrList<KMultiTabBarTab>* tabs(){return &m_tabs;}
+private:
+ friend class KMultiTabBar;
+ QWidget *box;
+ QBoxLayout *mainLayout;
+ QPtrList<KMultiTabBarTab> m_tabs;
+ enum KMultiTabBar::KMultiTabBarPosition m_position;
+ bool m_showActiveTabTexts;
+ enum KMultiTabBar::KMultiTabBarStyle m_style;
+ int m_expandedTabSize;
+ int m_lines;
+ KMultiTabBar::KMultiTabBarMode m_barMode;
+protected:
+ virtual bool eventFilter(QObject *,QEvent*);
+ virtual void drawContents ( QPainter *, int, int, int, int);
+
+ /**
+ * [contentsM|m]ousePressEvent are reimplemented from QScrollView
+ * in order to ignore all mouseEvents on the viewport, so that the
+ * parent can handle them.
+ */
+ virtual void contentsMousePressEvent(QMouseEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+};
+#endif
+
diff --git a/kutils/kplugininfo.cpp b/kutils/kplugininfo.cpp
new file mode 100644
index 000000000..4b80aa776
--- /dev/null
+++ b/kutils/kplugininfo.cpp
@@ -0,0 +1,359 @@
+/* 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 "kplugininfo.h"
+#include <ksimpleconfig.h>
+#include <ktrader.h>
+#include <kdebug.h>
+#include <kconfigbase.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kdesktopfile.h>
+#include <kservice.h>
+
+class KPluginInfo::KPluginInfoPrivate
+{
+ public:
+ KPluginInfoPrivate()
+ : hidden( false )
+ , enabledbydefault( false )
+ , pluginenabled( false )
+ , config( 0 )
+ , kcmservicesCached( false )
+ {}
+
+ ~KPluginInfoPrivate()
+ {
+ delete config;
+ }
+
+ QString specfile; // the filename of the file containing all the info
+ QString name;
+ QString comment;
+ QString icon;
+ QString author;
+ QString email;
+ QString pluginName; // the name attribute in the .rc file
+ QString version;
+ QString website; // URL to the website of the plugin/author
+ QString category;
+ QString license;
+ QStringList dependencies;
+
+ bool hidden;
+ bool enabledbydefault;
+ bool pluginenabled;
+
+ KConfig * config;
+ QString configgroup;
+ KService::Ptr service;
+ QValueList<KService::Ptr> kcmservices;
+ bool kcmservicesCached;
+};
+
+KPluginInfo::KPluginInfo( const QString & filename, const char* resource )
+: d( new KPluginInfoPrivate )
+{
+ KDesktopFile file( filename, true, resource );
+
+ d->specfile = filename;
+
+ if( filename.endsWith( QString::fromAscii( ".desktop" ) ) )
+ {
+ file.setDesktopGroup();
+ d->hidden = file.readBoolEntry( "Hidden", false );
+ if( d->hidden )
+ return;
+
+ d->name = file.readName();
+ d->comment = file.readComment();
+ d->icon = file.readEntry( "Icon" );
+ d->author = file.readEntry( "X-KDE-PluginInfo-Author" );
+ d->email = file.readEntry( "X-KDE-PluginInfo-Email" );
+ d->pluginName = file.readEntry( "X-KDE-PluginInfo-Name" );
+ d->version = file.readEntry( "X-KDE-PluginInfo-Version" );
+ d->website = file.readEntry( "X-KDE-PluginInfo-Website" );
+ d->category = file.readEntry( "X-KDE-PluginInfo-Category" );
+ d->license = file.readEntry( "X-KDE-PluginInfo-License" );
+ d->dependencies = file.readListEntry( "X-KDE-PluginInfo-Depends" );
+ d->enabledbydefault = file.readBoolEntry(
+ "X-KDE-PluginInfo-EnabledByDefault", false );
+ }
+ else if( filename.endsWith( QString::fromAscii( ".plugin" ) ) )
+ { // provided for noatun style .plugin files compatibility
+
+ d->name = file.readName();
+ d->comment = file.readComment();
+ d->icon = file.readEntry( "Icon" );
+ d->author = file.readEntry( "Author" );
+ d->email = file.readEntry( "Email" );
+ d->pluginName = file.readPathEntry( "Filename" );
+ // no version
+ d->website = file.readEntry( "Site" );
+ d->category = file.readEntry( "Type" );
+ d->license = file.readEntry( "License" );
+ d->dependencies = file.readListEntry( "Require" );
+ }
+}
+
+KPluginInfo::KPluginInfo( const KService::Ptr service )
+: d( new KPluginInfoPrivate )
+{
+ d->service = service;
+ d->specfile = service->desktopEntryPath();
+
+ if ( service->isDeleted() )
+ {
+ d->hidden = true;
+ return;
+ }
+
+ d->name = service->name();
+ d->comment = service->comment();
+ d->icon = service->icon();
+ d->author = service->property( "X-KDE-PluginInfo-Author" ).toString();
+ d->email = service->property( "X-KDE-PluginInfo-Email" ).toString();
+ d->pluginName = service->property( "X-KDE-PluginInfo-Name" ).toString();
+ d->version = service->property( "X-KDE-PluginInfo-Version" ).toString();
+ d->website = service->property( "X-KDE-PluginInfo-Website" ).toString();
+ d->category = service->property( "X-KDE-PluginInfo-Category" ).toString();
+ d->license = service->property( "X-KDE-PluginInfo-License" ).toString();
+ d->dependencies =
+ service->property( "X-KDE-PluginInfo-Depends" ).toStringList();
+ QVariant tmp = service->property( "X-KDE-PluginInfo-EnabledByDefault" );
+ d->enabledbydefault = tmp.isValid() ? tmp.toBool() : false;
+}
+
+//X KPluginInfo::KPluginInfo()
+//X : d( new KPluginInfoPrivate )
+//X {
+//X d->hidden = true;
+//X }
+
+KPluginInfo::~KPluginInfo()
+{
+ delete d;
+}
+
+QValueList<KPluginInfo*> KPluginInfo::fromServices( const KService::List & services, KConfig * config, const QString & group )
+{
+ QValueList<KPluginInfo*> infolist;
+ KPluginInfo * info;
+ for( KService::List::ConstIterator it = services.begin();
+ it != services.end(); ++it )
+ {
+ info = new KPluginInfo( *it );
+ info->setConfig( config, group );
+ infolist += info;
+ }
+ return infolist;
+}
+
+QValueList<KPluginInfo*> KPluginInfo::fromFiles( const QStringList & files, KConfig * config, const QString & group )
+{
+ QValueList<KPluginInfo*> infolist;
+ for( QStringList::ConstIterator it = files.begin(); it != files.end(); ++it )
+ {
+ KPluginInfo * info = new KPluginInfo( *it );
+ info->setConfig( config, group );
+ infolist += info;
+ }
+ return infolist;
+}
+
+QValueList<KPluginInfo*> KPluginInfo::fromKPartsInstanceName( const QString & name, KConfig * config, const QString & group )
+{
+ QStringList files = KGlobal::dirs()->findAllResources( "data", name +
+ "/kpartplugins/*.desktop", true, false );
+ return fromFiles( files, config, group );
+}
+
+bool KPluginInfo::isHidden() const
+{
+ return d->hidden;
+}
+
+void KPluginInfo::setPluginEnabled( bool enabled )
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ d->pluginenabled = enabled;
+}
+
+bool KPluginInfo::isPluginEnabled() const
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ return d->pluginenabled;
+}
+
+bool KPluginInfo::isPluginEnabledByDefault() const
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ return d->enabledbydefault;
+}
+
+const QString & KPluginInfo::name() const
+{
+ return d->name;
+}
+
+const QString & KPluginInfo::comment() const
+{
+ return d->comment;
+}
+
+const QString & KPluginInfo::icon() const
+{
+ return d->icon;
+}
+
+const QString & KPluginInfo::specfile() const
+{
+ return d->specfile;
+}
+
+const QString & KPluginInfo::author() const
+{
+ return d->author;
+}
+
+const QString & KPluginInfo::email() const
+{
+ return d->email;
+}
+
+const QString & KPluginInfo::category() const
+{
+ return d->category;
+}
+
+const QString & KPluginInfo::pluginName() const
+{
+ return d->pluginName;
+}
+
+const QString & KPluginInfo::version() const
+{
+ return d->version;
+}
+
+const QString & KPluginInfo::website() const
+{
+ return d->website;
+}
+
+const QString & KPluginInfo::license() const
+{
+ return d->license;
+}
+
+const QStringList & KPluginInfo::dependencies() const
+{
+ return d->dependencies;
+}
+
+KService::Ptr KPluginInfo::service() const
+{
+ return d->service;
+}
+
+const QValueList<KService::Ptr> & KPluginInfo::kcmServices() const
+{
+ if ( !d->kcmservicesCached )
+ {
+ d->kcmservices = KTrader::self()->query( "KCModule", "'" + d->pluginName +
+ "' in [X-KDE-ParentComponents]" );
+ kdDebug( 703 ) << "found " << d->kcmservices.count() << " offers for " <<
+ d->pluginName << endl;
+
+ d->kcmservicesCached = true;
+ }
+
+ return d->kcmservices;
+}
+
+void KPluginInfo::setConfig( KConfig * config, const QString & group )
+{
+ d->config = config;
+ d->configgroup = group;
+}
+
+KConfig * KPluginInfo::config() const
+{
+ return d->config;
+}
+
+const QString & KPluginInfo::configgroup() const
+{
+ return d->configgroup;
+}
+
+QVariant KPluginInfo::property( const QString & key ) const
+{
+ if( d->service )
+ return d->service->property( key );
+ else
+ return QVariant();
+}
+
+QVariant KPluginInfo::operator[]( const QString & key ) const
+{
+ return property( key );
+}
+
+void KPluginInfo::save( KConfigGroup * config )
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ if( 0 == config )
+ {
+ if( 0 == d->config )
+ {
+ kdWarning( 703 ) << "no KConfigGroup, cannot save" << endl;
+ return;
+ }
+ d->config->setGroup( d->configgroup );
+ d->config->writeEntry( d->pluginName + "Enabled", isPluginEnabled() );
+ }
+ else
+ config->writeEntry( d->pluginName + "Enabled", isPluginEnabled() );
+}
+
+void KPluginInfo::load( KConfigGroup * config )
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ if( 0 == config )
+ {
+ if( 0 == d->config )
+ {
+ kdWarning( 703 ) << "no KConfigGroup, cannot load" << endl;
+ return;
+ }
+ d->config->setGroup( d->configgroup );
+ setPluginEnabled( d->config->readBoolEntry( d->pluginName + "Enabled", isPluginEnabledByDefault() ) );
+ }
+ else
+ setPluginEnabled( config->readBoolEntry( d->pluginName + "Enabled", isPluginEnabledByDefault() ) );
+}
+
+void KPluginInfo::defaults()
+{
+ kdDebug( 703 ) << k_funcinfo << endl;
+ setPluginEnabled( isPluginEnabledByDefault() );
+}
+
+// vim: sw=4 sts=4 et
diff --git a/kutils/kplugininfo.desktop b/kutils/kplugininfo.desktop
new file mode 100644
index 000000000..2521bedeb
--- /dev/null
+++ b/kutils/kplugininfo.desktop
@@ -0,0 +1,107 @@
+[Desktop Entry]
+Type=ServiceType
+X-KDE-ServiceType=KPluginInfo
+Name=KDE Plugin Information
+Name[af]=KDE Inprop module Informasie
+Name[ar]=معلومات ملحقات كيدي
+Name[az]=KDE Əlavə Mə'lumatı
+Name[be]=Звесткі аб дадатках KDE
+Name[bg]=Информация за приставките в KDE
+Name[bn]=কে.ডি.ই. প্লাগ-ইন তথ্য
+Name[br]=Titouroù al lugent KDE
+Name[bs]=KDE informacije o dodatku
+Name[ca]=Connector informatiu de KDE
+Name[cs]=Informace o pluginu KDE
+Name[csb]=Wëdowiédzô ò pluginsach KDE
+Name[cy]=Gwybodaeth Ategynnau KDE
+Name[da]=KDE's plugin-information
+Name[de]=KDE-Modul-Information
+Name[el]=Πληροφορίες πρόσθετου KDE
+Name[eo]=KDEa kromaĵinformo
+Name[es]=Información de los plugin de KDE
+Name[et]=KDE plugina info
+Name[eu]=KDEren plugin-en informazioa
+Name[fa]=اطلاعات وصلۀ KDE
+Name[fi]=KDE:n lisäosatiedot
+Name[fr]=Information sur le module KDE
+Name[fy]=KDE Plugin ynformaasje
+Name[ga]=Eolas Bhreiseán KDE
+Name[gl]=Información de Plugin de KDE
+Name[he]=מידע על תוספים של KDE
+Name[hi]=केडीई प्लगिन जानकारी
+Name[hr]=Podaci o KDE dodatku
+Name[hu]=A KDE bővítőmodulok áttekintése
+Name[id]=Informasi Plugin KDE
+Name[is]=KDE íforritsupplýsingar
+Name[it]=Informazioni plugin KDE
+Name[ja]=KDE プラグイン情報
+Name[ka]=KDE მოდულის ინფორმაცია
+Name[kk]=KDE модулі туралы мәлімет
+Name[km]=ព័ត៌មាន​កម្មវិធី​ជំនួយ​ខាង​ក្នុង KDE
+Name[ko]=KDE 플러그인 정보
+Name[lb]=KDE-Plugininformatioun
+Name[lt]=KDE priedų informacija
+Name[lv]=KDE spraudņa informācija
+Name[mk]=KDE Информација за приклучок
+Name[mn]=КДЭ плугин мэдээлэл
+Name[ms]=Maklumat Plugmasuk KDE
+Name[nb]=KDEs informasjon om programtillegg
+Name[nds]=Plugin-Informatschoon
+Name[ne]=KDE प्लगइन सूचना
+Name[nl]=KDE-plugininformatie
+Name[nn]=Informasjon om KDE-programtillegg
+Name[pa]=KDE ਪਲੱਗਿੰਨ ਜਾਣਕਾਰੀ
+Name[pl]=Informacja o wtyczkach KDE
+Name[pt]=Informação do 'Plugin' do KDE
+Name[pt_BR]=Informações do Plug-in do KDE
+Name[ro]=Informaţii modul KDE
+Name[ru]=Сведения о модуле KDE
+Name[rw]=Ibisobanuro by'Icomeka KDE
+Name[se]=Dieđut KDE-moduvlla birra
+Name[sk]=Informácia o module KDE
+Name[sl]=Informacija o vstavkih v KDE
+Name[sq]=Informata për Shtojca të KDE
+Name[sr]=Информација о KDE dodatku
+Name[sr@Latn]=Informacija o KDE dodatku
+Name[sv]=KDE-insticksinformation
+Name[ta]=KDE சொருகுப்பொருள் தகவல்
+Name[te]=కెడిఈ ప్లగిన్ సమాచారం
+Name[tg]=KDE барномаҳои чоп кунӣ
+Name[th]=ข้อมูลปลั้กอินของ KDE
+Name[tr]=KDE Eklenti Bilgisi
+Name[tt]=KDE Östämäse Turında
+Name[uk]=Інформація про втулок KDE
+Name[uz]=KDE plagin haqida maʼlumot
+Name[uz@cyrillic]=KDE плагин ҳақида маълумот
+Name[vi]=Thông tin bộ cầm phít KDE
+Name[wa]=Informåcion sol tchôke-divins di KDE
+Name[zh_CN]=KDE 打印信息
+Name[zh_HK]=KDE 外掛資訊
+Name[zh_TW]=KDE 外掛資訊
+
+[PropertyDef::X-KDE-PluginInfo-Author]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Email]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Name]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Version]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Website]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Category]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-Depends]
+Type=QStringList
+
+[PropertyDef::X-KDE-PluginInfo-License]
+Type=QString
+
+[PropertyDef::X-KDE-PluginInfo-EnabledByDefault]
+Type=bool
diff --git a/kutils/kplugininfo.h b/kutils/kplugininfo.h
new file mode 100644
index 000000000..32ee5b5a7
--- /dev/null
+++ b/kutils/kplugininfo.h
@@ -0,0 +1,319 @@
+/* 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 KPLUGININFO_H
+#define KPLUGININFO_H
+
+#include <qstring.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qvaluelist.h>
+#include <kservice.h>
+
+class KConfigGroup;
+
+/**
+ * @ingroup main
+ * @ingroup plugin
+ * Information about a plugin.
+ *
+ * This holds all the information about a plugin there is. It's used for the
+ * user to decide whether he wants to use this plugin or not.
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT KPluginInfo
+{
+ public:
+ typedef QValueList<KPluginInfo*> List;
+
+ /**
+ * Read plugin info from @p filename.
+ *
+ * The file should be of the following form:
+ * \verbatim
+ [Desktop Entry]
+ Name=User Visible Name
+ Comment=Description of what the plugin does
+
+ [X-KDE Plugin Info]
+ Author=Author's Name
+ Email=author@foo.bar
+ PluginName=internalname
+ Version=1.1
+ Website=http://www.plugin.org/
+ Category=playlist
+ Depends=plugin1,plugin3
+ License=GPL
+ EnabledByDefault=true
+ \endverbatim
+ * The first two entries in the "Desktop Entry" group always need to
+ * be present.
+ *
+ * The "X-KDE-PluginInfo" keys you may add further entries which
+ * will be available using property(). The Website,Category,Require
+ * keys are optional.
+ * For EnabledByDefault look at isPluginEnabledByDefault.
+ *
+ * @param filename The filename of the .desktop file.
+ * @param resource If filename is relative, you need to specify a resource type
+ * (e.g. "service", "apps"... KStandardDirs). Otherwise,
+ * resource isn't used.
+ */
+ KPluginInfo( const QString & filename, const char* resource = 0 );
+
+ /**
+ * Read plugin info from a KService object.
+ *
+ * The .desktop file should look like this:
+ * \verbatim
+ [Desktop Entry]
+ Encoding=UTF-8
+ Icon=mypluginicon
+ Type=Service
+ ServiceTypes=KPluginInfo
+
+ X-KDE-PluginInfo-Author=Author's Name
+ X-KDE-PluginInfo-Email=author@foo.bar
+ X-KDE-PluginInfo-Name=internalname
+ X-KDE-PluginInfo-Version=1.1
+ X-KDE-PluginInfo-Website=http://www.plugin.org/
+ X-KDE-PluginInfo-Category=playlist
+ X-KDE-PluginInfo-Depends=plugin1,plugin3
+ X-KDE-PluginInfo-License=GPL
+ X-KDE-PluginInfo-EnabledByDefault=true
+
+ Name=User Visible Name
+ Comment=Description of what the plugin does
+ \endverbatim
+ * In the first three entries the Icon entry is optional.
+ */
+ KPluginInfo( const KService::Ptr service );
+
+//X /**
+//X * Create an empty hidden plugin.
+//X * @internal
+//X */
+//X KPluginInfo();
+
+ virtual ~KPluginInfo();
+
+ /**
+ * @return A list of KPluginInfo objects constructed from a list of
+ * KService objects. If you get a trader offer of the plugins you want
+ * to use you can just pass them to this function.
+ */
+ static KPluginInfo::List fromServices( const KService::List & services, KConfig * config = 0, const QString & group = QString::null );
+
+ /**
+ * @return A list of KPluginInfo objects constructed from a list of
+ * filenames. If you make a lookup using, for example,
+ * KStandardDirs::findAllResources() you pass the list of files to this
+ * function.
+ */
+ static KPluginInfo::List fromFiles( const QStringList & files, KConfig * config = 0, const QString & group = QString::null );
+
+ /**
+ * @return A list of KPluginInfo objects for the KParts plugins of an
+ * instance. You only need the name of the instance not a pointer to the
+ * KInstance object.
+ */
+ static KPluginInfo::List fromKPartsInstanceName( const QString &, KConfig * config = 0, const QString & group = QString::null );
+
+ /**
+ * @return Whether the plugin should be hidden.
+ */
+ bool isHidden() const;
+
+ /**
+ * Set whether the plugin is currently loaded.
+ *
+ * You might need to reimplement this method for special needs.
+ *
+ * @see isPluginEnabled()
+ * @see save()
+ */
+ virtual void setPluginEnabled( bool enabled );
+
+ /**
+ * @return Whether the plugin is currently loaded.
+ *
+ * You might need to reimplement this method for special needs.
+ *
+ * @see setPluginEnabled()
+ * @see load()
+ */
+ virtual bool isPluginEnabled() const;
+
+ /**
+ * @return The default value whether the plugin is enabled or not.
+ * Defaults to the value set in the desktop file, or if that isn't set
+ * to false.
+ */
+ bool isPluginEnabledByDefault() const;
+
+ /**
+ * @return The value associated the the @p key. You can use it if you
+ * want to read custom values. To do this you need to define
+ * your own servicetype and add it to the ServiceTypes keys.
+ *
+ * @see operator[]
+ */
+ QVariant property( const QString & key ) const;
+
+ /**
+ * This is the same as property(). It is provided for convenience.
+ *
+ * @return The value associated with the @p key.
+ *
+ * @see property()
+ */
+ QVariant operator[]( const QString & key ) const;
+
+ /**
+ * @return The user visible name of the plugin.
+ */
+ const QString & name() const;
+
+ /**
+ * @return A comment describing the plugin.
+ */
+ const QString & comment() const;
+
+ /**
+ * @return The iconname for this plugin
+ */
+ const QString & icon() const;
+
+ /**
+ * @return The file containing the information about the plugin.
+ */
+ const QString & specfile() const;
+
+ /**
+ * @return The author of this plugin.
+ */
+ const QString & author() const;
+
+ /**
+ * @return The email address of the author.
+ */
+ const QString & email() const;
+
+ /**
+ * @return The category of this plugin (e.g. playlist/skin).
+ */
+ const QString & category() const;
+
+ /**
+ * @return The internal name of the plugin (for KParts Plugins this is
+ * the same name as set in the .rc file).
+ */
+ const QString & pluginName() const;
+
+ /**
+ * @return The version of the plugin.
+ */
+ const QString & version() const;
+
+ /**
+ * @return The website of the plugin/author.
+ */
+ const QString & website() const;
+
+
+ /**
+ * @return The license of this plugin.
+ */
+ const QString & license() const;
+
+ /**
+ * @return A list of plugins required for this plugin to be enabled. Use
+ * the pluginName in this list.
+ */
+ const QStringList & dependencies() const;
+
+ /**
+ * @return The KService object for this plugin. You might need it if you
+ * want to read custom values. To do this you need to define
+ * your own servicetype and add it to the ServiceTypes keys.
+ * Then you can use the KService::property() method to read your
+ * keys.
+ *
+ * @see property()
+ */
+ KService::Ptr service() const;
+
+ /**
+ * @return A list of Service pointers if the plugin installs one or more
+ * KCModule
+ */
+ const QValueList<KService::Ptr> & kcmServices() const;
+
+ /**
+ * Set the KConfigGroup to use for load()ing and save()ing the
+ * configuration. This will be overridden by the KConfigGroup passed to
+ * save() or load() (if one is passed).
+ */
+ void setConfig( KConfig * config, const QString & group );
+
+ /**
+ * @return If the KPluginInfo object has a KConfig object set return
+ * it, else return 0.
+ */
+ KConfig * config() const;
+
+ /**
+ * @return The groupname used in the KConfig object for load()ing and
+ * save()ing whether the plugin is enabled.
+ */
+ const QString & configgroup() const;
+
+ /**
+ * Save state of the plugin - enabled or not. This function is provided
+ * for reimplementation if you need to save somewhere else.
+ * @param config The KConfigGroup holding the information whether
+ * plugin is enabled.
+ */
+ virtual void save( KConfigGroup * config = 0 );
+
+ /**
+ * Load the state of the plugin - enabled or not. This function is provided
+ * for reimplementation if you need to save somewhere else.
+ * @param config The KConfigGroup holding the information whether
+ * plugin is enabled.
+ */
+ virtual void load( KConfigGroup * config = 0 );
+
+ /**
+ * Restore defaults (enabled or not).
+ */
+ virtual void defaults();
+
+ private:
+ KPluginInfo( const KPluginInfo & );
+ const KPluginInfo & operator=( const KPluginInfo & );
+
+ class KPluginInfoPrivate;
+ KPluginInfoPrivate * d;
+};
+
+// vim: sw=4 sts=4 et tw=80
+#endif // KPLUGININFO_H
diff --git a/kutils/kpluginselector.cpp b/kutils/kpluginselector.cpp
new file mode 100644
index 000000000..4bcc166d1
--- /dev/null
+++ b/kutils/kpluginselector.cpp
@@ -0,0 +1,727 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-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 "kpluginselector.h"
+#include "kpluginselector_p.h"
+
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qlabel.h>
+#include <qstrlist.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qlayout.h>
+#include <qptrlist.h>
+#include <qwidgetstack.h>
+#include <qcursor.h>
+#include <qapplication.h>
+#include <qobjectlist.h>
+#include <qcstring.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <klistview.h>
+#include <ksimpleconfig.h>
+#include <kdialog.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kstandarddirs.h>
+#include <ktabctl.h>
+#include <kcmoduleinfo.h>
+#include <qvaluelist.h>
+#include <kservice.h>
+#include <ktrader.h>
+#include <ktabwidget.h>
+#include <kiconloader.h>
+#include <kcmodule.h>
+#include "kcmoduleinfo.h"
+#include "kcmoduleloader.h"
+#include <qsplitter.h>
+#include <qframe.h>
+#include "kplugininfo.h"
+#include <kinstance.h>
+#include <qptrdict.h>
+#include <qstringlist.h>
+#include "kcmoduleproxy.h"
+
+/*
+ QCheckListViewItem that holds a pointer to the KPluginInfo object.
+ Used in the tooltip code to access additional fields
+*/
+class KPluginInfoLVI : public QCheckListItem
+{
+public:
+ KPluginInfoLVI( KPluginInfo *pluginInfo, KListView *parent )
+ : QCheckListItem( parent, pluginInfo->name(), QCheckListItem::CheckBox ), m_pluginInfo( pluginInfo )
+ {
+ }
+
+ KPluginInfo * pluginInfo() { return m_pluginInfo; }
+
+private:
+ KPluginInfo *m_pluginInfo;
+};
+
+/*
+ Custom QToolTip for the list view.
+ The decision whether or not to show tooltips is taken in
+ maybeTip(). See also the QListView sources from Qt itself.
+*/
+class KPluginListViewToolTip : public QToolTip
+{
+public:
+ KPluginListViewToolTip( QWidget *parent, KListView *lv );
+
+ void maybeTip( const QPoint &pos );
+
+private:
+ KListView *m_listView;
+};
+
+KPluginListViewToolTip::KPluginListViewToolTip( QWidget *parent, KListView *lv )
+: QToolTip( parent ), m_listView( lv )
+{
+}
+
+void KPluginListViewToolTip::maybeTip( const QPoint &pos )
+{
+ if ( !parentWidget() || !m_listView )
+ return;
+
+ KPluginInfoLVI *item = dynamic_cast<KPluginInfoLVI *>( m_listView->itemAt( pos ) );
+ if ( !item )
+ return;
+
+ QString toolTip = i18n( "<qt><table>"
+ "<tr><td><b>Description:</b></td><td>%1</td></tr>"
+ "<tr><td><b>Author:</b></td><td>%2</td></tr>"
+ "<tr><td><b>Version:</b></td><td>%3</td></tr>"
+ "<tr><td><b>License:</b></td><td>%4</td></tr></table></qt>" ).arg( item->pluginInfo()->comment(),
+ item->pluginInfo()->author(), item->pluginInfo()->version(), item->pluginInfo()->license() );
+
+ //kdDebug( 702 ) << k_funcinfo << "Adding tooltip: itemRect: " << itemRect << ", tooltip: " << toolTip << endl;
+ tip( m_listView->itemRect( item ), toolTip );
+}
+
+struct KPluginSelectionWidget::KPluginSelectionWidgetPrivate
+{
+ KPluginSelectionWidgetPrivate( KPluginSelector * _kps,
+ const QString & _cat,
+ KConfigGroup * _config )
+ : widgetstack( 0 )
+ , kps( _kps )
+ , config( _config )
+ , tooltip( 0 )
+ , catname( _cat )
+ , currentplugininfo( 0 )
+ , visible( true )
+ , currentchecked( false )
+ , changed( 0 )
+ {
+ moduleParentComponents.setAutoDelete( true );
+ }
+
+ ~KPluginSelectionWidgetPrivate()
+ {
+ delete config;
+ }
+
+ QMap<QCheckListItem*, KPluginInfo*> pluginInfoMap;
+
+ QWidgetStack * widgetstack;
+ KPluginSelector * kps;
+ KConfigGroup * config;
+ KPluginListViewToolTip *tooltip;
+
+ QDict<KCModuleInfo> pluginconfigmodules;
+ QMap<QString, int> widgetIDs;
+ QMap<KPluginInfo*, bool> plugincheckedchanged;
+ QString catname;
+ QValueList<KCModuleProxy*> modulelist;
+ QPtrDict<QStringList> moduleParentComponents;
+
+ KPluginInfo * currentplugininfo;
+ bool visible;
+ bool currentchecked;
+ int changed;
+};
+
+KPluginSelectionWidget::KPluginSelectionWidget(
+ const QValueList<KPluginInfo*> & plugininfos, KPluginSelector * kps,
+ QWidget * parent, const QString & catname, const QString & category,
+ KConfigGroup * config, const char * name )
+ : QWidget( parent, name )
+ , d( new KPluginSelectionWidgetPrivate( kps, catname, config ) )
+{
+ init( plugininfos, category );
+}
+
+inline QString KPluginSelectionWidget::catName() const
+{
+ return d->catname;
+}
+
+void KPluginSelectionWidget::init( const QValueList<KPluginInfo*> & plugininfos,
+ const QString & category )
+{
+ // setup Widgets
+ ( new QVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ KListView * listview = new KListView( this );
+ d->tooltip = new KPluginListViewToolTip( listview->viewport(), listview );
+ connect( listview, SIGNAL( pressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( listview, SIGNAL( spacePressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( listview, SIGNAL( returnPressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( listview, SIGNAL( selectionChanged( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ listview->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Preferred );
+ listview->setAcceptDrops( false );
+ listview->setFullWidth( true );
+ listview->setSelectionModeExt( KListView::Single );
+ listview->setAllColumnsShowFocus( true );
+ listview->addColumn( i18n( "Name" ) );
+ for( QValueList<KPluginInfo*>::ConstIterator it = plugininfos.begin();
+ it != plugininfos.end(); ++it )
+ {
+ d->plugincheckedchanged[ *it ] = false;
+ if( !( *it )->isHidden() &&
+ ( category.isNull() || ( *it )->category() == category ) )
+ {
+ QCheckListItem * item = new KPluginInfoLVI( *it, listview );
+ if( ! ( *it )->icon().isEmpty() )
+ item->setPixmap( 0, SmallIcon( ( *it )->icon(), IconSize( KIcon::Small ) ) );
+ item->setOn( ( *it )->isPluginEnabled() );
+ d->pluginInfoMap.insert( item, *it );
+ }
+ }
+
+ // widgetstack
+ d->widgetstack = d->kps->widgetStack();
+ load();
+ // select and highlight the first item in the plugin list
+ if( listview->firstChild() )
+ listview->setSelected( listview->firstChild(), true );
+}
+
+KPluginSelectionWidget::~KPluginSelectionWidget()
+{
+ delete d->tooltip;
+ delete d;
+}
+
+bool KPluginSelectionWidget::pluginIsLoaded( const QString & pluginName ) const
+{
+ for( QMap<QCheckListItem*, KPluginInfo*>::ConstIterator it =
+ d->pluginInfoMap.begin(); it != d->pluginInfoMap.end(); ++it )
+ if( it.data()->pluginName() == pluginName )
+ return it.data()->isPluginEnabled();
+ return false;
+}
+
+
+QWidget * KPluginSelectionWidget::insertKCM( QWidget * parent,
+ const KCModuleInfo & moduleinfo )
+{
+ KCModuleProxy * module = new KCModuleProxy( moduleinfo, false,
+ parent );
+ if( !module->realModule() )
+ {
+ //FIXME: not very verbose
+ QLabel * label = new QLabel( i18n( "Error" ), parent );
+ label->setAlignment( Qt::AlignCenter );
+
+ return label;
+ }
+ // add the KCM to the list so that we can call load/save/defaults on it
+ d->modulelist.append( module );
+ QStringList * parentComponents = new QStringList(
+ moduleinfo.service()->property(
+ "X-KDE-ParentComponents" ).toStringList() );
+ d->moduleParentComponents.insert( module, parentComponents );
+ connect( module, SIGNAL( changed( bool ) ), SLOT( clientChanged( bool ) ) );
+ return module;
+}
+
+void KPluginSelectionWidget::embeddPluginKCMs( KPluginInfo * plugininfo, bool checked )
+{
+ //if we have Services for the plugin we should be able to
+ //create KCM(s)
+ QApplication::setOverrideCursor( Qt::WaitCursor );
+ if( plugininfo->kcmServices().size() > 1 )
+ {
+ // we need a tabwidget
+ KTabWidget * tabwidget = new KTabWidget( d->widgetstack );
+ tabwidget->setEnabled( checked );
+
+ int id = d->widgetstack->addWidget( tabwidget );
+ d->kps->configPage( id );
+ d->widgetIDs[ plugininfo->pluginName() ] = id;
+
+ for( QValueList<KService::Ptr>::ConstIterator it =
+ plugininfo->kcmServices().begin();
+ it != plugininfo->kcmServices().end(); ++it )
+ {
+ if( !( *it )->noDisplay() )
+ {
+ KCModuleInfo moduleinfo( *it );
+ QWidget * module = insertKCM( tabwidget, moduleinfo );
+ tabwidget->addTab( module, moduleinfo.moduleName() );
+ }
+ }
+ }
+ else
+ {
+ if( !plugininfo->kcmServices().front()->noDisplay() )
+ {
+ KCModuleInfo moduleinfo(
+ plugininfo->kcmServices().front() );
+ QWidget * module = insertKCM( d->widgetstack, moduleinfo );
+ module->setEnabled( checked );
+
+ int id = d->widgetstack->addWidget( module );
+ d->kps->configPage( id );
+ d->widgetIDs[ plugininfo->pluginName() ] = id;
+ }
+ }
+ QApplication::restoreOverrideCursor();
+}
+
+inline void KPluginSelectionWidget::updateConfigPage()
+{
+ updateConfigPage( d->currentplugininfo, d->currentchecked );
+}
+
+void KPluginSelectionWidget::updateConfigPage( KPluginInfo * plugininfo,
+ bool checked )
+{
+ //kdDebug( 702 ) << k_funcinfo << endl;
+ d->currentplugininfo = plugininfo;
+ d->currentchecked = checked;
+
+ // if this widget is not currently visible (meaning that it's in a tabwidget
+ // and another tab is currently opened) it's not allowed to change the
+ // widgetstack
+ if( ! d->visible )
+ return;
+
+ if( 0 == plugininfo )
+ {
+ d->kps->configPage( 1 );
+ return;
+ }
+
+ if( plugininfo->kcmServices().empty() )
+ d->kps->configPage( 1 );
+ else
+ {
+ if( !d->widgetIDs.contains( plugininfo->pluginName() ) )
+ // if no widget exists for the plugin create it
+ embeddPluginKCMs( plugininfo, checked );
+ else
+ {
+ // the page already exists
+ int id = d->widgetIDs[ plugininfo->pluginName() ];
+ d->kps->configPage( id );
+ d->widgetstack->widget( id )->setEnabled( checked );
+ }
+ }
+}
+
+void KPluginSelectionWidget::clientChanged( bool didchange )
+{
+ kdDebug( 702 ) << k_funcinfo << endl;
+ d->changed += didchange ? 1 : -1;
+ if( d->changed == 1 )
+ emit changed( true );
+ else if( d->changed == 0 )
+ emit changed( false );
+ else if( d->changed < 0 )
+ kdError( 702 ) << "negative changed value: " << d->changed << endl;
+}
+
+void KPluginSelectionWidget::tabWidgetChanged( QWidget * widget )
+{
+ if( widget == this )
+ {
+ d->visible = true;
+ updateConfigPage();
+ }
+ else
+ d->visible = false;
+}
+
+void KPluginSelectionWidget::executed( QListViewItem * item )
+{
+ kdDebug( 702 ) << k_funcinfo << endl;
+ if( item == 0 )
+ return;
+
+ // Why not a dynamic_cast? - Martijn
+ // because this is what the Qt API suggests; and since gcc 3.x I don't
+ // trust dynamic_cast anymore - mkretz
+ if( item->rtti() != 1 ) //check for a QCheckListItem
+ return;
+
+ QCheckListItem * citem = static_cast<QCheckListItem *>( item );
+ bool checked = citem->isOn();
+ //kdDebug( 702 ) << "it's a " << ( checked ? "checked" : "unchecked" )
+ // << " QCheckListItem" << endl;
+
+ KPluginInfo * info = d->pluginInfoMap[ citem ];
+ Q_ASSERT( !info->isHidden() );
+
+ if ( info->isPluginEnabled() != checked )
+ {
+ kdDebug( 702 ) << "Item changed state, emitting changed()" << endl;
+
+ if( ! d->plugincheckedchanged[ info ] )
+ {
+ ++d->changed;
+ if ( d->changed == 1 )
+ emit changed( true );
+ }
+ d->plugincheckedchanged[ info ] = true;
+
+ checkDependencies( info );
+ }
+ else
+ {
+ if( d->plugincheckedchanged[ info ] )
+ {
+ --d->changed;
+ if ( d->changed == 0 )
+ emit changed( false );
+ }
+ d->plugincheckedchanged[ info ] = false;
+ // FIXME: plugins that depend on this plugin need to be disabled, too
+ }
+
+ updateConfigPage( info, checked );
+}
+
+void KPluginSelectionWidget::load()
+{
+ //kdDebug( 702 ) << k_funcinfo << endl;
+
+ for( QMap<QCheckListItem*, KPluginInfo*>::Iterator it =
+ d->pluginInfoMap.begin(); it != d->pluginInfoMap.end(); ++it )
+ {
+ KPluginInfo * info = it.data();
+ info->load( d->config );
+ it.key()->setOn( info->isPluginEnabled() );
+ if( d->visible && info == d->currentplugininfo )
+ d->currentchecked = info->isPluginEnabled();
+ }
+
+ for( QValueList<KCModuleProxy*>::Iterator it = d->modulelist.begin();
+ it != d->modulelist.end(); ++it )
+ if( ( *it )->changed() )
+ ( *it )->load();
+
+ updateConfigPage();
+ // TODO: update changed state
+}
+
+void KPluginSelectionWidget::save()
+{
+ kdDebug( 702 ) << k_funcinfo << endl;
+
+ for( QMap<QCheckListItem*, KPluginInfo*>::Iterator it =
+ d->pluginInfoMap.begin(); it != d->pluginInfoMap.end(); ++it )
+ {
+ KPluginInfo * info = it.data();
+ bool checked = it.key()->isOn();
+ info->setPluginEnabled( checked );
+ info->save( d->config );
+ d->plugincheckedchanged[ info ] = false;
+ }
+ QStringList updatedModules;
+ for( QValueList<KCModuleProxy*>::Iterator it = d->modulelist.begin();
+ it != d->modulelist.end(); ++it )
+ if( ( *it )->changed() )
+ {
+ ( *it )->save();
+ QStringList * names = d->moduleParentComponents[ *it ];
+ if( names->size() == 0 )
+ names->append( QString::null );
+ for( QStringList::ConstIterator nameit = names->begin();
+ nameit != names->end(); ++nameit )
+ if( updatedModules.find( *nameit ) == updatedModules.end() )
+ updatedModules.append( *nameit );
+ }
+ for( QStringList::ConstIterator it = updatedModules.begin(); it != updatedModules.end(); ++it )
+ emit configCommitted( ( *it ).latin1() );
+
+ updateConfigPage();
+ kdDebug( 702 ) << "syncing config file" << endl;
+ d->config->sync();
+ d->changed = 0;
+ emit changed( false );
+}
+
+void KPluginSelectionWidget::checkDependencies( const KPluginInfo * info )
+{
+ if( info->dependencies().isEmpty() )
+ return;
+
+ for( QStringList::ConstIterator it = info->dependencies().begin();
+ it != info->dependencies().end(); ++it )
+ for( QMap<QCheckListItem*,
+ KPluginInfo*>::Iterator infoIt = d->pluginInfoMap.begin();
+ infoIt != d->pluginInfoMap.end(); ++infoIt )
+ if( infoIt.data()->pluginName() == *it )
+ {
+ if( !infoIt.key()->isOn() )
+ {
+ infoIt.key()->setOn( true );
+ checkDependencies( infoIt.data() );
+ }
+ continue;
+ }
+}
+
+class KPluginSelector::KPluginSelectorPrivate
+{
+ public:
+ KPluginSelectorPrivate()
+ : frame( 0 )
+ , tabwidget( 0 )
+ , widgetstack( 0 )
+ , hideconfigpage( false )
+ {
+ }
+
+ QFrame * frame;
+ KTabWidget * tabwidget;
+ QWidgetStack * widgetstack;
+ QValueList<KPluginSelectionWidget *> pswidgets;
+ bool hideconfigpage;
+};
+
+KPluginSelector::KPluginSelector( QWidget * parent, const char * name )
+: QWidget( parent, name )
+, d( new KPluginSelectorPrivate )
+{
+ QBoxLayout * hbox = new QHBoxLayout( this, 0, KDialog::spacingHint() );
+ hbox->setAutoAdd( true );
+
+ QSplitter* splitter = new QSplitter( QSplitter::Horizontal, this );
+ d->frame = new QFrame( splitter, "KPluginSelector left frame" );
+ d->frame->setFrameStyle( QFrame::NoFrame );
+ ( new QVBoxLayout( d->frame, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+
+ // widgetstack
+ d->widgetstack = new QWidgetStack( splitter, "KPluginSelector Config Pages" );
+ d->widgetstack->setFrameStyle( QFrame::Panel | QFrame::Sunken );
+ d->widgetstack->setMinimumSize( 200, 200 );
+
+ QLabel * label = new QLabel( i18n( "(This plugin is not configurable)" ),
+ d->widgetstack );
+ ( new QVBoxLayout( label, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ label->setAlignment( Qt::AlignCenter );
+ label->setMinimumSize( 200, 200 );
+
+ d->widgetstack->addWidget( label, 1 );
+
+ configPage( 1 );
+}
+
+KPluginSelector::~KPluginSelector()
+{
+ delete d;
+}
+
+void KPluginSelector::checkNeedForTabWidget()
+{
+ kdDebug( 702 ) << k_funcinfo << endl;
+ if( ! d->tabwidget && d->pswidgets.size() == 1 )
+ {
+ kdDebug( 702 ) << "no TabWidget and one KPluginSelectionWidget" << endl;
+ // there's only one KPluginSelectionWidget yet, we need a TabWidget
+ KPluginSelectionWidget * w = d->pswidgets.first();
+ if( w )
+ {
+ kdDebug( 702 ) << "create TabWidget" << endl;
+ d->tabwidget = new KTabWidget( d->frame,
+ "KPluginSelector TabWidget" );
+ w->reparent( d->tabwidget, QPoint( 0, 0 ) );
+ d->tabwidget->addTab( w, w->catName() );
+ connect( d->tabwidget, SIGNAL( currentChanged( QWidget * ) ), w,
+ SLOT( tabWidgetChanged( QWidget * ) ) );
+ }
+ }
+}
+
+static QValueList<KPluginInfo*> kpartsPluginInfos( const QString& instanceName )
+{
+ if( instanceName.isNull() )
+ return QValueList<KPluginInfo*>(); //nothing
+
+ const QStringList desktopfilenames = KGlobal::dirs()->findAllResources( "data",
+ instanceName + "/kpartplugins/*.desktop", true, false );
+ return KPluginInfo::fromFiles( desktopfilenames );
+}
+
+void KPluginSelector::addPlugins( const QString & instanceName,
+ const QString & catname, const QString & category, KConfig * config )
+{
+ const QValueList<KPluginInfo*> plugininfos = kpartsPluginInfos( instanceName );
+ if ( plugininfos.isEmpty() )
+ return;
+ checkNeedForTabWidget();
+ Q_ASSERT( config ); // please set config, or use addPlugins( instance, ... ) which takes care of it
+ if ( !config ) // KDE4: ensure that config is always set; make it second in the arg list?
+ config = new KSimpleConfig( instanceName ); // memleak!
+ KConfigGroup * cfgGroup = new KConfigGroup( config, "KParts Plugins" );
+ kdDebug( 702 ) << k_funcinfo << "cfgGroup = " << cfgGroup << endl;
+ addPluginsInternal( plugininfos, catname, category, cfgGroup );
+}
+
+void KPluginSelector::addPluginsInternal( const QValueList<KPluginInfo*> plugininfos,
+ const QString & catname, const QString & category,
+ KConfigGroup* cfgGroup )
+{
+ KPluginSelectionWidget * w;
+ if( d->tabwidget )
+ {
+ w = new KPluginSelectionWidget( plugininfos, this,
+ d->tabwidget, catname, category, cfgGroup );
+ d->tabwidget->addTab( w, catname );
+ connect( d->tabwidget, SIGNAL( currentChanged( QWidget * ) ), w,
+ SLOT( tabWidgetChanged( QWidget * ) ) );
+ }
+ else
+ w = new KPluginSelectionWidget( plugininfos, this, d->frame,
+ catname, category, cfgGroup );
+ w->setMinimumSize( 200, 200 );
+ connect( w, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
+ connect( w, SIGNAL( configCommitted( const QCString & ) ), this,
+ SIGNAL( configCommitted( const QCString & ) ) );
+ d->pswidgets += w;
+}
+
+void KPluginSelector::addPlugins( const KInstance * instance, const QString &
+ catname, const QString & category, KConfig * config )
+{
+ if ( !config )
+ config = instance->config();
+ addPlugins( instance->instanceName(), catname, category, config );
+}
+
+void KPluginSelector::addPlugins( const QValueList<KPluginInfo*> & plugininfos,
+ const QString & catname, const QString & category, KConfig * config )
+{
+ checkNeedForTabWidget();
+ // the KConfigGroup becomes owned by KPluginSelectionWidget
+ KConfigGroup * cfgGroup = new KConfigGroup( config ? config : KGlobal::config(), "Plugins" );
+ kdDebug( 702 ) << k_funcinfo << "cfgGroup = " << cfgGroup << endl;
+ addPluginsInternal( plugininfos, catname, category, cfgGroup );
+}
+
+QWidgetStack * KPluginSelector::widgetStack()
+{
+ return d->widgetstack;
+}
+
+inline void KPluginSelector::configPage( int id )
+{
+ if( id == 1 )
+ {
+ // no config page
+ if( d->hideconfigpage )
+ {
+ d->widgetstack->hide();
+ return;
+ }
+ }
+ else
+ d->widgetstack->show();
+
+ d->widgetstack->raiseWidget( id );
+}
+
+void KPluginSelector::setShowEmptyConfigPage( bool show )
+{
+ d->hideconfigpage = !show;
+ if( d->hideconfigpage )
+ if( d->widgetstack->id( d->widgetstack->visibleWidget() ) == 1 )
+ d->widgetstack->hide();
+}
+
+void KPluginSelector::load()
+{
+ for( QValueList<KPluginSelectionWidget *>::Iterator it =
+ d->pswidgets.begin(); it != d->pswidgets.end(); ++it )
+ {
+ ( *it )->load();
+ }
+}
+
+void KPluginSelector::save()
+{
+ for( QValueList<KPluginSelectionWidget *>::Iterator it =
+ d->pswidgets.begin(); it != d->pswidgets.end(); ++it )
+ {
+ ( *it )->save();
+ }
+}
+
+void KPluginSelector::defaults()
+{
+ kdDebug( 702 ) << k_funcinfo << endl;
+
+ // what should defaults do? here's what I think:
+ // Pressing a button in the dialog should not change any widgets that are
+ // not visible for the user. Therefor we may only change the currently
+ // visible plugin's KCM. Restoring the default plugin selections is therefor
+ // not possible. (if the plugin has multiple KCMs they will be shown in a
+ // tabwidget - defaults() will be called for all of them)
+
+ QWidget * pluginconfig = d->widgetstack->visibleWidget();
+ KCModuleProxy * kcm = ( KCModuleProxy* )pluginconfig->qt_cast(
+ "KCModuleProxy" );
+ if( kcm )
+ {
+ kdDebug( 702 ) << "call KCModule::defaults() for the plugins KCM"
+ << endl;
+ kcm->defaults();
+ return;
+ }
+
+ // if we get here the visible Widget must be a tabwidget holding more than
+ // one KCM
+ QObjectList * kcms = pluginconfig->queryList( "KCModuleProxy",
+ 0, false, false );
+ QObjectListIt it( *kcms );
+ QObject * obj;
+ while( ( obj = it.current() ) != 0 )
+ {
+ ++it;
+ ( ( KCModule* )obj )->defaults();
+ }
+ delete kcms;
+ // FIXME: update changed state
+}
+
+// vim: sw=4 sts=4 et
+
+#include "kpluginselector.moc"
+#include "kpluginselector_p.moc"
diff --git a/kutils/kpluginselector.h b/kutils/kpluginselector.h
new file mode 100644
index 000000000..b21e41b3e
--- /dev/null
+++ b/kutils/kpluginselector.h
@@ -0,0 +1,218 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002-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 KPLUGINSELECTOR_H
+#define KPLUGINSELECTOR_H
+
+#include <qwidget.h>
+#include <qstring.h>
+
+#include <kdelibs_export.h>
+
+class KInstance;
+class KPluginInfo;
+class QWidgetStack;
+class KConfig;
+class KConfigGroup;
+
+/**
+ * @ingroup main
+ * @ingroup plugin
+ * @short A widget to select what plugins to load and configure the plugins.
+ *
+ * It shows the list of available plugins on top (if there's more than one
+ * category this is a TabWidget) and the configuration of the selected plugin
+ * below that.
+ *
+ * Since the user needs a way to know what a specific plugin does every plugin
+ * sould install a desktop file containing a name, comment and category field.
+ * The category is usefull for applications that can use different kinds of
+ * plugins like a playlist, skin or visualization.
+ *
+ * The location of these desktop files is the
+ * share/apps/&lt;instancename&gt;/&lt;plugindir&gt; directory. But if you need
+ * you may use a different directory.
+ *
+ * Often a program has more than one kind of plugin. In that case you want to
+ * make a visible distinction between those plugins. All you have to do is to
+ * create a KPluginSelectionWidget for every category and then add them all
+ * to the KPluginSelector.
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT KPluginSelector : public QWidget
+{
+ friend class KPluginSelectionWidget;
+
+ Q_OBJECT
+ public:
+ /**
+ * Create a new KPluginSelector.
+ */
+ KPluginSelector( QWidget * parent, const char * name = 0 );
+ ~KPluginSelector();
+
+ /**
+ * Add a list of KParts plugins
+ *
+ * If you want to support non-KParts plugins use the following
+ * function.
+ *
+ * The information about the plugins will be loaded from the
+ * share/apps/&lt;instancename&gt;/kpartplugins directory.
+ *
+ * @param instanceName The name of the KInstance of the plugin's parent.
+ * @param catname The translated name of the category. This is the
+ * name that is shown in the TabWidget if there is
+ * more than one category.
+ * @param category When you have different categories of KParts
+ * plugins you distinguish between the plugins using
+ * the Category key in the .desktop file. Use this
+ * parameter to select only those KParts plugins
+ * with the Category key == @p category. If @p
+ * category is not set the Category key is ignored
+ * and all plugins are shown.
+ * @param config The KConfig object that holds the state of the
+ * plugins being enabled or not. By default it should
+ * be instance->config(). It is recommended to
+ * always pass a KConfig object if you use
+ * KSettings::PluginPage since you never know from where the
+ * page will be called (think global config app).
+ * For example KViewCanvas passes KSimpleConfig(
+ * "kviewcanvas" ).
+ */
+ void addPlugins( const QString & instanceName,
+ const QString & catname = QString::null,
+ const QString & category = QString::null,
+ KConfig * config = 0 );
+
+ /**
+ * Add a list of KParts plugins. Convenience method for the one above.
+ * If not set explicitely, @p config is set to instance->config().
+ */
+ void addPlugins( const KInstance * instance,
+ const QString & catname = QString::null,
+ const QString & category = QString::null,
+ KConfig * config = 0 );
+
+ /**
+ * Add a list of non-KParts plugins
+ *
+ * @param plugininfos A list of KPluginInfo objects containing the
+ * necessary information for the plugins you want to
+ * add to the list.
+ * @param catname The translated name of the category. This is the
+ * name that is shown in the TabWidget if there is
+ * more than one category.
+ * @param category When you have different categories of KParts
+ * plugins you distinguish between the plugins using
+ * the Category key in the .desktop file. Use this
+ * parameter to select only those KParts plugins
+ * with the Category key == @p category. If @p
+ * category is not set the Category key is ignored
+ * and all plugins are shown.
+ * @param config The KConfig object that holds the state of the
+ * plugins being enabled or not. By default it will
+ * use KGlobal::config(). It is recommended to
+ * always pass a KConfig object if you use
+ * KSettings::PluginPage since you never know from where the
+ * page will be called (think global config app).
+ * For example KViewCanvas passes KSimpleConfig(
+ * "kviewcanvas" ).
+ */
+ void addPlugins( const QValueList<KPluginInfo*> & plugininfos,
+ const QString & catname = QString::null,
+ const QString & category = QString::null,
+ KConfig * config = 0 );
+
+ /**
+ * Set whether the area for showing the KCMs of the plugins should be
+ * hidden if the plugin doesn't have a KCM or whether the layout should
+ * rather stay static and only an message should be shown.
+ *
+ * By default the config page is not hidden.
+ */
+ void setShowEmptyConfigPage( bool );
+
+ /**
+ * 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::isPluginEnabled() method. If that doesn't fit your needs
+ * you can also reimplement this method.
+ */
+ void load();
+
+ /**
+ * Save the configuration
+ */
+ void save();
+
+ /**
+ * Change to applications defaults
+ */
+ void defaults();
+
+ signals:
+ /**
+ * Tells you whether the configuration is changed or not.
+ */
+ void changed( bool );
+
+ /**
+ * Emitted after the config of an embedded KCM has been saved. The
+ * argument is the name of the parent component that needs to reload
+ * its config
+ */
+ void configCommitted( const QCString & instanceName );
+
+ private:
+ /**
+ * return the KCM widgetstack
+ *
+ * @internal
+ */
+ QWidgetStack * widgetStack();
+
+ /**
+ * Show an info page in the widgetstack.
+ *
+ * @internal
+ */
+ void configPage( int id );
+
+ /**
+ * @internal
+ */
+ void checkNeedForTabWidget();
+
+ /**
+ * @internal
+ */
+ void addPluginsInternal( const QValueList<KPluginInfo*> plugininfos,
+ const QString & catname, const QString & category,
+ KConfigGroup* cfgGroup );
+
+ class KPluginSelectorPrivate;
+ KPluginSelectorPrivate * d;
+};
+
+// vim: sw=4 sts=4 et tw=80
+#endif // KPLUGINSELECTOR_H
diff --git a/kutils/kpluginselector_p.h b/kutils/kpluginselector_p.h
new file mode 100644
index 000000000..c9b968c0f
--- /dev/null
+++ b/kutils/kpluginselector_p.h
@@ -0,0 +1,188 @@
+/* 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 KPLUGINSELECTOR_P_H
+#define KPLUGINSELECTOR_P_H
+
+#include <qwidget.h>
+
+#include <kdelibs_export.h>
+
+class KConfigGroup;
+class QListViewItem;
+class KPluginInfo;
+class KCModuleInfo;
+
+/**
+ * This is a widget to configure what Plugins should be loaded. This widget is
+ * used by KPluginSelector and has no direct use.
+ *
+ * @internal
+ * @see KPluginSelector
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KPluginSelectionWidget : public QWidget
+{
+ Q_OBJECT
+ public:
+ /**
+ * Create a new Plugin Selector widget.
+ *
+ * @param plugininfos A list of KPluginInfo objects containing the
+ * necessary information for the plugins you want to
+ * add the selector's list.
+ * @param kps A KPluginSelector object.
+ * @param parent The parent widget.
+ * @param catname The translated name of the category.
+ * @param category The unstranslated category key name.
+ * @param config Set the KConfigGroup object that holds the
+ * state of the plugins being enabled or not.
+ * @param name The name of the widget (passed to QWidget)
+ *
+ * @internal
+ */
+ KPluginSelectionWidget( const QValueList<KPluginInfo*> & plugininfos,
+ KPluginSelector * kps, QWidget * parent, const QString & catname,
+ const QString & category, KConfigGroup * config = 0,
+ const char * name = 0 );
+
+ virtual ~KPluginSelectionWidget();
+
+
+ /**
+ * Returns the translated category name
+ *
+ * @internal
+ */
+ QString catName() const;
+
+ /**
+ * Tell the KPluginInfo objects to load their state (enabled/disabled).
+ */
+ virtual void load();
+
+ /**
+ * It tells the KPluginInfo objects to save their current state
+ * (enabled/disabled).
+ */
+ virtual void save();
+
+ /**
+ * @return whether the plugin is enabled in the ListView or not.
+ */
+ bool pluginChecked( const QString & pluginName ) const;
+
+ signals:
+ /**
+ * Emits true when at least one embedded KCM is changed, or the plugin
+ * selection was changed.
+ * Emits false when the configuration is back to what it was.
+ */
+ void changed( bool );
+
+ /**
+ * Emitted after the config of an embedded KCM has been saved. The
+ * argument is the name of the parent component that needs to reload
+ * its config
+ */
+ void configCommitted( const QCString & instanceName );
+
+ protected:
+ /**
+ * Reimplement in your subclass if you have special needs: The standard
+ * implementation looks at the KPluginInfo objects to find the
+ * needed information. But if, for some reason, your program doesn't
+ * work with that here's your chance to get it working.
+ *
+ * @return Whether the plugin is loaded.
+ */
+ virtual bool pluginIsLoaded( const QString & pluginName ) const;
+
+ private slots:
+ /**
+ * Called when a QCheckListItem is checked or unchecked. It calls
+ * checkDependencies on the Plugin and then updateConfigPage.
+ *
+ * @internal
+ */
+ void executed( QListViewItem * );
+
+ /**
+ * Called whenever the visible config page should change (plugin
+ * selection changed, plugin checked changed)
+ *
+ * First it checks for a widget for the plugin - if there is one, great:
+ * show it.
+ * If there is none, check whether there should be one and try to load
+ * the KCM. If there are more than one KCM create a TabWidget and put
+ * all of them inside, else just use the "bare" KCM. If there is no KCM
+ * us the infoPage( NoKCM ). If there should be one but it can't be
+ * loaded us the infoPage( LoadError ).
+ * Depending on whether the currently selected Plugin is checked or not
+ * disable or enable the "page" (which is the TabWidget or the KCM).
+ *
+ * @internal
+ */
+ void updateConfigPage( KPluginInfo * plugininfo, bool checked );
+ void updateConfigPage();
+
+ /**
+ * Whenever an embedded KCM emits the changed signal we count the number
+ * of changed KCMs. If it becomes one we emit changed( true ), if it
+ * becomes zero we emit changed( false ).
+ *
+ * @internal
+ */
+ void clientChanged( bool );
+
+ /**
+ * Called whenever the tabWidget changes. It checks whether this
+ * KPluginSelectionWidget is shown and sets the currentplugininfo
+ * accordingly.
+ */
+ void tabWidgetChanged( QWidget * );
+
+ private:
+ /**
+ * Load a KCM from a KCModuleInfo. If successfull connect changed
+ * signal and return the module. If not, create a label showing "Error",
+ * show the loaderError and return the label.
+ *
+ * @internal
+ */
+ QWidget * insertKCM( QWidget * parent, const KCModuleInfo & );
+
+ /**
+ * Embed the KCMs for the plugin into the widgetstack
+ *
+ * @internal
+ */
+ void embeddPluginKCMs( KPluginInfo *, bool );
+
+ void init( const QValueList<KPluginInfo*> & plugininfos, const QString & );
+ void checkDependencies( const KPluginInfo * );
+
+ struct KPluginSelectionWidgetPrivate;
+ KPluginSelectionWidgetPrivate * d;
+};
+
+// vim: sw=4 sts=4 et
+#endif // KPLUGINSELECTOR_P_H
diff --git a/kutils/kreplace.cpp b/kutils/kreplace.cpp
new file mode 100644
index 000000000..6740c297a
--- /dev/null
+++ b/kutils/kreplace.cpp
@@ -0,0 +1,328 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 <qlabel.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include "kreplace.h"
+#include "kreplacedialog.h"
+#include <qregexp.h>
+
+//#define DEBUG_REPLACE
+#define INDEX_NOMATCH -1
+
+class KReplaceNextDialog : public KDialogBase
+{
+public:
+ KReplaceNextDialog( QWidget *parent );
+ void setLabel( const QString& pattern, const QString& replacement );
+private:
+ QLabel* m_mainLabel;
+};
+
+KReplaceNextDialog::KReplaceNextDialog(QWidget *parent) :
+ KDialogBase(parent, 0, false, // non-modal!
+ i18n("Replace"),
+ User3 | User2 | User1 | Close,
+ User3,
+ false,
+ i18n("&All"), i18n("&Skip"), i18n("Replace"))
+{
+ m_mainLabel = new QLabel( this );
+ setMainWidget( m_mainLabel );
+ resize(minimumSize());
+}
+
+void KReplaceNextDialog::setLabel( const QString& pattern, const QString& replacement )
+{
+ m_mainLabel->setText( i18n("Replace '%1' with '%2'?").arg(pattern).arg(replacement) );
+}
+
+////
+
+KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent) :
+ KFind( pattern, options, parent )
+{
+ m_replacements = 0;
+ m_replacement = replacement;
+}
+
+KReplace::KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget *dlg) :
+ KFind( pattern, options, parent, dlg )
+{
+ m_replacements = 0;
+ m_replacement = replacement;
+}
+
+KReplace::~KReplace()
+{
+ // KFind::~KFind will delete m_dialog
+}
+
+KDialogBase* KReplace::replaceNextDialog( bool create )
+{
+ if ( m_dialog || create )
+ return dialog();
+ return 0L;
+}
+
+KReplaceNextDialog* KReplace::dialog()
+{
+ if ( !m_dialog )
+ {
+ m_dialog = new KReplaceNextDialog( parentWidget() );
+ connect( m_dialog, SIGNAL( user1Clicked() ), this, SLOT( slotReplaceAll() ) );
+ connect( m_dialog, SIGNAL( user2Clicked() ), this, SLOT( slotSkip() ) );
+ connect( m_dialog, SIGNAL( user3Clicked() ), this, SLOT( slotReplace() ) );
+ connect( m_dialog, SIGNAL( finished() ), this, SLOT( slotDialogClosed() ) );
+ }
+ return static_cast<KReplaceNextDialog *>(m_dialog);
+}
+
+void KReplace::displayFinalDialog() const
+{
+ if ( !m_replacements )
+ KMessageBox::information(parentWidget(), i18n("No text was replaced."));
+ else
+ KMessageBox::information(parentWidget(), i18n("1 replacement done.", "%n replacements done.", m_replacements ) );
+}
+
+KFind::Result KReplace::replace()
+{
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "m_index=" << m_index << endl;
+#endif
+ if ( m_index == INDEX_NOMATCH && m_lastResult == Match )
+ {
+ m_lastResult = NoMatch;
+ return NoMatch;
+ }
+
+ do // this loop is only because validateMatch can fail
+ {
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "beginning of loop: m_index=" << m_index << endl;
+#endif
+ // Find the next match.
+ if ( m_options & KReplaceDialog::RegularExpression )
+ m_index = KFind::find(m_text, *m_regExp, m_index, m_options, &m_matchedLength);
+ else
+ m_index = KFind::find(m_text, m_pattern, m_index, m_options, &m_matchedLength);
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "KFind::find returned m_index=" << m_index << endl;
+#endif
+ if ( m_index != -1 )
+ {
+ // Flexibility: the app can add more rules to validate a possible match
+ if ( validateMatch( m_text, m_index, m_matchedLength ) )
+ {
+ if ( m_options & KReplaceDialog::PromptOnReplace )
+ {
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "PromptOnReplace" << endl;
+#endif
+ // Display accurate initial string and replacement string, they can vary
+ QString matchedText = m_text.mid( m_index, m_matchedLength );
+ QString rep = matchedText;
+ KReplace::replace(rep, m_replacement, 0, m_options, m_matchedLength);
+ dialog()->setLabel( matchedText, rep );
+ dialog()->show();
+
+ // Tell the world about the match we found, in case someone wants to
+ // highlight it.
+ emit highlight(m_text, m_index, m_matchedLength);
+
+ m_lastResult = Match;
+ return Match;
+ }
+ else
+ {
+ doReplace(); // this moves on too
+ }
+ }
+ else
+ {
+ // not validated -> move on
+ if (m_options & KFindDialog::FindBackwards)
+ m_index--;
+ else
+ m_index++;
+ }
+ } else
+ m_index = INDEX_NOMATCH; // will exit the loop
+ }
+ while (m_index != INDEX_NOMATCH);
+
+ m_lastResult = NoMatch;
+ return NoMatch;
+}
+
+int KReplace::replace(QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength)
+{
+ int matchedLength;
+
+ index = KFind::find(text, pattern, index, options, &matchedLength);
+ if (index != -1)
+ {
+ *replacedLength = replace(text, replacement, index, options, matchedLength);
+ if (options & KReplaceDialog::FindBackwards)
+ index--;
+ else
+ index += *replacedLength;
+ }
+ return index;
+}
+
+int KReplace::replace(QString &text, const QRegExp &pattern, const QString &replacement, int index, long options, int *replacedLength)
+{
+ int matchedLength;
+
+ index = KFind::find(text, pattern, index, options, &matchedLength);
+ if (index != -1)
+ {
+ *replacedLength = replace(text, replacement, index, options, matchedLength);
+ if (options & KReplaceDialog::FindBackwards)
+ index--;
+ else
+ index += *replacedLength;
+ }
+ return index;
+}
+
+int KReplace::replace(QString &text, const QString &replacement, int index, long options, int length)
+{
+ QString rep = replacement;
+ // Backreferences: replace \0 with the right portion of 'text'
+ if ( options & KReplaceDialog::BackReference )
+ rep.replace( "\\0", text.mid( index, length ) );
+ // Then replace rep into the text
+ text.replace(index, length, rep);
+ return rep.length();
+}
+
+void KReplace::slotReplaceAll()
+{
+ doReplace();
+ m_options &= ~KReplaceDialog::PromptOnReplace;
+ emit optionsChanged();
+ emit findNext();
+}
+
+void KReplace::slotSkip()
+{
+ if (m_options & KReplaceDialog::FindBackwards)
+ m_index--;
+ else
+ m_index++;
+ if ( m_dialogClosed ) {
+ delete m_dialog; // hide it again
+ m_dialog = 0L;
+ } else
+ emit findNext();
+}
+
+void KReplace::slotReplace()
+{
+ doReplace();
+ if ( m_dialogClosed ) {
+ delete m_dialog; // hide it again
+ m_dialog = 0L;
+ } else
+ emit findNext();
+}
+
+void KReplace::doReplace()
+{
+ int replacedLength = KReplace::replace(m_text, m_replacement, m_index, m_options, m_matchedLength);
+
+ // Tell the world about the replacement we made, in case someone wants to
+ // highlight it.
+ emit replace(m_text, m_index, replacedLength, m_matchedLength);
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "after replace() signal: m_index=" << m_index << " replacedLength=" << replacedLength << endl;
+#endif
+ m_replacements++;
+ if (m_options & KReplaceDialog::FindBackwards)
+ m_index--;
+ else {
+ m_index += replacedLength;
+ // when replacing the empty pattern, move on. See also kjs/regexp.cpp for how this should be done for regexps.
+ if ( m_pattern.isEmpty() )
+ ++m_index;
+ }
+#ifdef DEBUG_REPLACE
+ kdDebug() << k_funcinfo << "after adjustement: m_index=" << m_index << endl;
+#endif
+}
+
+void KReplace::resetCounts()
+{
+ KFind::resetCounts();
+ m_replacements = 0;
+}
+
+bool KReplace::shouldRestart( bool forceAsking, bool showNumMatches ) const
+{
+ // Only ask if we did a "find from cursor", otherwise it's pointless.
+ // ... Or if the prompt-on-replace option was set.
+ // Well, unless the user can modify the document during a search operation,
+ // hence the force boolean.
+ if ( !forceAsking && (m_options & KFindDialog::FromCursor) == 0
+ && (m_options & KReplaceDialog::PromptOnReplace) == 0 )
+ {
+ displayFinalDialog();
+ return false;
+ }
+ QString message;
+ if ( showNumMatches )
+ {
+ if ( !m_replacements )
+ message = i18n("No text was replaced.");
+ else
+ message = i18n("1 replacement done.", "%n replacements done.", m_replacements );
+ }
+ else
+ {
+ if ( m_options & KFindDialog::FindBackwards )
+ message = i18n( "Beginning of document reached." );
+ else
+ message = i18n( "End of document reached." );
+ }
+
+ message += "\n";
+ // Hope this word puzzle is ok, it's a different sentence
+ message +=
+ ( m_options & KFindDialog::FindBackwards ) ?
+ i18n("Do you want to restart search from the end?")
+ : i18n("Do you want to restart search at the beginning?");
+
+ int ret = KMessageBox::questionYesNo( parentWidget(), message, QString::null, i18n("Restart"), i18n("Stop") );
+ return( ret == KMessageBox::Yes );
+}
+
+void KReplace::closeReplaceNextDialog()
+{
+ closeFindNextDialog();
+}
+
+#include "kreplace.moc"
diff --git a/kutils/kreplace.h b/kutils/kreplace.h
new file mode 100644
index 000000000..2a174af23
--- /dev/null
+++ b/kutils/kreplace.h
@@ -0,0 +1,269 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 KREPLACE_H
+#define KREPLACE_H
+
+#include "kfind.h"
+
+class KReplaceNextDialog;
+
+/**
+ * @ingroup main
+ * @ingroup findreplace
+ * @brief A generic implementation of the "replace" function.
+ *
+ * @author S.R.Haque <srhaque@iee.org>, David Faure <faure@kde.org>
+ *
+ * \b Detail:
+ *
+ * This class includes prompt handling etc. Also provides some
+ * static functions which can be used to create custom behavior
+ * instead of using the class directly.
+ *
+ * \b Example:
+ *
+ * To use the class to implement a complete replace feature:
+ *
+ * In the slot connect to the replace action, after using KReplaceDialog:
+ * \code
+ *
+ * // This creates a replace-on-prompt dialog if needed.
+ * m_replace = new KReplace(pattern, replacement, options, this);
+ *
+ * // Connect signals to code which handles highlighting
+ * // of found text, and on-the-fly replacement.
+ * connect( m_replace, SIGNAL( highlight( const QString &, int, int ) ),
+ * this, SLOT( slotHighlight( const QString &, int, int ) ) );
+ * // Connect findNext signal - called when pressing the button in the dialog
+ * connect( m_replace, SIGNAL( findNext() ),
+ * this, SLOT( slotReplaceNext() ) );
+ * // Connect replace signal - called when doing a replacement
+ * connect( m_replace, SIGNAL( replace(const QString &, int, int, int) ),
+ * this, SLOT( slotReplace(const QString &, int, int, int) ) );
+ * \endcode
+ * Then initialize the variables determining the "current position"
+ * (to the cursor, if the option FromCursor is set,
+ * to the beginning of the selection if the option SelectedText is set,
+ * and to the beginning of the document otherwise).
+ * Initialize the "end of search" variables as well (end of doc or end of selection).
+ * Swap begin and end if FindBackwards.
+ * Finally, call slotReplaceNext();
+ *
+ * \code
+ * void slotReplaceNext()
+ * {
+ * KFind::Result res = KFind::NoMatch;
+ * while ( res == KFind::NoMatch && <position not at end> ) {
+ * if ( m_replace->needData() )
+ * m_replace->setData( <current text fragment> );
+ *
+ * // Let KReplace inspect the text fragment, and display a dialog if a match is found
+ * res = m_replace->replace();
+ *
+ * if ( res == KFind::NoMatch ) {
+ * <Move to the next text fragment, honoring the FindBackwards setting for the direction>
+ * }
+ * }
+ *
+ * if ( res == KFind::NoMatch ) // i.e. at end
+ * <Call either m_replace->displayFinalDialog(); delete m_replace; m_replace = 0L;
+ * or if ( m_replace->shouldRestart() ) { reinit (w/o FromCursor) and call slotReplaceNext(); }
+ * else { m_replace->closeReplaceNextDialog(); }>
+ * }
+ * \endcode
+ *
+ * Don't forget delete m_find in the destructor of your class,
+ * unless you gave it a parent widget on construction.
+ *
+ */
+class KUTILS_EXPORT KReplace :
+ public KFind
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Only use this constructor if you don't use KFindDialog, or if
+ * you use it as a modal dialog.
+ * @param pattern The pattern to look for.
+ * @param replacement The replacement string.
+ * @param options Options for the find dialog. @see KFindDialog and KReplaceDialog.
+ * @param parent The parent widget.
+ */
+ KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent = 0);
+ /**
+ * This is the recommended constructor if you also use KReplaceDialog (non-modal).
+ * You should pass the pointer to it here, so that when a message box
+ * appears it has the right parent. Don't worry about deletion, KReplace
+ * will notice if the find dialog is closed.
+ * @param pattern The pattern to look for.
+ * @param replacement The replacement string.
+ * @param options Options for the find dialog. @see KFindDialog and KReplaceDialog.
+ * @param parent The parent widget.
+ * @param replaceDialog A pointer to the KReplaceDialog object.
+ */
+ KReplace(const QString &pattern, const QString &replacement, long options, QWidget *parent, QWidget* replaceDialog);
+
+ /**
+ * Destructor.
+ */
+ virtual ~KReplace();
+
+ /**
+ * Return the number of replacements made (i.e. the number of times
+ * the replace signal was emitted).
+ * Can be used in a dialog box to tell the user how many replacements were made.
+ * The final dialog does so already, unless you used setDisplayFinalDialog(false).
+ * @return The number of replacements.
+ */
+ int numReplacements() const { return m_replacements; }
+
+ /**
+ * Call this to reset the numMatches & numReplacements counts.
+ * Can be useful if reusing the same KReplace for different operations,
+ * or when restarting from the beginning of the document.
+ */
+ virtual void resetCounts();
+
+ /**
+ * Walk the text fragment (e.g. kwrite line, kspread cell) looking for matches.
+ * For each match, if prompt-on-replace is specified, emits the highlight() signal
+ * and displays the prompt-for-replace dialog before doing the replace.
+ * @return Whether or not there has been a match.
+ */
+ Result replace();
+
+ /**
+ * Return (or create) the dialog that shows the "find next?" prompt.
+ * Usually you don't need to call this.
+ * One case where it can be useful, is when the user selects the "Find"
+ * menu item while a find operation is under way. In that case, the
+ * program may want to call setActiveWindow() on that dialog.
+ * @return The replace next dialog.
+ */
+ KDialogBase* replaceNextDialog( bool create = false );
+
+ /**
+ * Close the "replace next?" dialog. The application should do this when
+ * the last match was hit. If the application deletes the KReplace, then
+ * "find previous" won't be possible anymore.
+ */
+ void closeReplaceNextDialog();
+
+ /**
+ * Searches the given string, replaces with the given replacement string,
+ * and returns whether a match was found. If one is,
+ * the replacement string length is also returned.
+ *
+ * A performance optimised version of the function is provided for use
+ * with regular expressions.
+ *
+ * @param text The string to search.
+ * @param pattern The pattern to look for.
+ * @param replacement The replacement string to insert into the text.
+ * @param index The starting index into the string.
+ * @param options The options to use.
+ * @param replacedLength Output parameter, contains the length of the replaced string.
+ * Not always the same as replacement.length(), when backreferences are used.
+ * @return The index at which a match was found, or -1 if no match was found.
+ */
+ static int replace( QString &text, const QString &pattern, const QString &replacement, int index, long options, int *replacedLength );
+
+ /**
+ * Searches the given regular expression, replaces with the given replacement string,
+ * and returns whether a match was found. If one is,
+ * the replacement string length is also returned.
+ *
+ * Another version of the function is provided for use with strings.
+ *
+ * @param text The string to search.
+ * @param pattern The regular expression pattern to look for.
+ * @param replacement The replacement string to insert into the text.
+ * @param index The starting index into the string.
+ * @param options The options to use.
+ * @param replacedLength Output parameter, contains the length of the replaced string.
+ * Not always the same as replacement.length(), when backreferences are used.
+ * @return The index at which a match was found, or -1 if no match was found.
+ */
+ static int replace( QString &text, const QRegExp &pattern, const QString &replacement, int index, long options, int *replacedLength );
+
+ /**
+ * Returns @c true if we should restart the search from scratch.
+ * Can ask the user, or return @c false (if we already searched/replaced the
+ * whole document without the PromptOnReplace option).
+ *
+ * @param forceAsking set to @c true if the user modified the document during the
+ * search. In that case it makes sense to restart the search again.
+ *
+ * @param showNumMatches set to @c true if the dialog should show the number of
+ * matches. Set to @c false if the application provides a "find previous" action,
+ * in which case the match count will be erroneous when hitting the end,
+ * and we could even be hitting the beginning of the document (so not all
+ * matches have even been seen).
+ *
+ * @return @c true, if the search should be restarted.
+ */
+ virtual bool shouldRestart( bool forceAsking = false, bool showNumMatches = true ) const;
+
+ /**
+ * Displays the final dialog telling the user how many replacements were made.
+ * Call either this or shouldRestart().
+ */
+ virtual void displayFinalDialog() const;
+
+signals:
+
+ /**
+ * Connect to this slot to implement updating of replaced text during the replace
+ * operation.
+ *
+ * Extra care must be taken to properly implement the "no prompt-on-replace" case.
+ * For instance highlight isn't emitted in that case (some code might rely on it),
+ * and for performance reasons one should repaint after replace() ONLY if
+ * prompt-on-replace was selected.
+ *
+ * @param text The text, in which the replacement has already been done.
+ * @param replacementIndex Starting index of the matched substring
+ * @param replacedLength Length of the replacement string
+ * @param matchedLength Length of the matched string
+ */
+ void replace(const QString &text, int replacementIndex, int replacedLength, int matchedLength);
+
+protected slots:
+
+ void slotSkip();
+ void slotReplace();
+ void slotReplaceAll();
+
+private:
+ KReplaceNextDialog* dialog();
+ void doReplace();
+ static int replace( QString &text, const QString &replacement, int index, long options, int length );
+
+ QString m_replacement;
+ unsigned m_replacements;
+
+ // Binary compatible extensibility.
+ class KReplacePrivate;
+ KReplacePrivate *d;
+};
+#endif
diff --git a/kutils/kreplacedialog.cpp b/kutils/kreplacedialog.cpp
new file mode 100644
index 000000000..80a36e5df
--- /dev/null
+++ b/kutils/kreplacedialog.cpp
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 "kreplacedialog.h"
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qregexp.h>
+#include <kcombobox.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kdebug.h>
+
+/**
+ * we need to insert the strings after the dialog is set
+ * up, otherwise QComboBox will deliver an aweful big sizeHint
+ * for long replacement texts.
+ */
+class KReplaceDialog::KReplaceDialogPrivate {
+ public:
+ KReplaceDialogPrivate() : m_initialShowDone(false) {}
+ QStringList replaceStrings;
+ bool m_initialShowDone;
+};
+
+KReplaceDialog::KReplaceDialog(QWidget *parent, const char *name, long options, const QStringList &findStrings, const QStringList &replaceStrings, bool hasSelection) :
+ KFindDialog(parent, name, true)
+{
+ d = new KReplaceDialogPrivate;
+ d->replaceStrings = replaceStrings;
+ init(true, findStrings, hasSelection);
+ setOptions(options);
+}
+
+KReplaceDialog::~KReplaceDialog()
+{
+ delete d;
+}
+
+void KReplaceDialog::showEvent( QShowEvent *e )
+{
+ if ( !d->m_initialShowDone )
+ {
+ d->m_initialShowDone = true; // only once
+
+ if (!d->replaceStrings.isEmpty())
+ {
+ setReplacementHistory(d->replaceStrings);
+ m_replace->lineEdit()->setText( d->replaceStrings[0] );
+ }
+ }
+
+ KFindDialog::showEvent(e);
+}
+
+long KReplaceDialog::options() const
+{
+ long options = 0;
+
+ options = KFindDialog::options();
+ if (m_promptOnReplace->isChecked())
+ options |= PromptOnReplace;
+ if (m_backRef->isChecked())
+ options |= BackReference;
+ return options;
+}
+
+QWidget *KReplaceDialog::replaceExtension()
+{
+ if (!m_replaceExtension)
+ {
+ m_replaceExtension = new QWidget(m_replaceGrp);
+ m_replaceLayout->addMultiCellWidget(m_replaceExtension, 3, 3, 0, 1);
+ }
+
+ return m_replaceExtension;
+}
+
+QString KReplaceDialog::replacement() const
+{
+ return m_replace->currentText();
+}
+
+QStringList KReplaceDialog::replacementHistory() const
+{
+ QStringList lst = m_replace->historyItems();
+ // historyItems() doesn't tell us about the case of replacing with an empty string
+ if ( m_replace->lineEdit()->text().isEmpty() )
+ lst.prepend( QString::null );
+ return lst;
+}
+
+void KReplaceDialog::setOptions(long options)
+{
+ KFindDialog::setOptions(options);
+ m_promptOnReplace->setChecked(options & PromptOnReplace);
+ m_backRef->setChecked(options & BackReference);
+}
+
+void KReplaceDialog::setReplacementHistory(const QStringList &strings)
+{
+ if (strings.count() > 0)
+ m_replace->setHistoryItems(strings, true);
+ else
+ m_replace->clearHistory();
+}
+
+void KReplaceDialog::slotOk()
+{
+ // If regex and backrefs are enabled, do a sanity check.
+ if ( m_regExp->isChecked() && m_backRef->isChecked() )
+ {
+ QRegExp r ( pattern() );
+ int caps = r.numCaptures();
+ QRegExp check(QString("((?:\\\\)+)(\\d+)"));
+ int p = 0;
+ QString rep = replacement();
+ while ( (p = check.search( rep, p ) ) > -1 )
+ {
+ if ( check.cap(1).length()%2 && check.cap(2).toInt() > caps )
+ {
+ KMessageBox::information( this, i18n(
+ "Your replacement string is referencing a capture greater than '\\%1', ").arg( caps ) +
+ ( caps ?
+ i18n("but your pattern only defines 1 capture.",
+ "but your pattern only defines %n captures.", caps ) :
+ i18n("but your pattern defines no captures.") ) +
+ i18n("\nPlease correct.") );
+ return; // abort OKing
+ }
+ p += check.matchedLength();
+ }
+
+ }
+
+ KFindDialog::slotOk();
+ m_replace->addToHistory(replacement());
+}
+
+// kate: space-indent on; indent-width 4; replace-tabs on;
+#include "kreplacedialog.moc"
diff --git a/kutils/kreplacedialog.h b/kutils/kreplacedialog.h
new file mode 100644
index 000000000..459c0c5ae
--- /dev/null
+++ b/kutils/kreplacedialog.h
@@ -0,0 +1,160 @@
+/*
+ Copyright (C) 2001, S.R.Haque <srhaque@iee.org>.
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 KREPLACEDIALOG_H
+#define KREPLACEDIALOG_H
+
+#include "kfinddialog.h"
+
+class KHistoryCombo;
+class QCheckBox;
+class QGroupBox;
+class QLabel;
+class QPopupMenu;
+class QPushButton;
+class QRect;
+
+/**
+ * @ingroup main
+ * @ingroup findreplace
+ * @short A generic "replace" dialog.
+ *
+ * @author S.R.Haque <srhaque@iee.org>
+ *
+ * \b Detail:
+ *
+ * This widget inherits from KFindDialog and implements
+ * the following additional functionalities: a replacement string
+ * object and an area for a user-defined widget to extend the dialog.
+ *
+ * \b Example:
+ *
+ * To use the basic replace dialog:
+ *
+ * \code
+ * \endcode
+ *
+ * To use your own extensions:
+ *
+ * \code
+ * \endcode
+ */
+class KUTILS_EXPORT KReplaceDialog:
+ public KFindDialog
+{
+ Q_OBJECT
+
+public:
+
+ /**
+ * Options.
+ */
+ enum Options
+ {
+ PromptOnReplace = 256, ///< Should the user be prompted before the replace operation?
+ BackReference = 512
+ };
+
+ /**
+ * Construct a replace dialog.read-only or rather select-only combo box with a
+ * parent object and a name.
+ *
+ * @param parent The parent object of this widget
+ * @param name The name of this widget
+ * @param options A bitfield of the Options to be enabled.
+ * @param findStrings A QStringList to insert in the combo box of text to find
+ * @param replaceStrings A QStringList to insert in the combo box of text to
+ * replace with
+ * @param hasSelection Whether a selection exists
+ */
+ KReplaceDialog( QWidget *parent = 0, const char *name = 0, long options = 0,
+ const QStringList &findStrings = QStringList(),
+ const QStringList &replaceStrings = QStringList(),
+ bool hasSelection = true );
+
+ /**
+ * Destructor.
+ */
+ virtual ~KReplaceDialog();
+
+ /**
+ * Provide the list of @p strings to be displayed as the history
+ * of replacement strings. @p strings might get truncated if it is
+ * too long.
+ *
+ * @param history The replacement history.
+ * @see replacementHistory
+ */
+ void setReplacementHistory( const QStringList &history );
+
+ /**
+ * Returns the list of history items.
+ *
+ * @return The replacement history.
+ * @see setReplacementHistory
+ */
+ QStringList replacementHistory() const;
+
+ /**
+ * Set the options which are enabled.
+ *
+ * @param options The setting of the Options.
+ * @see Options, KFindDialog::Options
+ */
+ void setOptions( long options );
+
+ /**
+ * Returns the state of the options. Disabled options may be returned in
+ * an indeterminate state.
+ *
+ * @return The options.
+ * @see setOptions, Options, KFindDialog::Options
+ */
+ long options() const;
+
+ /**
+ * Returns the replacement string.
+ * @return The replacement string.
+ */
+ QString replacement() const;
+
+ /**
+ * Returns an empty widget which the user may fill with additional UI
+ * elements as required. The widget occupies the width of the dialog,
+ * and is positioned immediately the regular expression support widgets
+ * for the replacement string.
+ * @return An extensible QWidget.
+ */
+ QWidget *replaceExtension();
+
+protected slots:
+
+ void slotOk();
+ virtual void showEvent ( QShowEvent * );
+
+private:
+
+ // Binary compatible extensibility.
+ class KReplaceDialogPrivate;
+ KReplaceDialogPrivate *d;
+};
+
+
+#endif // KREPLACEDIALOG_H
diff --git a/kutils/ksettings/Makefile.am b/kutils/ksettings/Makefile.am
new file mode 100644
index 000000000..39e7183b2
--- /dev/null
+++ b/kutils/ksettings/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES=-I$(srcdir)/.. $(all_includes)
+noinst_LTLIBRARIES = libksettings.la
+
+libksettings_la_SOURCES = dispatcher.cpp dialog.cpp pluginpage.cpp componentsdialog.cpp
+
+ksettingsincludedir = $(includedir)/ksettings
+ksettingsinclude_HEADERS = dispatcher.h dialog.h pluginpage.h componentsdialog.h
+
+METASOURCES = AUTO
diff --git a/kutils/ksettings/README.dox b/kutils/ksettings/README.dox
new file mode 100644
index 000000000..0660324c1
--- /dev/null
+++ b/kutils/ksettings/README.dox
@@ -0,0 +1,276 @@
+/**
+
+\namespace KSettings
+
+\short A collection of classes to create configuration dialogs that work over
+component boundaries
+
+<h2>How to use KSettings::Dialog in your application.</h2>
+
+<hr>
+<h3>1. Open the dialog from your app</h3>
+
+All you need to do is instanciate KSettings::Dialog and show() it. I recommend
+the following:
+
+create the 'Configure MyApp' StdAction like this:
+\code
+KStdAction::preferences( this, SLOT( showConfigDialog() ), actionCollection );
+\endcode
+
+and the slot looks like this:
+\code
+if( m_dlg == 0 )
+ m_dlg = new KSettings::Dialog( this );
+m_dlg->show();
+\endcode
+
+Of course you need to have the 'KSettings::Dialog * m_dlg' member var and
+initialize it to 0 in the ctor.
+
+If your application uses KParts that don't set 'X-KDE-ParentApp=&lt;the instance
+name of your application&gt;' then you need to use the second ctor of
+KSettings::Dialog:
+\code
+m_dlg = new KSettings::Dialog( QStringList::split( ';', "component1;component2" ) );
+\endcode
+
+The KSettings::Dialog object will be destructed automatically by the QObject
+mechanisms.
+
+
+<hr>
+<h3>2. Create pages for your dialog</h3>
+
+Every page is a KCM. This is what you need for creating a page:
+
+\code
+class MyAppConfig : public KCModule
+{
+ Q_OBJECT
+public:
+ MyAppConfig( QWidget *parent, const char *name = 0, const QStringList &args =
+ QStringList() );
+ ~MyAppConfig();
+
+ void load();
+ void save();
+ void defaults();
+}
+\endcode
+
+and in the cpp file:
+
+\code
+typedef KGenericFactory<MyAppConfig, QWidget> MyAppConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_myappconfig, MyAppConfigFactory(
+ "kcm_myappconfig" ) );
+
+MyAppConfig::MyAppConfig( QWidget *parent, const char *, const QStringList &args )
+ : KCModule( MyAppConfigFactory::instance(), parent, args )
+{
+ // create the pages GUI
+ load();
+}
+
+// implementations for the other methods
+\endcode
+
+For the KConfig object you can either use
+KGlobal::config() (I don't recommend it) or KSimpleConfig( "myapprc" ).
+I added a method to KSettings::Dispatcher that gives you the KConfig
+object for every registered instance name: \ref KSettings::Dispatcher::configForInstanceName
+
+
+<hr>
+<h3>3. The .desktop file for the page</h3>
+
+The .desktop file holds all the information for the dialog to find the page and
+insert it at the right place (with the right icon, name and comment).
+
+An example file:
+\verbatim
+[Desktop Entry]
+Encoding=UTF-8
+Icon=myapp
+Type=Service
+ServiceTypes=KCModule
+
+X-KDE-ModuleType=Library
+X-KDE-Library=myappconfig
+X-KDE-FactoryName=MyAppConfigFactory
+X-KDE-ParentApp=myapp
+X-KDE-ParentComponents=myapp
+X-KDE-Weight=10
+
+Name=General
+Comment=General configuration of my app
+\endverbatim
+
+
+Some explanation for those keys:
+- You just keep 'Encoding', 'Type', 'ServiceTypes' and 'X-KDE-ModuleType' like
+ in the example. For very special needs you might add another ServiceType to
+ the list...
+- Icon is the icon that will be used in the listview/iconview for your page.
+- X-KDE-Library is the name of the library where the page is in. The library
+ always needs to be prefixed with kcm_ but you don't write the prefix in the
+ desktop file. For more docu on this look for the KCModule docu.
+- X-KDE-FactoryName is either the name of the Factory you used in the
+ KGenericFactory call or the suffix of the create_ function that you created.
+ Again for more info look for the KCModule docu.
+- X-KDE-ParentApp is the name of the application this config page belongs to. It
+ is used by the first two \ref KSettings::Dialog constructors. The Dialog will
+ use all modules that set X-KDE-ParentApp to
+ KGlobal::instance()->instanceName(). It
+ should be pretty easy to find out what name that is: look at the first
+ argument to the KAboutData ctor.
+- X-KDE-ParentComponents is a list of the components (plugin/KPart/whatever)
+ this config page belongs to. Normally there is only one component.
+ It is used for two things:
+ -# If you use KSettings::Dispatcher the dispatcher will notify all components
+ in this list after the save() method of your KCM has been called. The
+ components then can reload the configuration and apply the changes the user
+ did to the config.
+ -# If your component is used by another application (that is not =
+ X-KDE-ParentApp) then it may add the name of the component to the ctor of
+ KSettings::Dialog and the dialog will automatically include all config
+ pages that have the components name in their ParentComponents list.
+- X-KDE-Weight sets the order for the modules to be inserted into the dialog.
+ The higher the number (heavier) the lower the module will appear in the list.
+ (the default value is 100)
+- Name is the string that is shown in the listview/iconview right below the
+ icon.
+- Comment is the string that is shown on top of the config page for a short
+ description what you can do on this page.
+
+
+<hr>
+<h3>4. The .setdlg file for hierarchical (TreeList) page layouts</h3>
+
+If your config dialog should show a tree of pages in the config dialog you need
+to define that hierarchy with a .setdlg file.
+
+The file should be installed in apps/&lt;appname&gt;/&lt;appname&gt;.setdlg. If third party
+plugins need to merge in they will install their file to
+apps/&lt;appname&gt;/ksettingsdialog/&lt;pluginname&gt;.setdlg.
+
+A .setdlg file contains one or more blocks like the following:
+
+\verbatim
+[id]
+Name=
+Comment=
+Icon=
+Weight=
+Parent=
+\endverbatim
+
+- The group name (id) is the name you use in the .desktop file of the page:
+ If your page's .desktop file says "X-KDE-CfgDlgHierarchy=id" then it will be
+ inserted as a child of this entry.
+- \p Name: The name of the section. It will appear in the listview.
+- \p Comment: A description of what the modules in this section are. It will
+ appear in the place where the KCMs are placed when the user clicks on the item
+ in the listview.
+- \p Icon: An icon for the item.
+- \p Weight: Defines the position in the listview. See X-KDE-Weight above.
+- \p Parent: If this group should be a child of another group write the parent's
+ group id here.
+
+<hr>
+<h3>5. The Pluginselector</h3>
+
+There are two ways to use the KPluginSelector widget. One is to use the class
+directly and the second to use KSettings::PluginPage as baseclass for a config
+page that shows the KPluginSelector widget.
+
+I'll cover the second usage here and the calls to addPlugins are just the same
+for the first.
+
+To create a plugin page you need the following code:
+
+\code
+typedef KGenericFactory<MyAppPluginConfig, QWidget> MyAppPluginConfigFactory;
+K_EXPORT_COMPONENT_FACTORY( kcm_myapppluginconfig, MyAppPluginConfigFactory( "kcm_myapppluginconfig" ) );
+
+MyAppPluginConfig( QWidget * parent, const char *, const QStringList & args )
+ : PluginPage( MyAppPluginConfigFactory::instance(), parent, args )
+{
+ pluginSelector()->addPlugins( ... );
+ pluginSelector()->addPlugins( ... );
+ .
+ .
+ .
+}
+\endcode
+
+pluginSelector() returns a pointer to the KPluginSelector widget of the page.
+There are three addPlugins methods available, two for adding KParts plugins and
+one for the rest.
+
+
+<hr>
+<h3>6. The .desktop files of plugin config pages</h3>
+
+this is the entry for the Makefile.am:
+
+\verbatim
+myappconfigpagedir = $(kde_servicesdir)/<appname>
+myappconfigpage_DATA = myappconfigpage.desktop
+\endverbatim
+
+
+And this is what the .desktop file looks like:
+
+\verbatim
+[Desktop Entry]
+Encoding=UTF-8
+Type=Service
+Icon=<iconname>
+ServiceTypes=KPluginInfo
+
+Name=MyPlugin
+Comment=My plugin is cool and does foo and bar.
+
+X-KDE-PluginInfo-Name=myplugin
+
+X-KDE-PluginInfo-Author=<your name>
+X-KDE-PluginInfo-Email=<your email>
+X-KDE-PluginInfo-Website=http://www.myplugin.org/
+X-KDE-PluginInfo-Category=CoolPlugins
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=true
+X-KDE-PluginInfo-Depends=myotherplugin
+X-KDE-CfgDlgHierarchy=GroupID
+\endverbatim
+
+Explanation:
+mandatory entries:
+- leave \p Type and \p Encoding like in the example
+- \p Name
+- \p Comment
+- \p X-KDE-PluginInfo-Name is the "internal name" of the plugin.
+- You need to have \p KPluginInfo in \p ServiceTypes but of course you may have more
+ entries in there.
+
+optional entries:
+- \p Icon is the icon used for your plugin (it's shown in the pluginselector if you
+ set one).
+- \p X-KDE-PluginInfo-Author and \p X-KDE-PluginInfo-Email is some information about the author of the plugin.
+- \p X-KDE-PluginInfo-Website is the address for a webpage for this plugin.
+- \p X-KDE-PluginInfo-Category is used if your application has different categories of plugins.
+- \p X-KDE-PluginInfo-Version is the version of this plugin.
+- \p X-KDE-PluginInfo-License is the license of this plugin.
+- \p X-KDE-PluginInfo-EnabledByDefault tells the program whether the plugin
+ should be enabled on first startup or not.
+- \p X-KDE-PluginInfo-Depends can be used to tell the application that you need to have
+ myotherplugin enabled for your plugin to work.
+- \p X-KDE-CfgDlgHierarchy is used if you use a \p KSettings::Dialog::ConfigurableInline
+ KSettings::Dialog to put the plugin checkbox into the group with the GroupID
+ you set here.
+
+If you have questions contact Matthias Kretz <kretz@kde.org>.
+*/
+// vim: tw=80
diff --git a/kutils/ksettings/TODO b/kutils/ksettings/TODO
new file mode 100644
index 000000000..9950ddfd9
--- /dev/null
+++ b/kutils/ksettings/TODO
@@ -0,0 +1,13 @@
+- KPluginSelct.. listview should only show the name and comment, the rest is
+ shown in a tooltip (I don't know how to get the tooltip working in the
+ listview.)
+- Handle unsaved changes in KCMs that are hidden if the user deselects some
+ plugin KCMs (in KSettings::Dialog::Configurable mode). Ideas:
+ - don't allow changes to the plugin selection if there are unsaved changes in
+ the main dlg ("You have unsaved changes in the configuration dialog, please
+ save them first before changing the components selection.")
+ - automatically save changes of hidden modules when the dialog is deleted
+ - ask as soon as KCMultiDialog can detect that there are unsaved changes to
+ hidden KCMs (it might even be possible to show the KCMs for that)
+
+# vim: tw=80
diff --git a/kutils/ksettings/componentsdialog.cpp b/kutils/ksettings/componentsdialog.cpp
new file mode 100644
index 000000000..5093af985
--- /dev/null
+++ b/kutils/ksettings/componentsdialog.cpp
@@ -0,0 +1,180 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/componentsdialog.h"
+#include <klocale.h>
+#include <qlayout.h>
+#include <klistview.h>
+#include <qlabel.h>
+#include <qheader.h>
+#include <kplugininfo.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kseparator.h>
+
+namespace KSettings
+{
+
+class ComponentsDialog::ComponentsDialogPrivate
+{
+ public:
+ KListView * listview;
+ QFrame * infowidget;
+ QLabel * iconwidget;
+ QLabel * commentwidget;
+ QLabel * descriptionwidget;
+ QMap<QCheckListItem*, KPluginInfo*> plugininfomap;
+ QValueList<KPluginInfo*> plugininfolist;
+};
+
+ComponentsDialog::ComponentsDialog( QWidget * parent, const char * name )
+ : KDialogBase( parent, name, false, i18n( "Select Components" ) )
+, d( new ComponentsDialogPrivate )
+{
+ QWidget * page = new QWidget( this );
+ setMainWidget( page );
+ ( new QHBoxLayout( page, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->listview = new KListView( page );
+ d->listview->setMinimumSize( 200, 200 );
+ d->infowidget = new QFrame( page );
+ d->infowidget->setMinimumSize( 200, 200 );
+ ( new QVBoxLayout( d->infowidget, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->iconwidget = new QLabel( d->infowidget );
+ ( void )new KSeparator( d->infowidget );
+ d->commentwidget = new QLabel( d->infowidget );
+ d->commentwidget->setAlignment( Qt::WordBreak );
+ d->descriptionwidget = new QLabel( d->infowidget );
+ d->descriptionwidget->setAlignment( Qt::WordBreak );
+
+ d->listview->addColumn( QString::null );
+ d->listview->header()->hide();
+ d->listview->setRootIsDecorated( true );
+ d->listview->setSorting( -1 );
+ d->listview->setAcceptDrops( false );
+ d->listview->setSelectionModeExt( KListView::Single );
+ d->listview->setAllColumnsShowFocus( true );
+
+ connect( d->listview, SIGNAL( pressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( spacePressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( returnPressed( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+ connect( d->listview, SIGNAL( selectionChanged( QListViewItem * ) ), this,
+ SLOT( executed( QListViewItem * ) ) );
+}
+
+ComponentsDialog::~ComponentsDialog()
+{
+}
+
+void ComponentsDialog::addPluginInfo( KPluginInfo * info )
+{
+ d->plugininfolist.append( info );
+}
+
+void ComponentsDialog::setPluginInfos( const QMap<QString, KPluginInfo*> &
+ plugininfos )
+{
+ for( QMap<QString, KPluginInfo*>::ConstIterator it = plugininfos.begin();
+ it != plugininfos.end(); ++it )
+ {
+ d->plugininfolist.append( it.data() );
+ }
+}
+
+void ComponentsDialog::setPluginInfos( const QValueList<KPluginInfo *> &plugins )
+{
+ d->plugininfolist = plugins;
+}
+
+void ComponentsDialog::show()
+{
+ // clear the treelist
+ d->listview->clear();
+ d->plugininfomap.clear();
+
+ // construct the treelist
+ for( QValueList<KPluginInfo*>::ConstIterator it = d->plugininfolist.begin();
+ it != d->plugininfolist.end(); ++it )
+ {
+ ( *it )->load();
+ QCheckListItem * item = new QCheckListItem( d->listview, ( *it )->name(),
+ QCheckListItem::CheckBox );
+ if( ! ( *it )->icon().isEmpty() )
+ item->setPixmap( 0, SmallIcon( ( *it )->icon(), IconSize( KIcon::Small ) ) );
+ item->setOn( ( *it )->isPluginEnabled() );
+ d->plugininfomap[ item ] = ( *it );
+ }
+ KDialogBase::show();
+}
+
+void ComponentsDialog::executed( QListViewItem * item )
+{
+ kdDebug( 704 ) << k_funcinfo << endl;
+ if( item == 0 )
+ return;
+ if( item->rtti() != 1 ) // check for QCheckListItem
+ return;
+
+ QCheckListItem * citem = static_cast<QCheckListItem *>( item );
+ bool checked = citem->isOn();
+
+ kdDebug( 704 ) << "it's a " << ( checked ? "checked" : "unchecked" )
+ << " QCheckListItem" << endl;
+
+ KPluginInfo * info = d->plugininfomap[ citem ];
+ info->setPluginEnabled( checked );
+ //checkDependencies( info );
+ // show info about the component on the right
+ d->iconwidget->setPixmap( SmallIcon( info->icon(), KIcon::SizeLarge ) );
+ d->commentwidget->setText( info->comment() );
+ //d->descriptionwidget->setText( info->description() );
+}
+
+void ComponentsDialog::savePluginInfos()
+{
+ for( QValueList<KPluginInfo*>::ConstIterator it = d->plugininfolist.begin();
+ it != d->plugininfolist.end(); ++it )
+ {
+ if( ( *it )->config() )
+ {
+ ( *it )->save();
+ ( *it )->config()->sync();
+ }
+ }
+}
+
+void ComponentsDialog::slotOk()
+{
+ savePluginInfos();
+ KDialogBase::slotOk();
+}
+
+void ComponentsDialog::slotApply()
+{
+ savePluginInfos();
+ KDialogBase::slotApply();
+}
+
+} //namespace
+
+#include "componentsdialog.moc"
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/componentsdialog.h b/kutils/ksettings/componentsdialog.h
new file mode 100644
index 000000000..e5325f707
--- /dev/null
+++ b/kutils/ksettings/componentsdialog.h
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_COMPONENTSDIALOG_H
+#define KSETTINGS_COMPONENTSDIALOG_H
+
+#include <kdialogbase.h>
+
+class QString;
+class KPluginInfo;
+
+namespace KSettings
+{
+
+/**
+ @ingroup plugin
+ @ingroup settings
+ Dialog for selecting which plugins should be active for an application. Set
+ the list of available plugins with \ref setPluginInfos. The dialog will save the
+ configuration on clicking ok or apply to the applications config file. Connect
+ to the okClicked() and applyClicked() signals to be notified about
+ configuration changes.
+*/
+class KUTILS_EXPORT ComponentsDialog : public KDialogBase
+{
+ Q_OBJECT
+ public:
+ /**
+ Create Dialog.
+
+ @param parent parent widget
+ @param name name
+ */
+ ComponentsDialog( QWidget * parent = 0, const char * name = 0 );
+ ~ComponentsDialog();
+
+ /**
+ Add a plugin that the dialog offers for selection.
+ */
+ void addPluginInfo( KPluginInfo * );
+ /**
+ Set list of plugins the dialog offers for selection. (Overwrites a previous list)
+ */
+ void setPluginInfos( const QMap<QString, KPluginInfo*> & plugininfos );
+ /**
+ Set list of plugins the dialog offers for selection. (Overwrites a previous list)
+ */
+ void setPluginInfos( const QValueList<KPluginInfo *> &plugins );
+
+ /**
+ * reimplemented
+ */
+ void show();
+
+ protected slots:
+ void slotOk();
+ void slotApply();
+
+ private slots:
+ void executed( QListViewItem * );
+
+ private:
+ void savePluginInfos();
+
+ class ComponentsDialogPrivate;
+ ComponentsDialogPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_COMPONENTSDIALOG_H
diff --git a/kutils/ksettings/dialog.cpp b/kutils/ksettings/dialog.cpp
new file mode 100644
index 000000000..c98c68872
--- /dev/null
+++ b/kutils/ksettings/dialog.cpp
@@ -0,0 +1,642 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/dialog.h"
+
+
+#include <kcmultidialog.h>
+#include <klocale.h>
+#include <kservicegroup.h>
+#include <kdebug.h>
+#include <ktrader.h>
+#include <kplugininfo.h>
+#include "ksettings/dispatcher.h"
+#include "ksettings/componentsdialog.h"
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kiconloader.h>
+#include <qvbox.h>
+#include <qlabel.h>
+#include "kcmoduleinfo.h"
+
+namespace KSettings
+{
+
+struct GroupInfo
+{
+ QString id;
+ QString name;
+ QString comment;
+ QString icon;
+ int weight;
+ QString parentid;
+ QWidget * page;
+};
+
+// The TreeList can get really complicated. That's why a tree data structure
+// is necessary to make it suck less
+class PageNode
+{
+ private:
+ typedef QValueList<PageNode*> List;
+ enum Type { KCM, Group, Root };
+ union Value
+ {
+ KCModuleInfo * kcm;
+ GroupInfo * group;
+ };
+ Type m_type;
+ Value m_value;
+
+ Dialog * m_dialog;
+ List m_children;
+ PageNode * m_parent;
+ bool m_visible;
+ bool m_dirty;
+
+ protected:
+ PageNode( KCModuleInfo * info, PageNode * parent )
+ : m_type( KCM )
+ , m_parent( parent )
+ , m_visible( true )
+ , m_dirty( true )
+ {
+ m_value.kcm = info;
+ m_dialog = parent->m_dialog;
+ }
+
+ PageNode( GroupInfo & group, PageNode * parent )
+ : m_type( Group )
+ , m_parent( parent )
+ , m_visible( true )
+ , m_dirty( true )
+ {
+ m_value.group = new GroupInfo( group );
+ m_value.group->page = 0;
+ m_dialog = parent->m_dialog;
+ }
+
+ void bubbleSort( List::Iterator begin, List::Iterator end )
+ {
+ --end;
+ bool finished;
+ List::Iterator lastswapped = begin;
+ List::Iterator i;
+ List::Iterator j;
+ while( begin != end )
+ {
+ finished = true;
+ i = j = end;
+ do {
+ --j;
+ if( **i < **j )
+ {
+ finished = false;
+ qSwap( *i, *j );
+ lastswapped = j;
+ }
+ --i;
+ } while( j != begin );
+ if( finished )
+ return;
+ ++lastswapped;
+ begin = lastswapped;
+ }
+ }
+
+ public:
+ PageNode( Dialog * dialog )
+ : m_type( Root )
+ , m_dialog( dialog )
+ , m_parent( 0 )
+ , m_visible( true )
+ , m_dirty( true )
+ {}
+
+ ~PageNode()
+ {
+ if( KCM == m_type )
+ delete m_value.kcm;
+ else if( Group == m_type )
+ delete m_value.group;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ delete ( *it );
+ }
+
+ int weight() const
+ {
+ int w = ( KCM == m_type ) ? m_value.kcm->weight()
+ : m_value.group->weight;
+ kdDebug( 700 ) << k_funcinfo << name() << " " << w << endl;
+ return w;
+ }
+
+ bool operator<( const PageNode & rhs ) const
+ {
+ return weight() < rhs.weight();
+ }
+
+ bool isVisible()
+ {
+ if( m_dirty )
+ {
+ if( KCM == m_type )
+ m_visible = m_dialog->isPluginForKCMEnabled( m_value.kcm );
+ else
+ {
+ m_visible = false;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end;
+ ++it )
+ if( ( *it )->isVisible() )
+ {
+ m_visible = true;
+ break;
+ }
+ }
+ m_dirty = false;
+ }
+ kdDebug( 700 ) << k_funcinfo << "returns " << m_visible << endl;
+ return m_visible;
+ }
+
+ void makeDirty()
+ {
+ m_dirty = true;
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->makeDirty();
+ }
+
+ QString name() const
+ {
+ if( Root == m_type )
+ return QString::fromAscii( "root node" );
+ return ( KCM == m_type ) ? m_value.kcm->moduleName()
+ : m_value.group->name;
+ }
+
+ QStringList parentNames() const
+ {
+ QStringList ret;
+ PageNode * node = m_parent;
+ while( node && node->m_type != Root )
+ {
+ ret.prepend( node->name() );
+ node = node->m_parent;
+ }
+ return ret;
+ }
+
+ void addToDialog( KCMultiDialog * dlg )
+ {
+ kdDebug( 700 ) << k_funcinfo << "for " << name() << endl;
+ if( ! isVisible() )
+ return;
+
+ if( KCM == m_type )
+ {
+ dlg->addModule( *m_value.kcm, parentNames() );
+ return;
+ }
+ if( Group == m_type && 0 == m_value.group->page )
+ {
+ QPixmap icon;
+ if( ! m_value.group->icon.isNull() )
+ icon = SmallIcon( m_value.group->icon,
+ IconSize( KIcon::Small ) );
+ QVBox * page = dlg->addVBoxPage( m_value.group->name,
+ QString::null, icon );
+ QLabel * comment = new QLabel( m_value.group->comment, page );
+ comment->setTextFormat( Qt::RichText );
+ m_value.group->page = page;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->addToDialog( dlg );
+ }
+
+ void removeFromDialog( KCMultiDialog * dlg )
+ {
+ kdDebug( 700 ) << k_funcinfo << "for " << name() << endl;
+ if( KCM == m_type )
+ return;
+ if( Root == m_type )
+ dlg->removeAllModules();
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ ( *it )->removeFromDialog( dlg );
+ if( Group == m_type )
+ {
+ delete m_value.group->page;
+ m_value.group->page = 0;
+ }
+ }
+
+ void sort()
+ {
+ kdDebug( 700 ) << k_funcinfo << name() << endl;
+ List::Iterator begin = m_children.begin();
+ List::Iterator end = m_children.end();
+ bubbleSort( begin, end );
+ for( List::Iterator it = begin ; it != end; ++it )
+ ( *it )->sort();
+ }
+
+ bool insert( GroupInfo & group )
+ {
+ if( group.parentid.isNull() )
+ {
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ else
+ kdFatal( 700 ) << "wrong PageNode insertion"
+ << kdBacktrace() << endl;
+ }
+ if( Group == m_type && group.parentid == m_value.group->id )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->insert( group ) )
+ return true;
+ // no parent with the right parentid
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( group, this ) );
+ return true;
+ }
+ return false;
+ }
+
+ bool insert( KCModuleInfo * info, const QString & parentid )
+ {
+ if( parentid.isNull() )
+ {
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ else
+ kdFatal( 700 ) << "wrong PageNode insertion"
+ << kdBacktrace() << endl;
+ }
+ if( Group == m_type && parentid == m_value.group->id )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ List::Iterator end = m_children.end();
+ for( List::Iterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->insert( info, parentid ) )
+ return true;
+ // no parent with the right parentid
+ if( Root == m_type )
+ {
+ m_children.append( new PageNode( info, this ) );
+ return true;
+ }
+ return false;
+ }
+
+ bool needTree()
+ {
+ List::ConstIterator end = m_children.end();
+ for( List::ConstIterator it = m_children.begin(); it != end; ++it )
+ if( ( *it )->m_children.count() > 0 )
+ return true;
+ return false;
+ }
+
+ bool singleChild()
+ {
+ return ( m_children.count() == 1 );
+ }
+};
+
+class Dialog::DialogPrivate
+{
+ public:
+ DialogPrivate( Dialog * parent )
+ : dlg( 0 )
+ , pagetree( parent )
+ {
+ }
+
+ bool staticlistview;
+ KCMultiDialog * dlg;
+ PageNode pagetree;
+ QWidget * parentwidget;
+ QStringList registeredComponents;
+ QValueList<KService::Ptr> services;
+ QMap<QString, KPluginInfo*> plugininfomap;
+};
+
+Dialog::Dialog( QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = true;
+ d->services = instanceServices();
+}
+
+Dialog::Dialog( ContentInListView content,
+ QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = ( content == Static );
+ d->services = instanceServices();
+}
+
+Dialog::Dialog( const QStringList & components,
+ QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = true;
+ d->services = instanceServices() + parentComponentsServices( components );
+}
+
+Dialog::Dialog( const QStringList & components,
+ ContentInListView content, QWidget * parent, const char * name )
+ : QObject( parent, name )
+ , d( new DialogPrivate( this ) )
+{
+ d->parentwidget = parent;
+ d->staticlistview = ( content == Static );
+ d->services = instanceServices() + parentComponentsServices( components );
+}
+
+Dialog::~Dialog()
+{
+ delete d;
+}
+
+void Dialog::addPluginInfos( const QValueList<KPluginInfo*> & plugininfos )
+{
+ for( QValueList<KPluginInfo*>::ConstIterator it = plugininfos.begin();
+ it != plugininfos.end(); ++it )
+ {
+ d->registeredComponents.append( ( *it )->pluginName() );
+ d->services += ( *it )->kcmServices();
+ d->plugininfomap[ ( *it )->pluginName() ] = *it;
+ }
+}
+
+void Dialog::show()
+{
+ if( 0 == d->dlg )
+ createDialogFromServices();
+ Dispatcher::self()->syncConfiguration();
+ return d->dlg->show();
+}
+
+KCMultiDialog * Dialog::dialog()
+{
+ if( 0 == d->dlg )
+ createDialogFromServices();
+ return d->dlg;
+}
+
+QValueList<KService::Ptr> Dialog::instanceServices() const
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+ QString instanceName = KGlobal::instance()->instanceName();
+ d->registeredComponents.append( instanceName );
+ kdDebug( 700 ) << "calling KServiceGroup::childGroup( " << instanceName
+ << " )" << endl;
+ KServiceGroup::Ptr service = KServiceGroup::childGroup( instanceName );
+
+ QValueList<KService::Ptr> ret;
+
+ if( service && service->isValid() )
+ {
+ kdDebug( 700 ) << "call was successfull" << endl;
+ KServiceGroup::List list = service->entries();
+ for( KServiceGroup::List::ConstIterator it = list.begin();
+ it != list.end(); ++it )
+ {
+ KSycocaEntry * p = *it;
+ if( p->isType( KST_KService ) )
+ {
+ kdDebug( 700 ) << "found service" << endl;
+ ret << static_cast<KService *>( p );
+ }
+ else
+ kdWarning( 700 ) << "KServiceGroup::childGroup returned"
+ " something else than a KService (kinda)" << endl;
+ }
+ }
+
+ return ret;
+}
+
+QValueList<KService::Ptr> Dialog::parentComponentsServices(
+ const QStringList & kcdparents ) const
+{
+ d->registeredComponents += kcdparents;
+ QString constraint = kcdparents.join(
+ "' in [X-KDE-ParentComponents]) or ('" );
+ constraint = "('" + constraint + "' in [X-KDE-ParentComponents])";
+
+ kdDebug( 700 ) << "constraint = " << constraint << endl;
+ return KTrader::self()->query( "KCModule", constraint );
+}
+
+bool Dialog::isPluginForKCMEnabled( KCModuleInfo * moduleinfo ) const
+{
+ // if the user of this class requested to hide disabled modules
+ // we check whether it should be enabled or not
+ bool enabled = true;
+ kdDebug( 700 ) << "check whether the " << moduleinfo->moduleName()
+ << " KCM should be shown" << endl;
+ // for all parent components
+ QStringList parentComponents = moduleinfo->service()->property(
+ "X-KDE-ParentComponents" ).toStringList();
+ for( QStringList::ConstIterator pcit = parentComponents.begin();
+ pcit != parentComponents.end(); ++pcit )
+ {
+ // if the parentComponent is not registered ignore it
+ if( d->registeredComponents.find( *pcit ) ==
+ d->registeredComponents.end() )
+ continue;
+
+ // we check if the parent component is a plugin
+ if( ! d->plugininfomap.contains( *pcit ) )
+ {
+ // if not the KCModule must be enabled
+ enabled = true;
+ // we're done for this KCModuleInfo
+ break;
+ }
+ // if it is a plugin we check whether the plugin is enabled
+ KPluginInfo * pinfo = d->plugininfomap[ *pcit ];
+ pinfo->load();
+ enabled = pinfo->isPluginEnabled();
+ kdDebug( 700 ) << "parent " << *pcit << " is "
+ << ( enabled ? "enabled" : "disabled" ) << endl;
+ // if it is enabled we're done for this KCModuleInfo
+ if( enabled )
+ break;
+ }
+ return enabled;
+}
+
+void Dialog::parseGroupFile( const QString & filename )
+{
+ KSimpleConfig file( filename );
+ QStringList groups = file.groupList();
+ for( QStringList::ConstIterator it = groups.begin(); it != groups.end();
+ ++it )
+ {
+ GroupInfo group;
+ QString id = *it;
+ file.setGroup( id.utf8() );
+ group.id = id;
+ group.name = file.readEntry( "Name" );
+ group.comment = file.readEntry( "Comment" );
+ group.weight = file.readNumEntry( "Weight", 100 );
+ group.parentid = file.readEntry( "Parent" );
+ group.icon = file.readEntry( "Icon" );
+ d->pagetree.insert( group );
+ }
+}
+
+void Dialog::createDialogFromServices()
+{
+ // read .setdlg files
+ QString setdlgpath = locate( "appdata",
+ KGlobal::instance()->instanceName() + ".setdlg" );
+ QStringList setdlgaddon = KGlobal::dirs()->findAllResources( "appdata",
+ "ksettingsdialog/*.setdlg" );
+ if( ! setdlgpath.isNull() )
+ parseGroupFile( setdlgpath );
+ if( setdlgaddon.size() > 0 )
+ for( QStringList::ConstIterator it = setdlgaddon.begin();
+ it != setdlgaddon.end(); ++it )
+ parseGroupFile( *it );
+
+ // now we process the KCModule services
+ for( QValueList<KService::Ptr>::ConstIterator it = d->services.begin();
+ it != d->services.end(); ++it )
+ {
+ // we create the KCModuleInfo
+ KCModuleInfo * info = new KCModuleInfo( *it );
+ QString parentid;
+ QVariant tmp = info->service()->property( "X-KDE-CfgDlgHierarchy",
+ QVariant::String );
+ if( tmp.isValid() )
+ parentid = tmp.toString();
+ d->pagetree.insert( info, parentid );
+ }
+
+ // At this point d->pagetree holds a nice structure of the pages we want
+ // to show. It's not going to change anymore so we can sort it now.
+ d->pagetree.sort();
+
+ int dialogface = KJanusWidget::IconList;
+ if( d->pagetree.needTree() )
+ dialogface = KJanusWidget::TreeList;
+ else if( d->pagetree.singleChild() )
+ dialogface = KJanusWidget::Plain;
+
+ kdDebug( 700 ) << "creating KCMultiDialog" << endl;
+ d->dlg = new KCMultiDialog( dialogface, i18n( "Configure" ),
+ d->parentwidget );
+
+ if( dialogface == KJanusWidget::TreeList )
+ d->dlg->setShowIconsInTreeList( true );
+
+ // TODO: Don't show the reset button until the issue with the
+ // KPluginSelector::load() method is solved.
+ // Problem:
+ // KCMultiDialog::show() call KCModule::load() to reset all KCMs
+ // (KPluginSelector::load() resets all plugin selections and all plugin
+ // KCMs).
+ // The reset button calls KCModule::load(), too but in this case we want the
+ // KPluginSelector to only reset the current visible plugin KCM and not
+ // touch the plugin selections.
+ // I have no idea how to check that in KPluginSelector::load()...
+ //d->dlg->showButton( KDialogBase::User1, true );
+
+ if( ! d->staticlistview )
+ d->dlg->addButtonBelowList( i18n( "Select Components..." ), this,
+ SLOT( configureTree() ) );
+
+ connect( d->dlg, SIGNAL( okClicked() ), Dispatcher::self(),
+ SLOT( syncConfiguration() ) );
+ connect( d->dlg, SIGNAL( applyClicked() ), Dispatcher::self(),
+ SLOT( syncConfiguration() ) );
+ connect( d->dlg, SIGNAL( configCommitted( const QCString & ) ),
+ Dispatcher::self(), SLOT( reparseConfiguration( const QCString & ) ) );
+
+ d->pagetree.addToDialog( d->dlg );
+
+ if( dialogface == KJanusWidget::TreeList )
+ d->dlg->unfoldTreeList();
+}
+
+void Dialog::configureTree()
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+ ComponentsDialog * subdlg = new ComponentsDialog( d->dlg );
+ subdlg->setPluginInfos( d->plugininfomap );
+ subdlg->show();
+ connect( subdlg, SIGNAL( okClicked() ), this, SLOT( updateTreeList() ) );
+ connect( subdlg, SIGNAL( applyClicked() ), this, SLOT( updateTreeList() ) );
+ connect( subdlg, SIGNAL( okClicked() ), this,
+ SIGNAL( pluginSelectionChanged() ) );
+ connect( subdlg, SIGNAL( applyClicked() ), this,
+ SIGNAL( pluginSelectionChanged() ) );
+ connect( subdlg, SIGNAL( finished() ), subdlg, SLOT( delayedDestruct() ) );
+}
+
+void Dialog::updateTreeList()
+{
+ kdDebug( 700 ) << k_funcinfo << endl;
+
+ d->pagetree.makeDirty();
+
+ // remove all pages from the dialog and then add them again. This is needed
+ // because KDialogBase/KJanusWidget can only append to the end of the list
+ // and we need to have a predefined order.
+
+ d->pagetree.removeFromDialog( d->dlg );
+ d->pagetree.addToDialog( d->dlg );
+
+ if( d->pagetree.needTree() )
+ d->dlg->unfoldTreeList( true );
+}
+
+} //namespace
+
+#include "dialog.moc"
+
+// vim: sw=4 ts=4 noet
diff --git a/kutils/ksettings/dialog.h b/kutils/ksettings/dialog.h
new file mode 100644
index 000000000..84f4619be
--- /dev/null
+++ b/kutils/ksettings/dialog.h
@@ -0,0 +1,224 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_DIALOG_H
+#define KSETTINGS_DIALOG_H
+
+#include <qobject.h>
+#include <kservice.h>
+
+template<class T> class QValueList;
+class KPluginInfo;
+class KCMultiDialog;
+class KCModuleInfo;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup main
+ * @ingroup settings
+ * @short Generic configuration dialog that even works over component boundaries
+ *
+ * For more information see \ref KSettings.
+ *
+ * This class aims to standardize the use of configuration dialogs in KDE
+ * applications. Especially when using KParts and/or Plugins you face problems
+ * creating a consistent config dialog.
+ *
+ * To show a configuration dialog you only have to call the show method and be
+ * done with it. A code example:
+ *
+ * You initialize \p m_cfgdlg with
+ * \code
+ * m_cfgdlg = new Dialog( Dialog::Static, this );
+ * \endcode
+ * If you use a KPart that was not especially designed for your app you can use
+ * the second constructor:
+ * \code
+ * QStringList kpartslist;
+ * for( all my kparts )
+ * kpartslist += m_mypart->instance().instanceName();
+ * m_cfgdlg = new Dialog( kpartslist, this );
+ * \endcode
+ * and the action for the config dialog is connected to the show slot:
+ * \code
+ * KStdAction::preferences( m_cfgdlg, SLOT( show() ), actionCollection() );
+ * \endcode
+ *
+ * If you need to be informed when the config was changed and applied in the
+ * dialog you might want to take a look at Dispatcher.
+ *
+ * For more information see \ref KSettings.
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT Dialog : public QObject
+{
+ friend class PageNode;
+ Q_OBJECT
+ public:
+ /**
+ * Tells the dialog whether the entries in the listview are all static
+ * or whether it should add a Configure... button to select which parts
+ * of the optional functionality should be active or not.
+ */
+ enum ContentInListView
+ {
+ /**
+ * Static listview, while running no entries are added or deleted
+ */
+ Static,
+ /**
+ * Configurable listview. The user can select what functionality he
+ * wants.
+ */
+ Configurable
+ };
+
+ /**
+ * Construct a new Preferences Dialog for the application. It uses all
+ * KCMs with X-KDE-ParentApp set to KGlobal::instance()->instanceName().
+ *
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( QWidget * parent = 0, const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog for the application. It uses all
+ * KCMs with X-KDE-ParentApp set to KGlobal::instance()->instanceName().
+ *
+ * @param content Select whether you want a static or configurable
+ * config dialog.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( ContentInListView content = Static, QWidget * parent = 0,
+ const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog with the pages for the selected
+ * instance names. For example if you want to have the configuration
+ * pages for the kviewviewer KPart you would pass a
+ * QStringList consisting of only the name of the part "kviewviewer".
+ *
+ * @param components A list of the names of the components that your
+ * config dialog should merge the config pages in.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( const QStringList & components, QWidget * parent = 0,
+ const char * name = 0 );
+
+ /**
+ * Construct a new Preferences Dialog with the pages for the selected
+ * instance names. For example if you want to have the configuration
+ * pages for the kviewviewer KPart you would pass a
+ * QStringList consisting of only the name of the part "kviewviewer".
+ *
+ * @param components A list of the names of the components that your
+ * config dialog should merge the config pages in.
+ * @param content Select whether you want a static or configurable
+ * config dialog.
+ * @param parent The parent is only used as the parent for the
+ * dialog - centering the dialog over the parent
+ * widget.
+ * @param name name
+ */
+ Dialog( const QStringList & components, ContentInListView
+ content, QWidget * parent = 0, const char * name = 0 );
+
+ ~Dialog();
+
+ /**
+ * If you use a Configurable dialog you need to pass KPluginInfo
+ * objects that the dialog should configure.
+ */
+ void addPluginInfos( const QValueList<KPluginInfo*> & plugininfos );
+
+ KCMultiDialog * dialog();
+
+ public slots:
+ /**
+ * Show the config dialog. The slot immediatly returns since the dialog
+ * is non-modal.
+ */
+ void show();
+
+ signals:
+ /**
+ * If you use the dialog in Configurable mode and want to be notified
+ * when the user changes the plugin selections use this signal. It's
+ * emitted if the selection has changed and the user pressed Apply or
+ * Ok. In the slot you would then load and unload the plugins as
+ * requested.
+ */
+ void pluginSelectionChanged();
+
+ protected slots:
+ void configureTree();
+ void updateTreeList();
+
+ private:
+ /**
+ * @internal
+ * Check whether the plugin associated with this KCM is enabled.
+ */
+ bool isPluginForKCMEnabled( KCModuleInfo * ) const;
+
+ QValueList<KService::Ptr> instanceServices() const;
+ QValueList<KService::Ptr> parentComponentsServices(
+ const QStringList & ) const;
+ /**
+ * @internal
+ * Read the .setdlg file and add it to the groupmap
+ */
+ void parseGroupFile( const QString & );
+
+ /**
+ * @internal
+ * If this module is put into a TreeList hierarchy this will return a
+ * list of the names of the parent modules.
+ */
+ QStringList parentModuleNames( KCModuleInfo * );
+
+ /**
+ * @internal
+ * This method is called only once. The KCMultiDialog is not created
+ * until it's really needed. So if some method needs to access d->dlg it
+ * checks for 0 and if it's not created this method will do it.
+ */
+ void createDialogFromServices();
+
+ class DialogPrivate;
+ DialogPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_DIALOG_H
diff --git a/kutils/ksettings/dispatcher.cpp b/kutils/ksettings/dispatcher.cpp
new file mode 100644
index 000000000..4e54538d8
--- /dev/null
+++ b/kutils/ksettings/dispatcher.cpp
@@ -0,0 +1,157 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/dispatcher.h"
+
+#include <qsignal.h>
+
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <assert.h>
+
+namespace KSettings
+{
+
+//class Dispatcher::DispatcherPrivate
+//{
+//};
+
+static KStaticDeleter<Dispatcher> ksd_kpd;
+
+Dispatcher * Dispatcher::m_self = 0;
+
+Dispatcher * Dispatcher::self()
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ if( m_self == 0 )
+ ksd_kpd.setObject( m_self, new Dispatcher() );
+ return m_self;
+}
+
+Dispatcher::Dispatcher( QObject * parent, const char * name )
+ : QObject( parent, name )
+ //, d( 0 )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+}
+
+Dispatcher::~Dispatcher()
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ //delete d;
+}
+
+void Dispatcher::registerInstance( KInstance * instance, QObject * recv, const char * slot )
+{
+ assert( instance != 0 );
+ // keep the KInstance around and call
+ // instance->config()->reparseConfiguration when the app should reparse
+ QCString instanceName = instance->instanceName();
+ kdDebug( 701 ) << k_funcinfo << instanceName << endl;
+ m_instanceName[ recv ] = instanceName;
+ QSignal * sig;
+ if( m_instanceInfo.contains( instanceName ) )
+ {
+ sig = m_instanceInfo[ instanceName ].signal;
+ }
+ else
+ {
+ sig = new QSignal( this, "signal dispatcher" );
+ m_instanceInfo[ instanceName ].signal = sig;
+ m_instanceInfo[ instanceName ].instance = instance;
+ }
+ sig->connect( recv, slot );
+
+ ++m_instanceInfo[ instanceName ].count;
+ connect( recv, SIGNAL( destroyed( QObject * ) ), this, SLOT( unregisterInstance( QObject * ) ) );
+}
+
+KConfig * Dispatcher::configForInstanceName( const QCString & instanceName )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ if( m_instanceInfo.contains( instanceName ) )
+ {
+ KInstance * inst = m_instanceInfo[ instanceName ].instance;
+ if( inst )
+ return inst->config();
+ }
+ //if( fallback )
+ //return new KSimpleConfig( instanceName );
+ return 0;
+}
+
+QStrList Dispatcher::instanceNames() const
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ QStrList names;
+ for( QMap<QCString, InstanceInfo>::ConstIterator it = m_instanceInfo.begin(); it != m_instanceInfo.end(); ++it )
+ if( ( *it ).count > 0 )
+ names.append( it.key() );
+ return names;
+}
+
+void Dispatcher::reparseConfiguration( const QCString & instanceName )
+{
+ kdDebug( 701 ) << k_funcinfo << instanceName << endl;
+ // check if the instanceName is valid:
+ if( ! m_instanceInfo.contains( instanceName ) )
+ return;
+ // first we reparse the config of the instance so that the KConfig object
+ // will be up to date
+ m_instanceInfo[ instanceName ].instance->config()->reparseConfiguration();
+ QSignal * sig = m_instanceInfo[ instanceName ].signal;
+ if( sig )
+ {
+ kdDebug( 701 ) << "emit signal to instance" << endl;
+ sig->activate();
+ }
+}
+
+void Dispatcher::syncConfiguration()
+{
+ for( QMap<QCString, InstanceInfo>::ConstIterator it = m_instanceInfo.begin(); it != m_instanceInfo.end(); ++it )
+ {
+ ( *it ).instance->config()->sync();
+ }
+}
+
+void Dispatcher::unregisterInstance( QObject * obj )
+{
+ kdDebug( 701 ) << k_funcinfo << endl;
+ QCString name = m_instanceName[ obj ];
+ m_instanceName.remove( obj ); //obj will be destroyed when we return, so we better remove this entry
+ --m_instanceInfo[ name ].count;
+ if( m_instanceInfo[ name ].count == 0 )
+ {
+ delete m_instanceInfo[ name ].signal;
+ m_instanceInfo.remove( name );
+ }
+}
+
+//X KInstance * Dispatcher::instanceForName( const QCString & instanceName )
+//X {
+//X return m_instanceInfo[ instanceName ].instance;
+//X }
+
+} //namespace
+
+#include "dispatcher.moc"
+
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/dispatcher.h b/kutils/ksettings/dispatcher.h
new file mode 100644
index 000000000..5f6270bb5
--- /dev/null
+++ b/kutils/ksettings/dispatcher.h
@@ -0,0 +1,133 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_DISPATCHER_H
+#define KSETTINGS_DISPATCHER_H
+
+#include <qobject.h>
+#include <qmap.h>
+#include <kdelibs_export.h>
+
+class QCString;
+class QSignal;
+class QStrList;
+template<class T> class KStaticDeleter;
+class KInstance;
+class KConfig;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup settings
+ * @short Dispatch change notifications from the KCMs to the program.
+ *
+ * Since your program does not have direct control over the KCMs that get loaded
+ * into the KConfigureDialog you need a way to get notified. This is what you
+ * do:
+ * \code
+ * Dispatcher::self()->registerInstance( instance(), this, SLOT( loadSettings() ) );
+ * \endcode
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT Dispatcher : public QObject
+{
+ friend class KStaticDeleter<Dispatcher>;
+
+ Q_OBJECT
+ public:
+ /**
+ * Get a reference the the Dispatcher object.
+ */
+ static Dispatcher * self();
+
+ /**
+ * Register a slot to be called when the configuration for the instance
+ * has changed. @p instance is the KInstance object
+ * that is passed to KGenericFactory (if it is used). You can query
+ * it with KGenericFactory<YourClassName>::instance().
+ * instance->instanceName() is also the same name that is put into the
+ * .desktop file of the KCMs for the X-KDE-ParentComponents.
+ *
+ * @param instance The KInstance object
+ * @param recv The object that should receive the signal
+ * @param slot The slot to be called: SLOT( slotName() )
+ */
+ void registerInstance( KInstance * instance, QObject * recv, const char * slot );
+
+ /**
+ * @return the KConfig object that belongs to the instanceName
+ */
+ KConfig * configForInstanceName( const QCString & instanceName );
+
+ /**
+ * @return a list of all the instance names that are currently
+ * registered
+ */
+ QStrList instanceNames() const;
+
+//X /**
+//X * @return The KInstance object belonging to the instance name you pass
+//X * (only works for registered instances of course).
+//X */
+//X KInstance * instanceForName( const QCString & instanceName );
+
+ public slots:
+ /**
+ * Call this slot when the configuration belonging to the associated
+ * instance name has changed. The registered slot will be called.
+ *
+ * @param instanceName The value of X-KDE-ParentComponents.
+ */
+ void reparseConfiguration( const QCString & instanceName );
+
+ /**
+ * When this slot is called the KConfig objects of all the registered
+ * instances are sync()ed. This is usefull when some other KConfig
+ * objects will read/write from/to the same config file, so that you
+ * can first write out the current state of the KConfig objects.
+ */
+ void syncConfiguration();
+
+ private slots:
+ void unregisterInstance( QObject * );
+
+ private:
+ Dispatcher( QObject * parent = 0, const char * name = 0 );
+ ~Dispatcher();
+ static Dispatcher * m_self;
+
+ struct InstanceInfo {
+ KInstance * instance;
+ QSignal * signal;
+ int count;
+ };
+ QMap<QCString, InstanceInfo> m_instanceInfo;
+ QMap<QObject *, QCString> m_instanceName;
+
+ class DispatcherPrivate;
+ DispatcherPrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+#endif // KSETTINGS_DISPATCHER_H
diff --git a/kutils/ksettings/pluginpage.cpp b/kutils/ksettings/pluginpage.cpp
new file mode 100644
index 000000000..2456ebf86
--- /dev/null
+++ b/kutils/ksettings/pluginpage.cpp
@@ -0,0 +1,88 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "ksettings/pluginpage.h"
+#include "kpluginselector.h"
+#include <qlayout.h>
+#include <kdialog.h>
+#include "ksettings/dispatcher.h"
+
+namespace KSettings
+{
+
+class PluginPage::PluginPagePrivate
+{
+ public:
+ PluginPagePrivate()
+ : selwid( 0 )
+ {
+ }
+
+ KPluginSelector * selwid;
+};
+
+ PluginPage::PluginPage( QWidget * parent, const char * name, const QStringList & args )
+ : KCModule( parent, name, args )
+ , d( new PluginPagePrivate )
+{
+ ( new QVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->selwid = new KPluginSelector( this );
+ connect( d->selwid, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
+}
+
+ PluginPage::PluginPage( KInstance * instance, QWidget * parent, const QStringList & args )
+ : KCModule( instance, parent, args )
+ , d( new PluginPagePrivate )
+{
+ ( new QVBoxLayout( this, 0, KDialog::spacingHint() ) )->setAutoAdd( true );
+ d->selwid = new KPluginSelector( this );
+ connect( d->selwid, SIGNAL( changed( bool ) ), this, SIGNAL( changed( bool ) ) );
+ connect( d->selwid, SIGNAL( configCommitted( const QCString & ) ),
+ Dispatcher::self(), SLOT( reparseConfiguration( const QCString & ) ) );
+}
+
+PluginPage::~PluginPage()
+{
+ delete d;
+}
+
+KPluginSelector * PluginPage::pluginSelector()
+{
+ return d->selwid;
+}
+
+void PluginPage::load()
+{
+ d->selwid->load();
+}
+
+void PluginPage::save()
+{
+ d->selwid->save();
+}
+
+void PluginPage::defaults()
+{
+ d->selwid->defaults();
+}
+
+} //namespace
+
+#include "pluginpage.moc"
+// vim: sw=4 sts=4 et
diff --git a/kutils/ksettings/pluginpage.h b/kutils/ksettings/pluginpage.h
new file mode 100644
index 000000000..fc6120f78
--- /dev/null
+++ b/kutils/ksettings/pluginpage.h
@@ -0,0 +1,120 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef KSETTINGS_PLUGINPAGE_H
+#define KSETTINGS_PLUGINPAGE_H
+
+#include <kcmodule.h>
+#include <kdelibs_export.h>
+
+class KPluginSelector;
+
+namespace KSettings
+{
+
+/**
+ * @ingroup settings
+ * @ingroup plugin
+ * @short Convenience KCModule for creating a plugins config page.
+ *
+ * This class makes it very easy to create a plugins configuration page to your
+ * program. All you need to do is create a class that is derived from
+ * PluginPage and add the appropriate plugin infos to the KPluginSelector.
+ * This is done using the pluginSelector() method:
+ * \code
+ * typedef KGenericFactory<MyAppPluginConfig, QWidget> MyAppPluginConfigFactory;
+ * K_EXPORT_COMPONENT_FACTORY( kcm_myapppluginconfig, MyAppPluginConfigFactory( "kcm_myapppluginconfig" ) );
+ *
+ * MyAppPluginConfig( QWidget * parent, const char *, const QStringList & args )
+ * : PluginPage( MyAppPluginConfigFactory::instance(), parent, args )
+ * {
+ * pluginSelector()->addPlugins( KGlobal::instance()->instanceName(), i18n( "General Plugins" ), "General" );
+ * pluginSelector()->addPlugins( KGlobal::instance()->instanceName(), i18n( "Effects" ), "Effects" );
+ * }
+ * \endcode
+ *
+ * All that remains to be done is to create the appropriate .desktop file
+ * \verbatim
+ [Desktop Entry]
+ Encoding=UTF-8
+ Icon=plugin
+ Type=Service
+ ServiceTypes=KCModule
+
+ X-KDE-ModuleType=Library
+ X-KDE-Library=myapppluginconfig
+ X-KDE-FactoryName=MyAppPluginConfigFactory
+ X-KDE-ParentApp=myapp
+ X-KDE-ParentComponents=myapp
+
+ Name=Plugins
+ Comment=Select and configure your plugins:
+ \endverbatim
+ *
+ * @author Matthias Kretz <kretz@kde.org>
+ * @since 3.2
+ */
+class KUTILS_EXPORT PluginPage : public KCModule
+{
+ Q_OBJECT
+ public:
+ /**
+ * Standart KCModule constructor. Automatically creates the the
+ * KPluginSelector widget.
+ */
+ PluginPage( QWidget * parent = 0, const char * name = 0, const QStringList & args = QStringList() );
+
+ /**
+ * Standart KCModule constructor. Automatically creates the the
+ * KPluginSelector widget.
+ */
+ PluginPage( KInstance * instance, QWidget * parent = 0, const QStringList & args = QStringList() );
+
+ ~PluginPage();
+
+ /**
+ * @return a reference to the KPluginSelector.
+ */
+ KPluginSelector * pluginSelector();
+
+ /**
+ * Load the state of the plugins (selected or not) from the KPluginInfo
+ * objects. For KParts plugins everything should work automatically. For
+ * your own type of plugins you might need to reimplement the
+ * KPluginInfo::pluginLoaded() method. If that doesn't fit your needs
+ * you can also reimplement this method.
+ */
+ virtual void load();
+
+ /**
+ * Save the state of the plugins to KConfig objects
+ */
+ virtual void save();
+ virtual void defaults();
+
+ private:
+ class PluginPagePrivate;
+ PluginPagePrivate * d;
+};
+
+}
+
+// vim: sw=4 sts=4 et
+
+#endif // KSETTINGS_PLUGINPAGE_H
diff --git a/kutils/tests/Makefile.am b/kutils/tests/Makefile.am
new file mode 100644
index 000000000..3bde7a9a2
--- /dev/null
+++ b/kutils/tests/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = $(all_includes)
+
+check_PROGRAMS = kfindtest kreplacetest
+
+TESTS = kfindtest kreplacetest
+
+kfindtest_SOURCES = kfindtest.cpp
+kfindtest_LDADD = ../libkutils.la
+kfindtest_LDFLAGS = $(all_libraries)
+
+kreplacetest_SOURCES = kreplacetest.cpp
+kreplacetest_LDADD = ../libkutils.la
+kreplacetest_LDFLAGS = $(all_libraries)
+
+METASOURCES = AUTO
+
diff --git a/kutils/tests/kfindtest.cpp b/kutils/tests/kfindtest.cpp
new file mode 100644
index 000000000..17e8da7ee
--- /dev/null
+++ b/kutils/tests/kfindtest.cpp
@@ -0,0 +1,252 @@
+/*
+ Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl>
+ This file is part of the KDE project
+
+ 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 "../kfind.h"
+#include "../kfinddialog.h"
+#include "kfindtest.h"
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include <stdlib.h>
+#include <assert.h>
+
+static bool check(QString txt, QString a, QString b) // from kurltest
+{
+ if (a.isEmpty())
+ a = QString::null;
+ if (b.isEmpty())
+ b = QString::null;
+ if (a == b) {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl;
+ }
+ else {
+ kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl;
+ exit(1);
+ }
+ return true;
+}
+
+void KFindTest::changeText(uint line, const QString &text)
+{
+ Q_ASSERT(line < m_text.count());
+ Q_ASSERT(m_find != 0);
+
+ m_line = line;
+ m_text[line] = text;
+ m_find->setData(line, text);
+}
+
+void KFindTest::find(const QString &pattern, long options)
+{
+ delete m_find;
+ m_find = new KFind(pattern, options, 0);
+
+ connect(m_find, SIGNAL(highlight(const QString &, int, int)),
+ SLOT(slotHighlight(const QString &, int, int)));
+ connect(m_find, SIGNAL(highlight(int, int, int)),
+ SLOT(slotHighlight(int, int, int)));
+
+ m_line = 0;
+ KFind::Result result = KFind::NoMatch;
+ do
+ {
+ if(options & KFindDialog::FindIncremental)
+ m_find->setData(m_line, m_text[m_line]);
+ else
+ m_find->setData(m_text[m_line]);
+
+ m_line++;
+
+ result = m_find->find();
+ } while(result == KFind::NoMatch && m_line < m_text.count());
+}
+
+void KFindTest::findNext(const QString &pattern)
+{
+ Q_ASSERT(m_find != 0);
+
+ if(!pattern.isNull())
+ {
+ m_find->setPattern(pattern);
+ }
+
+ KFind::Result result = KFind::NoMatch;
+ do
+ {
+ //kdDebug() << "m_line: " << m_line << endl;
+
+ result = m_find->find();
+
+ if(result == KFind::NoMatch && m_line < m_text.count())
+ {
+ //kdDebug() << "incrementing m_line..." << endl;
+ if(m_find->options() & KFindDialog::FindIncremental)
+ m_find->setData(m_line, m_text[m_line]);
+ else
+ m_find->setData(m_text[m_line]);
+
+ m_line++;
+ }
+ } while(result == KFind::NoMatch && m_line < m_text.count());
+ //kdDebug() << "find next completed" << m_line << endl;
+}
+
+void KFindTest::slotHighlight(const QString &text, int index, int matchedLength)
+{
+ m_hits.append("line: \"" + text + "\", index: " + QString::number(index) +
+ ", length: " + QString::number(matchedLength) + "\n");
+}
+
+void KFindTest::slotHighlight(int id, int index, int matchedLength)
+{
+ m_hits.append("line: \"" + m_text[id] + "\", index: " + QString::number(index) +
+ ", length: " + QString::number(matchedLength) + "\n");
+}
+
+int main(int argc, char **argv)
+{
+ KCmdLineArgs::init(argc, argv, "kfindtest", "KFindTest", 0, 0, false);
+ KApplication app;
+
+ QString text = "This file is part of the KDE project.\n"
+ "This library is free software; you can redistribute it and/or\n"
+ "modify it under the terms of the GNU Library General Public\n"
+ "License version 2, as published by the Free Software Foundation.\n"
+ "\n"
+ " This library is distributed in the hope that it will be useful,\n"
+ " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+ " Library General Public License for more details.\n"
+ "\n"
+ " You should have received a copy of the GNU Library General Public License\n"
+ " along with this library; see the file COPYING.LIB. If not, write to\n"
+ " the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n"
+ " Boston, MA 02110-1301, USA.\n";
+
+ QString output1 = "line: \"This file is part of the KDE project.\", index: 0, length: 4\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 0, length: 4\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 4, length: 4\n"
+ "line: \" along with this library; see the file COPYING.LIB. If not, write to\", index: 15, length: 4\n";
+
+ QString output2 = "line: \"This file is part of the KDE project.\", index: 0, length: 0\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 1\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 2\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 3\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 5\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 2\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 25, length: 1\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 25, length: 2\n"
+ "line: \" but WITHOUT ANY WARRANTY; without even the implied warranty of\", index: 20, length: 8\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 16, length: 4\n"
+ "line: \"License version 2, as published by the Free Software Foundation.\", index: 44, length: 19\n";
+
+ QString output3 = "line: \"This file is part of the KDE project.\", index: 0, length: 0\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 1\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 2\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 3\n"
+ "line: \"This library is free software; you can redistribute it and/or\", index: 42, length: 4\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 5\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 4\n"
+ "line: \" This library is distributed in the hope that it will be useful,\", index: 21, length: 3\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 2\n"
+ "line: \"This file is part of the KDE project.\", index: 2, length: 1\n"
+ "line: \"The second line now looks a whole lot different.\", index: 18, length: 1\n"
+ "line: \"License version 2, as published by the Free Software Foundation.\", index: 48, length: 2\n"
+ "line: \" but WITHOUT ANY WARRANTY; without even the implied warranty of\", index: 20, length: 8\n"
+ "line: \" but WITHOUT ANY xxxx; without even the implied warranty of\", index: 51, length: 6\n"
+ "line: \"License version 2, as published by the Free Software Foundation.\", index: 39, length: 4\n"
+ "line: \"License version 2, as published by the Free Software Foundation.\", index: 44, length: 19\n";
+
+ KFindTest *test = new KFindTest(QStringList::split('\n', text, true));
+
+ kdDebug() << "Plain static search..." << endl;
+
+ // first we do a simple text searching the text and doing a few find nexts
+ test->find("This", 0);
+ test->findNext();
+ test->findNext();
+ test->findNext();
+ test->findNext();
+ test->findNext();
+
+ check("result", test->hits().join(""), output1);
+ test->clearHits();
+ kdDebug() << "PASSED" << endl;
+
+ kdDebug() << "FindIncremental with static contents..." << endl;
+
+ // now we'll do some searches using FindIncremental
+ test->find("", KFindDialog::FindIncremental);
+ test->findNext("i");
+ test->findNext("is");
+ test->findNext("ist");
+ test->findNext();
+ test->findNext("istri");
+ test->findNext("istr");
+ test->findNext("ist");
+ test->findNext("is");
+ test->findNext("W");
+ test->findNext("WA");
+ test->findNext("WARRANTY");
+ test->findNext("Free");
+ test->findNext("Software Foundation");
+
+ check("result", test->hits().join(""), output2);
+ test->clearHits();
+ kdDebug() << "PASSED" << endl;
+
+ kdDebug() << "FindIncremental with dynamic contents..." << endl;
+
+ // now do that again but with pages that change between searches
+ test->find("", KFindDialog::FindIncremental);
+ test->findNext("i");
+ test->findNext("is");
+ test->findNext("ist");
+ test->findNext("istr");
+ test->findNext();
+ test->changeText(1, "The second line now looks a whole lot different.");
+ test->findNext("istri");
+ test->findNext("istr");
+ test->findNext("ist");
+ test->findNext("is");
+ test->findNext("i");
+ test->findNext("W");
+ test->findNext("WA");
+ test->findNext("WARRANTY");
+ test->changeText(6, " but WITHOUT ANY xxxx; without even the implied warranty of");
+ test->findNext("WARRAN");
+ test->findNext("Free");
+ test->findNext("Software Foundation");
+
+ check("result", test->hits().join(""), output3);
+ test->clearHits();
+ kdDebug() << "PASSED" << endl;
+
+ //return app.exec();
+ delete test;
+ return 0;
+}
+
+#include "kfindtest.moc"
diff --git a/kutils/tests/kfindtest.h b/kutils/tests/kfindtest.h
new file mode 100644
index 000000000..2cb60ed76
--- /dev/null
+++ b/kutils/tests/kfindtest.h
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2004, Arend van Beelen jr. <arend@auton.nl>
+ This file is part of the KDE project
+
+ 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 KFINDTEST_H
+#define KFINDTEST_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class KFind;
+
+class KFindTest : public QObject
+{
+ Q_OBJECT
+
+ public:
+ KFindTest(const QStringList &text) :
+ QObject(0),
+ m_find(0),
+ m_text(text),
+ m_line(0)
+ {}
+
+ void find(const QString &pattern, long options = 0);
+ void findNext(const QString &pattern = QString::null);
+
+ void changeText(uint line, const QString &text);
+
+ const QStringList &hits() const { return m_hits; }
+ void clearHits() { m_hits.clear(); }
+
+ public slots:
+ void slotHighlight(const QString &text, int index, int matchedLength);
+ void slotHighlight(int id, int index, int matchedLengthlength);
+
+ private:
+ KFind *m_find;
+ QStringList m_text;
+ uint m_line;
+ QStringList m_hits;
+};
+
+#endif
diff --git a/kutils/tests/kreplacetest.cpp b/kutils/tests/kreplacetest.cpp
new file mode 100644
index 000000000..9c6f67b3a
--- /dev/null
+++ b/kutils/tests/kreplacetest.cpp
@@ -0,0 +1,342 @@
+/*
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 <assert.h>
+
+#include <kcmdlineargs.h>
+#include <kapplication.h>
+#include <qeventloop.h>
+#include <kpushbutton.h>
+#include "../kreplace.h"
+#include "../kreplacedialog.h"
+
+#include "kreplacetest.h"
+#include <kdebug.h>
+#include <stdlib.h>
+
+void KReplaceTest::replace( const QString &pattern, const QString &replacement, long options )
+{
+ m_needEventLoop = false;
+ // This creates a replace-next-prompt dialog if needed.
+ m_replace = new KReplace(pattern, replacement, options);
+
+ // Connect highlight signal to code which handles highlighting
+ // of found text.
+ connect(m_replace, SIGNAL( highlight( const QString &, int, int ) ),
+ this, SLOT( slotHighlight( const QString &, int, int ) ) );
+ // Connect findNext signal - called when pressing the button in the dialog
+ connect(m_replace, SIGNAL( findNext() ),
+ this, SLOT( slotReplaceNext() ) );
+ // Connect replace signal - called when doing a replacement
+ connect(m_replace, SIGNAL( replace(const QString &, int, int, int) ),
+ this, SLOT( slotReplace(const QString &, int, int, int) ) );
+
+ // Go to initial position
+ if ( (options & KReplaceDialog::FromCursor) == 0 )
+ {
+ if ( m_replace->options() & KFindDialog::FindBackwards )
+ m_currentPos = m_text.fromLast();
+ else
+ m_currentPos = m_text.begin();
+ }
+
+ // Launch first replacement
+ slotReplaceNext();
+
+ if ( m_needEventLoop )
+ qApp->eventLoop()->enterLoop();
+}
+
+void KReplaceTest::slotHighlight( const QString &str, int matchingIndex, int matchedLength )
+{
+ kdDebug() << "slotHighlight Index:" << matchingIndex << " Length:" << matchedLength
+ << " Substr:" << str.mid(matchingIndex, matchedLength)
+ << endl;
+ // Emulate the user saying yes
+ // animateClick triggers a timer, hence the enterloop/exitloop
+ // Calling slotReplace directly would lead to infinite loop anyway (Match never returned,
+ // so slotReplaceNext never returns)
+ if ( m_replace->options() & KReplaceDialog::PromptOnReplace ) {
+ m_replace->replaceNextDialog( false )->actionButton( (KDialogBase::ButtonCode)m_button )->animateClick();
+ m_needEventLoop = true;
+ }
+}
+
+
+void KReplaceTest::slotReplace(const QString &text, int replacementIndex, int replacedLength, int matchedLength)
+{
+ kdDebug() << "slotReplace index=" << replacementIndex << " replacedLength=" << replacedLength << " matchedLength=" << matchedLength << " text=" << text.left( 50 ) << endl;
+ *m_currentPos = text; // KReplace hacked the replacement into 'text' in already.
+}
+
+void KReplaceTest::slotReplaceNext()
+{
+ //kdDebug() << k_funcinfo << endl;
+ KFind::Result res = KFind::NoMatch;
+ while ( res == KFind::NoMatch && m_currentPos != m_text.end() ) {
+ if ( m_replace->needData() )
+ m_replace->setData( *m_currentPos );
+
+ // Let KReplace inspect the text fragment, and display a dialog if a match is found
+ res = m_replace->replace();
+
+ if ( res == KFind::NoMatch ) {
+ if ( m_replace->options() & KFindDialog::FindBackwards )
+ m_currentPos--;
+ else
+ m_currentPos++;
+ }
+ }
+
+#if 0 // commented out so that this test doesn't require interaction
+ if ( res == KFind::NoMatch ) // i.e. at end
+ if ( m_replace->shouldRestart() ) {
+ if ( m_replace->options() & KFindDialog::FindBackwards )
+ m_currentPos = m_text.fromLast();
+ else
+ m_currentPos = m_text.begin();
+ slotReplaceNext();
+ }
+#endif
+ if ( res == KFind::NoMatch && m_needEventLoop )
+ qApp->eventLoop()->exitLoop();
+}
+
+void KReplaceTest::print()
+{
+ QStringList::Iterator it = m_text.begin();
+ for ( ; it != m_text.end() ; ++it )
+ kdDebug() << *it << endl;
+}
+
+/* button is the button that we emulate pressing, when options includes PromptOnReplace.
+ Valid possibilities are User1 (replace all) and User3 (replace) */
+static void testReplaceSimple( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceSimple: " << options << endl;
+ KReplaceTest test( QString( "hellohello" ), button );
+ test.replace( "hello", "HELLO", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( textLines[ 0 ] != "HELLOHELLO" ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of 'HELLOHELLO'" << endl;
+ exit(1);
+ }
+}
+
+// Replacing "a" with "".
+// input="aaaaaa", expected output=""
+static void testReplaceBlank( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceBlank: " << options << endl;
+ KReplaceTest test( QString( "aaaaaa" ), button );
+ test.replace( "a", "", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( !textLines[ 0 ].isEmpty() ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of ''" << endl;
+ exit(1);
+ }
+}
+
+// Replacing "" with "foo"
+// input="bbbb", expected output="foobfoobfoobfoobfoo"
+static void testReplaceBlankSearch( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceBlankSearch: " << options << endl;
+ KReplaceTest test( QString( "bbbb" ), button );
+ test.replace( "", "foo", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( textLines[ 0 ] != "foobfoobfoobfoobfoo" ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of 'foobfoobfoobfoobfoo'" << endl;
+ exit(1);
+ }
+}
+
+static void testReplaceLonger( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceLonger: " << options << endl;
+ // Standard test of a replacement string longer than the matched string
+ KReplaceTest test( QString( "aaaa" ), button );
+ test.replace( "a", "bb", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( textLines[ 0 ] != "bbbbbbbb" ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of 'bbbbbbbb'" << endl;
+ exit(1);
+ }
+}
+
+static void testReplaceLongerInclude( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceLongerInclude: " << options << endl;
+ // Similar test, where the replacement string includes the search string
+ KReplaceTest test( QString( "a foo b" ), button );
+ test.replace( "foo", "foobar", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( textLines[ 0 ] != "a foobar b" ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of 'a foobar b'" << endl;
+ exit(1);
+ }
+}
+
+static void testReplaceLongerInclude2( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceLongerInclude2: " << options << endl;
+ // Similar test, but with more chances of matches inside the replacement string
+ KReplaceTest test( QString( "aaaa" ), button );
+ test.replace( "a", "aa", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ if ( textLines[ 0 ] != "aaaaaaaa" ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of 'aaaaaaaa'" << endl;
+ exit(1);
+ }
+}
+
+// Test for the \0 backref
+static void testReplaceBackRef( int options, int button = 0 )
+{
+ kdDebug() << "testReplaceBackRef: " << options << endl;
+ KReplaceTest test( QString( "abc def" ), button );
+ test.replace( "abc", "(\\0)", options );
+ QStringList textLines = test.textLines();
+ assert( textLines.count() == 1 );
+ QString expected = options & KReplaceDialog::BackReference ? "(abc) def" : "(\\0) def";
+ if ( textLines[ 0 ] != expected ) {
+ kdError() << "ASSERT FAILED: replaced text is '" << textLines[ 0 ] << "' instead of '"<< expected << "'" << endl;
+ exit(1);
+ }
+}
+
+static void testReplacementHistory( const QStringList& findHistory, const QStringList& replaceHistory )
+{
+ KReplaceDialog dlg( 0, 0, 0, findHistory, replaceHistory );
+ dlg.show();
+ kdDebug() << "testReplacementHistory:" << dlg.replacementHistory() << endl;
+ assert( dlg.replacementHistory() == replaceHistory );
+}
+
+static void testReplacementHistory()
+{
+ QStringList findHistory;
+ QStringList replaceHistory;
+ findHistory << "foo" << "bar";
+ replaceHistory << "FOO" << "BAR";
+ testReplacementHistory( findHistory, replaceHistory );
+
+ findHistory.clear();
+ replaceHistory.clear();
+ findHistory << "foo" << "bar";
+ replaceHistory << QString::null << "baz"; // #130831
+ testReplacementHistory( findHistory, replaceHistory );
+}
+
+int main( int argc, char **argv )
+{
+ KCmdLineArgs::init(argc, argv, "kreplacetest", 0, 0);
+ KApplication app;
+
+ testReplacementHistory(); // #130831
+
+ testReplaceBlank( 0 );
+ testReplaceBlank( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBlank( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceBlank( KReplaceDialog::FindBackwards, 0 );
+ testReplaceBlank( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBlank( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceBlankSearch( 0 );
+ testReplaceBlankSearch( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBlankSearch( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceBlankSearch( KReplaceDialog::FindBackwards, 0 );
+ testReplaceBlankSearch( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBlankSearch( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceSimple( 0 );
+ testReplaceSimple( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceSimple( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceSimple( KReplaceDialog::FindBackwards, 0 );
+ testReplaceSimple( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceSimple( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceLonger( 0 );
+ testReplaceLonger( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLonger( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceLonger( KReplaceDialog::FindBackwards, 0 );
+ testReplaceLonger( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLonger( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceLongerInclude( 0 );
+ testReplaceLongerInclude( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLongerInclude( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceLongerInclude( KReplaceDialog::FindBackwards, 0 );
+ testReplaceLongerInclude( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLongerInclude( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceLongerInclude2( 0 );
+ testReplaceLongerInclude2( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLongerInclude2( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceLongerInclude2( KReplaceDialog::FindBackwards, 0 );
+ testReplaceLongerInclude2( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceLongerInclude2( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ testReplaceBackRef( 0 );
+ testReplaceBackRef( KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBackRef( KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceBackRef( KReplaceDialog::FindBackwards, 0 );
+ testReplaceBackRef( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBackRef( KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceBackRef( KReplaceDialog::BackReference | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBackRef( KReplaceDialog::BackReference | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+ testReplaceBackRef( KReplaceDialog::BackReference | KReplaceDialog::FindBackwards, 0 );
+ testReplaceBackRef( KReplaceDialog::BackReference | KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User3 ); // replace
+ testReplaceBackRef( KReplaceDialog::BackReference | KReplaceDialog::FindBackwards | KReplaceDialog::PromptOnReplace, KDialogBase::User1 ); // replace all
+
+ QString text = "This file is part of the KDE project.\n"
+ "This library is free software; you can redistribute it and/or\n"
+ "modify it under the terms of the GNU Library General Public\n"
+ "License version 2, as published by the Free Software Foundation.\n"
+ "\n"
+ " This library is distributed in the hope that it will be useful,\n"
+ " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+ " Library General Public License for more details.\n"
+ "\n"
+ " You should have received a copy of the GNU Library General Public License\n"
+ " along with this library; see the file COPYING.LIB. If not, write to\n"
+ " the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,\n"
+ " Boston, MA 02110-1301, USA.\n"
+ "More tests:\n"
+ "ThisThis This, This. This\n"
+ "aGNU\n"
+ "free";
+ KReplaceTest test( QStringList::split( '\n', text, true ), 0 );
+
+ test.replace( "GNU", "KDE", 0 );
+ test.replace( "free", "*free*", 0 );
+ test.replace( "This", "THIS*", KFindDialog::FindBackwards );
+
+ test.print();
+ //return app.exec();
+ return 0;
+}
+#include "kreplacetest.moc"
diff --git a/kutils/tests/kreplacetest.h b/kutils/tests/kreplacetest.h
new file mode 100644
index 000000000..a60563e77
--- /dev/null
+++ b/kutils/tests/kreplacetest.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2002, David Faure <david@mandrakesoft.com>
+ This file is part of the KDE project
+
+ 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 KREPLACETEST_H
+#define KREPLACETEST_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class KReplace;
+
+class KReplaceTest : public QObject
+{
+ Q_OBJECT
+public:
+ KReplaceTest( const QStringList& text, int button )
+ : QObject( 0L ), m_text( text ), m_replace( 0 ), m_button( button ) {}
+
+ void replace( const QString &pattern, const QString &replacement, long options );
+ void print();
+ const QStringList& textLines() const { return m_text; }
+
+public slots:
+ void slotHighlight( const QString &, int, int );
+ void slotReplaceNext();
+ void slotReplace(const QString &text, int replacementIndex, int replacedLength, int matchedLength);
+
+private:
+ QStringList::Iterator m_currentPos;
+ QStringList m_text;
+ KReplace* m_replace;
+ bool m_needEventLoop;
+ int m_button;
+};
+
+#endif