Development/Tutorials/Kate/KTextEditor Plugins: Difference between revisions
(Mark for updating) |
|||
(14 intermediate revisions by 6 users not shown) | |||
Line 5: | Line 5: | ||
name=Creating your first Kate Plugin| | name=Creating your first Kate Plugin| | ||
pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build | pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build|KDE development environment]]| | ||
next=Add a configuration dialog for it | next=[[Development/Tutorials/Kate/KTextEditor_Plugins_Advanced|Add a configuration dialog for it]]| | ||
reading=[[Development/Tutorials/CMake|CMake]], [http://websvn.kde.org/trunk/KDE/kdelibs/kate/plugins/timedate The actual plugin code] | reading=[[Development/Tutorials/CMake|CMake]], [http://websvn.kde.org/trunk/KDE/kdelibs/kate/plugins/timedate The actual plugin code] | ||
}} | }} | ||
{{Review|Port to KF5}} | |||
==Abstract== | ==Abstract== | ||
We are going to create a dummy plugin for Kate in this tutorial. Our plugin will be able to insert current Date & Time. For now, our plugin will add on the cursor position on the active document the next information: | We are going to create a dummy plugin for Kate in this tutorial. Our plugin will be able to insert current Date & Time. For now, our plugin will add on the cursor position on the active document the next information: | ||
Line 23: | Line 26: | ||
'''ktexteditor_timedate.desktop''' | '''ktexteditor_timedate.desktop''' | ||
< | <syntaxhighlight lang="ini"> | ||
[Desktop Entry] | [Desktop Entry] | ||
Encoding=UTF-8 | Encoding=UTF-8 | ||
Line 43: | Line 46: | ||
Name=Time & Date | Name=Time & Date | ||
Comment=Insert current Time & Date | Comment=Insert current Time & Date | ||
</ | </syntaxhighlight> | ||
There are important parts on this file: | There are important parts on this file: | ||
Line 61: | Line 64: | ||
'''timedateui.rc''' | '''timedateui.rc''' | ||
< | <syntaxhighlight lang="xml"> | ||
<!DOCTYPE kpartgui> | <!DOCTYPE kpartgui> | ||
<kpartplugin name="ktexteditor_timedate" library="ktexteditor_timedate" version="2"> | <kpartplugin name="ktexteditor_timedate" library="ktexteditor_timedate" version="2"> | ||
Line 71: | Line 74: | ||
</MenuBar> | </MenuBar> | ||
</kpartplugin> | </kpartplugin> | ||
</ | </syntaxhighlight> | ||
=== The header file === | === The header file === | ||
'''timedate.h''' | '''timedate.h''' | ||
< | <syntaxhighlight lang="cpp-qt"> | ||
// Avoid multiple header inclusion | |||
#ifndef TIMEDATE_H | #ifndef TIMEDATE_H | ||
#define TIMEDATE_H | #define TIMEDATE_H | ||
// Include the basics | |||
#include <ktexteditor/plugin.h> | #include <ktexteditor/plugin.h> | ||
#include <ktexteditor/view.h> | #include <ktexteditor/view.h> | ||
#include <kxmlguiclient.h> | #include <kxmlguiclient.h> | ||
#include <klocalizedstring.h> | #include <klocalizedstring.h> | ||
#include <QtCore/QEvent> | #include <QtCore/QEvent> | ||
#include <QtCore/QObject> | #include <QtCore/QObject> | ||
#include <QtCore/QList> | #include <QtCore/QList> | ||
/** | |||
* This is the plugin class. There will be only one instance of this class. | |||
* We always want to inherit KTextEditor::Plugin here. | |||
*/ | |||
class TimeDatePlugin | class TimeDatePlugin | ||
: public KTextEditor::Plugin | : public KTextEditor::Plugin | ||
{ | { | ||
public: | public: | ||
explicit TimeDatePlugin(QObject *parent | // Constructor | ||
const QVariantList &args | explicit TimeDatePlugin(QObject *parent, | ||
const QVariantList &args); | |||
// Destructor | |||
virtual ~TimeDatePlugin(); | virtual ~TimeDatePlugin(); | ||
// Overriden methods | |||
// This method is called when a plugin has to be added to a view. As there | |||
// is only one instance of this plugin, but it is possible for plugins to | |||
// behave in different ways in different opened views where it is loaded, in | |||
// Kate plugins are added to views. For that reason we have the plugin itself | |||
// (this class) and then the plugin view class. | |||
// In this methods we have to create/remove TimeDatePluginView classes. | |||
void addView (KTextEditor::View *view); | void addView (KTextEditor::View *view); | ||
void removeView (KTextEditor::View *view); | void removeView (KTextEditor::View *view); | ||
Line 107: | Line 119: | ||
void readConfig(); | void readConfig(); | ||
void writeConfig(); | void writeConfig(); | ||
private: | private: | ||
QList<class TimeDatePluginView*> m_views; | QList<class TimeDatePluginView*> m_views; | ||
}; | }; | ||
/** | |||
* This is the plugin view class. There can be as much instances as views exist. | |||
*/ | |||
class TimeDatePluginView | class TimeDatePluginView | ||
: public QObject, public KXMLGUIClient | : public QObject, public KXMLGUIClient | ||
Line 126: | Line 133: | ||
public: | public: | ||
explicit TimeDatePluginView( | explicit TimeDatePluginView(KTextEditor::View *view = 0); | ||
~TimeDatePluginView(); | ~TimeDatePluginView(); | ||
private Q_SLOTS: | private Q_SLOTS: | ||
Line 138: | Line 141: | ||
private: | private: | ||
KTextEditor::View *m_view; | KTextEditor::View *m_view; | ||
}; | }; | ||
#endif // TIMEDATE_H | #endif // TIMEDATE_H | ||
</ | </syntaxhighlight> | ||
=== The source file === | === The source file === | ||
'''timedate.cpp''' | '''timedate.cpp''' | ||
< | <syntaxhighlight lang="cpp-qt"> | ||
// Own includes | // Own includes | ||
#include "timedate.h" | #include "timedate.h" | ||
Line 182: | Line 182: | ||
// We put there the X-KDE-LibraryName. | // We put there the X-KDE-LibraryName. | ||
// Is important to provide as last parameter "ktexteditor_plugins". | // Is important to provide as last parameter "ktexteditor_plugins". | ||
K_EXPORT_PLUGIN(TimeDatePluginFactory("ktexteditor_timedate", "ktexteditor_plugins") | K_EXPORT_PLUGIN(TimeDatePluginFactory("ktexteditor_timedate", "ktexteditor_plugins")) | ||
// Constructor | // Constructor | ||
Line 234: | Line 234: | ||
, m_view(view) | , m_view(view) | ||
{ | { | ||
setComponentData(TimeDatePluginFactory::componentData()); | setComponentData(TimeDatePluginFactory::componentData()); | ||
Line 281: | Line 279: | ||
// the Q_OBJECT macro on the TimeDatePluginView class. | // the Q_OBJECT macro on the TimeDatePluginView class. | ||
#include "timedate.moc" | #include "timedate.moc" | ||
</ | </syntaxhighlight> | ||
=== Building it all, the CMakeLists.txt === | === Building it all, the CMakeLists.txt === | ||
Line 288: | Line 286: | ||
For more details on CMake please read [[Development/Tutorials/CMake]] | For more details on CMake please read [[Development/Tutorials/CMake]] | ||
< | <syntaxhighlight lang="cmake"> | ||
find_package(KDE4 REQUIRED) | |||
include (KDE4Defaults) | |||
include_directories(${KDE4_INCLUDES}) | |||
# We are calling our plugin "ktexteditor_timedate", and it contains only | # We are calling our plugin "ktexteditor_timedate", and it contains only | ||
# one source file: timedate.cpp. | # one source file: timedate.cpp. | ||
Line 294: | Line 296: | ||
# We need to link our plugin against kdecore libs, as well as ktexteditor | # We need to link our plugin against kdecore libs, as well as ktexteditor | ||
target_link_libraries(ktexteditor_timedate ${KDE4_KDECORE_LIBS} ktexteditor) | target_link_libraries(ktexteditor_timedate ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ktexteditor) | ||
# Well, we want to install our plugin on the plugin directory | # Well, we want to install our plugin on the plugin directory | ||
Line 307: | Line 309: | ||
# on the services directory. | # on the services directory. | ||
install(FILES ktexteditor_timedate.desktop DESTINATION ${SERVICES_INSTALL_DIR}) | install(FILES ktexteditor_timedate.desktop DESTINATION ${SERVICES_INSTALL_DIR}) | ||
</ | </syntaxhighlight> | ||
=== The next part === | === The next part === |
Latest revision as of 12:24, 31 May 2019
Tutorial Series | Kate Plugin Tutorial |
Previous | C++, Qt, KDE development environment |
What's Next | Add a configuration dialog for it |
Further Reading | CMake, The actual plugin code |
Parts to be reviewed:
Port to KF5Abstract
We are going to create a dummy plugin for Kate in this tutorial. Our plugin will be able to insert current Date & Time. For now, our plugin will add on the cursor position on the active document the next information:
- MM-DD-YYYY HH:MM (something like "08-22-2007 23:45")
On the next chapter we will learn how to customize this output string creating a configuration dialog for this plugin.
The Code
The .desktop file
Every plugin needs a .desktop file that describes it.
ktexteditor_timedate.desktop
[Desktop Entry]
Encoding=UTF-8
X-KDE-Library=ktexteditor_timedate
X-KDE-PluginInfo-Author=Konqui the Dragon
X-KDE-PluginInfo-Email=[email protected]
X-KDE-PluginInfo-Name=ktexteditortimedate
X-KDE-PluginInfo-Version=0.1
X-KDE-PluginInfo-Website=http://kate.kde.org
X-KDE-PluginInfo-Category=Editor
X-KDE-PluginInfo-Depends=
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=false
X-KDE-ParentApp=kate
X-KDE-Version=4.0
ServiceTypes=KTextEditor/Plugin
Type=Service
Icon=korganizer
Name=Time & Date
Comment=Insert current Time & Date
There are important parts on this file:
- X-KDE-Library: will define the library that provides this plugin. It is _really_ important that this variable matches exactly the same with the actual name of the library being built. You will understand this better when getting to the finish of this tutorial on the part "Building it all, the CMakeLists.txt".
- X-KDE-PluginInfo-Name: no special characters. The name of the plugin.
- X-KDE-PluginInfo-Category: important when the plugin selector loads it. Usually here you want always to put "Editor".
- X-KDE-PluginInfo-Depends: if your plugin depends on another, write here the corresponding X-KDE-PluginInfo-Name that is needed to be loaded too when loading this plugin.
- X-KDE-PluginInfo-EnabledByDefault: whether the plugin is enabled by default. Applied when clicking on "Defaults" button on the plugin selector.
- X-KDE-ParentApp: you want here always "kate".
- X-KDE-Version: KWrite/Kate does a check for only loading 4 series plugins.
- ServiceTypes: you will always need here "KTextEditor/Plugin".
- Type: in order to load it properly this is needed as "Service".
The resource contents file
This file is the one that will let our plugin merge with the Kate environment (toolbars and/or menubars). In this case when our plugin is loaded this file tells the KDE XML classes that it will add a separator, and that there is an action named "tools_insert_timedate". We will need to interact with this action later.
timedateui.rc
<!DOCTYPE kpartgui>
<kpartplugin name="ktexteditor_timedate" library="ktexteditor_timedate" version="2">
<MenuBar>
<Menu name="tools"><Text>&Tools</Text>
<separator group="tools_operations" />
<Action name="tools_insert_timedate" group="tools_operations"/>
</Menu>
</MenuBar>
</kpartplugin>
The header file
timedate.h
// Avoid multiple header inclusion
#ifndef TIMEDATE_H
#define TIMEDATE_H
// Include the basics
#include <ktexteditor/plugin.h>
#include <ktexteditor/view.h>
#include <kxmlguiclient.h>
#include <klocalizedstring.h>
#include <QtCore/QEvent>
#include <QtCore/QObject>
#include <QtCore/QList>
/**
* This is the plugin class. There will be only one instance of this class.
* We always want to inherit KTextEditor::Plugin here.
*/
class TimeDatePlugin
: public KTextEditor::Plugin
{
public:
// Constructor
explicit TimeDatePlugin(QObject *parent,
const QVariantList &args);
// Destructor
virtual ~TimeDatePlugin();
// Overriden methods
// This method is called when a plugin has to be added to a view. As there
// is only one instance of this plugin, but it is possible for plugins to
// behave in different ways in different opened views where it is loaded, in
// Kate plugins are added to views. For that reason we have the plugin itself
// (this class) and then the plugin view class.
// In this methods we have to create/remove TimeDatePluginView classes.
void addView (KTextEditor::View *view);
void removeView (KTextEditor::View *view);
void readConfig();
void writeConfig();
private:
QList<class TimeDatePluginView*> m_views;
};
/**
* This is the plugin view class. There can be as much instances as views exist.
*/
class TimeDatePluginView
: public QObject, public KXMLGUIClient
{
Q_OBJECT
public:
explicit TimeDatePluginView(KTextEditor::View *view = 0);
~TimeDatePluginView();
private Q_SLOTS:
void slotInsertTimeDate();
private:
KTextEditor::View *m_view;
};
#endif // TIMEDATE_H
The source file
timedate.cpp
// Own includes
#include "timedate.h"
// Include the basics
#include <ktexteditor/document.h>
#include <kpluginfactory.h>
#include <kpluginloader.h>
#include <klocale.h>
#include <kaction.h>
#include <kactioncollection.h>
#include <kdatetime.h>
// This macro defines a KPluginFactory subclass named TimeDatePluginFactory. The second
// argument to the macro is code that is inserted into the constructor of the class.
// I our case all we need to do is register one plugin. If you want to have more
// than one plugin in the same library then you can register multiple plugin classes
// here. The registerPlugin function takes an optional QString parameter which is a
// keyword to uniquely identify the plugin then (it maps to X-KDE-PluginKeyword in the
// .desktop file).
K_PLUGIN_FACTORY(TimeDatePluginFactory,
registerPlugin<TimeDatePlugin>();
)
// With the next macro call, the library exports version information about the
// Qt and KDE libraries being used and (most important) the entry symbol to get at
// the factory we defined above.
// The argument this macro takes is the constructor call of the above factory which
// provides two constructors. One which takes a KAboutData* and another one
// that takes two (optional) const char* parameters (Same as for KComponentData
// constructors).
// We put there the X-KDE-LibraryName.
// Is important to provide as last parameter "ktexteditor_plugins".
K_EXPORT_PLUGIN(TimeDatePluginFactory("ktexteditor_timedate", "ktexteditor_plugins"))
// Constructor
TimeDatePlugin::TimeDatePlugin(QObject *parent, const QVariantList &args)
: KTextEditor::Plugin(parent)
{
// Avoid warning on compile time because of unused argument
Q_UNUSED(args);
}
// Destructor
TimeDatePlugin::~TimeDatePlugin()
{
}
// Create the plugin view class and add it to the views list
void TimeDatePlugin::addView(KTextEditor::View *view)
{
TimeDatePluginView *nview = new TimeDatePluginView(view);
m_views.append(nview);
}
// Find the view where we want to remove the plugin from, and remove it.
// Do not forget to free the memory.
void TimeDatePlugin::removeView(KTextEditor::View *view)
{
for (int z = 0; z < m_views.size(); z++)
{
if (m_views.at(z)->parentClient() == view)
{
TimeDatePluginView *nview = m_views.at(z);
m_views.removeAll(nview);
delete nview;
}
}
}
// We do nothing on this methods since our plugin is not configurable yet
void TimeDatePlugin::readConfig()
{
}
void TimeDatePlugin::writeConfig()
{
}
// Plugin view class
TimeDatePluginView::TimeDatePluginView(KTextEditor::View *view)
: QObject(view)
, KXMLGUIClient(view)
, m_view(view)
{
setComponentData(TimeDatePluginFactory::componentData());
KAction *action = new KAction(i18n("Insert Time && Date"), this);
// Here we need as first parameter the same we declared at the resource
// contents file (timedateui.rc). We named the action "tools_insert_timedate".
// Here is where we connect it to an actual KDE action.
actionCollection()->addAction("tools_insert_timedate", action);
action->setShortcut(Qt::CTRL + Qt::Key_D);
// As usual, we connect the signal triggered() to a slot here. When the menu
// element is clicked, we go to the slot slotInsertTimeDate().
connect(action, SIGNAL(triggered()), this, SLOT(slotInsertTimeDate()));
// This is always needed, tell the KDE XML GUI client that we are using
// that file for reading actions from.
setXMLFile("timedateui.rc");
}
// Destructor
TimeDatePluginView::~TimeDatePluginView()
{
}
// The slot that will be called when the menu element "Insert Time & Date" is
// clicked.
void TimeDatePluginView::slotInsertTimeDate()
{
QString localizedTimeDate = i18nc("This is a localized string for default time & date printing on kate document."
"%e means day in XX format."
"%m means month in XX format."
"%Y means year in XXXX format."
"%H means hours in XX format."
"%M means minutes in XX format."
"Please, if in your language time or date is written in a different order, change it here",
"%m-%e-%Y %H:%M");
// We create a KDateTime object with the current time & date.
KDateTime dt(QDateTime::currentDateTime());
// We insert the information in the document at the current cursor position
// with the default string declared on the header.
m_view->document()->insertText(m_view->cursorPosition(), dt.toString(localizedTimeDate));
}
// We need to include the moc file since we have declared slots and we are using
// the Q_OBJECT macro on the TimeDatePluginView class.
#include "timedate.moc"
Building it all, the CMakeLists.txt
Finally, to put everything together you need to build everything, to tell cmake what needs to go where there is the CMakeLists.txt file.
For more details on CMake please read Development/Tutorials/CMake
find_package(KDE4 REQUIRED)
include (KDE4Defaults)
include_directories(${KDE4_INCLUDES})
# We are calling our plugin "ktexteditor_timedate", and it contains only
# one source file: timedate.cpp.
kde4_add_plugin(ktexteditor_timedate timedate.cpp)
# We need to link our plugin against kdecore libs, as well as ktexteditor
target_link_libraries(ktexteditor_timedate ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ktexteditor)
# Well, we want to install our plugin on the plugin directory
install(TARGETS ktexteditor_timedate DESTINATION ${PLUGIN_INSTALL_DIR})
# We also want to install the resource contents file on the data directory, at
# the subdirectory of our plugin name, so it does not mix up with other resource
# contents files.
install(FILES timedateui.rc DESTINATION ${DATA_INSTALL_DIR}/ktexteditor_timedate)
# We want to install the desktop file that describes our plugin too. It will go
# on the services directory.
install(FILES ktexteditor_timedate.desktop DESTINATION ${SERVICES_INSTALL_DIR})
The next part
Now you are ready for adding to this plugin a configuration dialog that lets the user customize the way the string is printed out on the screen.