You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
643 lines
17 KiB
643 lines
17 KiB
/* This file is part of the KDE project
|
|
Copyright (C) 2003 Matthias Kretz <kretz@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
#include "ksettings/dialog.h"
|
|
|
|
|
|
#include <kcmultidialog.h>
|
|
#include <klocale.h>
|
|
#include <kservicegroup.h>
|
|
#include <kdebug.h>
|
|
#include <ktrader.h>
|
|
#include <kplugininfo.h>
|
|
#include "ksettings/dispatcher.h"
|
|
#include "ksettings/componentsdialog.h"
|
|
#include <ksimpleconfig.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kiconloader.h>
|
|
#include <tqvbox.h>
|
|
#include <tqlabel.h>
|
|
#include "kcmoduleinfo.h"
|
|
|
|
namespace KSettings
|
|
{
|
|
|
|
struct GroupInfo
|
|
{
|
|
TQString id;
|
|
TQString name;
|
|
TQString comment;
|
|
TQString icon;
|
|
int weight;
|
|
TQString parentid;
|
|
TQWidget * page;
|
|
};
|
|
|
|
// The TreeList can get really complicated. That's why a tree data structure
|
|
// is necessary to make it suck less
|
|
class PageNode
|
|
{
|
|
private:
|
|
typedef TQValueList<PageNode*> List;
|
|
enum Type { KCM, Group, Root };
|
|
union Value
|
|
{
|
|
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();
|
|
}
|
|
|
|
TQString name() const
|
|
{
|
|
if( Root == m_type )
|
|
return TQString::fromAscii( "root node" );
|
|
return ( KCM == m_type ) ? m_value.kcm->moduleName()
|
|
: m_value.group->name;
|
|
}
|
|
|
|
TQStringList parentNames() const
|
|
{
|
|
TQStringList ret;
|
|
PageNode * node = m_parent;
|
|
while( node && node->m_type != Root )
|
|
{
|
|
ret.prepend( node->name() );
|
|
node = node->m_parent;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void addToDialog( KCMultiDialog * dlg )
|
|
{
|
|
kdDebug( 700 ) << k_funcinfo << "for " << name() << endl;
|
|
if( ! isVisible() )
|
|
return;
|
|
|
|
if( KCM == m_type )
|
|
{
|
|
dlg->addModule( *m_value.kcm, parentNames() );
|
|
return;
|
|
}
|
|
if( Group == m_type && 0 == m_value.group->page )
|
|
{
|
|
TQPixmap icon;
|
|
if( ! m_value.group->icon.isNull() )
|
|
icon = SmallIcon( m_value.group->icon,
|
|
IconSize( KIcon::Small ) );
|
|
TQVBox * page = dlg->addVBoxPage( m_value.group->name,
|
|
TQString::null, icon );
|
|
TQLabel * comment = new TQLabel( m_value.group->comment, page );
|
|
comment->setTextFormat( 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 TQString & parentid )
|
|
{
|
|
if( parentid.isNull() )
|
|
{
|
|
if( Root == m_type )
|
|
{
|
|
m_children.append( new PageNode( info, this ) );
|
|
return true;
|
|
}
|
|
else
|
|
kdFatal( 700 ) << "wrong PageNode insertion"
|
|
<< kdBacktrace() << endl;
|
|
}
|
|
if( Group == m_type && parentid == m_value.group->id )
|
|
{
|
|
m_children.append( new PageNode( info, this ) );
|
|
return true;
|
|
}
|
|
List::Iterator end = m_children.end();
|
|
for( List::Iterator it = m_children.begin(); it != end; ++it )
|
|
if( ( *it )->insert( info, parentid ) )
|
|
return true;
|
|
// no parent with the right parentid
|
|
if( Root == m_type )
|
|
{
|
|
m_children.append( new PageNode( info, this ) );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool needTree()
|
|
{
|
|
List::ConstIterator end = m_children.end();
|
|
for( List::ConstIterator it = m_children.begin(); it != end; ++it )
|
|
if( ( *it )->m_children.count() > 0 )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool singleChild()
|
|
{
|
|
return ( m_children.count() == 1 );
|
|
}
|
|
};
|
|
|
|
class Dialog::DialogPrivate
|
|
{
|
|
public:
|
|
DialogPrivate( Dialog * parent )
|
|
: dlg( 0 )
|
|
, pagetree( parent )
|
|
{
|
|
}
|
|
|
|
bool staticlistview;
|
|
KCMultiDialog * dlg;
|
|
PageNode pagetree;
|
|
TQWidget * parentwidget;
|
|
TQStringList registeredComponents;
|
|
TQValueList<KService::Ptr> services;
|
|
TQMap<TQString, KPluginInfo*> plugininfomap;
|
|
};
|
|
|
|
Dialog::Dialog( TQWidget * parent, const char * name )
|
|
: TQObject( parent, name )
|
|
, d( new DialogPrivate( this ) )
|
|
{
|
|
d->parentwidget = parent;
|
|
d->staticlistview = true;
|
|
d->services = instanceServices();
|
|
}
|
|
|
|
Dialog::Dialog( ContentInListView content,
|
|
TQWidget * parent, const char * name )
|
|
: TQObject( parent, name )
|
|
, d( new DialogPrivate( this ) )
|
|
{
|
|
d->parentwidget = parent;
|
|
d->staticlistview = ( content == Static );
|
|
d->services = instanceServices();
|
|
}
|
|
|
|
Dialog::Dialog( const TQStringList & components,
|
|
TQWidget * parent, const char * name )
|
|
: TQObject( parent, name )
|
|
, d( new DialogPrivate( this ) )
|
|
{
|
|
d->parentwidget = parent;
|
|
d->staticlistview = true;
|
|
d->services = instanceServices() + parentComponentsServices( components );
|
|
}
|
|
|
|
Dialog::Dialog( const TQStringList & components,
|
|
ContentInListView content, TQWidget * parent, const char * name )
|
|
: TQObject( parent, name )
|
|
, d( new DialogPrivate( this ) )
|
|
{
|
|
d->parentwidget = parent;
|
|
d->staticlistview = ( content == Static );
|
|
d->services = instanceServices() + parentComponentsServices( components );
|
|
}
|
|
|
|
Dialog::~Dialog()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void Dialog::addPluginInfos( const TQValueList<KPluginInfo*> & plugininfos )
|
|
{
|
|
for( TQValueList<KPluginInfo*>::ConstIterator it = plugininfos.begin();
|
|
it != plugininfos.end(); ++it )
|
|
{
|
|
d->registeredComponents.append( ( *it )->pluginName() );
|
|
d->services += ( *it )->kcmServices();
|
|
d->plugininfomap[ ( *it )->pluginName() ] = *it;
|
|
}
|
|
}
|
|
|
|
void Dialog::show()
|
|
{
|
|
if( 0 == d->dlg )
|
|
createDialogFromServices();
|
|
Dispatcher::self()->syncConfiguration();
|
|
return d->dlg->show();
|
|
}
|
|
|
|
KCMultiDialog * Dialog::dialog()
|
|
{
|
|
if( 0 == d->dlg )
|
|
createDialogFromServices();
|
|
return d->dlg;
|
|
}
|
|
|
|
TQValueList<KService::Ptr> Dialog::instanceServices() const
|
|
{
|
|
kdDebug( 700 ) << k_funcinfo << endl;
|
|
TQString instanceName = KGlobal::instance()->instanceName();
|
|
d->registeredComponents.append( instanceName );
|
|
kdDebug( 700 ) << "calling KServiceGroup::childGroup( " << instanceName
|
|
<< " )" << endl;
|
|
KServiceGroup::Ptr service = KServiceGroup::childGroup( instanceName );
|
|
|
|
TQValueList<KService::Ptr> ret;
|
|
|
|
if( service && service->isValid() )
|
|
{
|
|
kdDebug( 700 ) << "call was successfull" << endl;
|
|
KServiceGroup::List list = service->entries();
|
|
for( KServiceGroup::List::ConstIterator it = list.begin();
|
|
it != list.end(); ++it )
|
|
{
|
|
KSycocaEntry * p = *it;
|
|
if( p->isType( KST_KService ) )
|
|
{
|
|
kdDebug( 700 ) << "found service" << endl;
|
|
ret << static_cast<KService *>( p );
|
|
}
|
|
else
|
|
kdWarning( 700 ) << "KServiceGroup::childGroup returned"
|
|
" something else than a KService (kinda)" << endl;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
TQValueList<KService::Ptr> Dialog::parentComponentsServices(
|
|
const TQStringList & kcdparents ) const
|
|
{
|
|
d->registeredComponents += kcdparents;
|
|
TQString constraint = kcdparents.join(
|
|
"' in [X-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
|
|
TQStringList parentComponents = moduleinfo->service()->property(
|
|
"X-KDE-ParentComponents" ).toStringList();
|
|
for( TQStringList::ConstIterator pcit = parentComponents.begin();
|
|
pcit != parentComponents.end(); ++pcit )
|
|
{
|
|
// if the parentComponent is not registered ignore it
|
|
if( d->registeredComponents.find( *pcit ) ==
|
|
d->registeredComponents.end() )
|
|
continue;
|
|
|
|
// we check if the parent component is a plugin
|
|
if( ! d->plugininfomap.contains( *pcit ) )
|
|
{
|
|
// if not the 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 TQString & filename )
|
|
{
|
|
KSimpleConfig file( filename );
|
|
TQStringList groups = file.groupList();
|
|
for( TQStringList::ConstIterator it = groups.begin(); it != groups.end();
|
|
++it )
|
|
{
|
|
GroupInfo group;
|
|
TQString id = *it;
|
|
file.setGroup( id.utf8() );
|
|
group.id = id;
|
|
group.name = file.readEntry( "Name" );
|
|
group.comment = file.readEntry( "Comment" );
|
|
group.weight = file.readNumEntry( "Weight", 100 );
|
|
group.parentid = file.readEntry( "Parent" );
|
|
group.icon = file.readEntry( "Icon" );
|
|
d->pagetree.insert( group );
|
|
}
|
|
}
|
|
|
|
void Dialog::createDialogFromServices()
|
|
{
|
|
// read .setdlg files
|
|
TQString setdlgpath = locate( "appdata",
|
|
KGlobal::instance()->instanceName() + ".setdlg" );
|
|
TQStringList setdlgaddon = KGlobal::dirs()->findAllResources( "appdata",
|
|
"ksettingsdialog/*.setdlg" );
|
|
if( ! setdlgpath.isNull() )
|
|
parseGroupFile( setdlgpath );
|
|
if( setdlgaddon.size() > 0 )
|
|
for( TQStringList::ConstIterator it = setdlgaddon.begin();
|
|
it != setdlgaddon.end(); ++it )
|
|
parseGroupFile( *it );
|
|
|
|
// now we process the KCModule services
|
|
for( TQValueList<KService::Ptr>::ConstIterator it = d->services.begin();
|
|
it != d->services.end(); ++it )
|
|
{
|
|
// we create the KCModuleInfo
|
|
KCModuleInfo * info = new KCModuleInfo( *it );
|
|
TQString parentid;
|
|
TQVariant tmp = info->service()->property( "X-KDE-CfgDlgHierarchy",
|
|
TQVariant::String );
|
|
if( tmp.isValid() )
|
|
parentid = tmp.toString();
|
|
d->pagetree.insert( info, parentid );
|
|
}
|
|
|
|
// At this point d->pagetree holds a nice structure of the pages we want
|
|
// to show. It's not going to change anymore so we can sort it now.
|
|
d->pagetree.sort();
|
|
|
|
int dialogface = KJanusWidget::IconList;
|
|
if( d->pagetree.needTree() )
|
|
dialogface = KJanusWidget::TreeList;
|
|
else if( d->pagetree.singleChild() )
|
|
dialogface = KJanusWidget::Plain;
|
|
|
|
kdDebug( 700 ) << "creating KCMultiDialog" << endl;
|
|
d->dlg = new KCMultiDialog( dialogface, i18n( "Configure" ),
|
|
d->parentwidget );
|
|
|
|
if( dialogface == KJanusWidget::TreeList )
|
|
d->dlg->setShowIconsInTreeList( true );
|
|
|
|
// TODO: Don't show the reset button until the issue with the
|
|
// KPluginSelector::load() method is solved.
|
|
// Problem:
|
|
// KCMultiDialog::show() call 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,
|
|
TQT_SLOT( configureTree() ) );
|
|
|
|
connect( d->dlg, TQT_SIGNAL( okClicked() ), Dispatcher::self(),
|
|
TQT_SLOT( syncConfiguration() ) );
|
|
connect( d->dlg, TQT_SIGNAL( applyClicked() ), Dispatcher::self(),
|
|
TQT_SLOT( syncConfiguration() ) );
|
|
connect( d->dlg, TQT_SIGNAL( configCommitted( const TQCString & ) ),
|
|
Dispatcher::self(), TQT_SLOT( reparseConfiguration( const TQCString & ) ) );
|
|
|
|
d->pagetree.addToDialog( d->dlg );
|
|
|
|
if( dialogface == KJanusWidget::TreeList )
|
|
d->dlg->unfoldTreeList();
|
|
}
|
|
|
|
void Dialog::configureTree()
|
|
{
|
|
kdDebug( 700 ) << k_funcinfo << endl;
|
|
ComponentsDialog * subdlg = new ComponentsDialog( d->dlg );
|
|
subdlg->setPluginInfos( d->plugininfomap );
|
|
subdlg->show();
|
|
connect( subdlg, TQT_SIGNAL( okClicked() ), this, TQT_SLOT( updateTreeList() ) );
|
|
connect( subdlg, TQT_SIGNAL( applyClicked() ), this, TQT_SLOT( updateTreeList() ) );
|
|
connect( subdlg, TQT_SIGNAL( okClicked() ), this,
|
|
TQT_SIGNAL( pluginSelectionChanged() ) );
|
|
connect( subdlg, TQT_SIGNAL( applyClicked() ), this,
|
|
TQT_SIGNAL( pluginSelectionChanged() ) );
|
|
connect( subdlg, TQT_SIGNAL( finished() ), subdlg, TQT_SLOT( delayedDestruct() ) );
|
|
}
|
|
|
|
void Dialog::updateTreeList()
|
|
{
|
|
kdDebug( 700 ) << k_funcinfo << endl;
|
|
|
|
d->pagetree.makeDirty();
|
|
|
|
// remove all pages from the dialog and then add them again. This is needed
|
|
// because KDialogBase/KJanusWidget can only append to the end of the list
|
|
// and we need to have a predefined order.
|
|
|
|
d->pagetree.removeFromDialog( d->dlg );
|
|
d->pagetree.addToDialog( d->dlg );
|
|
|
|
if( d->pagetree.needTree() )
|
|
d->dlg->unfoldTreeList( true );
|
|
}
|
|
|
|
} //namespace
|
|
|
|
#include "dialog.moc"
|
|
|
|
// vim: sw=4 ts=4 noet
|