TDEConfig Module HOWTO

From Trinity Desktop Project Wiki
Jump to navigation Jump to search

A TDEConfig Module is a small library with a TDECModule subclass containing code which builds and support a interface for configuration tasks. The module is then encapsulated in a parent application, for example tdecmshell, KControl or a KCMultiDialog.

This HOWTO describes how to write TDEConfig Modules. This technology was originally designed for KControl. Although KControl is the application where the TDECModule tehnology is the most used, it is used in several other places as well, such as Kopete and Kontact. This HOWTO describes how to write config modules, regardless of where it will be used. TDEConfig Modules were originally called "KControl Modules".

If the TDECModule in question is intended for KControl the KControl Module Guidelines must be consulted. Much of it applies to KConfig Modules in general, it is probably a good read regardless of what application the KCM targets.


How to write a KConfig Module

Now to the practical side of the matter. Modules have to inherit from the class TDECModule, defined in kcmodule.h, which is part of the tdeui library. This class defines some important functions that are used in the communication between the framework and the module:

virtual void load();

This method is invoked whenever the module should read its configuration (most of the times from a config file) and update the user interface. This happens when the user clicks the "Reset" button in the control center, to undo all of his changes and restore the currently valid settings. NOTE that this is not called after the modules is loaded, so you probably want to call this method in the constructor.

virtual void save();

You might have guessed it: this function gets called when the user wants to save the settings in the user interface, updating the config files or wherever the configuration is stored. The method is called when the user clicks "Apply" or "Ok".

virtual void defaults();

This function is called to set the settings in the module to sensible default values. It gets called when hitting the "Defaults" button. The default values should probably be the same as the ones the application uses when started without a config file.

int buttons();

The module loader calls this function to decide which buttons should be displayed. For example, it does not make sense to display an "Apply" button for one of the information modules. The value returned can be set by modules using setButtons. E.g:

setButtons( KCModule::Ok | KCModule::Help );

will make sure that only the "Ok" and the "Help" button is enabled.

There is also two signals used by the modules:

void changed(bool state);

This signal should be emitted whenever the state of the modules changes. "state" is true, if the contents of the modules differ from the ones in the current configuration, "false" otherwise. For example, if the user changes a value, you emit changed(true). When the user reloads the systems settings, emit changed(false). The module loader uses this signal to keep track about unsaved changes in certain modules.

void quickHelpChanged();

Emit this signal whenever the module's quickhelp changes. Modules implemented as tabbed dialogs might want to implement per-tab quickhelp for example.

There is also a slot for convenience:

void changed();

Calling this slot is equal to emitting changed(true).


Making a module available

Ok, now that you have implemented your class with the module, you must make sure it can be accessed.

To achieve this, the preferred way is to simply use KGenericFactory. Don't forget to #include <kgenericfactory.h>.

typedef KGenericFactory<body TQWidget> KDEDFactory;
K_EXPORT_COMPONENT_FACTORY( kcm_xyz, KDEDFactory( "kcm_xyz" ) );

If you get errors, make sure that the constructor of your derived class equals the one of the TDECModule baseclass (the TQStringList argument matters).

If you need to export more than one module per library, you have to use the old loader. That is, you need to create a function like this:

extern "C"
{
  TDEModule *create_xyz(TQWidget *parent, const char *name)
  {
    return new XYZ(parent, name); // XYZ inherits from TDECModule
  };
}

This function and the implementation of the module is then compiled as a shared library. If the name of your modules is 'xyz', the name of the library should be kcm_xyz(.la|.so) and should be installed into $TDEDIR/lib/trinity.


Initializing on startup

If your module needs to initialize on TDE session startup, you must have a construct like:

extern "C"
{
  TDECModule *init_xyz(TQWidget *parent, const char *name)
  {
    // Do initialization here
  };
}

Don't forget to add X-TDE-Init to your desktop file. (see below)

Making a KCModule dynamically available

Sometimes it is of interest to hide a TDECModule when it does not have any use for the current situation, for example certain hardware or software is lacking. This is achieved by adding to the library:

extern "C"
{
  TDECModule *test_xyz(TQWidget *parent, const char *name)
  {
    // Probing for hardware/software precense
    return true; // If the TDECModule should show up, otherwise it should return false.
  };
}

The desktop file must also contain "X-TDE-Test-Module=true" in order for this to work.

The TDECModule will now be "conditionally" loaded, for example when requested via KCMultiDialog, tdecmshell or KControl (as of 2004-03-21 not implemented in KControl).


The desktop file

To declare a TDECModule's existence a desktop file must be installed. Call it "kcm_modulename.desktop".

It could be a good idea to have a look at the desktop file standard.

A module's .desktop file supports the following .desktop directives:

Directive Description
X-TDE-Library This is the name of the library, without the kcm_ prefix. So in the example, the library name would be "xyz".
X-TDE-FactoryName This entry can be used to set the name of the factory function in the library. If you only have one TDECModule in a library this directive is not needed. If you have several modules in one library you will need a .desktop file for each TDECModule.

For example, if you have a library named kcm_frog.la with two modules, named "kermit" and "quak", you would have:

X-KDE-Library=frog
X-KDE-FactoryName=kermit

in one of the .desktop files, and in the other:

X-KDE-Library=frog
X-KDE-FactoryName=quak

The module loader would then call the "create_kermit" and "create_quak" functions respectively.

X-TDE-RootOnly If this is set to "true", the module must be executed with root permissions. The module loader will then show the module in greyed-out (disabled) state with a warning until the "modify" button is pressed which allows running the module in an root environment using tdesu and QXEmbed.
X-TDE-ParentApp The application you put in this entry determines in what situations it will show. For example, if the line says X-TDE-ParentApp=kcontrol the module will show up in KControl. It is very crucial the selected ParentApp is correct, otherwise the module show up in unnecessary places.
X-TDE-Init If the module has to perform some action at system startup, use this entry to build the name of a function to call. if X-TDE-Init is "bell", for example, the function "init_bell" is called in the library indicated by X-TDE-Library.
X-TDE-Test-Module If the module has to perform some action at system startup, use this entry to build the name of a function to call. if X-TDE-Init is "bell", for example, the function "init_bell" is called in the library indicated by X-TDE-Library.
NoDisplay If this is set to true the module will not show up in kcontrol or when viewed with tdecmshell. This is usable when you need to do something at startup (X-TDE-Init etc.) but don't want the module to show up in kcontrol, e.g. the module has no GUI.
Name This is 'labels' for the modules and fill the nodes in the three view. Please see the module guidelines on how to pick a good Name.
Comment This directive shows up in the main area in KControl if you select a top node in the three view. See the same section as for Name in the module guidelines for how to pick a good phrase.
Categories This describes where the module should be put in KControl's navigation. It should look like "Categories=QT;TDE;X;" where X is the category. A list of available categories, as well as which one to choose is found in the module guidelines.
Icon Specifies the icon for the module.
Exec/Type These two should say Exec=kcmshell modulename and Type=Application. Used for internal purposes
Keywords A semi colon separated list containing words/phrases search functionality should trigger on.

To summarize, a valid .desktop file must contain these entries:

  • Name
  • Icon
  • Type=Application
  • Exec=tdecmshell modulename
  • Categories=QT;KDE;X; (replace X with your category)
  • Keywords
  • X-TDE-Library
  • X-TDE-ParentApp
  • Comment

And these are optional:

  • X-TDE-Init
  • X-TDE-Test-Module
  • NoDisplay
  • X-TDE-Root-Only
  • X-TDE-Factory-Name
  • DocPath

Any other directives(except the translators) can safely be removed, since they most likely are abundant or are leftovers from old KDE versions. For example, X-ModuleType was relevant for KDE 2.0 but not in any new versions.


What else do I need?

There are a number of additional things for convenience.

tdecmshell

Consider you want to run a module standalone. Call tdecmshell [module]. For example, to get the font and the desktop color settings, use:

$ tdecmshell fonts colors

KCMultiDialog

Sometimes, you may want to reuse your TDECModule inside an application. There are two ways to accomplish this:

  • Use KCMultiDialog (in tdelibs/tdeutils)
    This is a simple dialog that can show an abitrary number of modules in a normal KDialogBase. The advantage is that you can control the behaviour and the results much easier that with a separate process. And as your module is a simple library, you can just link to it anyway.
  • Call tdecmshell [modules...].

KCModuleContainer

The class KCModuleContainer allows great flexibility with handling modules. The API docs explain it usage the best.

Debugging your module

You can attach gdb, valgrind or whatever to tdecmshell [yourmodule] to track down leaks or crashes. If you need to trace it down inside kcontrol, make sure you pass it --nofork on startup. You really want to use tdecmshell for debugging as long as your debugging does not involve debugging bad interaction with the kcontrol framework itself.



Copyright notice

Imported from KDE Developer Documentation SVN.

Copyright (C) 2003 Daniel Molkentin <molketin@kde.org>
Copyright (C) 2004 Frans Englich <frans.englich@telia.com>
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".