Development/Tutorials/Plasma4/ContainmentAction

    From KDE TechBase

    You can use Plasma ContainmentAction e.g. for Context Menus on your Plasma Desktop. They will run as plugins meaning you can add and remove them after compilation. If they exist, their name will be stored in .desktop files. ContainmentActions can only be written in C++ or QML, not in other Plasma programming languages, e.g. NOT in javascript.

    Example

    We will now create a simple example for Plasma ContainmentAction plugins. The code can be downloaded at https://github.com/tstaerk/kde-contextmenu/tree/tutorial. It creates a custom context menu that looks like this:

    The code is here:

    kde-contextmenu.desktop

    [Desktop Entry]
    Name=KDE Context Menu
    Type=Service
    Icon=favorites
    Comment=Simple application launcher
    
    ServiceTypes=Plasma/ContainmentActions
    
    X-KDE-Library=kde-contextmenu
    X-KDE-PluginInfo-Author=R. Hacker
    [email protected]
    X-KDE-PluginInfo-Name=kde-contextmenu
    X-KDE-PluginInfo-Version=pre0.1
    X-KDE-PluginInfo-Website=http://techbase.kde.org
    X-KDE-PluginInfo-EnabledByDefault=true
    

    If you want to know which ServiceTypes exist, you can find out with the Linux command

    ls `kde4-config --prefix`/share/kde4/servicetypes
    

    CMakeLists.txt

    project(kde-contextmenu)
    
    set(KDE_MIN_VERSION "4.3.85") # for the < 4.2 macro
    find_package(KDE4 4.3.85 REQUIRED)
    
    include(MacroLibrary)
    include(KDE4Defaults)
    
    add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
    include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES})
    
    set(contextmenu_SRCS
        launch.cpp
    )
    
    kde4_add_plugin(kde-contextmenu ${contextmenu_SRCS})
    target_link_libraries(kde-contextmenu ${KDE4_PLASMA_LIBS} ${KDE4_KIO_LIBS})
    
    install(TARGETS kde-contextmenu DESTINATION ${PLUGIN_INSTALL_DIR})
    install(FILES kde-contextmenu.desktop DESTINATION ${SERVICES_INSTALL_DIR})
    

    launch.h

    #ifndef CONTEXTMENU_HEADER
    #define CONTEXTMENU_HEADER
    
    #include <plasma/containmentactions.h>
    
    class QAction;
    class KMenu;
    
    class ConTextMenu : public Plasma::ContainmentActions
    {
        Q_OBJECT
        public:
            ConTextMenu(QObject* parent, const QVariantList& args);
            ~ConTextMenu();
    
            void init(const KConfigGroup &config);
    
            void contextEvent(QEvent *event);
            //returns true if something (other than a separator) was successfully added
            bool addApps(QMenu *menu);
    
        public slots:
            void switchTo(QAction *action);
    
        protected:
            void makeMenu();
    
        private:
            KMenu *m_menu;
            QAction *m_action;
    };
    
    K_EXPORT_PLASMA_CONTAINMENTACTIONS(favorites, ConTextMenu)
    
    #endif
    

    launch.cpp

    #include "launch.h"
    
    #include <QGraphicsSceneMouseEvent>
    #include <QGraphicsSceneWheelEvent>
    #include <QFileInfo>
    
    #include <KDebug>
    #include <KIcon>
    #include <KMenu>
    
    #include <Plasma/DataEngine>
    #include <Plasma/Containment>
    #include <Plasma/Service>
    
    ConTextMenu::ConTextMenu(QObject *parent, const QVariantList &args)
        : Plasma::ContainmentActions(parent, args)
        , m_action(new QAction(this))
    {
        m_menu = new KMenu();
        connect(m_menu, SIGNAL(triggered(QAction*)), this, SLOT(switchTo(QAction*)));
    
        m_action->setMenu(m_menu);
    }
    
    ConTextMenu::~ConTextMenu()
    {
        delete m_menu;
    }
    
    void ConTextMenu::init(const KConfigGroup &)
    {
    }
    
    void ConTextMenu::contextEvent(QEvent *event)
    {
        makeMenu();
        m_menu->adjustSize();
        m_menu->exec(popupPosition(m_menu->size(), event));
    }
    
    void ConTextMenu::makeMenu()
    {
        m_menu->clear();
        addApps(m_menu);
    }
    
    bool ConTextMenu::addApps(QMenu *menu)
    {
        QAction* action = menu->addAction(KIcon("system-run"), "Open a console");
        action->setData("kde4-konsole.desktop");
    
        action = menu->addAction(KIcon("firefox"), "Surf the web");
        action->setData("firefox.desktop");
        
        action = menu->addAction(KIcon("ksnapshot"), "Take a screenshot");
        action->setData("kde4-ksnapshot.desktop");
        return true;
    }
    
    void ConTextMenu::switchTo(QAction *action)
    {
        QString source = action->data().toString();
        kDebug() << source;
        Plasma::Service *service = dataEngine("apps")->serviceForSource(source);
        if (service)
        {
            service->startOperationCall(service->operationDescription("launch"));
        }
    }
    
    #include "launch.moc"
    

    Install it

    Compile, link and install it using the command

    cmake -DCMAKE_INSTALL_PREFIX=`kde4-config --prefix` . && make -j8 && make install
    

    Test it

    To test it, have your system re-discover its .desktop files by running the command

    kbuildsycoca4
    

    Then right-click onto your desktop, select "Folder View Settings" -> Mouse Actions and the context menu:

    Then click "Apply". Next time you middle-click (or whatever you selected) onto your desktop, your own context menu will appear:

    Debugging

    To debug your Plasma ContainmentAction call

    kdebugdialog --fullmode
    

    search for "plasma" and direct kDebug's output to /tmp/whatever.txt

    See also