Konqueror • Technical • Embedded tutorial • Part 6

Embedded Components Tutorial - Page 6

Finishing Off With KAction

The Plan

The aKtion embedded component needs only one thing, now -- it's Play, Stop, Forward, and Backward buttons displayed on Konqueror's toolbar and View menu.

This is done by creating "actions" corresponding to those, well, actions. The resulting actions (encapsulated in the KAction class) can then be "plugged" or unplugged into toolbar or menubars or pretty much anywhere else. The location of these actions will be described in an XML file.

As with everything else, this takes only a few lines of code. Here is the aktion_part.h file with the necessary changes in bold:

 aktion_part.h

#ifndef __aktion_part_h__
#define __aktion_part_h__

#include <kparts/browserextension.h>
#include <klibloader.h>

class KInstance;
class KXAnim;
class AktionBrowserExtension;
class KAction;

class AktionFactory : public KLibFactory
{
    Q_OBJECT
public:
    AktionFactory();
    virtual ~AktionFactory();

    virtual QObject* create(QObject* parent = 0, const char* name = 0,
                            const char* classname = "QObject",
                            const QStringList &args = QStringList());

    static KInstance *instance();

private:
    static KInstance *s_instance;
};

class AktionPart: public KParts::ReadOnlyPart
{
    Q_OBJECT
public:
    AktionPart(QWidget *parent, const char *name);
    virtual ~AktionPart();

    virtual bool closeURL();

protected:
    virtual bool openFile();

protected slots:
    void slotPlay();
    void slotStop();
    void slotForward();
    void slotBackward();

private:
    KXAnim *widget;
    AktionBrowserExtension *m_extension;

    KAction *m_playAction;
    KAction *m_stopAction;
    KAction *m_forwardAction;
    KAction *m_backwardAction;
};

class AktionBrowserExtension : public KParts::BrowserExtension
{
    Q_OBJECT
    friend class AktionPart;
public:
    AktionBrowserExtension(AktionPart *parent);
    virtual ~AktionBrowserExtension();
};

#endif

 

Thing To Remember:
Each real action ("play", "cut", "quit") in your application should have exactly one corresponding KAction

Here is the aktion_part.cpp file with changes in bold:
 aktion_part.cpp

#include "aktion_part.h"

#include <kinstance.h>
#include <klocale.h>
#include <kaboutdata.h>
#include <kaction.h>

#include "kxanim.h"
#include <qtimer.h>

extern "C"
{
    /**
     * This function is the 'main' function of this part.  It takes
     * the form 'void *init_lib<library name>()'.  It always returns a
     * new factory object
     */
    void *init_libaktion()
    {
        return new AktionFactory;
    }
};

/**
 * We need one static instance of the factory for our C 'main'
 * function
 */
KInstance *AktionFactory::s_instance = 0L;

AktionFactory::AktionFactory()
{
}

AktionFactory::~AktionFactory()
{
    if (s_instance)
        delete s_instance;

    s_instance = 0;
}

QObject *AktionFactory::create(QObject *parent, const char *name, const char*,
                               const QStringList& )
{
    QObject *obj = new AktionPart((QWidget*)parent, name);
    emit objectCreated(obj);
    return obj;
}

KInstance *AktionFactory::instance()
{
    if ( !s_instance )
    {
        KAboutData about("aktion", I18N_NOOP("aKtion"), "1.99");
        s_instance = new KInstance(&about);
    }
    return s_instance;
}

AktionPart::AktionPart(QWidget *parent, const char *name)
    : KParts::ReadOnlyPart(parent, name)
{
    setInstance(AktionFactory::instance());

    // create a canvas to insert our widget
    QWidget *canvas = new QWidget(parent);
    canvas->setFocusPolicy(QWidget::ClickFocus);
    setWidget(canvas);

    m_extension = new AktionBrowserExtension(this);

    // create our animation widget
    widget = new KXAnim(this);
    widget->setLoop(true);
    widget->show();

    // create and connect our actions
    m_playAction = new KAction(i18n("Play"), QIconSet(BarIcon("tocar",
                               AktionFactory::instance())), 0, this,
                               SLOT(slotPlay()), actionCollection(),
                               "play");

    m_stopAction = new KAction(i18n("Stop"), QIconSet(BarIcon("parar",
                               AktionFactory::instance())), 0, this,
                               SLOT(slotStop()), actionCollection(),
                               "stop");

    m_backwardAction = new KAction(i18n("Backward"),
                                   QIconSet(BarIcon("retroceder",
                                   AktionFactory::instance())), 0, this,
                                   SLOT(slotBackward()), actionCollection(),
                                   "backward");

    m_forwardAction = new KAction(i18n("Forward"), QIconSet(BarIcon("avanzar",
                                  AktionFactory::instance())), 0, this,
                                  SLOT(slotForward()), actionCollection(),
                                  "forward");

    setXMLFile("aktion_part.rc");
}

AktionPart::~AktionPart()
{
    slotStop();
}

bool AktionPart::openFile()
{
    widget->setFile(m_file);
    widget->stop();
    widget->show();
    QTimer::singleShot(2000, this, SLOT(slotPlay()));

    return true;
}

bool AktionPart::closeURL()
{
    slotStop();
    return true;
}

void AktionPart::slotPlay()
{
    widget->play();
    m_playAction->setEnabled(false);
    m_stopAction->setEnabled(true);
    m_forwardAction->setEnabled(true);
    m_backwardAction->setEnabled(true);
}

void AktionPart::slotStop()
{
    widget->stop();
    m_playAction->setEnabled(true);
    m_stopAction->setEnabled(false);
    m_forwardAction->setEnabled(false);
    m_backwardAction->setEnabled(false);
}

void AktionPart::slotForward()
{
    widget->stepForward();
}

void AktionPart::slotBackward()
{
    widget->stepBack();
}

AktionBrowserExtension::AktionBrowserExtension(AktionPart *parent)
    : KParts::BrowserExtension(parent, "AktionBrowserExtension")
{
}

AktionBrowserExtension::~AktionBrowserExtension()
{
}

Line By Line

Using actions always follow the same steps:
  1. Create KAction with name, icon(s), keyboard shortcut, and slot
  2. Connect the action with any toolbars/menubars/etc that you want it to display on using an XML file
Thing To Remember:
There are only two necessary steps to use actions with KParts

1. Create a KAction (or derived class) for each real action
2. Connect the KAction to the toolbar/menubars in an XML file

The first step is done with this line:

m_playAction = new KAction(i18n("Play"), QIconSet(BarIcon("tocar",
                           AktionFactory::instance())), 0, this,
                           SLOT(slotPlay()), actionCollection(),
                           "play");

This creates a new action with the following parameters:

Text to display - "Play"
Icon to display - tocar.png
Keyboard accel - none
SLOT parent - current widget
SLOT - slotPlay()
Action parent - the KParts action collection
action name - used to match XML description with action

As a side note, KDE has a number of standard actions available for use. They take advantage of the fact that certain actions (like "quit", "open document", etc) have standard text, icons, and keyboard accelerators. These parameters are encapsulated into KStdAction actions. You, as a developer using them, need only supply a SLOT to connect to. aKtion does not use any standard actions, hence they are not used here.

The second step (connecting the action to our widgets) is accomplished with in this file:

 aktion_part.rc

<!DOCTYPE kpartgui>
<kpartgui name="aktion">
<MenuBar>
 <Menu name="view">
  <Action name="play"/>
  <Action name="stop"/>
  <Action name="backward"/>
  <Action name="forward"/>
 </Menu>
</MenuBar>
<ToolBar name="Aktion-ToolBar">
  <Action name="play"/>
  <Action name="stop"/>
  <Action name="backward"/>
  <Action name="forward"/>
</ToolBar>
<StatusBar/>
</kpartgui>

You can see that each action has a corresponding Action item in the XML. The names for the Menu and ToolBars are either specific or generic depending on what you want them to do. If you use a reserved name like "view" or "edit", then your actions will be plugged into an existing menu or toolbar. If you use a new name like "Aktion-ToolBar", then you will get a new toolbar.

You'll also need to add the following lines to your Makefile.am:


# this is where the menu and toolbar description file goes
partdir   = $(kde_datadir)/aktion
part_DATA = aktion_part.rc

That's pretty much it!

Visible Result

Final Result

Practical Matters

As you saw, this code was pretty straight-forward. There really wasn't anything specific to aKtion, here. Do check the KStdAction actions before creating your own, however. Using those actions are strongly encouraged.

[ Edit ]

Global navigation links