Documentation/Maemo 5 Developer Guide/Application Development/Writing Desktop Widgets

The following code examples are used in this chapter:

Desktop widgets are programs designed to provide a certain functionality from the desktop area. These programs are usually limited, in the sense that they are not complex applications, normally representing "small" utilities or an extension of an existing application.

Desktop widgets are written as shared libraries and need also to provide a desktop file that describes them. Depending on their placement, they can be Status Menu widgets or Home Area widgets.

They must have a .desktop file describing them. The file contents are fields that describe certain properties of the widget.

Contents

[edit] Home Widgets

Home widgets are located in the Home Area of the desktop. Home Area widgets are not resizable.

To better show how to write a Home Area widget, consider a widget "TimeOut" that allows the user to choose an action to be performed at a given time.

Home widgets must inherit from libhildondesktop's HDHomePluginItem. The following example presents a header file for the mentioned widget.

Example 1.1. Header file for TimeOut widget: lib-timeout-home-widget.h

#ifndef TIME_OUT_PLUGIN_H
#define TIME_OUT_PLUGIN_H
 
#include <glib-object.h>
 
#include <libhildondesktop/libhildondesktop.h>
 
G_BEGIN_DECLS
 
typedef struct _TimeOutPlugin TimeOutPlugin;
typedef struct _TimeOutPluginClass TimeOutPluginClass;
 
#define TIME_OUT_TYPE_HOME_PLUGIN   (time_out_home_plugin_get_type ())
 
#define TIME_OUT_HOME_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
                        TIME_OUT_TYPE_HOME_PLUGIN, TimeOutHomePlugin))
 
#define TIME_OUT_HOME_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
                        TIME_OUT_TYPE_HOME_PLUGIN,  TimeOutHomePluginClass))
 
#define TIME_OUT_IS_HOME_PLUGIN(obj)  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
                        TIME_OUT_TYPE_HOME_PLUGIN))
 
#define TIME_OUT_IS_HOME_PLUGIN_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
                        TIME_OUT_TYPE_HOME_PLUGIN))
 
#define TIME_OUT_HOME_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                        TIME_OUT_TYPE_HOME_PLUGIN,  TimeOutHomePluginClass))
 
struct _TimeOutPlugin
{
    HDHomePluginItem hitem;
};
 
struct _TimeOutPluginClass
{
    HDHomePluginItemClass parent_class;
};
 
GType time_out_home_plugin_get_type(void);
 
G_END_DECLS
 
#endif

The first and more important function is HD_DEFINE_PLUGIN_MODULE. This function receives three parameters to register the object that is supplied by the plug-in. The first argument is the object type name in Camel case, the second is the type name in lowercase with underscores separating the words. The third is the GType of the object's parent. Apart from this macro, use also three other functions. The name of the functions is the name given as second argument to HD_DEFINE_PLUGIN_MODULE with the suffixes _init, _class_init and _class_finalize.

The following example shows the source code for "TimeOut" widget.

Example 1.2. Source file for TimeOut widget: lib-timeout-home-widget.c

#include <gtk/gtk.h>
#include <hildon/hildon.h>
 
#include <libhildondesktop/libhildondesktop.h>
 
#include "lib-timeout-home-widget.h"
 
HD_DEFINE_PLUGIN_MODULE (TimeOutPlugin, time_out_plugin,      HD_TYPE_HOME_PLUGIN_ITEM)
 
static GtkWidget *
build_ui (void)
{
    GtkVBox *contents = GTK_VBOX (gtk_vbox_new (0, FALSE));
    GtkLabel *label = GTK_LABEL (gtk_label_new ("Time out widget."));
    HildonPickerButton *action;
    action = HILDON_PICKER_BUTTON (hildon_picker_button_new  (HILDON_SIZE_FINGER_HEIGHT,
                                        HILDON_BUTTON_ARRANGEMENT_VERTICAL));
    HildonTouchSelector *action_selector;
    action_selector = HILDON_TOUCH_SELECTOR (hildon_touch_selector_new_text ());
    hildon_button_set_title (HILDON_BUTTON (action), "Action");
    hildon_touch_selector_append_text (action_selector, "Blank Screen");
    hildon_touch_selector_append_text (action_selector, "Suspend");
    hildon_touch_selector_append_text (action_selector, "Turn Off");
    hildon_picker_button_set_selector (action, action_selector);
    hildon_picker_button_set_active (action, 0); 
 
    HildonTimeButton *time;
    time = HILDON_TIME_BUTTON (hildon_time_button_new (HILDON_SIZE_FINGER_HEIGHT,
                                        HILDON_BUTTON_ARRANGEMENT_VERTICAL));
    hildon_time_button_set_time (time, 22, 00); 
 
    GtkHBox *buttons = GTK_HBOX (gtk_hbox_new (0, TRUE));
    gtk_container_add (GTK_CONTAINER (buttons), GTK_WIDGET (action));
    gtk_container_add (GTK_CONTAINER (buttons), GTK_WIDGET (time));
 
    gtk_box_pack_start (GTK_BOX (contents), GTK_WIDGET (label), FALSE, FALSE, 0);
    gtk_box_pack_end (GTK_BOX (contents), GTK_WIDGET (buttons), FALSE, FALSE, 0);
    gtk_widget_show_all (GTK_WIDGET (contents)); 
 
    return GTK_WIDGET (contents);
}
 
static void
time_out_plugin_init (TimeOutPlugin *desktop_plugin)
{
  GtkWidget *contents = build_ui ();
  gtk_container_add (GTK_CONTAINER (desktop_plugin), contents);
} 
 
static void
time_out_plugin_class_init (TimeOutPluginClass *class) {} 
 
static void
time_out_plugin_class_finalize (TimeOutPluginClass *class) {}

As you can see in the example above, the controls used in the widget (build_ui function) must be added to "TimeOutPlugin". In this case, the widget works as a standalone application, but it can just provide a widget to activate a program defined in another header and independent from the widget.

[edit] The .desktop File

Although other fields can be assigned in a .desktop file, the example bellow shows a simple .desktop file for the "TimeOut" widget. The "Name" field is the widget's name in the list when the user is choosing widgets to add to the desktop. The "X-Path" field should be set to the .so file that was previously generated.

Example 1.3. A .desktop file for the TimeOut widget: timeout-widget.desktop

[Desktop Entry] 
Name=TimeOut Widget 
Comment=Execute an action at a given time
Type=default
X-Path=lib-timeout-home-widget.so

For Home Area widget, place .desktop files in the directory outputed by the following command:

pkg-config libhildondesktop-1 --variable=hildonhomedesktopentrydir

[edit] Status Menu widgets

Status Menu widgets are located in the Status Menu and can be divided into three categories: permanent, conditional and temporary. Permanent widgets are shown all the time. Conditional and temporary widgets are shown when a certain condition is fulfilled.

The way to write a widget for the Status Menu is pretty similar to writing it for the Home Area. The widget inherits from HDStatusMenuItem. The next examples present a Status Menu widget which only shows a message when clicked.

Example 1.4. Header file for Example Status Menu Widget: lib-example-status-menu-widget.h

#ifndef __EXAMPLE_STATUS_PLUGIN_H__
#define __EXAMPLE_STATUS_PLUGIN_H__
 
#include <libhildondesktop/libhildondesktop.h>
 
G_BEGIN_DECLS
 
#define TYPE_EXAMPLE_STATUS_PLUGIN            (example_status_plugin_get_type ())
 
#define EXAMPLE_STATUS_PLUGIN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
                                    TYPE_EXAMPLE_STATUS_PLUGIN, ExampleStatusPlugin))
 
#define EXAMPLE_STATUS_PLUGIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), \
                                TYPE_EXAMPLE_STATUS_PLUGIN, ExampleStatusPluginClass))
 
#define IS_EXAMPLE_STATUS_PLUGIN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
                                                    TYPE_EXAMPLE_STATUS_PLUGIN))
 
#define IS_EXAMPLE_STATUS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
                                                    TYPE_EXAMPLE_STATUS_PLUGIN))
 
#define EXAMPLE_STATUS_PLUGIN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                            TYPE_EXAMPLE_STATUS_PLUGIN, ExampleStatusPluginClass))
 
#define STATUS_AREA_EXAMPLE_ICON_SIZE 22
 
typedef struct _ExampleStatusPlugin        ExampleStatusPlugin;
typedef struct _ExampleStatusPluginClass   ExampleStatusPluginClass;
typedef struct _ExampleStatusPluginPrivate ExampleStatusPluginPrivate; 
 
struct _ExampleStatusPlugin
{
    HDStatusMenuItem parent;
 
    ExampleStatusPluginPrivate *priv;
};
 
struct _ExampleStatusPluginClass
{
    HDStatusMenuItemClass parent;
};
 
GType example_status_plugin_get_type (void);
 
G_END_DECLS
 
#endif

Example 1.5. Source file for Example Status Menu Widget: lib-example-status-menu-widget.c

#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <hildon/hildon.h>
 
#include "lib-example-status-menu-widget.h"
 
#define EXAMPLE_STATUS_PLUGIN_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE (obj,   \
                            TYPE_EXAMPLE_STATUS_PLUGIN, ExampleStatusPluginPrivate))
 
struct _ExampleStatusPluginPrivate
{
    GtkWidget *label;
    gpointer data;
};
 
HD_DEFINE_PLUGIN_MODULE (ExampleStatusPlugin, example_status_plugin,   HD_TYPE_STATUS_MENU_ITEM);
 
static void
example_status_plugin_class_finalize (ExampleStatusPluginClass *klass) {}
 
static void
example_status_plugin_class_init (ExampleStatusPluginClass *klass)
{
    g_type_class_add_private (klass, sizeof (ExampleStatusPluginPrivate));
}
 
static void
example_status_plugin_init (ExampleStatusPlugin *plugin)
{
    plugin->priv = EXAMPLE_STATUS_PLUGIN_GET_PRIVATE (plugin);
 
    GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
    GList *list = gtk_icon_theme_list_icons (icon_theme, NULL);
    GdkPixbuf *pixbuf = gtk_icon_theme_load_icon (icon_theme, "general_email",
                    STATUS_AREA_EXAMPLE_ICON_SIZE, GTK_ICON_LOOKUP_NO_SVG, NULL);
    hd_status_plugin_item_set_status_area_icon (HD_STATUS_PLUGIN_ITEM (plugin), pixbuf);
    g_object_unref (pixbuf);
 
    GtkWidget *b = gtk_label_new ("Example message");
    gtk_widget_show_all (b);
 
    plugin->priv->label = b;
 
    gtk_container_add (GTK_CONTAINER (plugin), plugin->priv->label);
 
    gtk_widget_show_all (plugin->priv->label);
 
   gtk_widget_show (GTK_WIDGET (plugin));
}

[edit] The .desktop File

The .desktop file for the Status Menu is similar to the Home Area one but also contains the "Category" field where the priority of the widget is set (permanent, conditional or temporary) and the "Icon" field to set the icon's name. If the "Category" key is not set, it is considered as permanent.

Example 1.6. A .desktop file for the Status Menu example widget: lib-example-widget.desktop

[Desktop Entry]
Name=Example
Icon=general_email 
Comment=An example status menu widget
Category=permanent
Type=default
X-Path=lib-example-status-menu-widget.so

For Status Menu widget, .desktop files should be placed in the directory outputted by the following command:

pkg-config --variable=hildonstatusmenudesktopentrydir libhildondesktop-1

[edit] Building Widgets

To use a widget, it needs to be built as a shared library. This is done by passing the -shared flag to gcc.

gcc -shared `pkg-config --libs --cflags hildon-1 libhildondesktop-1` widget.c -o lib-widget.so

Status Menu and Home Area widgets binaries are located in the same directory, so assign distinguishable names to them, such as "lib-example-home-widget.so" and "lib-example-status-menu-widget.so" for the Home Area and Status Menu widgets, respectively. The directory where the binary files are copied to is given by the following command (which normally outputs /usr/lib/hildon-desktop):

pkg-config --variable=hildondesktoplibdir libhildondesktop-1