The following code examples are used in this chapter:
The maemo platform's basic starting point for graphical user interface programming is the Hildon[44], an application framework comprising of a lightweight desktop, a set of widgets optimized for handheld devices, a set of theming tools and other complementary libraries and applications.
Hildon is based on GNOME[29] technologies to a great extent. Compared to a GNOME desktop from a user interface point of view, Hildon is designed to provide a new desktop for mobile embedded devices. Therefore it for example uses a lighter window manager called Matchbox[76].
The following shows the components making up a typical GUI application developed for maemo (starting from the bottom):
There are some caveats related to API changes between major GTK+ versions, which will be mentioned in the text, so existing code should not be copy-pasted blindly.
Hildon framework's main UI elements are separated to four categories and all of them can be extended with plug-ins:
GUI applications in maemo usually have one or more HildonWindows, which are the top level application windows. They can include a standardized menubar and a toolbar.
The Hildon framework also includes other auxiliary widgets and services specialized for the Hildon environment. Examples of these are the Hildon-FM library for file system dialogs and widgets, HildonBanner for displaying notifications to the user and HildonWizardDialog for creating wizard user interfaces. For information about Hildon's widgets, see Maemo API References[57].
Another important element of the Hildon framework is the Hildon Input Method API, which is an interface for creating new user input systems in addition to the included virtual keyboard and handwriting recognition.
Hildon is a graphical user interface designed for small mobile devices. This section aims to assist developers in getting started with the development of Hildon-compatible applications.
Most GTK+ widgets and methods work unaltered in the Hildon environment, but to make applications fit in the maemo GUI environment, some source code changes for plain GTK+ applications are required.
The following sections introduce the most important widgets in Hildon; all GTK+ widgets can be used in addition of these. For more information, see the GTK+ Reference Manual at [41].
This section shows basic layout modes of the Hildon user interface. Applications can use all of these and switch dynamically between different views. In all the views application area can, of course, be shared in any possible ways using GTK+ containers (e.g. GtkHBox, GtkVBox and GtkTable).
The figures below describe the basic layout of the Hildon user interface in the normal view. This view consists of the Task Navigator area, statusbar/titlebar area, application area and three areas of inactive skin graphics.
This basic layout contains all types of functional areas. The layout is basically the same as the Normal View, but there is a toolbar area at the bottom, replacing the inactive skin graphic area. The toolbar area width is 720 pixels and the height varies depending on the toolbar type:
In this view, the whole screen (800x480 px) is reserved for the application area. The full screen mode can be activated and deactivated by a hardware button, or in the application code. All applications should provide full screen mode to enable maximized screen usage when needed.
This is a variation of the full screen view. There is a toolbar area at the bottom of the screen, reducing the space available for the application area. The toolbar can be visible in both normal and full screen modes, so it should be made scalable.
#include <hildon/hildon-program.h> #include <gtk/gtkmain.h>
A HildonProgram is the base of any Hildon application. It is inherited from GObject, and represents an application running in the Hildon framework. The HildonProgram tracks the top-most status of an application, and informs the Task Navigator when the application can be hibernated. It also provides tools to get/set common menus and toolbars common for all application windows.
The HildonProgram widget is created by calling the function
hildon_program_get_instance(), which does not take any parameters, and returns newly-created HildonProgram. For this tutorial, the following API functions are of interest:
HildonProgram *hildon_program_get_instance (void)- Creates new HildonProgram widget.
void hildon_program_add_window (HildonProgram *self,
HildonWindow *window)
- Adds HildonWindow to HildonProgram.
hildon_program_set_can_hibernate (HildonProgram *self,
gboolean killable)
- Informs Task Navigator that it can hibernate the HildonProgram.
void hildon_program_set_common_menu (HildonProgram *self,
GtkMenu *menu)
- Sets common menu for all application windows.
void hildon_program_set_common_toolbar (HildonProgram *self,
GtkToolbar *toolbar)
- Sets common toolbar for all application windows.
This is an example (example_hildonprogram.c) application, which creates new HildonProgram and HildonWindow. It sets a title for the application and creates a sample label.
int main(int argc, char *argv[])
{
/* Create needed variables */
HildonProgram *program;
HildonWindow *window;
/* Initialize the GTK. */
gtk_init(&argc, &argv);
/* Create the Hildon program and setup the title */
program = HILDON_PROGRAM(hildon_program_get_instance());
g_set_application_name("App Title");
/* Create HildonWindow and set it to HildonProgram */
window = HILDON_WINDOW(hildon_window_new());
hildon_program_add_window(program, window);
/* Add example label to window */
gtk_container_add(GTK_CONTAINER(window),
GTK_WIDGET(gtk_label_new("HildonProgram Example")));
/* Begin the main application */
gtk_widget_show_all(GTK_WIDGET(window));
/* Connect signal to X in the upper corner */
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
/* Exit */
return 0;
}
To compile the program, type
$ gcc -o example_hildonprogram example_hildonprogram.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall
To run this sample from inside Scratchbox, start the Xephyr server and
$ run-standalone.sh ./example_hildonprogram
And you will be able to see
#include <hildon/hildon-window.h>
The HildonWindow is inherited from the GtkWindow. It represents a top-level window of an application running in the Hildon framework. It provides facilities to manage window menus and toolbars. GtkWindow API applies to it as well (gtk_window_fullscreen, gtk_window_set_urgency_hint, ...).
Each HildonWindow can have its own menu and toolbars. Applications can also have common menu and toolbar, shared between all application windows. To get more information, check HildonProgram, Menus and Toolbars sections.
The following HildonWindow functions are the most important ones:
GtkWidget *hildon_window_new (void)- Creates a new HildonWindow widget.
void hildon_window_set_menu (HildonWindow *self,
GtkMenu *menu)
- Sets a menu for the HildonWindow.
void hildon_window_add_toolbar (HildonWindow *self,
GtkToolbar *toolbar)
- Adds a toolbar for the HildonWindow.
See the previous section for a sample code.
#include <hildon/hildon-program.h> #include <gtk/gtk.h>
The menus in maemo use GTK+ menu functions, with the exception that the menu is attached to the HildonProgram/Window, and appears in the title bar. A GtkMenu can be created and attached to the HildonProgram to be used as a common menu for all HildonWindows that do not have their own menu. Another way is to use a GtkMenu in a HildonWindow. This way, every application window can have different menu.
GTK+ menus can be made both manually and using GtkUIManager. The GtkUIManager is a new action-based method to create menus and toolbars using an XML description. This tutorial introduces only manual creation. More about GtkUIManager can be read at [42].
The following example (example_menu.c) creates a menu with a submenu. This submenu contains radio menu items and a check menu item, all of which can be toggled. The last item in the menu (Close) is attached to a callback function, so that the application can be closed also from the menu. The starting point here is the function creating this menu. In this example, the menu is attached to the one and only HildonWindow.
/* Create the menu items needed for the main view */
static void create_menu(HildonWindow * main_window)
{
/* Create needed variables */
GtkWidget *main_menu;
GtkWidget *menu_others;
GtkWidget *item_others;
GtkWidget *item_radio1;
GtkWidget *item_radio2;
GtkWidget *item_check;
GtkWidget *item_close;
GtkWidget *item_separator;
/* Create new main menu */
main_menu = gtk_menu_new();
/* Create new submenu for "Others" */
menu_others = gtk_menu_new();
/* Create menu items */
item_others = gtk_menu_item_new_with_label("Others");
item_radio1 = gtk_radio_menu_item_new_with_label(NULL, "Radio1");
item_radio2 =
gtk_radio_menu_item_new_with_label_from_widget(GTK_RADIO_MENU_ITEM
(item_radio1),
"Radio2");
item_check = gtk_check_menu_item_new_with_label("Check");
item_close = gtk_menu_item_new_with_label("Close");
item_separator = gtk_separator_menu_item_new();
/* Add menu items to right menus */
gtk_menu_append(main_menu, item_others);
gtk_menu_append(menu_others, item_radio1);
gtk_menu_append(menu_others, item_radio2);
gtk_menu_append(menu_others, item_separator);
gtk_menu_append(menu_others, item_check);
gtk_menu_append(main_menu, item_close);
/* Add others submenu to the "Others" item */
hildon_window_set_menu(HILDON_WINDOW(main_window), GTK_MENU(main_menu));
gtk_menu_item_set_submenu(GTK_MENU_ITEM(item_others), menu_others);
/* Attach the callback functions to the activate signal */
g_signal_connect(G_OBJECT(item_close), "activate",
GTK_SIGNAL_FUNC(item_close_cb), NULL);
/* Make all menu widgets visible */
gtk_widget_show_all(GTK_WIDGET(main_menu));
}
The menu entry "Close" is attached with the function "g_signal_connect" to the callback function "item_close_cb". This function, which is presented next, ends the application by executing "gtk_main_quit()".
/* Callback for "Close" menu entry */
void item_close_cb()
{
g_print("Closing application...\n");
gtk_main_quit();
}
The main function in this application is similar to others presented earlier. The only new thing here is executing the function "create_menu()" HildonWindow as its parameter. This function creates the menu and attach it to the window.
/* Main application */
int main(int argc, char *argv[])
{
/* Create needed variables */
HildonProgram *program;
HildonWindow *window;
/* Initialize the GTK. */
gtk_init(&argc, &argv);
/* Create the hildon program and setup the title */
program = HILDON_PROGRAM(hildon_program_get_instance());
g_set_application_name("Menu Example");
/* Create HildonWindow and set it to HildonProgram */
window = HILDON_WINDOW(hildon_window_new());
hildon_program_add_window(program, window);
/* Add example label to HildonWindow */
gtk_container_add(GTK_CONTAINER(window),
gtk_label_new("Menu Example"));
/* Create menu for HildonWindow */
create_menu(window);
/* Connect signal to X in the upper corner */
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(item_close_cb), NULL);
/* Begin the main application */
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
/* Exit */
return 0;
}
To compile and run, start Xephyr server and
$ gcc -o example_menu example_menu.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall $ run-standalone.sh ./example_menu
Screenshot of the application is presented below, with the submenu opened.
As the maemo menus are GTK+ menus, they are not explained here in more detail. For full API documentation regarding GTK+ menu widgets, see [40] .
#include <hildon/hildon-program.h> #include <gtk/gtk.h>
To create an application that has a toolbar, create a normal HildonWindow and then a normal GtkToolbar. The toolbar is used as any GtkToolbar is used, and it can contain normal GtkToolItems, like:
The function below creates a toolbar and adds it to the HildonWindow. In this example (example_toolbar.c), the defined function is called in the main function in the same way as in the previous menu example. The callback function for the 'Close' button (tb_close_cb) is similar to the earlier example, also.
/* Create the toolbar needed for the main view */
static void create_toolbar(HildonWindow * main_window)
{
/* Create needed variables */
GtkWidget *main_toolbar;
GtkToolItem *tb_new;
GtkToolItem *tb_open;
GtkToolItem *tb_save;
GtkToolItem *tb_close;
GtkToolItem *tb_separator;
GtkToolItem *tb_comboitem;
GtkComboBox *tb_combo;
/* Create toolbar */
main_toolbar = gtk_toolbar_new();
/* Create toolbar button items */
tb_new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
tb_open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
tb_save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
tb_close = gtk_tool_button_new_from_stock(GTK_STOCK_CLOSE);
/* Create toolbar combobox item */
tb_comboitem = gtk_tool_item_new();
tb_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
gtk_combo_box_append_text(tb_combo, "Entry 1");
gtk_combo_box_append_text(tb_combo, "Entry 2");
gtk_combo_box_append_text(tb_combo, "Entry 3");
/* Select second item as default */
gtk_combo_box_set_active(GTK_COMBO_BOX(tb_combo), 1);
/* Make combobox to use all available toolbar space */
gtk_tool_item_set_expand(tb_comboitem, TRUE);
/* Add combobox inside toolitem */
gtk_container_add(GTK_CONTAINER(tb_comboitem), GTK_WIDGET(tb_combo));
/* Create separator */
tb_separator = gtk_separator_tool_item_new();
/* Add all items to toolbar */
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_new, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_separator, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_open, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_save, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_comboitem, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_close, -1);
/* Add signal lister to "Close" button */
g_signal_connect(G_OBJECT(tb_close), "clicked",
G_CALLBACK(tb_close_cb), NULL);
/* Add toolbar HildonWindow */
hildon_window_add_toolbar(main_window, GTK_TOOLBAR(main_toolbar));
}
The screenshot presents the created toolbar with several buttons and combobox.
To compile and run, start Xephyr server and
$ gcc -o example_toolbar example_toolbar.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall $ run-standalone.sh ./example_toolbar
#include <hildon/hildon-program.h> #include <hildon/hildon-find-toolbar.h> #include <gtk/gtk.h>
A HildonFindToolbar is a special toolbar for a find feature. Maemo applications may have several toolbars, which are then attached on top of each other. The Find toolbar is generally placed on the top of the main toolbar, as in this example.
Now a bit more complete application (example_findtoolbar.c), for which the Find toolbar needs to be opened and hidden. A structure is created to contain the main UI widget pointers. This is the way a real maemo application should be built to make accessing generated widgets easier. The code is reviewed here piece by piece, at first the data structure, holding important data needed to be accessed by other functions.
/* Application UI data struct */
typedef struct _AppData AppData;
struct _AppData {
/* View items */
HildonProgram *program;
HildonWindow *window;
/* Toolbar */
GtkWidget *main_toolbar;
/* Find toolbar */
HildonFindToolbar *find_toolbar;
/* Is Find toolbar visible or not */
gboolean find_visible;
};
The next part presents all the callback functions required: "Close" and "Find" for the main toolbar, and "Close" and "Search" for the Find toolbar. The actual search is not implemented, but it should be included in the find_tb_search() function.
/* Callback for "Close" toolbar button */
void tb_close_cb(GtkToolButton * widget, AppData * view)
{
g_print("Closing application...\n");
gtk_main_quit();
}
/* Callback for "Find" toolbar button */
void tb_find_cb(GtkToolButton * widget, AppData * view)
{
/* Show or hide find toolbar */
if (view->find_visible) {
gtk_widget_hide_all(GTK_WIDGET(view->find_toolbar));
view->find_visible = FALSE;
} else {
gtk_widget_show_all(GTK_WIDGET(view->find_toolbar));
view->find_visible = TRUE;
}
}
/* Callback for "Close" find toolbar button */
void find_tb_close(GtkWidget * widget, AppData * view)
{
gtk_widget_hide_all(GTK_WIDGET(view->find_toolbar));
view->find_visible = FALSE;
}
/* Callback for "Search" find toolbar button */
void find_tb_search(GtkWidget * widget, AppData * view)
{
gchar *find_text = NULL;
g_object_get(G_OBJECT(widget), "prefix", &find_text, NULL);
/* Implement the searching here... */
g_print("Search for: %s\n", find_text);
}
Next the HildonFindToolbar is created in a separate function, and connected to the callback listeners presented before. N.B. There is no gtk_widget_show() function for the toolbar here, as it is designed to be kept hidden when application starts.
/* Create the find toolbar */
void create_find_toolbar(AppData * view)
{
HildonFindToolbar *find_toolbar;
find_toolbar = HILDON_FIND_TOOLBAR
(hildon_find_toolbar_new("Find String: "));
/* Add signal listers to "Search" and "Close" buttons */
g_signal_connect(G_OBJECT(find_toolbar), "search",
G_CALLBACK(find_tb_search), view);
g_signal_connect(G_OBJECT(find_toolbar), "close",
G_CALLBACK(find_tb_close), view);
hildon_window_add_toolbar(view->window, GTK_TOOLBAR(find_toolbar));
/* Set variables to AppData */
view->find_toolbar = find_toolbar;
view->find_visible = FALSE;
}
The function create_toolbar() is very similar to the earlier example, only a callback is added for the Find toolbar button. Also, instead of finding, the find button in the main toolbar just shows or hides the find_toolbar
/* Create the toolbar for the main view */
static void create_toolbar(AppData * appdata)
{
/* Create needed variables */
GtkWidget *main_toolbar;
GtkToolItem *tb_new;
GtkToolItem *tb_open;
GtkToolItem *tb_save;
GtkToolItem *tb_find;
GtkToolItem *tb_close;
GtkToolItem *tb_separator;
GtkToolItem *tb_comboitem;
GtkComboBox *tb_combo;
/* Create toolbar */
main_toolbar = gtk_toolbar_new();
/* Create toolbar button items */
tb_new = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
tb_open = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
tb_save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
tb_find = gtk_tool_button_new_from_stock(GTK_STOCK_FIND);
tb_close = gtk_tool_button_new_from_stock(GTK_STOCK_CLOSE);
/* Create toolbar combobox item */
tb_comboitem = gtk_tool_item_new();
tb_combo = GTK_COMBO_BOX(gtk_combo_box_new_text());
gtk_combo_box_append_text(tb_combo, "Click to show-hide findtoolbar");
gtk_combo_box_append_text(tb_combo, "Entry 2");
gtk_combo_box_append_text(tb_combo, "Entry 3");
/* Select second item as default */
gtk_combo_box_set_active(GTK_COMBO_BOX(tb_combo), 1);
/* Make combobox to use all available toolbar space */
gtk_tool_item_set_expand(tb_comboitem, TRUE);
/* Add combobox inside toolitem */
gtk_container_add(GTK_CONTAINER(tb_comboitem), GTK_WIDGET(tb_combo));
/* Create separator */
tb_separator = gtk_separator_tool_item_new();
/* Add all items to toolbar */
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_new, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_separator, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_open, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_save, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_comboitem, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_find, -1);
gtk_toolbar_insert(GTK_TOOLBAR(main_toolbar), tb_close, -1);
/* Add signal lister to "Close" button */
g_signal_connect(G_OBJECT(tb_close), "clicked",
G_CALLBACK(tb_close_cb), NULL);
/* Add signal lister to "Find" button */
g_signal_connect(G_OBJECT(tb_find), "clicked",
G_CALLBACK(tb_find_cb), appdata);
/* Add toolbar HildonWindow */
hildon_window_add_toolbar(appdata->window, GTK_TOOLBAR(main_toolbar));
gtk_widget_show_all(main_toolbar);
appdata->main_toolbar = main_toolbar;
}
Then the main application loop creates a new AppData structure and stores the newly-created HildonProgram and HildonWindow into it. Both toolbars are created, but only the main toolbar is set visible.
/* Main application */
int main(int argc, char *argv[])
{
/* Create needed variables */
HildonProgram *program;
HildonWindow *window;
/* Initialize the GTK. */
gtk_init(&argc, &argv);
/* Create the Hildon program and setup the title */
program = HILDON_PROGRAM(hildon_program_get_instance());
g_set_application_name("HildonFindToolbar Example");
/* Create HildonWindow and set it to HildonProgram */
window = HILDON_WINDOW(hildon_window_new());
hildon_program_add_window(program, window);
/* Add example label to window */
gtk_container_add(GTK_CONTAINER(window),
gtk_label_new("HildonFindToolbar Example"));
/* Create AppData */
AppData *appdata;
appdata = g_new0(AppData, 1);
appdata->program = program;
appdata->window = window;
/* Create toolbar for view */
create_toolbar(appdata);
/* Create find toolbar, but keep it hidden */
create_find_toolbar(appdata);
/* Connect signal to X in the upper corner */
g_signal_connect(G_OBJECT(appdata->window), "delete_event",
G_CALLBACK(item_close_cb), NULL);
/* Show all other widgets except the find toolbar */
gtk_widget_show_all(GTK_WIDGET(appdata->window));
/* Hide the find toolbar */
gtk_widget_hide(GTK_WIDGET(appdata->find_toolbar));
/* Begin the main application */
gtk_main();
/* Exit */
return 0;
}
To compile and run, start Xephyr server and
$ gcc -o example_findtoolbar example_findtoolbar.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall $ run-standalone.sh ./example_findtoolbar
Now the application is ready; the screenshot below presents the result. The Find toolbar can be opened and closed by pressing the 'Find' toolbar button (second from right). As no real functionality was created for the search, pressing "Find" only prints the search string into the command line.
#include <hildon-widgets/hildon-program.h>
#include <hildon-widgets/hildon-file-chooser-dialog.h>
#include <gtk/gtk.h>
/* Application UI data struct */
typedef struct _AppData AppData;
struct _AppData {
HildonProgram *program;
HildonWindow *window;
GtkWidget * main_vbox;
GtkWidget * label;
/* Menu stuff */
GtkWidget * main_menu;
GtkWidget * menu_item_file_open;
GtkWidget * menu_item_file_save;
GtkWidget * menu_item_quit;
};
HildonFileChooserDialog is a dialog used to save and open the user files. It is based on the GtkFileChooser, so the API is similar to the one used in GTK+.
HildonFileChooserDialog is usually opened by event callback of "Open" or "Save" menu entries, or toolbar buttons. To use the file dialog, these callbacks should include similar code as below (example_file_chooser.c).
void cb_example_file_open (GtkWidget *w, AppData *data)
{
gchar *filename = NULL;
filename = interface_file_chooser (data, GTK_FILE_CHOOSER_ACTION_OPEN);
if (filename == NULL) {
filename = "NULL";
}
example_show_test_result (data, "File Opened", filename);
}
void cb_example_file_save (GtkWidget *w, AppData *data)
{
gchar *filename = NULL;
filename = interface_file_chooser (data, GTK_FILE_CHOOSER_ACTION_SAVE);
if (filename == NULL) {
filename = "NULL";
}
else {
FILE *f = fopen (filename, "w");
fprintf (f, "This file was generated by Hildon File Chooser example.");
fclose (f);
}
example_show_test_result (data, "File saved as", filename);
}
The interface_file_chooser() function creates a dialog, attached to a HildonFileSystemModel:
static HildonFileSystemModel* get_file_system_model(GtkWidget *ref_widget)
{
return HILDON_FILE_SYSTEM_MODEL(g_object_new(HILDON_TYPE_FILE_SYSTEM_MODEL,
"ref_widget", ref_widget, NULL));
}
gchar * interface_file_chooser (AppData * appdata, GtkFileChooserAction action)
{
GtkWidget *dialog;
gchar *filename = NULL;
HildonFileSystemModel *model = NULL;
if( (model = get_file_system_model(GTK_WIDGET(appdata->window)) ) == NULL)
{
g_print("could not get file system model\n\n");
return NULL;
}
dialog = hildon_file_chooser_dialog_new_with_properties
(GTK_WINDOW(appdata->window),
"file_system_model", model,
"action", action,
NULL);
gtk_widget_show_all (GTK_WIDGET (dialog));
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
}
gtk_widget_destroy(dialog);
return filename;
}
To compile and run this program, include additional hildon-fm-2 to the pkg-config path, as follows:
$ gcc -o example_file_chooser example_file_chooser.c `pkg-config gtk+-2.0 hildon-1 hildon-fm-2 --cflags --libs` -Wall $ run-standalone.sh ./example_file_chooser
Below are screenshots of both Save and Open dialogs.
For more information about HildonFileChooserDialog, see Hildon FM Reference Manual [57] or in section 6.11.
#include <hildon/hildon-program.h> #include <hildon/hildon-color-chooser.h> #include <hildon/hildon-color-chooser-dialog.h> #include <hildon/hildon-color-button.h> #include <gtk/gtk.h>
HildonColorChooser is a dialog for selecting colors. It is quite similar to GtkColorSelectionDialog, but more suitable for embedded devices.
There are two different ways to use HildonColorChooser: either by using HildonColorButton, showing the selected color in the button area and launching HildonColorChooser dialog automatically when clicked, or by creating the dialog manually.
First, the HildonColorButton should be created (example_color_chooser.c). It can be created by hildon_color_button_new(), and attached to wherever wanted in the UI.
...
color_button = HILDON_COLOR_BUTTON(hildon_color_button_new());
g_signal_connect(G_OBJECT(color_button), "clicked",
G_CALLBACK(color_button_clicked), NULL);
...
In the color_button_clicked callback function, the selected color is retrieved from the HildonColorButton using the "color" property of the widget.
void color_button_clicked(GtkWidget * widget, gpointer data)
{
GdkColor *new_color = NULL;
g_object_get(widget, "color", &new_color, NULL);
}
If HildonColorButton is not needed, HildonColorChooser can be created manually:
void ui_show_color_chooser(GtkWidget * widget, AppData * appdata)
{
GdkColor *current_color;
GdkColor *new_color = NULL;
GtkWidget *chooser;
gint result;
/* Create new color chooser (needs access to HildonWindow!) */
chooser = hildon_color_chooser_new();
/* Set the current selected color to chooser */
hildon_color_chooser_set_color(HILDON_COLOR_CHOOSER(chooser),
current_color);
/* Show dialog */
result = gtk_dialog_run(GTK_DIALOG(chooser));
/* Wait for user to select OK or Cancel */
switch (result) {
case GTK_RESPONSE_OK:
/* Get the current selected color from chooser */
hildon_color_chooser_get_color
(HILDON_COLOR_CHOOSER(chooser), new_color);
/* Now the new color is in 'new_color' variable */
/* Use it however suitable for the application */
gtk_widget_destroy(chooser);
break;
default:
/* If dialog did not return OK then it was canceled */
gtk_widget_destroy(chooser);
break;
}
}
In HildonColorChooser, a pre-defined color can be selected, or a new color can be generated. Both are presented in the screenshots below.
To compile and run this program:
$ gcc -o example_color_chooser example_color_chooser.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall $ run-standalone.sh ./example_color_chooser
#include <hildon/hildon-program.h>
#include <hildon/hildon-font-selection-dialog.h>
#include <gtk/gtk.h>
Where HildonColorChooser provides method to query color from the user, HildonFontSelectionDialog is used to define font. It contains three tabs with the different font formatting information, and is ideally used in text editors and other applications, where the font style can be selected (example_font_selector.c).
To be able to use HildonFontSelectionDialog, some knowledge of Pango text formatting is required, as the returned variable is of type PangoAttrList. For more information about Pango, see Pango Reference Manual
[83].
HildonFontSelectionDialog *dialog;
gint result;
PangoAttrList *list = NULL;
GSList *attrs = NULL;
PangoAttrIterator *iter;
AppData *appdata = (AppData *) data;
/* Create dialog */
dialog = HILDON_FONT_SELECTION_DIALOG
(hildon_font_selection_dialog_new(GTK_WINDOW(appdata->window), "Font selector"));
/* Show the dialog */
gtk_widget_show_all(GTK_WIDGET(dialog));
/* Wait for user to select OK or Cancel */
result = gtk_dialog_run(GTK_DIALOG(dialog));
if (result == GTK_RESPONSE_OK) {
/* Get selected font from dialog */
list = hildon_font_selection_dialog_get_font(dialog);
iter = pango_attr_list_get_iterator(list);
attrs = pango_attr_iterator_get_attrs(iter);
/* Now the new font setting are in 'attrs' variable */
/* Use it however suitable for the application */
}
/* Close the dialog */
gtk_widget_destroy(GTK_WIDGET(dialog));
To compile and run this program:
$ gcc -o example_file_chooser example_file_chooser.c `pkg-config gtk+-2.0 hildon-1 hildon-fm-2 --cflags --libs` -Wall $ run-standalone.sh ./example_file_chooser
The screenshot below shows the HildonFontSelectionDialog with the Style tab open.
#include <hildon/hildon-program.h>
#include <hildon/hildon-file-details-dialog.h>
#include <gtk/gtk.h>
/* Application UI data struct */
typedef struct _AppData AppData;
struct _AppData {
HildonProgram *program;
HildonWindow *window;
};
HildonFileDetailsDialog is a dialog to show file information. It has two tabs: the first one containing file name, size, last modification date, and some general information; and the second one being empty for the use of applications. These kinds of dialogs are used not only in a file manager application, but also in all kinds of applications with user files, such as editors and viewers.
The following source code is an excerpt from this example example_file_details.c
describes how to create HildonFileDetailsDialog. Remember to make sure the file exists first. A HildonFileSystemModel is needed, just like File Open/Save Dialog.
#define TEXT_FILE "file://home/user/MyDocs/.documents/foo.txt"
/* Create a Hildon File System Model to be used in the File details dialog */
static HildonFileSystemModel* get_file_system_model(GtkWidget *ref_widget)
{
return HILDON_FILE_SYSTEM_MODEL(g_object_new(HILDON_TYPE_FILE_SYSTEM_MODEL,
"ref_widget", ref_widget, NULL));
}
This is how the file details dialog is created. A specific file TEXT_FILE is attached to the HildonFileSystemModel object which is attached to the file details dialog
void callback_file_details(GtkWidget * widget, AppData * data)
{
HildonFileDetailsDialog *dialog;
GtkWidget *label = NULL;
HildonFileSystemModel *model = NULL;
gint result;
GtkTreeIter iter;
/* Create a hildon file system model */
if( (model = get_file_system_model(GTK_WIDGET(data->window)) ) == NULL)
{
g_print("could not get file system model\n\n");
return;
}
/* Load file uri */
if( !hildon_file_system_model_load_uri( model,
TEXT_FILE,
&iter ) )
{
g_print("couldnot load uri %s\n", TEXT_FILE);
return;
}
/* Create dialog */
dialog = HILDON_FILE_DETAILS_DIALOG
(hildon_file_details_dialog_new_with_model(GTK_WINDOW(data->window), model));
hildon_file_details_dialog_set_file_iter
(HILDON_FILE_DETAILS_DIALOG(dialog), &iter);
/*.....*/
}
The file details dialog can also have an additional application-specific tab. In this example, the extra tab only reshows the file name; but in a real application, it could contain line count, preview of a document, length of the sound etc. The type of the object "additional tab" is GTK_LABEL, so all kind of widgets can be inserted inside it.
#define TEXT_FILE "file://home/user/MyDocs/.documents/foo.txt"
void callback_file_details(GtkWidget * widget, AppData * data)
{
/*.....*/
/* Set the dialog to show tabs */
g_object_set(dialog, "show-tabs", TRUE, NULL);
/* Create some content to additional tab */
label = gtk_label_new(g_strdup_printf
("Name of this \nfile really is:\n%s", TEXT_FILE));
/* Add content to additional tab label */
g_object_set(dialog, "additional-tab", label, NULL);
/* Change tab label */
g_object_set(dialog, "additional-tab-label", "More Info", NULL);
/*.....*/
}
Then the dialog is run just like any other GTK dialog.
/* Show the dialog */
gtk_widget_show_all(GTK_WIDGET(dialog));
/* Wait for user to select OK or CANCEL */
result = gtk_dialog_run(GTK_DIALOG(dialog));
Here is an example screenshot of the File Details dialog.
To compile and run, start Xephyr server and:
$ gcc -o example_file_details example_file_details.c `pkg-config gtk+-2.0 hildon-1 hildon-fm-2 --cflags --libs` -Wall $ run-standalone.sh ./example_file_details
#include <hildon-widgets/hildon-program.h> #include <hildon-widgets/hildon-banner.h> #include <gtk/gtk.h>
HildonBanners are used in many places in maemo applications. They show a small information banner in the top right-hand corner of the application view for a few seconds, and then disappear automatically. They are used for all kinds of notifications, such as "File Saved", "Unable to make connection" or "Please choose only one option".
There are multiple functions to choose from, depending on the information that is wanted to be shown. The banner can show plain text, custom icon, or progress bar to the user. The next example (example_banner.c) presents all these alternatives. First the function which shows the different HildonBanners one by one.
static gint banner_type = 1;
GtkWidget *banner = NULL;
/* Callback to show information banners */
void show_banner(GtkButton * widget, HildonWindow * window)
{
switch (banner_type) {
case 1:
/* Show normal information banner and this automatically goes away */
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Hi there!");
break;
case 2:
/* Information banner with animation icon.
* This banner does not automatically disapear. */
banner = hildon_banner_show_animation(GTK_WIDGET(window), NULL,
"This is animation icon");
break;
case 3:
/* Remove current information banner */
gtk_widget_destroy(GTK_WIDGET(banner));
break;
case 4:
/* Information banner with progressbar */
banner = hildon_banner_show_progress(GTK_WIDGET(window), NULL,
"Info with progress bar");
/* Set bar to be 20% full */
hildon_banner_set_fraction(HILDON_BANNER(banner), 0.2);
break;
case 5:
/* Set bar to be 80% full */
hildon_banner_set_fraction(HILDON_BANNER(banner), 0.8);
break;
case 6:
/* With sixth click, end the application */
gtk_main_quit();
}
/* Increase the counter */
banner_type++;
}
The main function introduces how to pack widgets inside the HildonWindow using GtkVBox, which is a vertical box container in GTK+. Inside the vbox, it places one button, which is connected to the show_banner callback function.
/* Main application */
int main(int argc, char *argv[])
{
/* Create needed variables */
HildonProgram *program;
HildonWindow *window;
GtkWidget *main_vbox;
GtkWidget *button1;
/* Initialize the GTK. */
gtk_init(&argc, &argv);
/* Create the hildon program and setup the title */
program = HILDON_PROGRAM(hildon_program_get_instance());
g_set_application_name("App Title");
/* Create HildonWindow and set it to HildonProgram */
window = HILDON_WINDOW(hildon_window_new());
hildon_program_add_window(program, window);
/* Add vbox to hildon window */
main_vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER(window), main_vbox);
/* Add button to vbox */
button1 = gtk_button_new_with_label("Show Info");
gtk_box_pack_start(GTK_BOX(main_vbox), button1, FALSE, TRUE, 0);
/* Add signal listener to button */
g_signal_connect(G_OBJECT(button1), "clicked",
G_CALLBACK(show_banner), window);
/* Connect signal to X in the upper corner */
g_signal_connect(G_OBJECT(window), "delete_event",
G_CALLBACK(gtk_main_quit), NULL);
/* Begin the main application */
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
/* Exit */
return 0;
}
Using HildonBanner widgets is very simple, as the HildonWindow takes care of showing them. The output of the application can be seen in the screenshots below. When clicking the button, a normal banner appears first, then one with the custom icon, and at last the progress bar. The last click closes the application.
There are a lot more Hildon widgets available; to get more information about them, see the Hildon API documentation.
To compile and run, start Xephyr server and:
$ gcc -o example_banner example_banner.c `pkg-config gtk+-2.0 hildon-1 --cflags --libs` -Wall $ run-standalone.sh ./example_banner
The maemo platform is designed to be used with a touchscreen, and the SDK with a mouse to emulate the touchscreen functionality. Some aspects to remember:
Context sensitive menus are implemented in the GtkWidget class. An application may register these menus for its widgets, accessed by tap-and-hold over the widget.
An example on how to add this functionality can be found in example_context.c.
#include <hildon-widgets/hildon-program.h> #include <hildon-widgets/hildon-banner.h> #include <gtk/gtk.h> #include <gdk/gdkkeysyms.h>
The maemo device has hardware keys, which are also supported in the SDK using different keyboard events. The actual number and type of keys depends on the device, but the common ones are presented in the table below.
| Button | Description | Keyboard | Key Event |
|---|---|---|---|
| Move up | Arrow key up | GDK_Up | |
| Move down | Arrow key down | GDK_Down | |
| Move left | Arrow key left | GDK_Left | |
| Move right | Arrow key right | GDK_Right | |
| Select, Confirm | Return | GDK_Return | |
| Cancel, Close | Esc | GDK_Escape | |
| Open menu | F4 | GDK_F4 | |
| Full screen | F6 | GDK_F6 | |
| Increase / Zoom in / Volume up | F7 | GDK_F7 | |
| Decrease / Zoom out / Volume down | F8 | GDK_F8 |
Adding support for a wider number of keys is easy, as the actual key pressing signals can be implemented with GDK key events. An example (example_hard_keys.c) of this mapping is presented below, first the callback function that is called whenever keys are pressed.
/* Callback for hardware keys */
gboolean key_press_cb(GtkWidget * widget, GdkEventKey * event,
HildonWindow * window)
{
switch (event->keyval) {
case GDK_Up:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Navigation Key Up");
return TRUE;
case GDK_Down:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Navigation Key Down");
return TRUE;
case GDK_Left:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Navigation Key Left");
return TRUE;
case GDK_Right:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Navigation Key Right");
return TRUE;
case GDK_Return:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Navigation Key select");
return TRUE;
case GDK_F6:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Full screen");
return TRUE;
case GDK_F7:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Increase (zoom in)");
return TRUE;
case GDK_F8:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Decrease (zoom out)");
return TRUE;
case GDK_Escape:
hildon_banner_show_information(GTK_WIDGET(window), NULL, "Cancel/Close");
return TRUE;
}
return FALSE;
}
In the main function, the callback function is connected with g_signal_connect to listen "key_press_event" signal. Whenever a hardware key is pressed, key_press_cb function is called to process the event.
int main( int argc, char* argv[] )
{
...
/* Add hardware button listener to application */
g_signal_connect(G_OBJECT(window),
"key_press_event", G_CALLBACK(key_press_cb), window);
...
}
The output of the hardware key example application is shown below in the case zoom in (F7) is pressed.
Just like the standard GTK+ lib, Hildon can also recognize accelerator keys. For example, pressing Ctrl+g or Ctrl+G can be recognized and handled as demonstrated in example_hard_keys.c:
guint modifiers;
modifiers = gtk_accelerator_get_default_mod_mask ();
if ((event->state & modifiers) == GDK_CONTROL_MASK)
&& (event->keyval == GDK_g || event->keyval == GDK_G))
{
hildon_banner_show_information(GTK_WIDGET(window), NULL,
"Ctrl+g or Ctrl+G key combination");
}
More information on the GTK+ key events can be found at GDK Key Values [25].
Maemo 4.x also supports GtkAcceleratorGroup, which is a group of keyboard accelerators, typically attached to a toplevel Gtk Window. To learn more about GtkAccelerator, look at GTK+ Accelerator Groups [39]. As an example, usually Gtk Applications have "Ctrl + Q" as a shortcut for menu item "Quit" to quit the application. The following excerpts from example_hard_keys.c demonstrate how it is implemented in maemo 4.x.
GtkAccelGroup* accel_group;
accel_group = gtk_accel_group_new ();
gtk_window_add_accel_group(GTK_WINDOW(main_window),
accel_group);
#define ACCEL_PATH_ROOT "/menu"
#define ACCEL_PATH_QUIT ACCEL_PATH_ROOT"/item_quit"
/* Create new main menu */
main_menu = gtk_menu_new();
item_quit = gtk_menu_item_new_with_label("Quit");
/* Attach the menu item to the Gtk Accelerator defined above */
gtk_menu_set_accel_group( GTK_MENU(main_menu),
accel_group);
gtk_menu_item_set_accel_path( GTK_MENU_ITEM(item_quit),
ACCEL_PATH_QUIT);
gtk_menu_append(main_menu, item_quit);
The accel_group is attached to the main menu, which contains the "Quit" menu item. Also, the item_quit is attached with an accelerator path, which is a constant string "<example-hard-keys>/menu/item_quit". Basically, this string is a unique string that identifies the menu item "Quit" of the application example_hard_keys.c. For example, you can also have "<example-hard-keys>/menu/item_new" as a valid accelerator path for "Open new file" menu item. Please read on for more information on accelerator path.
More information on how to attach an Accelerator with a Menu item can be found in GTK's documentation.
gtk_accel_map_add_entry(ACCEL_PATH_QUIT,GDK_q,GDK_CONTROL_MASK);
The above function defines, on the application's level, that a "Ctrl+q" key accelerator is attached to the accelerator path
"<example-hard-keys>/menu/item_quit". How to define a proper accelerator path and get it mapped is further explained in GTK's documentation.
Now "Ctrl+q" will activate the menu item "Quit".
To compile and run, see instructions in section 6.6.3. In addition to that, Gtk-Accel must be enabled by a gconf value:
$ gconftool-2 -s /apps/osso/gtk/enable-accels -t bool true
GTK Mnemonics
can also be enabled by:
$gconftool-2 -s /apps/osso/gtk/enable-mnemonics -t bool true
The maemo platform includes text input methods used with touch screen. There are two different input types: virtual keyboard and handwriting recognition. Only the virtual keyboard is provided in maemo SDK, but as the input is transparent for the application, all applications will automatically support also handwriting recognition. When using the device, it is possible to select between two keyboard layouts. When touching the screen using a finger, a thumb keyboard is provided.
The input methods work automatically, so that whenever text widgets (entry, text view) are pressed, or text inside them selected, the keyboard pops up. In the same way, the keyboard is hidden automatically, when the user selects another widget.
For the development use, it is also possible to use the PC keyboard to input text. It should work without any changes, but if any problems are experienced, it can also be enabled in SDK by clicking the right mouse button on top of a text widget, and then selecting "Input Methods -> X Input Method" from the menu.
The layout of maemo applications can be described as wide and not very tall, because of the screen dimensions (800x480 pixels). This wideness is emphasized, when a text input method is popped up, so there are some rules that should be followed, when the application UI is designed.
There are two functions needed for a control panel applet (libapplet.c). These functions are defined in
#include <hildon-cp-plugin/hildon-cp-plugin-interface.h>
The first, more important one, is called execute. It is called, when the applet is activated from Control Panel. Usually this function creates a dialog and waits until it is done. Any dialog created should be modal to the window in parameter. N.B. The library might be unloaded when execute returns, so no g_timeouts, gconf_notifys or such should be left when done. Gtk or osso initialization is not needed, as they are already done at this point.
#include <hildon-cp-plugin/hildon-cp-plugin-interface.h>
#include <gtk/gtk.h>
osso_return_t execute(osso_context_t *osso, gpointer data, gboolean user_activated)
{
GtkWidget *dialog;
gint response;
/* Create dialog with OK and Cancel buttons. Leave the separator out,
* as we do not have any content. */
dialog = gtk_dialog_new_with_buttons(
"Hello control panel",
GTK_WINDOW(data),
GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_OK,
GTK_RESPONSE_OK,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
NULL);
/* ... add something to the dialog ... */
if (!user_activated)
{
/* ... load state ... */
}
/* Wait until user finishes the dialog. */
response = gtk_dialog_run(GTK_DIALOG(dialog));
if (response == GTK_RESPONSE_OK)
{
/* ... do something with the dialog stuff ... */
}
/* Free the dialog (and it's children) */
gtk_widget_destroy(GTK_WIDGET(dialog));
return OSSO_OK;
}
The other function is called save_state. It is called when application using the applet is saving state. Usually this does nothing, but if support for state saving is needed, this should be used.
osso_return_t save_state(osso_context_t *osso, gpointer data)
{
/* ... save state ... */
return OSSO_OK;
}
To use the applet, it needs to be built into a dynamic library aka shared object. This can be accomplished by giving flag -shared to gcc.
[sbox-DIABLO_X86: ~] > gcc -shared `pkg-config gtk+-2.0 libosso --libs --cflags` applet.c -o libapplet.so
The binary produced by gcc should be installed to path specified in hildon-control-panel pkg-config entry. This path can be obtained with pkg-config hildon-control-panel -variable=pluginlibdir. By default this is /usr/lib/hildon-control-panel.
Any control panel applet needs a desktop file describing it. The file contains metadata like name, icon name and library of the applet. The applet desktop file is much like the desktop file for any other application.
Here is an example desktop file (applet.desktop) for the applet created above. Maemo 4.x brings new "Categories" field.
[Desktop Entry] Encoding=UTF-8 Version=1.0 Name=Control Panel Hello World Comment=A control panel example applet Type=HildonControlPanelPlugin Icon=qgn_list_cp_isetup X-control-panel-plugin=libapplet.so Categories=general
The desktop file must be installed to directory specified in pkg-config file for hildon-control-panel. This directory can be obtained by pkg-config hildon-control-panel -variable=plugindesktopentrydir. By default, this is /usr/share/applications/hildon-control-panel.
This section shows how to write Status Bar, Task Navigator, Control Panel and Home Area plug-ins for Hildon Desktop.
Hildon Desktop consists of several subcomponents: Task Navigator, Home Area and Status Bar. They support additional plug-ins that are displayed on each these components, extending the functionality of the host application. This section will explain how to write plug-ins for the desktop components using the new GTypeModule-based API. The section also explains the API for writing applets for Control Panel.
The Task Navigator implements a modular plug-in architecture, allowing binary plug-ins to be loaded. This extends the functionality of the Task Navigator, and is achieved with task buttons. The license for the Task Navigator plug-in can be open source or closed source; in this way, the Task Navigator is versatile and ideal for use in many kinds of devices and applications.
Task Navigator plug-ins can have any widget as their representation on the Task Navigator plug-in area. The plug-in representation, statically visible in the task navigator, can be e.g. GtkButton. Task navigator loads the library and displays the main widget of the plug-in in the plug-in area of the Task Navigator.
The plug-ins are responsible for their own signals, e.g. clicked, toggle and related events, and implementing the callback functions for them. Task navigator only provides a container for the plug-ins; the plug-ins themselves are responsible for implementing their own UI.
Task Navigator plug-ins must provide a desktop file. It is placed in the directory given by:
pkg-config osso-af-settings --variable=tasknavigatordesktopentrydir
The following describes the required contents of the .desktop file.
The Task Navigator Configuration Control Panel applet is the only one involved with the contents of these .desktop files. Thus, the Task Navigator itself does not read them, and the absence of the file does not prevent the plug-in from working.
The following is an example .desktop file for the Task Navigator applet:
[Desktop Entry] Name=Hello World Type=default X-Path=/usr/lib/hildon-navigator/libhelloworld-tn.so
Should look like a usual GObject-based class/object declation. The following is an example implementation:
#ifndef HELLO_NAVIGATOR_PLUGIN_H
#define HELLO_NAVIGATOR_PLUGIN_H
#include
/* For Task Navigator plugins */
#include
G_BEGIN_DECLS
/* Common struct types declarations */
typedef struct _HelloNavigatorPlugin HelloNavigatorPlugin;
typedef struct _HelloNavigatorPluginClass HelloNavigatorPluginClass;
typedef struct _HelloNavigatorPluginPrivate HelloNavigatorPluginPrivate;
/* Common macros */
#define HELLO_TYPE_NAVIGATOR_PLUGIN (hello_navigator_plugin_get_type ())
#define HELLO_NAVIGATOR_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
HELLO_TYPE_NAVIGATOR_PLUGIN, HelloNavigatorPlugin))
#define HELLO_NAVIGATOR_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
HELLO_TYPE_NAVIGATOR_PLUGIN, HelloNavigatorPluginClass))
#define HELLO_IS_NAVIGATOR_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
HELLO_TYPE_NAVIGATOR_PLUGIN))
#define HELLO_IS_NAVIGATOR_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
HELLO_TYPE_NAVIGATOR_PLUGIN))
#define HELLO_NAVIGATOR_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
HELLO_TYPE_NAVIGATOR_PLUGIN, HelloNavigatorPluginClass))
/* Instance struct */
struct _HelloNavigatorPlugin
{
TaskNavigatorItem tnitem;
HelloNavigatorPluginPrivate *priv;
GtkWidget *button;
GtkWidget *menu;
};
/* Class struct */
struct _HelloNavigatorPluginClass
{
TaskNavigatorItemClass parent_class;
};
GType hello_navigator_plugin_get_type (void);
GtkWidget *hello_world_button_new (int padding);
G_END_DECLS
#endif /* HELLO_NAVIGATOR_PLUGIN_H */
The function is called upon initialization. It is declared with HD_DEFINE_PLUGIN. It creates the widget that is displayed on the Task Navigator. As mentioned earlier, this is typically a GtkButton. The Task Navigator automatically sizes the button correctly. It also initializes the pop-up menu.
If the button needs to have proper skin, its name should be set as "hildon-navigator-button-one" with gtk_widget_set_name function.
The following is an example implementation:
HD_DEFINE_PLUGIN (HelloNavigatorPlugin, hello_navigator_plugin, TASKNAVIGATOR_TYPE_ITEM);
static void
hello_navigator_plugin_init (HelloNavigatorPlugin *navigator_plugin)
{
GtkWidget *button;
// button = gtk_button_new_with_label ("HW");
button = hello_world_button_new (10);
navigator_plugin->button=button;
navigator_plugin->menu=create_menu();
// gtk_button_set_image (GTK_BUTTON (button),
// gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_LARGE_TOOLBAR));
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (popup_menu), navigator_plugin);
gtk_widget_set_size_request (button, 80, 80);
gtk_widget_set_name (button, "hildon-navigator-button-one");
gtk_widget_show_all (button);
gtk_container_add (GTK_CONTAINER (navigator_plugin), button);
gtk_widget_show_all (navigator_plugin);
}
static void
hello_navigator_plugin_class_init (HelloNavigatorPluginClass *class)
{
}
It is advisable to set a pointer to the button in the data that is returned, since it is used later on (navigator_plugin->button).
The menu is created, and proper pop-up, position and action (show_dialog) functions are defined.
The following is an example implementation:
static void
show_dialog (GtkWidget *item, HelloNavigatorPlugin *thw)
{
GtkWidget *dialog;
dialog = gtk_message_dialog_new (NULL,
GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
"Hello world!");
gtk_window_set_title (GTK_WINDOW(dialog), "TN Plugin Example");
gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy (dialog);
}
static void
menu_position (GtkMenu *menu,
gint *x,
gint *y,
gboolean *push_in,
HelloNavigatorPlugin *thw)
{
g_return_if_fail (thw->button);
*push_in = TRUE;
*x = thw->button->allocation.x + thw->button->allocation.width;
*y = thw->button->allocation.y;
}
static void
popup_menu (GtkWidget *button, HelloNavigatorPlugin *plugin)
{
if (!plugin->menu)
return;
gtk_menu_popup (GTK_MENU (plugin->menu),
NULL,
NULL,
(GtkMenuPositionFunc)menu_position,
plugin,
0,
gtk_get_current_event_time());
}
static GtkWidget*
create_menu (void)
{
GtkWidget *menu;
GtkWidget *menu_item;
menu = gtk_menu_new ();
menu_item = gtk_menu_item_new_with_label ("Hello World!");
g_signal_connect (G_OBJECT (menu_item), "activate",
G_CALLBACK (show_dialog), NULL);
gtk_menu_append (menu, menu_item);
/* Name the menu to get the appropriate theming */
gtk_widget_set_name (menu, "menu_from_navigator");
gtk_widget_show_all (menu);
return menu;
}
The plug-in install path can be received using command:
pkg-config osso-af-settings --variable=hildondesktoplibdir
Home plug-ins are located in the Home Area of the desktop. They can be resized, if the resizability flag X-home-applet-resizable is mentioned in the .desktop file of the plug-in.
Each home applet needs to provide a .desktop file. The .desktop file location is determined with:
pkg-config osso-af-settings --variable=homedesktopentrydir
The following is an example hello-world-applet.desktop:
[Desktop Entry] Name=Hello, World! Comment=Example Home plugin Type=default X-Path=libhelloworld-home.so
The mandatory fields are the same as for Task Navigator plug-ins.
Home plug-ins should inherit from libhildondesktop's HildonDesktopHomeItem. Here is an example header file:
#ifndef HELLO_HOME_PLUGIN_H
#define HELLO_HOME_PLUGIN_H
#include <glib-object.h>
#include <libhildondesktop/hildon-desktop-home-item.h>
G_BEGIN_DECLS
/* Common struct types declarations */
typedef struct _HelloHomePlugin HelloHomePlugin;
typedef struct _HelloHomePluginClass HelloHomePluginClass;
typedef struct _HelloHomePluginPrivate HelloHomePluginPrivate;
/* Common macros */
#define HELLO_TYPE_HOME_PLUGIN (hello_statusbar_plugin_get_type ())
#define HELLO_HOME_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
HELLO_TYPE_HOME_PLUGIN, HelloHomePlugin))
#define HELLO_HOME_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
HELLO_TYPE_HOME_PLUGIN, HelloHomePluginClass))
#define HELLO_IS_HOME_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
HELLO_TYPE_HOME_PLUGIN))
#define HELLO_IS_HOME_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
HELLO_TYPE_HOME_PLUGIN))
#define HELLO_HOME_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
HELLO_TYPE_HOME_PLUGIN, HelloHomePluginClass))
/* Instance struct */
struct _HelloHomePlugin
{
HildonDesktopHomeItem hitem;
HelloHomePluginPrivate *priv;
};
/* Class struct */
struct _HelloHomePluginClass
{
HildonDesktopHomeItemClass parent_class;
};
GType hello_home_plugin_get_type (void);
G_END_DECLS
#endif
The main functionality consists of required headers, HD_DEFINE_PLUGIN and init functions. The following example creates a button which is defined in the libhelloworld.h header (see example files) and assigns a dialog showing function to it. Instead of a button, any other suitable GTK+ widget may be used. The widget will be shown in the Home view.
#include <glib.h>
#include <gtk/gtk.hh>
#include <libhildondesktop/libhildondesktop.hh>
#include "hello-world-home.h"
#include "libhelloworld.h"
HD_DEFINE_PLUGIN (HelloHomePlugin, hello_home_plugin, HILDON_DESKTOP_TYPE_HOME_ITEM);
static void
hello_home_plugin_init (HelloHomePlugin *home_plugin)
{
GtkWidget *button;
button = hello_world_button_new (10);
g_signal_connect (button, "clicked",
G_CALLBACK (hello_world_dialog_show),
NULL);
gtk_widget_show_all (button);
/* Set the resizing behavior */
hildon_desktop_home_item_set_resize_type (HILDON_DESKTOP_HOME_ITEM (home_plugin),
HILDON_DESKTOP_HOME_ITEM_RESIZE_BOTH);
gtk_container_add (GTK_CONTAINER (home_plugin), button);
}
static void
hello_home_plugin_class_init (HelloHomePluginClass *class)
{
}
The plug-in install path can be received using command:
pkg-config osso-af-settings --variable=hildondesktoplibdir
The Status Bar contains status-bar plug-ins. Order of plug-ins and other properties can be set with the Task Navigator and Status Bar configuration applet found on the Control Panel ("Navigation applet").
Status Bar plug-ins are divided into three categories; namely permanent, conditional and temporal. Permanent plug-ins are shown all the time. Conditional and temporal plug-ins are shown only when the condition is fulfilled.
The .desktop file shall be placed in the directory given by:
pkg-config osso-af-settings --variable=statusbardesktopentrydir
Each plug-in has to offer a .desktop file:
[Desktop Entry]
Name=<logical name identifier>
Icon=<logical icon identifier>
Category=<permanent/conditional/temporal>,
the default value is permanent if the key value read fails.
X-Path=lib<plugin name>.so
Type=default
Mandatory=<true/false>,
if not set, the default value is false
Here is an example:
[Desktop Entry] Name=Hello world Icon=hello-world Category=temporal Type=default X-Path=libhelloworld_sb.so