Development/Tutorials/Kate/KTextEditor Plugins Advanced: Difference between revisions

    From KDE TechBase
    No edit summary
    m (Text replace - "<syntaxhighlight lang="make">" to "<syntaxhighlight lang="cmake">")
    (9 intermediate revisions by 4 users not shown)
    Line 5: Line 5:
    name=Creating your first Kate Plugin with configuration dialog|
    name=Creating your first Kate Plugin with configuration dialog|


    pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build/KDE4|KDE4 development environment]]|
    pre=[http://mindview.net/Books/TICPP/ThinkingInCPP2e.html C++], [http://www.trolltech.com/products/qt/ Qt], [[Getting_Started/Build|KDE4 development environment]]|


    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]
    Line 18: Line 18:


    '''ktexteditor_timedate.desktop'''
    '''ktexteditor_timedate.desktop'''
    <code ini>
    <syntaxhighlight lang="ini">
    [Desktop Entry]
    [Desktop Entry]
    Encoding=UTF-8
    Encoding=UTF-8
    Line 39: Line 39:
    Name=Time & Date
    Name=Time & Date
    Comment=Insert current Time & Date
    Comment=Insert current Time & Date
    </code>
    </syntaxhighlight>


    '''ktexteditor_timedate_config.desktop'''
    '''ktexteditor_timedate_config.desktop'''
    <code ini>
    <syntaxhighlight lang="ini">
    [Desktop Entry]
    [Desktop Entry]
    Type=Service
    Type=Service
    Line 51: Line 51:
    X-KDE-ParentComponents=ktexteditortimedate
    X-KDE-ParentComponents=ktexteditortimedate
    Name=Format of Time & Date insertion
    Name=Format of Time & Date insertion
    </code>
    </syntaxhighlight>


    * '''X-KDE-ServiceTypes''': configuration widgets are always KCModule's.
    * '''X-KDE-ServiceTypes''': configuration widgets are always KCModule's.
    Line 61: Line 61:


    '''timedateui.rc'''
    '''timedateui.rc'''
    <code>
    <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 71:
    </MenuBar>
    </MenuBar>
    </kpartplugin>
    </kpartplugin>
    </code>
    </syntaxhighlight>


    === The header file of the plugin ===
    === The header file of the plugin ===
    '''timedate.h'''
    '''timedate.h'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    #ifndef TIMEDATE_H
    #ifndef TIMEDATE_H
    #define TIMEDATE_H
    #define TIMEDATE_H
    Line 90: Line 90:
    #include <QtCore/QVariantList>
    #include <QtCore/QVariantList>


    /**
      * Variable that will contain the localized time & date format
      * at the beginning.
      */
    static QString localizedTimeDate = QString();
    static QString localizedTimeDate = QString();


    Line 100: Line 104:
         virtual ~TimeDatePlugin();
         virtual ~TimeDatePlugin();


        /**
          * Since the plugin has a singleton structure, it will be created
          * only once, we return the instance of the plugin.
          *
          * It may return 0 if no plugin instance has been created. This has
          * to be taken in count specially when designing the configuration
          * dialog.
          */
         static TimeDatePlugin *self() { return plugin; }
         static TimeDatePlugin *self() { return plugin; }


        /**
          * Method for merging with the KTextEditor view.
          */
         void addView (KTextEditor::View *view);
         void addView (KTextEditor::View *view);
        /**
          * Method for removing from the KTextEditor view.
          */
         void removeView (KTextEditor::View *view);
         void removeView (KTextEditor::View *view);


         void readConfig();z
        /**
          * Methods for reading and writing the configuration
          * preferences.
          */
         void readConfig();
         void writeConfig();
         void writeConfig();


    Line 111: Line 134:
         virtual void writeConfig (KConfig *) {}
         virtual void writeConfig (KConfig *) {}


        /**
          * Set the format that will be used to print the
          * date and time string.
          */
         void setFormat(const QString &format);
         void setFormat(const QString &format);
        /**
          * Returns the current format that will be used to
          * print the date and time string.
          */
         QString format() const;
         QString format() const;


    Line 130: Line 162:
         ~TimeDatePluginView();
         ~TimeDatePluginView();


        /**
          * Set the format for this view.
          */
         void setFormat(const QString &format);
         void setFormat(const QString &format);
         QString format() const;
         QString format() const;


       private Q_SLOTS:
       private Q_SLOTS:
        /**
          * This slot is called when the user wants to insert the
          * time & date onto the document.
          */
         void slotInsertTimeDate();
         void slotInsertTimeDate();


    Line 144: Line 183:


    #endif // TIMEDATE_H
    #endif // TIMEDATE_H
    </code>
    </syntaxhighlight>


    === The source file of the plugin ===
    === The source file of the plugin ===
    '''timedate.cpp'''
    '''timedate.cpp'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    #include "timedate.h"
    #include "timedate.h"
    #include "timedate_config.h"
    #include "timedate_config.h"
    Line 282: Line 321:


    #include "timedate.moc"
    #include "timedate.moc"
    </code>
    </syntaxhighlight>


    === The header file of the configuration dialog ===
    === The header file of the configuration dialog ===
    '''timedate_config.h'''
    '''timedate_config.h'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    #ifndef TIMEDATE_CONFIG_H
    #ifndef TIMEDATE_CONFIG_H
    #define TIMEDATE_CONFIG_H
    #define TIMEDATE_CONFIG_H
    Line 315: Line 354:


    #endif // TIMEDATE_CONFIG_H
    #endif // TIMEDATE_CONFIG_H
    </code>
    </syntaxhighlight>


    === The source file of the configuration dialog ===
    === The source file of the configuration dialog ===
    '''timedate_config.cpp'''
    '''timedate_config.cpp'''
    <code cppqt>
    <syntaxhighlight lang="cpp-qt">
    #include "timedate_config.h"
    #include "timedate_config.h"
    #include "timedate.h"
    #include "timedate.h"
    Line 438: Line 477:


    #include "timedate_config.moc"
    #include "timedate_config.moc"
    </code>
    </syntaxhighlight>


    === Building it all, the CMakeLists.txt ===
    === Building it all, the CMakeLists.txt ===
    Line 445: Line 484:
    For more details on CMake please read [[Development/Tutorials/CMake]]
    For more details on CMake please read [[Development/Tutorials/CMake]]


    <code bash>
    <syntaxhighlight lang="cmake">
    set(ktexteditor_timedate_PART_SRCS timedate.cpp timedate_config.cpp )
    set(ktexteditor_timedate_PART_SRCS timedate.cpp timedate_config.cpp )


    Line 458: Line 497:
    install(FILES ktexteditor_timedate.desktop
    install(FILES ktexteditor_timedate.desktop
                   ktexteditor_timedate_config.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
                   ktexteditor_timedate_config.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
    </code>
    </syntaxhighlight>

    Revision as of 10:19, 30 June 2011

    Creating your first Kate Plugin with configuration dialog
    Tutorial Series   Kate Plugin Tutorial (2nd part)
    Previous   C++, Qt, KDE4 development environment
    What's Next   n/a
    Further Reading   CMake, The actual plugin code

    Abstract

    The plugin that we developed on the previous part of this tutorial was nice, but probably not enough. We also need it to be configurable, because the user might want to only add to his/her document the date or the time, not both. We are going to create a configuration dialog for the plugin, and make the output form of our time & date configurable by the user.

    The Code

    The .desktop files

    Every plugin needs a .desktop file that describes it. Same for each configuration page/widget. As we only need a page/widget for this plugin, we will only have two desktop files: one for the plugin itself, and another one for configuration the page/widget.

    ktexteditor_timedate.desktop

    [Desktop Entry]
    Encoding=UTF-8
    X-KDE-Library=ktexteditor_timedate
    X-KDE-PluginKeyword=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
    

    ktexteditor_timedate_config.desktop

    [Desktop Entry]
    Type=Service
    X-KDE-ServiceTypes=KCModule
    X-KDE-Library=ktexteditor_timedate
    X-KDE-PluginKeyword=ktexteditor_timedate_config
    X-KDE-FactoryName=ktexteditor_timedate_config
    X-KDE-ParentComponents=ktexteditortimedate
    Name=Format of Time & Date insertion
    
    • X-KDE-ServiceTypes: configuration widgets are always KCModule's.
    • X-KDE-Library: you need to specify which library provides this configuration dialog. In this case we are shipping on the same library "ktexteditor_timedate.so" both the plugin itself and its configuration dialog.
    • X-KDE-PluginKeyword: since we have two entry points on the library at this time: one for the plugin and another one for the configuration dialog we need to specify which keyword has each one. As you can see I also added this entry to the ktexteditor_timedate.desktop file. Of course they have to be different if we want to see it working.

    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>&amp;Tools</Text>
        <separator group="tools_operations" />
        <Action name="tools_insert_timedate" group="tools_operations"/>
     </Menu>
    </MenuBar>
    </kpartplugin>
    

    The header file of the plugin

    timedate.h

    #ifndef TIMEDATE_H
    #define TIMEDATE_H
    
    #include <ktexteditor/plugin.h>
    #include <ktexteditor/view.h>
    #include <kxmlguiclient.h>
    #include <klocalizedstring.h>
    #include <kpluginfactory.h>
    
    #include <QtCore/QEvent>
    #include <QtCore/QObject>
    #include <QtCore/QList>
    #include <QtCore/QVariantList>
    
    /**
      * Variable that will contain the localized time & date format
      * at the beginning.
      */
    static QString localizedTimeDate = QString();
    
    class TimeDatePlugin
      : public KTextEditor::Plugin
    {
      public:
        explicit TimeDatePlugin(QObject *parent = 0,
                                const QVariantList &args = QVariantList());
        virtual ~TimeDatePlugin();
    
        /**
          * Since the plugin has a singleton structure, it will be created
          * only once, we return the instance of the plugin.
          *
          * It may return 0 if no plugin instance has been created. This has
          * to be taken in count specially when designing the configuration
          * dialog.
          */
        static TimeDatePlugin *self() { return plugin; }
    
        /**
          * Method for merging with the KTextEditor view.
          */
        void addView (KTextEditor::View *view);
    
        /**
          * Method for removing from the KTextEditor view.
          */
        void removeView (KTextEditor::View *view);
    
        /**
          * Methods for reading and writing the configuration
          * preferences.
          */
        void readConfig();
        void writeConfig();
    
        virtual void readConfig (KConfig *) {}
        virtual void writeConfig (KConfig *) {}
    
        /**
          * Set the format that will be used to print the
          * date and time string.
          */
        void setFormat(const QString &format);
    
        /**
          * Returns the current format that will be used to
          * print the date and time string.
          */
        QString format() const;
    
      private:
        static TimeDatePlugin *plugin;
        QList<class TimeDatePluginView*> m_views;
        QString m_string;
    };
    
    class TimeDatePluginView
       : public QObject, public KXMLGUIClient
    {
      Q_OBJECT
    
      public:
        explicit TimeDatePluginView(const QString &string,
                                    KTextEditor::View *view = 0);
        ~TimeDatePluginView();
    
        /**
          * Set the format for this view.
          */
        void setFormat(const QString &format);
        QString format() const;
    
      private Q_SLOTS:
        /**
          * This slot is called when the user wants to insert the
          * time & date onto the document.
          */
        void slotInsertTimeDate();
    
      private:
        KTextEditor::View *m_view;
        QString m_string;
    };
    
    K_PLUGIN_FACTORY_DECLARATION(TimeDatePluginFactory)
    
    #endif // TIMEDATE_H
    

    The source file of the plugin

    timedate.cpp

    #include "timedate.h"
    #include "timedate_config.h"
    
    #include <ktexteditor/document.h>
    
    #include <kpluginfactory.h>
    #include <kpluginloader.h>
    #include <klocale.h>
    #include <kaction.h>
    #include <kactioncollection.h>
    #include <kdatetime.h>
    #include <kconfiggroup.h>
    
    TimeDatePlugin *TimeDatePlugin::plugin = 0;
    
    K_PLUGIN_FACTORY_DEFINITION(TimeDatePluginFactory,
            registerPlugin<TimeDatePlugin>("ktexteditor_timedate");
            registerPlugin<TimeDateConfig>("ktexteditor_timedate_config");
            )
    K_EXPORT_PLUGIN(TimeDatePluginFactory("ktexteditor_timedate", "ktexteditor_plugins"))
    
    TimeDatePlugin::TimeDatePlugin(QObject *parent, const QVariantList &args)
        : KTextEditor::Plugin(parent)
    {
        Q_UNUSED(args);
    
        if (localizedTimeDate.isNull())
        {
            localizedTimeDate = i18nc("This is a localized string for default time & date printing on kate document."
                                      "%d 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",
                                      "%d-%m-%Y %H:%M");
        }
    
        plugin = this;
        readConfig();
    }
    
    TimeDatePlugin::~TimeDatePlugin()
    {
        plugin = 0;
    }
    
    void TimeDatePlugin::addView(KTextEditor::View *view)
    {
        TimeDatePluginView *nview = new TimeDatePluginView(m_string, view);
        m_views.append(nview);
    }
    
    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;
            }
        }
    }
    
    void TimeDatePlugin::readConfig()
    {
        KConfigGroup cg(KGlobal::config(), "TimeDate Plugin");
        m_string = cg.readEntry("string", localizedTimeDate);
    }
    
    void TimeDatePlugin::writeConfig()
    {
        KConfigGroup cg(KGlobal::config(), "TimeDate Plugin" );
        cg.writeEntry("string", m_string );
    }
    
    void TimeDatePlugin::setFormat(const QString &format)
    {
        m_string = format;
    
        // If the property has been set for the plugin in general, let's set that
        // property to that value on all views where the plugin has been loaded.
        foreach (TimeDatePluginView *pluginView, m_views)
        {
            pluginView->setFormat(format);
        }
    }
    
    QString TimeDatePlugin::format() const
    {
        return m_string;
    }
    
    TimeDatePluginView::TimeDatePluginView(const QString &string,
                                           KTextEditor::View *view)
      : QObject(view)
      , KXMLGUIClient(view)
      , m_view(view)
      , m_string(string)
    {
        setComponentData(TimeDatePluginFactory::componentData());
    
        KAction *action = new KAction(i18n("Insert Time && Date"), this);
        actionCollection()->addAction("tools_insert_timedate", action);
        action->setShortcut(Qt::CTRL + Qt::Key_D);
        connect(action, SIGNAL(triggered()), this, SLOT(slotInsertTimeDate()));
    
        setXMLFile("timedateui.rc");
    }
    
    TimeDatePluginView::~TimeDatePluginView()
    {
    }
    
    void TimeDatePluginView::setFormat(const QString &format)
    {
        m_string = format;
    }
    
    QString TimeDatePluginView::format() const
    {
        return m_string;
    }
    
    void TimeDatePluginView::slotInsertTimeDate()
    {
        KDateTime dt(QDateTime::currentDateTime());
        m_view->document()->insertText(m_view->cursorPosition(), dt.toString(m_string));
    }
    
    #include "timedate.moc"
    

    The header file of the configuration dialog

    timedate_config.h

    #ifndef TIMEDATE_CONFIG_H
    #define TIMEDATE_CONFIG_H
    
    #include <kcmodule.h>
    
    class KLineEdit;
    
    class TimeDateConfig
        : public KCModule
    {
        Q_OBJECT
    
    public:
        explicit TimeDateConfig(QWidget *parent = 0, const QVariantList &args = QVariantList());
        virtual ~TimeDateConfig();
    
        virtual void save();
        virtual void load();
        virtual void defaults();
    
    private Q_SLOTS:
        void slotChanged();
    
    private:
        KLineEdit *format;
    };
    
    #endif // TIMEDATE_CONFIG_H
    

    The source file of the configuration dialog

    timedate_config.cpp

    #include "timedate_config.h"
    #include "timedate.h"
    
    #include <QtGui/QLabel>
    #include <QtGui/QBoxLayout>
    
    #include <klocale.h>
    #include <kpluginfactory.h>
    #include <kpluginloader.h>
    #include <klineedit.h>
    #include <kconfiggroup.h>
    
    TimeDateConfig::TimeDateConfig(QWidget *parent, const QVariantList &args)
        : KCModule(TimeDatePluginFactory::componentData(), parent, args)
    {
        QVBoxLayout *layout = new QVBoxLayout(this);
    
        QLabel *info = new QLabel(i18n(
         "%y\t2-digit year excluding century (00 - 99)\n"
         "%Y\tfull year number\n"
         "%:m\tmonth number, without leading zero (1 - 12)\n"
         "%m\tmonth number, 2 digits (01 - 12)\n"
         "%b\tabbreviated month name\n"
         "%B\tfull month name\n"
         "%e\tday of the month (1 - 31)\n"
         "%d\tday of the month, 2 digits (01 - 31)\n"
         "%a\tabbreviated weekday name\n"
         "%A\tfull weekday name\n"
         "\n"
         "%H\thour in the 24 hour clock, 2 digits (00 - 23)\n"
         "%k\thour in the 24 hour clock, without leading zero (0 - 23)\n"
         "%I\thour in the 12 hour clock, 2 digits (01 - 12)\n"
         "%l\thour in the 12 hour clock, without leading zero (1 - 12)\n"
         "%M\tminute, 2 digits (00 - 59)\n"
         "%S\tseconds (00 - 59)\n"
         "%P\t\"am\" or \"pm\"\n"
         "%p\t\"AM\" or \"PM\"\n"));
    
        // It is possible that the plugin has not been loaded yet, and the
        // configuration dialog has been asked to be shown. In that case, it is our
        // turn to set the static string to the correct value.
        if (localizedTimeDate.isNull())
        {
            localizedTimeDate = i18nc("This is a localized string for default time & date printing on kate document."
                                      "%d 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",
                                      "%d-%m-%Y %H:%M");
        }
    
        QHBoxLayout *hlayout = new QHBoxLayout(this);
        QLabel *lformat = new QLabel(i18n("Format"));
        format = new KLineEdit(this);
        hlayout->addWidget(lformat);
        hlayout->addWidget(format);
    
        layout->addWidget(info);
        layout->addLayout(hlayout);
    
        setLayout(layout);
    
        load();
    
        QObject::connect(format, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()));
    }
    
    TimeDateConfig::~TimeDateConfig()
    {
    }
    
    void TimeDateConfig::save()
    {
        if (TimeDatePlugin::self())
        {
            TimeDatePlugin::self()->setFormat(format->text());
            TimeDatePlugin::self()->writeConfig();
        }
        else
        {
            KConfigGroup cg(KGlobal::config(), "TimeDate Plugin");
            cg.writeEntry("string", format->text());
        }
    
        emit changed(false);
    }
    
    void TimeDateConfig::load()
    {
        if (TimeDatePlugin::self())
        {
            TimeDatePlugin::self()->readConfig();
            format->setText(TimeDatePlugin::self()->format());
        }
        else
        {
            KConfigGroup cg(KGlobal::config(), "TimeDate Plugin" );
            format->setText(cg.readEntry("string", localizedTimeDate));
        }
    
        emit changed(false);
    }
    
    void TimeDateConfig::defaults()
    {
        format->setText(localizedTimeDate);
    
        emit changed(true);
    }
    
    void TimeDateConfig::slotChanged()
    {
        emit changed(true);
    }
    
    #include "timedate_config.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

    set(ktexteditor_timedate_PART_SRCS timedate.cpp timedate_config.cpp )
    
    kde4_add_plugin(ktexteditor_timedate ${ktexteditor_timedate_PART_SRCS} )
    
    target_link_libraries(ktexteditor_timedate ${KDE4_KDECORE_LIBS} ktexteditor )
    
    install(TARGETS ktexteditor_timedate DESTINATION ${PLUGIN_INSTALL_DIR} )
    
    install(FILES timedateui.rc DESTINATION ${DATA_INSTALL_DIR}/ktexteditor_timedate )
    
    install(FILES ktexteditor_timedate.desktop
                  ktexteditor_timedate_config.desktop DESTINATION ${SERVICES_INSTALL_DIR} )