This tutorial is for KDE4.
First at all, why writing plugins for an editor ? Good question, and I hope I have a good answer: Because we want the base app be small and all extended features not all users need should go into plugins (like CVS suppport, project managment, coffee cooking ;) Therefore Kate provides a quite fullfeatured plugin interface and interfaces to all important stuff in the kate application (the documents, views, mainwindows, sidebar ...).
This tutorial is for people knowing the Qt/KDE libs (and how to code regular KDE apps) and will not describe how to compile, make ... a kde/qt program/lib in detail.
The "helloworld" plugin which is here described can be found in the "kdesdk" package of kde (located in kdesdk/kate/plugins/helloworld). A detailed description of the Kate API is available here..
Like in each programming language learning book I start with an easy "Hello World!" plugin, too ;) This plugin only implements a menuitem and inserts the string "Hello World!" into the current document. The example should show only the basic stuff you need to get your first plugin running, extensions won't be too hard because some existing plugins in kdesdk/kate/plugins are available to steal some code and the API is quiet self-explaining (I hope ;)
Each plugin consists of at least 3 files which are needed:
The desktop file needs the syntax shown here and must be located in kde services directory with the name kate"yourpluginname".desktop,
Example: katehelloworld.desktop
[Desktop Entry] Type=Service ServiceTypes=Kate/Plugin X-KDE-Library=katehelloworldplugin X-Kate-Version=3.0 Name=My first Kate Plugin Comment=Your short description about the plugin goes here
The "X-Kate-Version=3.0" line is important! Kate 3.0 and up won't load your plugin unless the property is similar to the version with which you want to use the plugin!
To install this desktop file to KDE's services directory, we'll later add the following line to the CMakeLists.txt file:
install( FILES katehelloworld.desktop DESTINATION ${SERVICES_INSTALL_DIR} )The xmlgui resource file describes how the menu and toolbar actions appear in the Kate application. For more detailed examples look into other plugins. The ui.rc file for the katehelloworldplugin looks like this:
<!DOCTYPE kpartgui>
<kpartplugin name="katehelloworld" library="libkatehelloworldplugin" version="1">
<MenuBar>
<Menu name="edit"><Text>&Edit</Text>
<Action name="edit_insert_helloworld" />
</Menu>
</MenuBar>
</kpartplugin>
To automatically install the ui.rc file into the right directory later we add the following line to CMakeLists.txt:
install( FILES ui.rc DESTINATION ${DATA_INSTALL_DIR}/kate/plugins/katehelloworld )Each kate plugin is a library which Kate will dynamically load on demand via KLibLoader. The name of the library is kate"yourpluginname"plugin.so and should be installed into KDE's plugin directory.
The complete result for the file CMakeLists.txt looks like this:
set(katehelloworldplugin_PART_SRCS plugin_katehelloworld.cpp )
kde4_add_plugin(katehelloworldplugin ${katehelloworldplugin_PART_SRCS})
target_link_libraries(katehelloworldplugin ${KDE4_KDEUI_LIBS} kateinterfaces )
########### install files ###############
install(TARGETS katehelloworldplugin DESTINATION ${PLUGIN_INSTALL_DIR} )
install( FILES ui.rc DESTINATION ${DATA_INSTALL_DIR}/kate/plugins/katehelloworld )
install( FILES katehelloworld.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
Now we can start with the real coding, The Hello World! plugin consists of 2 files, plugin_katehelloworld.cpp and .h.
#ifndef _PLUGIN_KATE_HELLOWORLD_H_
#define _PLUGIN_KATE_HELLOWORLD_H_
#include <kate/mainwindow.h>
#include <kate/plugin.h>
#include <kxmlguiclient.h>
class KatePluginHelloWorld : public Kate::Plugin
{
Q_OBJECT
public:
explicit KatePluginHelloWorld( QObject* parent = 0, const QList& = QList() );
virtual ~KatePluginHelloWorld();
Kate::PluginView *createView( Kate::MainWindow *mainWindow );
};
class KatePluginHelloWorldView : public Kate::PluginView, public KXMLGUIClient
{
Q_OBJECT
public:
KatePluginHelloWorldView( Kate::MainWindow *mainWindow );
~KatePluginHelloWorldView();
virtual void readSessionConfig( KConfigBase* config, const QString& groupPrefix );
virtual void writeSessionConfig( KConfigBase* config, const QString& groupPrefix );
public slots:
void slotInsertHello();
};
#endif
#ifndef _PLUGIN_KATE_HELLOWORLD_H_ #define _PLUGIN_KATE_HELLOWORLD_H_
This are the include guards, that avoid the file to be included more than once.
#include <kate/mainwindow.h> #include <kate/plugin.h> #include <kxmlguiclient.h>
This part includes the Kate Plugin Interface headers and some other needed kde headers.
class KatePluginHelloWorld : public Kate::Plugin
{
Q_OBJECT
public:
explicit KatePluginHelloWorld( QObject* parent = 0, const QList& = QList() );
virtual ~KatePluginHelloWorld();
Kate::PluginView *createView( Kate::MainWindow *mainWindow );
};
We derive a class from Kate::Plugin. The Kate application will create exactly one instance of this class. To hook into Kate's mainwindows, we have to implement the createView() fuction.
class KatePluginHelloWorldView : public Kate::PluginView, public KXMLGUIClient
{
Q_OBJECT
public:
KatePluginHelloWorldView( Kate::MainWindow *mainWindow );
~KatePluginHelloWorldView();
virtual void readSessionConfig( KConfigBase* config, const QString& groupPrefix );
virtual void writeSessionConfig( KConfigBase* config, const QString& groupPrefix );
public slots:
void slotInsertHello();
};
This class delivers the content for every mainwindow. Our class has one slot called slotInsertHello(). This will be called later when the menu item "Insert Hello World" is activated.
#include "plugin_katehelloworld.h" #include "plugin_katehelloworld.moc" #include <kate/application.h> #include <ktexteditor/view.h> #include <kaction.h> #include <kactioncollection.h> #include <klocale.h> #include <kpluginfactory.h> #include <kpluginloader.h> #include <kaboutdata.h> K_PLUGIN_FACTORY(KatePluginHelloWorldFactory, registerPlugin();) K_EXPORT_PLUGIN(KatePluginHelloWorldFactory(KAboutData("katehelloworld","katehelloworld",ki18n("Hello World"), "0.1", ki18n("Example kate plugin"))) ) KatePluginHelloWorld::KatePluginHelloWorld( QObject* parent, const QList & ) : Kate::Plugin( (Kate::Application*)parent, "kate-hello-world-plugin" ) { } KatePluginHelloWorld::~KatePluginHelloWorld() { } Kate::PluginView *KatePluginHelloWorld::createView( Kate::MainWindow *mainWindow ) { return new KatePluginHelloWorldView( mainWindow ); } KatePluginHelloWorldView::KatePluginHelloWorldView( Kate::MainWindow *mainWin ) : Kate::PluginView( mainWin ) { setComponentData( KatePluginHelloWorldFactory::componentData() ); setXMLFile( "plugins/katehelloworld/ui.rc" ); KAction *a = actionCollection()->addAction( "edit_insert_helloworld" ); a->setText( i18n("Insert Hello World") ); connect( a, SIGNAL( triggered(bool) ), this, SLOT( slotInsertHello() ) ); mainWindow()->guiFactory()->addClient( this ); } KatePluginHelloWorldView::~KatePluginHelloWorldView() { mainWindow()->guiFactory()->removeClient( this ); } void KatePluginHelloWorldView::slotInsertHello() { if (!mainWindow()) { return; } KTextEditor::View *kv = mainWindow()->activeView(); if (kv) { kv->insertText( "Hello World" ); } } void KatePluginHelloWorldView::readSessionConfig( KConfigBase* config, const QString& groupPrefix ) { // If you have session-dependant settings, load them here. // If you have application wide settings, you have to read your own KConfig, // see the Kate::Plugin docs for more information. Q_UNUSED( config ); Q_UNUSED( groupPrefix ); } void KatePluginHelloWorldView::writeSessionConfig( KConfigBase* config, const QString& groupPrefix ) { // If you have session-dependant settings, save them here. // If you have application wide settings, you have to create your own KConfig, // see the Kate::Plugin docs for more information. Q_UNUSED( config ); Q_UNUSED( groupPrefix ); }
#include "plugin_katehelloworld.h" #include "plugin_katehelloworld.moc" #include <kate/application.h> #include <ktexteditor/view.h> #include <kaction.h> #include <kactioncollection.h> #include <klocale.h> #include <kpluginfactory.h> #include <kpluginloader.h> #include <kaboutdata.h>
Include the header and moc file of the plugin, and some KDE includes needed by the plugin.
K_PLUGIN_FACTORY(KatePluginHelloWorldFactory, registerPlugin();) K_EXPORT_PLUGIN(KatePluginHelloWorldFactory(KAboutData("katehelloworld","katehelloworld",ki18n("Hello World"), "0.1", ki18n("Example kate plugin"))) )
This macros expands to functions that will create an instance of KatePluginHelloWorld.
KatePluginHelloWorld::KatePluginHelloWorld( QObject* parent, const QList& ) : Kate::Plugin( (Kate::Application*)parent, "kate-hello-world-plugin" ) { } KatePluginHelloWorld::~KatePluginHelloWorld() { }
Constructor/Destructor of our example plugin class. This is usually empty. The only thing to do in the constructor is to pass the arguments to Kate::Plugin.
Kate::PluginView *KatePluginHelloWorld::createView( Kate::MainWindow *mainWindow )
{
return new KatePluginHelloWorldView( mainWindow );
}
As already mentioned, createView() is called for every Kate mainwindow. This means here we have to return an instance of our KatePluginHelloWorldView for the mainwindow.
KatePluginHelloWorldView::KatePluginHelloWorldView( Kate::MainWindow *mainWin )
: Kate::PluginView( mainWin )
{
setComponentData( KatePluginHelloWorldFactory::componentData() );
setXMLFile( "plugins/katehelloworld/ui.rc" );
KAction *a = actionCollection()->addAction( "edit_insert_helloworld" );
a->setText( i18n("Insert Hello World") );
connect( a, SIGNAL( triggered(bool) ), this, SLOT( slotInsertHello() ) );
mainWindow()->guiFactory()->addClient( this );
}
KatePluginHelloWorldView::~KatePluginHelloWorldView()
{
mainWindow()->guiFactory()->removeClient( this );
}
Creating an instance of KatePluginHelloWorldView will call the constructor. This is where we have to set the xmlgui file ui.rc and then setup the action we defined there. The action's signal is then connected to our slot slotInsertHello(). Finally we add ourselves to the xmlgui factory by calling addClient(). Note the call of removeClient() in the desturctor for a clean exit.
void KatePluginHelloWorldView::slotInsertHello()
{
if (!mainWindow()) {
return;
}
KTextEditor::View *activeView = mainWindow()->activeView();
if (activeView) {
activeView->insertText( "Hello World" );
}
}
Here the actions takes place: this slot is called if you hit the "Insert Hello World" menuitem which is created by the pluginview.
mainWindow()->activeView() returns the current active view of kate and activeView->insertText("Hello World") inserts the text at the current cursor position. That's it! :)
void KatePluginHelloWorldView::readSessionConfig( KConfigBase* config, const QString& groupPrefix )
{
// If you have session-dependant settings, load them here.
// If you have application wide settings, you have to read your own KConfig,
// see the Kate::Plugin docs for more information.
Q_UNUSED( config );
Q_UNUSED( groupPrefix );
}
void KatePluginHelloWorldView::writeSessionConfig( KConfigBase* config, const QString& groupPrefix )
{
// If you have session-dependant settings, save them here.
// If you have application wide settings, you have to create your own KConfig,
// see the Kate::Plugin docs for more information.
Q_UNUSED( config );
Q_UNUSED( groupPrefix );
}
Session related data can be stored and loaded for every mainwindow separately. The helloworld plugin does not need this however.
You first have to go to the Settings -> Configure Kate -> Kate -> Plugins in Kate and activate the Hello World plugin, after that you have a "Insert Hello World" menuitem in your edit menu like on the screenshot. If you activate the menuitem the text "Hello World" will appear at the cursorposition in your current document.
To get more information about the plugin interface and its advanced functions please look at the Kate API documentation and the KTextEditor API documentation or contact us on our mailing list!
The complete sources (with CMakeLists.txt) of this example can be found in KDE's source repository under kdesdk/kate/plugins/helloworld or via the web interface.
Comments
The example is wrong
After some hours of be trying to complile the example, i just found some mistakes in the code. In plugin_katehelloworld.h where in this line
explicit KatePluginHelloWorld( QObject* parent = 0, const QList& = QList() );
change it for
explicit KatePluginHelloWorld( QObject* parent = 0,const QList<QVariant>& = QList<QVariant>());
and in plugin_katehelloworld.cpp
replace
KPLUGINFACTORY(KatePluginHelloWorldFactory, registerPlugin();) KEXPORTPLUGIN(KatePluginHelloWorldFactory(KAboutData(“katehelloworld”,”katehelloworld”,ki18n(“Hello World”), “0.1”, ki18n(“Example kate plugin”))) )
KatePluginHelloWorld::KatePluginHelloWorld( QObject* parent, const QList& )
whit this
KPLUGINFACTORY(KatePluginHelloWorldFactory, registerPlugin<KatePluginHelloWorld>();) KEXPORTPLUGIN(KatePluginHelloWorldFactory(KAboutData(“katehelloworld”,”katehelloworld”,ki18n(“Hello World”), “0.1”, ki18n(“Example kate plugin”))) )
KatePluginHelloWorld::KatePluginHelloWorld( QObject* parent, const QList<QVariant>& )
Kind Regards
Mixel Adm
a plugin to write plugins
pate>http://paul.giannaros.org/pate/
Plugin to modify printed output?
I’d like to print from Kate, but have the printout highlight alternating lines of text so lines that wrap will be visually easier to see. Is that possible with the Kate plugins, or do I need to look to something else? (I’m using Kubuntu 7.10)
A Letter to Kate
Just as a sidenote you might be interested in this letter from a certain J. Appleseed. :-)
/Andreas
make
compiling is most difficult to newbies; more difficult than reading and understanding sources !!!!
how to compile
Makefile.am Should this be compiled with automake?
automake:
configure.ac' orconfigure.in’ is requiredautom4te: configure.ac: no such file or directory
??
How can I compile it with automake? How to compile this by directly invoking gcc (if automake is not installed)?
Broken links
The link to KDE API documentation and the link to the source are broken. Particularly, the source link should point to the SVN not CVS ;-)