Documentation/Maemo 5 Developer Guide/Application Development/Writing a new maemo application
This section is a guide for making new applications for the Maemo platform. Maemo 5 SDK needs to be installed successfully as a pre-requisite.
A simple text editor with a few essential features is used as a example. Let us name it as MaemoPad.
MaemoPad has the following basic features: New, Open, Save, Save As..., Cut, Copy, Paste, Font, Full Screen, Full Screen hardware key handling, Send-Via email/bluetooth and Close. For simplicity, it does not contain advanced features like Undo, Redo, different fonts in one document, pictures, tables, etc.
The figure below is a screenshot of the text editor.
Contents |
[edit] Creating the application file structure
In general, a Maemo application uses the GNU Build System and has the following files and subdirectories:
-
src/
: Contains the source files. -
debian/
: Contains the files related to Debian packaging -
data/
: Contains all data files needed to run the application. Maemopad will need the following data files:maemopad.desktop
file- D-Bus
maemopad.service
file - application icons in a separate directory called
icons/
-
po/
: Contains the localization files. Maemopad contains the following files:-
POTFILES.in
list the names of the files to be localized. - Translation file
en_GB.po
containing British English strings for the application. - Translation file
fi_FI.po
containing Finnish strings for the application.
-
-
autogen.sh
is a shell script that provides automatic build system preparation. -
configure.ac
is an input file forautoconf
that containsautoconf
macros that test the system features the package needs or can use. It produces theconfigure
script. -
Makefile.am
is used byautomake
to produce a standards-compliantMakefile.in
. TheMakefile.am
in the top source directory is usually very simple and includes the files and subdirectories needed to make the application:src/
,po/
anddata/
and all theMakefile
s insrc/
,po/
, anddata/
directories.
You can compile the source as follows:
[sbox-FREMANTLE_X86 ~] > ./autogen.sh [sbox-FREMANTLE_X86 ~] >$ ./configure [sbox-FREMANTLE_X86 ~] >$ make
For more information about GNU autoconf and automake, see the GNU build system article.
[edit] Coding the application
The src/ directory of MaemoPad contains the following files:
- main.c
- maemopad-window.h
- maemopad-window.c
- fullscreenmanager.c
- fullscreenmanage.h
- Makefile.am
[edit] maemopad-window.h
maemopad-window.h
defines the MaemopadWindow
structure to hold the application data and other declarations. Even though the data varies depending on the application, a sample structure might look like this:
typedef struct _MaemopadWindow MaemopadWindow; struct _MaemopadWindow { HildonWindow parent; /* Osso context, needed for "send via" functionality. */ osso_context_t *osso; /* Fullscreen mode is on (TRUE) or off (FALSE): */ gboolean fullscreen; /* Button items for menu: */ GtkWidget *new_item; GtkWidget *open_item; GtkWidget *save_item; GtkWidget *saveas_item; GtkWidget *cut_item; GtkWidget *copy_item; GtkWidget *paste_item; /*.....more...truncated...*/ };
Where MaemopadWindow
is a structure containing pointers to all the UI objects, such as HildonWindow
, menu items, toolbar; and UI-related data, for instance a boolean variable indicating whether the application is in fullscreen mode.
Each application creates its own AppData
variable, and this variable is usually passed around as a parameter in the functions, particularly the callback functions, so that the applications data can be accessible by the functions.
Many applications declare this AppData
variable as global.
[edit] main.c
main.c usually performs, at least, the following functions:
- Initializing GTK+
- Initializing localization
- Creating an instance of
HildonProgram
- Calling to a function, usually defined in
interface.c
, to create the main view. - Connecting the main view window to the created
HildonProgram
- Running
gtk_main()
- Connecting the
delete_event
of the main view to a callback function to handle a proper application exit, such as destroying the main view window, freeing used memory and saving application states before exit. - Call
gtk_main_quit()
Here is the main function of MaemoPad with comments:
int main (int argc, char* argv[]) { osso_context_t *osso = NULL; AppData *data = g_new0 (AppData, 1); /* Initialize the locale stuff: */ setlocale (LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); /* Inititialize GTK+ and hildon: */ hildon_gtk_init (&argc, &argv); if (!g_thread_supported()) { g_thread_init (NULL); } /* Create the hildon application and setup the title: */ data->program = HILDON_PROGRAM (hildon_program_get_instance ()); g_set_application_name (_("MaemoPad")); /* We need the osso context to call libmodest_dbus_client_compose_mail() later. */ osso = osso_initialize (OSSO_SERVICE, VERSION, TRUE, NULL); g_assert (osso); /* Create the window for our application: */ data->window = maemopad_window_new (osso); hildon_program_add_window (data->program, data->window); /* Show the main window and start the mainloop, * quitting the mainloop when it the main window is hidden: */ gtk_widget_show (GTK_WIDGET (data->window)); g_signal_connect(data->window, "hide", G_CALLBACK (&on_main_window_hide), NULL); g_signal_connect(data->window, "delete_event", G_CALLBACK (>k_widget_hide_on_delete), NULL); gtk_main(); /* Clean up: */ gtk_widget_destroy (GTK_WIDGET (data->window)); g_free (data); return 0; }
[edit] User interface
The graphical user interface code is implemented in directory ./src/ui/
. It contains two .c files: interface.c
and callbacks.c
[edit] interface.c
This file creates the graphical user interface (GUI) and connects the signals and events to appropriate handlers defined in callbacks.c
. See the Hildon UI style guide for information on how to create GUI in Maemo. For more information on GTK+ see the GTK+ Reference Manual.
As a general practice, an AppUIData
struct variable is created when creating the GUI. And then, a HildonWindow
and smaller components are created in different functions, such as create_menu()
, create_toolbar()
. When creating each component, AppUIData
should refer to various necessary UI objects created along the way.
The following excerpt shows how AppUIData
is created, and how it points to the toolbar and the button New file on the toolbar.
/* Creates and initializes a main_view */ AppUIData* interface_main_view_new( AppData *data ) { /* Zero memory with g_new0 */ AppUIData* result = g_new0( AppUIData, 1 ); /*....*/ create_toolbar( result ); /*....*/ } /* Create toolbar to mainview */ static void create_toolbar ( AppUIData *main ) { /* Create new GTK toolbar */ main->toolbar = gtk_toolbar_new (); /*....*/ /* Create the "New file" button in the toolbar */ main->new_tb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW); /*....*/ }
[edit] callbacks.c
callbacks.c
defines all the functions that handle the signals or events that might be triggered by the UI. When creating different UI objects in interface.c
, the handlers are registered as follows.
/* Create the menu items needed for the drop down menu */ static void create_menu( AppUIData *main ) { /* ... */ main->new_item = gtk_menu_item_new_with_label ( _("New") ); /* ... */ /* Attach the callback functions to the activate signal */ g_signal_connect( G_OBJECT( main->new_item ), "activate", G_CALLBACK ( callback_file_new), main ); /* ... */ }
Function callback_file_new
is implemented in callbacks.c
, saving the current file if needed, and then opening a new file to edit.
void callback_file_new(GtkAction * action, gpointer data) { gint answer; AppUIData *mainview = NULL; mainview = ( AppUIData * ) data; g_assert(mainview != NULL && mainview->data != NULL ); /* save changes note if file is edited */ if( mainview->file_edited ) { answer = interface_save_changes_note( mainview ); if( answer == CONFRESP_YES ) { if( mainview->file_name == NULL ) { mainview->file_name = interface_file_chooser(mainview, GTK_FILE_CHOOSER_ACTION_SAVE); } write_buffer_to_file ( mainview ); } } /* clear buffer, filename and free buffer text */ gtk_text_buffer_set_text ( GTK_TEXT_BUFFER (mainview->buffer), "", -1 ); mainview->file_name = NULL; mainview->file_edited = FALSE; }
N.B. The AppUIData
struct variable mainview
is retrieved in such a way that the handlers can have a direct effect on the UI for the users.
MaemoPad contains many functions:
- File Save/Save-As/Open: This uses
HildonFileChooserDialog
. - Edit Cut/Copy/Paste: This uses the clipboard (clipboard usage)
- Font/Color Selector: These are explained in
HildonFontSelectionDialog
andHildonColorChooser
. - Send via Email/Bluetooth
[edit] interface.h
In the interface header file interface.h
, public functions are defined for main.c
and callbacks.c
. In the case of MaemoPad, confirmation responses for the save changes note, MaemopadError
enum for the Hildon error note and the AppUIData
are also defined here. N.B. AppUIData
can also be defined in appdata.h
in some other applications. MaemoPad's interface.h
looks like this:
#define MAIN_VIEW_NAME "AppUIData" typedef enum { MAEMOPAD_NO_ERROR = 0, MAEMOPAD_ERROR_INVALID_URI, MAEMOPAD_ERROR_SAVE_FAILED, MAEMOPAD_ERROR_OPEN_FAILED } MaemopadError; /* Struct to include view's information */ typedef struct _AppUIData AppUIData; struct _AppUIData { /* Handle to app's data */ AppData *data; /* Fullscreen mode is on (TRUE) or off (FALSE) */ gboolean fullscreen; /* Items for menu */ GtkWidget *file_item; GtkWidget *new_item; /* ... */ GtkWidget *font_item; GtkWidget *fullscreen_item; /* Toolbar */ GtkWidget* toolbar; GtkWidget* iconw; GtkToolItem* new_tb; GtkToolItem* open_tb; /* ... */ /* Textview related */ GtkWidget* scrolledwindow; /* textview is under this widget */ GtkWidget* textview; /* widget that shows the text */ GtkTextBuffer* buffer; /* buffer that contains the text */ GtkClipboard* clipboard; /* clipboard for copy/paste */ PangoFontDescription* font_desc; /* font used in textview */ gboolean file_edited; /* tells is our file on view edited */ gchar* file_name; /* directory/file under editing */ }; /* Public functions: */ AppUIData* interface_main_view_new( AppData* data ); void interface_main_view_destroy( AppUIData* main ); char* interface_file_chooser( AppUIData* main, GtkFileChooserAction action ); PangoFontDescription* interface_font_chooser( AppUIData * main ); /* ... */
[edit] Localization
Localization means translating the application into different languages. In Maemo, this is fairly easily performed by grouping all the strings needing translations into a .po file, giving them each an id, and then using the id in the code instead of hard-coded strings. The function used to generate the translated strings from an id in Maemo is the standard GNU gettext()
.
[edit] Initialization
When the application runs, gettext()
is used to determine the correct language depending on the locale settings. The application initializes the text domain as follows:
int main( int argc, char* argv[] ) { /* ... */ /* Initialize the locale stuff */ setlocale ( LC_ALL, "" ); bindtextdomain ( GETTEXT_PACKAGE, LOCALEDIR ); bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); textdomain ( GETTEXT_PACKAGE ); /* ... */ }
More information on localization can be found in the Maemo localization section.
[edit] File structure
Localization files are stored in the po/
directory. The following files are used for MaemoPad localization:
-
Makefile.am
-
POTFILES.in
-
en_GB.po
POTFILES.in
contains the list of the source code files to be localized. In MaemoPad, only main.c and interface.c contain strings that need to be localized.
# List of MaemoPad source files to be localized ../src/main.c ../src/ui/interface.c
File en_GB.po
includes translated text for British English. It contains pairs of id/string as follows:
# ... msgid "maemopad_yes" msgstr "Yes" # ...
N.B. The comments in .po file start with ``#``.
[edit] Using en_GB.po
The msgid(s) are passed to the GNU gettext()
function as a parameter to generate the translated string. In Maemo, the recommended way is
#define _(String) gettext(String)
Therefore, in MaemoPad, the string for Menu->File->Open menu is created as follows:
main->open_item = gtk_menu_item_new_with_label ( _("Open") );
[edit] Creating .po files from source
Sometimes code must be localized for applications that were not originally designed for localization. You can create .po files from source code using GNU xgettext, by extracting all the strings from the source files into a template.po
file
xgettext -f POTFILES.in -C -a -o template.po
Read the man page for xgettext for more information, in short:
-
-f POTFILES.in
usesPOTFILES.in
to get the files to be localized -
-C
is for C-code type of strings -
-a
is for ensuring that we get all strings from specified files -
-o template.po
defines the output filename.
The next step is to copy this template.po
into ./po/en_GB.po
and add or edit all the strings in British English. Other languages can be handled in the same way.
[edit] Adding application to menu
In short, the maemopad.desktop
and com.nokia.maemopad.service
files are stored in the ./data
directory, and they look like this, respectively:
[Desktop Entry] Encoding=UTF-8 Version=0.1 Type=Application Name=MaemoPad Exec=/usr/bin/maemopad Icon=maemopad X-Window-Icon=maemopad X-Window-Icon-Dimmed=maemopad X-Osso-Service=com.nokia.maemopad X-Osso-Type=application/x-executable
(N.B. Whitespace is not allowed after the lines.)
# Service description file [D-BUS Service] Name=com.nokia.maemopad Exec=/usr/bin/maemopad
These files reside on the device here:
-
/usr/share/applications/hildon
-
/usr/share/dbus-1/services
When the Debian package is installed to Maemo platform, .desktop and .service files are used to link MaemoPad to the Task Navigator.
[edit] Packaging the application
Main article: Packaging
A Debian package is an application packed in one file to make installing easy in Debian-based operating systems, like Maemo platform. More information about creating a Debian packages can be found in the creating Debian packages section of the Packaging, Deploying and Distributing chapter. Our goal in this section is to create a Debian package of MaemoPad, to be installed in the Maemo platform.
If creating a package that can be installed using the Application Manager, be sure that the package is in an allowed section.
[edit] Creating debian/ Directory
In order to create the package, the following files are created in the debian/
directory:
-
changelog
-
control
-
copyright
-
rules
- ... etc ...
The rules
file is the file defining how the Debian package is built. The rules
file tells where the files should be installed. Also a control
file is needed to define what kind of packages (often different language versions) are going to be created. changelog
and copyright
files are also needed, or the package does not build. The changelog
file consists of the version number of the package, and a short description about changes compared to older versions. The copyright
file includes information in plain text about the package copyrights.
Most important lines in rules file are:
# Add here commands to install the package into debian/<installation directory> $(MAKE) install DESTDIR=$(CURDIR)/debian/<installation directory>
These lines define where the package files are installed. debian/<installation directory>
is used as a temporary directory for package construction.
[edit] Creating and building the package
The package is made using the following command:
dpkg-buildpackage -rfakeroot -uc -us -sa -D
The result should be these MaemoPad files:
-
maemopad_x.x.dsc
-
maemopad_x.x.tar.gz
-
maemopad_x.x_i386.changes
-
maemopad_x.x_i386.deb
A .deb file now exists. This package can be installed using:
fakeroot dpkg -i maemopad_x.x_i386.deb
The icon to the application should now be in the Maemo Task Navigator menu, and it should be launchable from there. To remove the package, use the command
fakeroot dpkg -r maemopad
- This page was last modified on 14 May 2011, at 08:43.
- This page has been accessed 56,954 times.