Step 2: Add Boilerplate KParts Code
In this step, you will convert aKtion into an embedded component using
"standard" boilerplate code. After this step, when you click on an
animation file, it will embed aKtion instead of spawning it off in a
separate process. Since no aKtion specific code is used in this step,
it won't actually play the animation -- that's reserved for
step 3.
The code in this step is completely generic. You can drop it into any
application with no modifications!
You will do this in three baby steps:
- Add the aktion_part.cpp and aktion_part.h files
- Modify the Makefile.am to reflect the new files
- Modify the aktion.desktop file to indicate that it is an embedded
component
Add the following two files to your project with no modifications.
aktion_part.h
#ifndef __aktion_part_h__
#define __aktion_part_h__
#include <kparts/browserextension.h>
#include <klibloader.h>
class KAboutData;
class KInstance;
class AktionBrowserExtension;
class QLabel;
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();
static KAboutData *aboutData();
private:
static KInstance *s_instance;
};
class AktionPart: public KParts::ReadOnlyPart
{
Q_OBJECT
public:
AktionPart(QWidget *parent, const char *name);
virtual ~AktionPart();
protected:
virtual bool openFile();
private:
QLabel *widget;
AktionBrowserExtension *m_extension;
};
class AktionBrowserExtension : public KParts::BrowserExtension
{
Q_OBJECT
friend class AktionPart;
public:
AktionBrowserExtension(AktionPart *parent);
virtual ~AktionBrowserExtension();
};
#endif
| | |
aktion_part.cpp
#include "aktion_part.h"
#include <kinstance.h>
#include <klocale.h>
#include <kaboutdata.h>
#include <qlabel.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->aboutData();
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 )
s_instance = new KInstance( aboutData() );
return s_instance;
}
KAboutdata *AktionFactory::aboutData()
{
KAboutData *about = new KAboutData("aktion", I18N_NOOP("aKtion"), "1.99");
return about;
}
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);
// as an example, display a blank white widget
widget = new QLabel(canvas);
widget->setText("aKtion!");
widget->setAutoResize(true);
widget->show();
}
AktionPart::~AktionPart()
{
closeURL();
}
bool AktionPart::openFile()
{
widget->setText(m_file);
return true;
}
bool AktionPart::closeURL()
{
return true;
}
AktionBrowserExtension::AktionBrowserExtension(AktionPart *parent)
: KParts::BrowserExtension(parent, "AktionBrowserExtension")
{
}
AktionBrowserExtension::~AktionBrowserExtension()
{
}
| | |
All KParts components need to construct two classes: A factory
class derived from KLibFactory and a view class derived from
KParts::ReadOnlyPart. In this tutorial, they are:
#include <kparts/browserextension.h>
#include <klibloader.h>
class AktionFactory : public KLibFactory
class AktionPart : public KParts::ReadOnlyPart
The factory object is responsible for instantiating the components and
returning a pointer to them. This is how Konqueror gets a reference
to your component.
The entire process start here:
void *init_libaktion()
{
return new AktionFactory;
}
This is the first function that is called in the loading process. The
form of the name is void *init_libyourapp() Keep the
part after init_ in mind -- that is the string you will use in the
X-KDE-Library entry later on. This function
always returns a new instance of your factory object
You also need to overload two functions in the factory class:
create() and instance().
QObject *AktionFactory::create(QObject *parent, const char *name, const char*,
const QStringList& )
{
QObject *obj = new AktionPart((QWidget*)parent, name);
emit objectCreated(obj);
return obj;
}
The create() function is called each time your component is
needed. It is responsible for instantiating a new view object and
returning it.
KInstance *AktionFactory::instance()
{
if ( !s_instance )
{
KAboutData about("aktion", I18N_NOOP("aKtion"), "1.99");
s_instance = new KInstance(&about);
}
return s_instance;
}
The instance() function returns an instance of type
KInstance. The above code is very standard. Just use it.
Things To Remember:
From a practical point of view, all of the factory code is cut and
paste. Change the references to aktion and Aktion to your own naming
convention and you are set (e.g., s/aktion/yourapp,
s/Aktion/YourApp)
|
|
There are six methods that you need to overload for your view class...
but a lot of the code is very cut and paste. The code in
bold (related to QLabel) is the only application specific
code in the view class! Here is what each function is doing:
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);
// as an example, display a blank white widget
widget = new QLabel(this);
widget->setText("aKtion!");
widget->setAutoResize(true);
widget->show();
}
The constructor is responsible for initializing your internal
variables as well as your "workhorse" object. Typically, you do not
do much processing inside of your KParts derived class. Rather,
you have an existing class that has all of the functionality and you
just use that inside of your view class. In this example, a QLabel
label is used just so there is something displayed when you
run it.
Notice that a blank "canvas" is the main widget. This is because
Konqueror will resize the component to the full width of the view. In
many cases, you will want to control the size of your component;
both the QLabel and the actual aKtion component are examples of this.
In this case, you set a blank widget as the resized widget and just
use that as the parent of your "real" class.
bool AktionPart::openFile()
{
widget->setText(m_file);
return true;
}
The openFile method is arguably the most important method in
this class. It is what is called when Konqueror wants your
application to display a file. There is a lot going on behind the
scenes of this function, though. The KParts framework itself handles
all file downloads. That means that by the time openFile is
called, the file to play is already local.
Note that if your application can handle remote URLs already, then you
can overload the openURL function instead of
openFile
widget->setText(m_file);
The m_file variable is set by the KParts framework. It is a
QString with a path (not a URL) to a local file. If the original URL
was a local file, then it will be a path to the actual file. If it
the original URL was remote, then the path will likely be in the /tmp
directory.
Things To Remember:
Most of the parts code is cut and paste, also. Just rename the
references to "aktion" and "Aktion" and insert your own widget in the
place of widget and you're set.
|
|
Makefile.am
# since the "real" aktion is a library, we start constructing the
# stuff for it
bin_PROGRAMS = aktion
lib_LTLIBRARIES = libaktion.la
libaktion_la_SOURCES = main.cpp aktionConf.cpp capture.cpp \
aktionVm.cpp kxanim.cpp principal.cpp \
aktion_part.cpp
libaktion_la_LDFLAGS = $(all_libraries) -version-info 1:0:0 -module
libaktion_la_LIBADD = $(LIB_KFILE) $(LIBVM) -lkparts
aktion_SOURCES = main.cpp
aktion_LDADD = libaktion.la
# these are the headers for your project
noinst_HEADERS = principal.h aktionConf.h kxanim.h capture.h aktionVm.h \
aktion_part.h
| | |
The two parts files that you just added to the project need to be added
to the Makefile just like any other file.. and are done so above. You
also need to link to the KParts library since we make extensive use of
it now.
Things To Remember:
To build an embedded component, you need only to add:
1. Your "parts" files to _SOURCES and _HEADERS
2. The -lkparts library to _LIBADD
|
|
aktion.desktop
[Desktop Entry]
Name=aKtion!
Exec=aktion %i %m -caption "%c"
Icon=aktion
MimeType=video/mpeg;video/x-msvideo;video/quicktime;video/x-flic;
Type=Application
DocPath=aktion/aktion.html
Comment=Video Player
Comment[es]=Reproductor de videos
Terminal=0
ServiceTypes=Browser/View
X-KDE-Library=libaktion
| | |
So far, you've made all the changes necessary to make aKtion an
embedded component (albeit one that doesn't do much).. but there is no
way (yet) for Konqueror to know that when it clicks on a Quicktime
file, it should embed the component instead of spawning an external
viewer.
This is where the aktion.desktop file comes in to play. When you
install the desktop file, the KDE mimetypes system becomes aware of
all of its fields (if it doesn't do it immediately, you can force a
reload by running the kbuildsycoca command). The three that
matter in this case or the MimeType, ServiceTypes, and
X-KDE-Library entries.
MimeType=video/mpeg;video/x-msvideo;video/quicktime;video/x-flic;
ServiceTypes=Browser/View
X-KDE-Library=libaktion
These lines tell the mimetypes system that aKtion can handle the
mimetypes specified in MimeType; it implements an embedded component
of type Browser/View; and the name of the component is libaktion.
When Konqueror is asked to execute a Quicktime file, it asks the
mimetype system for a corresponding app that can handle
video/quicktime. It also asks if the app can handle
"Browser/View" embedding. When a pointer to aKtion is returned, it
uses X-KDE-Library to tell it that the component it needs to load is
'libaktion'.
Things To Remember:
To "enable" your embedded component, you need to have a .desktop
file with:
1. A MimeType entry with your supported mimetypes
2. A ServiceTypes=Browser/View entry (exactly like that)
3. A X-KDE-Library=libyourapp entry
|
|
If your own application uses a standard automake like the one above,
then converting it to a shared library uses exactly the same steps
(and code). There is little specific to aKtion in this procedure.
|