12.9. Creating a KParts Plug-in

A plug-in is the way to implement some functionality out of a part but still in a shared library, with actions defined by the plug-in to access this functionality. Those actions, whose layout is described in XML as usual, can be merged in a part's user interface or in a mainwindow's, depending on whether it applies to a part or to an application.

Several reasons exist for using plug-ins. One is saving memory, because the plug-in is not loaded until one of its actions is called, but the main reason is reusability—the same plug-in can apply to several parts or applications. For instance, a spell-checker plug-in can apply to all kinds of text editors, mail composers, word processors, and even presenters.

A plug-in can have a user interface, such as the dialog box for the spell checker, but not necessarily. Plug-ins can also act directly on the part or the application or anything else.

The XML for a spell-checker plug-in is shown below:

   2 <!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
   3 <kpartplugin library="libspellcheck">
   4 <MenuBar>
   5  <Menu name="edit"><Text>&amp;Edit</Text>
   6   <Action name="spellcheck"/>
   7  </Menu>
   8 </MenuBar>
   9 </kpartgui>

Note the additional attribute in the main tag: library defines the name of the library to open to find the plug-in. This is because no .desktop file exists for plug-ins. Installing the preceding XML file in partplugins/, under share/apps/notepadpart, automatically inserts the plug-in's action in the NotepadPart user interface.

You know how the plug-in's library will be opened; now you need only to create a factory in the library, as usual, and let it create an instance of the plug-in. Writing the factory, which doesn't even need an instance in the simple case, and the init_libspellcheck() function will be left as an exercise to the reader.

To define a plug-in, simply inherit KParts::Plugin and add slots for its actions:

   2 #include <kparts/plugin.h>
   3 class PluginSpellCheck : public KParts::Plugin
   4 {
   5     Q_OBJECT
   6 public:
   7     PluginSpellCheck( QObject* parent = 0, const char* name = 0 );
   8     virtual ~PluginSpellCheck() {}
   9 public slots:
  10     void slotSpellCheck();
  11 };

In the implementation, you have to create the plug-in actions; no setXMLFile is here because it has been found by the part already.

Because in this example you are not going to create a real spell checker—a libkspell exists for that— call the action "select current line" and implement that in the slot.

   2 #include "plugin_spellcheck.h"
   3 #include "notepad.h" // this plugin applies to a notepad part
   4 #include <qmultilineedit.h>
   5 #include <kaction.h>
   7 PluginSpellCheck::PluginSpellCheck( QObject* parent, const char* name )
   8     : Plugin( parent, name )
   9 {
  10     (void) new KAction( i18n( "&Select current line (plug-in)" ), 0, this,
  11                 SLOT(slotSpellCheck()), actionCollection(), "spellcheck" );
  12 }
  14 void PluginSpellCheck::slotSpellCheck()
  15 {
  16     // Check that the parent is a NotepadPart
  17     if ( !parent()->inherits("NotepadPart") )
  18        kdFatal() << "Spell-check plug-in for wrong part (not NotepadPart)" << endl;
  19     else
  20     {
  21          NotepadPart * part = (NotepadPart *) parent();
  22          QMultiLineEdit * widget = (QMultiLineEdit *) part->widget();
  23          widget->selectAll(); //selects current line !
  24     }
  25 }

Note that to access the part's widget, the plug-in has to assume—and check—that it has been installed for a NotepadPart. This means that you should not install it under another part's directory. But selecting the current line in an image viewer wouldn't mean much anyway.

A more flexible plug-in would instead check and cast the parent to ReadWritePart and then check the type of its widget to be QMultiLineEdit.