got the dot?

This article is the first in a series of tutorials intended to help introduce the development facilities available in the KDE 2 framework to a wider audience. All of the articles will be published on the KDE Developer Site (developer.kde.org) with dot.kde.org providing a forum for comments and questions.

Next time, Matthias Elter will explain how to extend Kicker, but first lets take a look at how to extend KDE with plugins.


Writing Plugins For KDE Applications

Richard Moore, rich@kde.org

Introduction

This tutorial aims to demonstrate how easy it is to write plugins for KDE 2 using KParts. It took me about 20 minutes to get my first plugin working, and hopefully after reading this, you will find it just as straight forward. The same mechanism applies to any KPart based application, so once you understand what's here you should be able to write plugins for pretty much any application or component in KDE!

Basics

KPart based applications such as Konqueror allow you to dynamically load components and actions, and provide facilities to merge the UIs of these components with that of the application. This sounds really complicated, but all it means to us is that the KParts framework does all the work, and we can pretty much forget about it. We write our code as normal, then write a little piece of boiler plate code and an XML file to set things going.

To KParts, a plugin is a way to extend an application, a part, or even another plugin. A plugin is only an extension to something else, and can't be used on its own. The most visible way plugins extend applications is by adding new commands to the menus and toolbars, in KParts these are automatically integrated with those of the host application using XML and KActions. In addition to this automatic functionality, the plugin can use the standard KParts interfaces and those defined by the parent application to add or change the behaviour of its host.

The HTML Validator Plugin

The plugin we'll be writing is an HTML validator that uses the W3C online validator - this is a very simple plugin, all it has to do is send the user to a particular URL. To get an idea of what to expect you might like to look at the validator results page for the KDE home page. Because the operation of this plugin is so simple, it will hopefully demonstrate the actual mechanics of KParts clearly.

The first thing we'll look at is the code that actually does the work:

KURL PluginHTMLValidator::validateURL( const KURL &url )
{
    // Check syntax
    if ( url.isMalformed() ) {
        QString title = i18n( "Malformed URL" );
        QString text = i18n( "<qt><p>The URL you entered is not valid, please "
                                    "correct it and try again</p></qt>" );
        QMessageBox::warning( 0, title, text );
        return QString::null;
    }

    // Create w3c URL
    KURL w3c( "http://validator.w3.org/check/" );

    // Set entered URL as a parameter
    QString p;
    QString q = url.url();
    q = KURL::encode_string( q );
    p = "uri=";
    p += q;

    w3c.setQuery( p );

    return w3c; // Check syntax
}

It is not important how this works, but to understand how the plugin operates you should note that this method takes the current URL being viewed as a parameter, and returns the URL at which you can find the validation results.

The Plugin Class

The main class of a plugin must inherit the KParts::Plugin base class provided by libkparts. Here is the declaration for the validator plugin class:

class PluginHTMLValidator : public KParts::Plugin
{
  Q_OBJECT
public:
  PluginHTMLValidator( QObject* parent = 0, const char* name = 0 );
  virtual ~PluginHTMLValidator();


  KURL validateURL( const KURL &url );
public slots:
  void slotValidate();
};

As you can see this is pretty simple - it only defines two methods, validateURL() which we've seen, and slotValidate() which will fire when the user decides to invoke the plugin.

The implementation of our plugin is just as simple as its declaration suggests. The constructor simply creates the KAction that will be plugged into the user interface, and adds it to the parent applications action collection. If you have used actions or KXMLGUIClient before, then you'll see that the process is is identical to that used in standalone applications. Notice that we specify a name for our action 'validatewebpage', this is used whenever we want to refer to the action in the XML we use to specify our user interface.

PluginHTMLValidator::PluginHTMLValidator( QObject* parent, const char* name )
  : Plugin( parent, name )
{
  (void) new KAction( "&Validate Web Page (plugin)", 0,
                      this, SLOT( slotValidate() ),
                      actionCollection(), "validatewebpage" );
}

PluginHTMLValidator::~PluginHTMLValidator()
{
}

void PluginHTMLValidator::slotValidate()
{
  // The parent is assumed to be a KHTMLPart
  if ( !parent()->inherits("KHTMLPart") ) {
    QString title = i18n( "Cannot validate source" );
    QString text = i18n( "You cannot validate anything except web pages with\n"
                         "this plugin, sorry." );

    QMessageBox::warning( 0, title, text );
  }
  else
  {
    KHTMLPart *part = (KHTMLPart *) parent();

    // Get URL
    KURL url = part->url();
    KURL w3c = validateURL( url );

    part->openURL( w3c );
  }
}

The final method in our plugin class is the slot that sets things going. The first part just checks that we are dealing with the kind of KPart we need - in our case KHTMLPart (or a subclass). Once we know we've got what we want we downcast our parent QObject to a KHTMLPart, and get to work. We first get the URL the user is currently viewing (using the url() method defined by KReadOnlyPart), then use the validateURL() method we defined earlier to figure out where we want to point the browser. Finally we send the browser to its new location by calling openURL(), which is another useful method defined by KReadOnlyPart.

Believe it or not, we've now done most of the work. All that remains is to fill out the boiler plate stuff we need to get our plugin attached to the application, which is the topic of the next section.

Getting The Plugin Loaded

In order to get our plugins loaded by their parent application, we need to do a little more work. The trouble is that the parent doesn't know anything about your plugin, not even how to instantiate one. To allow KParts to instantiate your plugin we need to provide another class called a factory. The factory is an example of one of the most common design patterns, so you may well have encountered the idea before. Here is the declaration of the factory class for the HTML validator plugin:

class KPluginFactory : public KLibFactory
{
  Q_OBJECT
public:
  KPluginFactory( QObject *parent = 0, const char *name = 0 );
  ~KPluginFactory() { delete s_instance; };


  virtual QObject* create( QObject* parent = 0, const char* pname = 0,
                           const char* name = "QObject",
                           const QStringList &args = QStringList() );

private:
  static KInstance* s_instance;
};

As you can see it is quite simple. The most important method is create() which will create and return an instance of our plugin.

There is only ever a single instance of the factory class, and in it we keep a pointer s_instance which stores the KInstance used by our plugin. Now that we've seen the declaration, we can examine the implementation:

KPluginFactory::KPluginFactory( QObject* parent, const char* name )
  : KLibFactory( parent, name )
{
  s_instance = new KInstance("KPluginFactory");
}

QObject* KPluginFactory::create( QObject* parent, const char* name,
                                 const char*, const QStringList & )
{
  QObject *obj = new PluginHTMLValidator( parent, name );
  emit objectCreated( obj );
  return obj;
}

extern "C"
{
  void *init_libhtmlvalidatorplugin()
  {
    return new KPluginFactory;
  }
}

KInstance *KPluginFactory::s_instance = 0L;

This code looks a little confusing, but in fact only the two lines in red are specific to the HTML validator plugin, all the rest is just a matter of cut and paste. The first creates an instance of our plugin object, setting the parent and name according to the parameters passed. The second is similar, but creates the factory object. The name of the method should match that of the shared library your plugin is defined in; this method has to have C linkage.

It is important that the constructor of our factory class creates our KInstance object, this object is used to manage our plugins resources such as XML files.

Attaching To The GUI

Most (but not all) plugins need to make some controls visible in the host applications user interface, this might consist of one or more menu items, toolbar items,  or might take a different form all together. Our requirements for the validator plugin are very modest - all we need is a menu item. We tell KParts this using a simple XML file.

<!DOCTYPE kpartgui>
<kpartgui library="libhtmlvalidatorplugin" name="htmlvalidatorplugin" version="1" >
<MenuBar>
  <Menu name="tools"><Text>&amp;Tools</Text>
    <Action name="validatewebpage"/>
  </Menu>
</MenuBar>
</kpartgui>

As before, if you have used KXMLGUIClient you will find this familiar. The first line specifies that this XML file is a kpartgui, and will follow the kpartgui DTD. The next line specifies the name of the shared library that contains the plugin, the name of our plugin and the version of the UI file we are using. The rest of the file describes our user interface.

What we are trying to do is add an item to the Konqueror tools menu, it should be fairly clear how this corresponds to the XML. The most important part of this is the Action tag which actually plugs our code into the GUI. As you can see we refer to the KAction we wrote by name - KParts will automatically look for it in our plugin's action collection. The name, icons, tooltips and all the other information about the action we've defined are all managed by the KAction class, so the XML file will always be fairly simple.

Building The Plugin

Thanks to the magic of autoconf, automake, libtool and am_edit building our plugin is easy.  We use the standard KDE 2 configuration scripts, and write a Makefile.am like this:

INCLUDES= $(all_includes)
LDFLAGS = $(all_libraries)
LDADD = $(LIB_KPARTS) $(LIB_KDECORE)

lib_LTLIBRARIES = libhtmlvalidatorplugin.la

libhtmlvalidatorplugin_la_SOURCES = plugin_htmlvalidator.cpp
libhtmlvalidatorplugin_la_LIBADD = $(LIB_KDEUI) $(LIB_KPARTS) $(LIB_KHTML)
libhtmlvalidatorplugin_la_LDFLAGS = $(KDE_PLUGIN)

pluginsdir = $(kde_datadir)/khtml/kpartplugins
plugins_DATA = plugin_htmlvalidator.rc

METASOURCES = AUTO

The line in red specifies what the shared library for our plugin will be called, the name should always be of the form libXXX.la (libtool will automatically handle adapting this to the naming requirements of the platform it is being compiled for). The next group of lines are like those used by any other KDE application and specify the source files, libraries required and some linker flags. The final line in the file is also pretty standard and simply tells am_edit to manage the moc files.

The remaining two lines in the file install our XML file and are critical to the operation of our plugin. The first specifies where our XML files will be installed, and the second lists the files (in our case the list has only one item). For our HTML validator we want to be a plugin to the KHTMLPart (the HTML component). Plugging directly into the component rather than into Konqueror ensures that the validate action will only be available when the user is viewing HTML and also means that our plugin can be used by any application that uses the HTML component not just Konqueror. It is very important when writing plugins to consider carefully what you are going to plug into. In the case of Konqueror you should remember that it has different personalities depending on what is being browsed, so unless your plugin is very general you should probably plug into one of the views rather than Konqueror itself. If your code is usable by several views, you can simply create several XML files. Here we install the XML file in KHTMLs data directory in a subdirectory called kpartplugins. If we wanted to plug into Konqueror it would have read $(kde_datadir)/konqueror/kpartplugins.

The build process itself is the same as any other KDE application, namely:

$ make -f Makefile.cvs
$ ./configure --prefix=/usr/local/kde2 --with-qt-dir=/usr/local/qt2
$ make
$ make install

You'll need to change the paths in the configure line to point to wherever you installed KDE and Qt. Once you've compiled and installed the plugin, you can test it by starting Konqueror and opening a web site. If you look at the Tools menu, you will see a new menu option to validate the web page.

Adding a Toolbar Button

Now that we've seen how to add new menu items, we'll take a look at how to add new buttons to a toolbar. This is actually trivial, we simply add a couple of lines XML like this:

<ToolBar name="mainToolBar">
  <Action name="validatewebpage"/>
</ToolBar>

This will add the validatewebpage action to the toolbar named 'mainToolBar'. There is no problem with using the same action in both menus and toolbars, so we just add these lines to our existing XML. As it stands however, the validatewebpage action won't work too well in a toolbar, because we didn't give the validatewebpage action any icons. This is easily remedied by making a minor change (in red) to our constructor:

  (void) new KAction( "&Validate Web Page (plugin)",
                      "spellcheck",
                      0,
                      this, SLOT( slotValidate() ),
                      actionCollection(), "validatewebpage" );

I've just used the standard spellchecker icon here, but you could use a custom icon by calling UserIcon() instead if you wanted. You do of course need to make sure an icon with the desired name exists, or you will get the unknown icon.

A final thing to consider is that even before we added the XML to display the toolbar button, the user could have added the button themself by using the standard configure toolbars dialog. You should bear this in mind when creating actions and provide an icon if at all possible.

More Advanced Plugins

The HTML validator plugin is very simple, and doesn't cover even a fraction of the possibilities offered by the KDE plugin system, more complex plugins might provide many actions, create dialogs or embed KParts. It is even possible to use custom actions that create your own widgets when they are plugged into an application and to get events that tell you what the host application is doing. The XML files are also much more powerful than we have seen, you can configure the widgets representing your action from the XML file using QProperties, create new toolbars and more.

The integrated nature of the KParts framework means that plugins, read-only parts etc. share common functionality, this means that knowledge, techniques and code that can be used in an application or part will also be useful for plugins. If you want to look at more complex use of the framework you should start off by examining the various subclasses of KAction, you will find examples of their use throughout the KDE sources. The source code for Konqueror provides some useful examples of both custom KActions and plugins.

Conclusion

I hope that this tutorial has made it easy to see just how simple it is to write plugins for KDE 2, and has given you a glimpse of just how powerful the API can be, I look forward to trying out your creations.

You can download the source code for the example program.

Questions or comments? Feel free to use the forum.


Ricarde Moore

About the Author

Richard Moore is a member of the KDE Core Team and the creator of the KJAS library used to add Java applet support to Konqueror. He has also written a number of KDE applications including Keystone and KSnapshot.