Developing new applications

This document is released under the GPL license.

Introduction

This document is a guide to making new applications for the maemo platform. When you start to write a new application, the first phase you must carry out is to setup the development environment, a process which is explained in the Maemo 2.2 Tutorial. The actual coding process is described in this document.

As an example, there is a simple plain text editor with only a few essential features. gtk2edit and gpe-edit are two good examples for this kind of plain text editors. From now on, the application is referred to as an "MaemoPad". MaemoPad has some basic features, such as: "New", "Open", "Save", "Save As...", "Cut", "Copy", "Paste", "Font", "Full Screen" and "Close". For the sake of simplicity there are no features like "Undo", "Redo", or different fonts in the same document, pictures or tables.

MaemoPad application

Figure 1. MaemoPad application

Figure 1 is a screenshot from the maemoPad. As you can see maemoPad application has its own toolbar at the bottom of screen and a drop-down menu in upper left corner. The left space is used for displaying the file.

Creating an application file structure

Firstly, you must create the file structure for maemoPad. The structure presented is quite general and can be used for various kinds of applications on the maemo SDK platform. The project directory has four subdirectories: src/, po/, debian/, and data/. Source files are located in the src/ directory, which contains the ui/ subdirectory for UI-related code. Localisation files are located in the po/ directory. Files for creating Debian package are located in the debian/ directory. This is explained in more detail in Debian packaging. The data subdirectory has files to add maemoPad to the maemo application menu, application icons and help files.

The project directory includes only three script files to configure and compile the project: autogen.sh, configure.ac, and Makefile.am. The autogen.sh file is a tiny script file that uses GNU-toolchain to make other script files with which to build the project. Configure.ac includes definitions on how to produce the configure script for this project. The Makefile.am file in the main project directory includes the subdirectories src/, po/ and the data/ and Makefiles in src/, po/, and data/ directories include the files and subdirectories under them needed to make the application. The GNU-toolchain is beyond the scope of this document, for more information see [2] and [3].

In the src/ directory, the main.c is located at the top of the hierarchy in the maemoPad's code. The file appdata.h contains the data required by the application. For more information on writing the code in main.c and appdata.h, see Section Coding application main. The file interface.c in the src/ui/ directory takes care of creating the graphical user interface. Callbacks.c contains the code that follows the messages between maemoPad and the maemo platform, for more information, see Section functionality.

The localisation directory po/ has a file POTFILES.in, which lists the names of the files to be localised and a translation file en_GB.po containing British English strings for the application, for more information, see Section Localisation.

The file structure of the maemoPad application looks as follows:

 
Makefile.am
autogen.sh
configure.ac
src/
Makefile.am
appdata.h
main.c
ui/
callbacks.h
callbacks.c
interface.h
interface.c
data/
Makefile.am
com.nokia.maemopad.service
maemopad.desktop
icons/
26x26/
maemopad.png
40x40/
maemopad.png
scalable/
maemopad.png
help/
en_GB/
maemoPad.xml
po/
Makefile.in.in
POTFILES.in
en_GB.po
debian/
changelog
control
copyright
maemoPad.install
maemopad.links
rules

Coding application main

This section explains how to make the main.c and appdata.h files. The main.c file has the code for initialising and setting up everything for the application, starting the applications, and freeing the used memory and finally exiting. The appdata.h file has the data needed by the application, such as pointers and state bits. You are making a Hildon-based application and therefore it is made by using HildonProgram1. The main.c file uses the interface_main_view_new() and interface_main_view_destroy() functions of interface.c to create a view for the application and to destroy the view when closing application.

main.c

The main function with comments is presented below:

 
int main( int argc, char* argv[])
{
  program = program;
  main_view = interface_main_view_new ( data );
  hildon_program_add_window( data->program, data->window );
  /* Begin the main app */
  gtk_widget_show ( GTK_WIDGET ( program ) );
  gtk_main();
  /* Clean up */
  interface_main_view_destroy ( main_view );
  destroy_data ( data );
  return 0;
}	  

appdata.h

The appdata.h file defines the AppData structure, which contains the basic information of the maemoPad application. The AppData structure is defined in the appdata.h file:

 
struct _AppData
{
  HildonProgram *program; /* handle to application */
  HildonWindow *window; /* handle to app's window */
  osso_context_t *osso; /* handle to osso */
};	

User interface

The source file interface.c includes all code which makes the graphical user interface (GUI) work. GTK and Hildon API are used to create the GUI. At first the application interface needs a view, which is implemented using HildonWindow [1]. There is drop down menu, toolbar, and scrollable text area in the application view. All of these are made using standard GTK components. Hildon does add some features to GTK, for example, themeing and differently formed dialogs.

Hildon applications have a predefined place for drop down menu and toolbar so attach your application menu to the Hildon application menu and the toolbar to the Hildon application toolbar. The rest of the view is dedicated for the scrollable text area. Figure 2 shows how the components are placed.

HildonWindow's components

Figure 2. HildonWindow's components

The interface.c has two public functions: main_view_new and main_view_destroy. Main_view_new creates a view for the application given by parameter and returns a handle to that view. Main_view_destroy disables the view given as a parameter. Main_view_new uses private functions create_menu to create a drop down menu, create_toolbar to create a tool bar and create_textarea to create a scrollable text view. Use the struct MainView to store view data.

For more information on Hildon, see the 1Maemo 2.2 Tutorial. If you are not familiar with GTK, see http://developer.gnome.org/doc/API/2.0/gtk/index.html4.

interface.h

In the interface.h interface header file, you define the public functions for the main.c and callbacks.c, confirmation responses for the save changes note, maemoPadError enum for the Hildon error note, the MainView struct for the state of our interface (file name under editing, used screen font, fullscreen on or off and so forth), and a list of GTK components used by menu, toolbar and text area. The interface.h file looks as follows:

 
#include 
#include 
#include 
#define _(String) gettext(String)
/* confirmation responses */
#define CONFRESP_YES 1
#define CONFRESP_NO 2
#define CONFRESP_OK 3
#define CONFRESP_CANCEL 4
#define CONFRESP_ERROR 5
#define MAIN_VIEW_NAME "MainView"
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 _MainView MainView;
struct _MainView
{
  /* 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;
  . . .
  /* 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 */
};

/* Publics: */
MainView* interface_main_view_new( AppData* data );
void interface_main_view_destroy( MainView* main );.

Creating and deleting GUI

The GUI initialisation needs a lot of code; so the explanation is broken here into sections. Your interface.c file has private functions to create menu, toolbar and text area. Destroying is done with a simple command, which just frees up used memory.

Creating the menu

For the menu you must attach your menu to the Hildon application view, create menu items, attach these menu items onto the right submenus, and connect callback signals to menu items.

 
static void create_menu( MainView *main )
{
  /* Create needed handles */
  GtkMenu *main_menu;
  GtkWidget *file_menu, *edit_menu;
  GtkWidget *separator = NULL;
  GtkWidget *close = NULL;

  /* Create main menu and new menus for submenus in our drop down menu */
  main_menu = GTK_MENU( gtk_menu_new () );
  file_menu = gtk_menu_new ();
  edit_menu = gtk_menu_new ();
  /* Create the menu items */
  main->file_item = gtk_menu_item_new_with_label ("File");
  main->new_item = gtk_menu_item_new_with_label ("New");
  . . .
  /* Add menu items to right menus */
  gtk_menu_append( main_menu, main->file_item );
  gtk_menu_append( file_menu, main->new_item );
  . . .
  /* Add submenus to the right items */
  gtk_menu_item_set_submenu( GTK_MENU_ITEM (main->file_item), file_menu );
  gtk_menu_item_set_submenu( GTK_MENU_ITEM (main->edit_item), edit_menu );
  /* Attach the callback functions to the activate signal */
  g_signal_connect( G_OBJECT( main->new_item ), "activate", 
  G_CALLBACK ( callback_file_new), main );
  g_signal_connect( G_OBJECT( main->open_item ), "activate", 
  G_CALLBACK ( callback_file_open), main );
  . . . 
  /* We need to show menu items */
  gtk_widget_show_all( GTK_WIDGET( main_menu ) );
}		

Creating the toolbar

The toolbar is implemented following the same principle as in the previous section. First create buttons, then add them to the toolbar, and then connect callback signals to them. Finally add the toolbar to your application main view.

 
static void create_toolbar ( MainView *main ) 
{
  /* Create new GTK toolbar */
  main->toolbar = gtk_toolbar_new ();
  /* Set toolbar properties */
  gtk_toolbar_set_orientation( GTK_TOOLBAR(main->toolbar),   
    GTK_ORIENTATION_HORIZONTAL);
  gtk_toolbar_set_style( GTK_TOOLBAR(main->toolbar), 
    GTK_TOOLBAR_BOTH_HORIZ);
  /* Make menus and buttons to toolbar: */
  /* Create toolitems using defined items from stock */
  main->new_tb = gtk_tool_button_new_from_stock(GTK_STOCK_NEW);
  main->open_tb = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN);
  . . .
  /* Insert items to toolbar */
  gtk_toolbar_insert
    ( GTK_TOOLBAR(main->toolbar), main->new_tb, -1);
  gtk_toolbar_insert
    ( GTK_TOOLBAR(main->toolbar), main->open_tb, -1);
  . . .
  /* Connect signals to buttons */
  g_signal_connect(G_OBJECT(main->new_tb), "clicked",
  G_CALLBACK(callback_file_new), main);
  g_signal_connect(G_OBJECT(main->open_tb), "clicked",
  G_CALLBACK(callback_file_open), main);
  . . .
  /* Add toolbar to the HildonWindow */
  hildon_window_add_toolbar(main->data->window, GTK_TOOLBAR(main->toolbar));
  /* Show toolbar */
  gtk_widget_show_all (GTK_WIDGET(main->toolbar));
  gtk_widget_show_all (GTK_WIDGET(main->data->window) );
}					

Creating text area

The text area is used to show the file under the editing process. Your text area includes scrolled_window with text_view. Use scrolled_window because many files often have more text than you can show on the screen at once. Connect your mainview->buffer to text_view and callback signals to detect text editing.

 
void create_textarea( MainView *main )
{
  /* Create scrolled window */
  main->scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
  gtk_widget_show(main->scrolledwindow);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(
  main->scrolledwindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  /* Text view */
  main->textview = gtk_text_view_new ();
  /* Some text view settings */
  gtk_text_view_set_editable (GTK_TEXT_VIEW (main->textview), TRUE);
  gtk_text_view_set_left_margin (GTK_TEXT_VIEW (main->textview), 10);
  gtk_text_view_set_right_margin (GTK_TEXT_VIEW (main->textview), 10);
  /* Get handle to buffer */
  main->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW
    (main->textview));
  /* Put textview under scrolledwindow and show it*/
  gtk_container_add(GTK_CONTAINER(main->scrolledwindow), main->textview);
  gtk_widget_show(main->textview);

  /* Change default font throughout the widget */
  main->font_desc = pango_font_description_from_string (DEFAULT_FONT);
  gtk_widget_modify_font (main->textview, main->font_desc);
  /* Connect signals to detect changes in text */
  g_signal_connect (G_OBJECT (main->buffer), "modified-changed",
  G_CALLBACK(callback_buffer_modified), main);
  g_signal_connect (G_OBJECT (main->buffer), "changed",
  G_CALLBACK(callback_buffer_modified), main);
}					

File and font chooser dialogs

Hildon dialogs are used to choose filename and font. These dialogs are almost the same as GTK dialogs, but there are little differences in how they work.

File chooser

To choose files in the open and save functions you need to create a Hildon dialog, which handles file choosing. Your file chooser function creates the dialog and returns the selected file's directory and name if an OK response is received.

 
  gchar* interface_file_chooser
    (MainView * mainview, GtkFileChooserAction action)
{
  GtkWidget *dialog;
  gchar* filename = NULL;
  dialog = hildon_file_chooser_dialog_new(
  GTK_WINDOW(mainview->data->program), action);
  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;
}			

Font chooser

The font callback needs a dialog from which to choose the font. For this, use a Hildon font selection dialog. Copy the settings from the dialog and set the font accordingly.

 
PangoFontDescription* interface_font_chooser( MainView * main )
{
  HildonFontSelectionDialog *dialog = NULL;
  PangoFontDescription *font = NULL;
  gint size;
  gboolean bold, italic;
  gchar *family = NULL;
  font = pango_font_description_new();
  /* create dialog */
  dialog = HILDON_FONT_SELECTION_DIALOG ( hildon_font_selection_dialog_new(   
    NULL, NULL ) );
  gtk_widget_show_all (GTK_WIDGET(dialog));
  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
  {
    g_object_get(G_OBJECT(dialog),
      "family", &family,
      "size", &size,
      "bold", &bold,
      "italic", &italic,
      NULL);
    pango_font_description_set_family(font, family);
    pango_font_description_set_size(font, size * PANGO_SCALE);
    if (bold) {
      pango_font_description_set_weight(font, PANGO_WEIGHT_BOLD);
    } else {
      pango_font_description_set_weight(font, PANGO_WEIGHT_NORMAL);
    }
    if (italic) {
      pango_font_description_set_style(font, PANGO_STYLE_ITALIC);
    } else {
    pango_font_description_set_style(font, PANGO_STYLE_NORMAL);
  }
}
gtk_widget_destroy(GTK_WIDGET(dialog));
return font;
}           

Save and error dialogs

A couple of different dialogs are also needed to show messages and to receive feedback from the maemoPad user. These are the save changes confirmation dialog and error dialog. Both of these are made using Hildon notes.

Save changes

The save changes note is made to ensure that you do not lose that which you edited before creating a new file or opening an existing file. Use hildonNote for this. Responses are defined in interface.h.

 
  gint interface_save_changes_note(MainView * main)
  {
  HildonNote *hn = NULL;
  gint response = FALSE;
  g_assert(main != NULL && main->data->program != NULL);
  hn = HILDON_NOTE(hildon_note_new_confirmation_add_buttons
    (GTK_WINDOW(main->data->program),
  _("maemopad_save_changes_made"),
  _("maemopad_yes"), CONFRESP_YES,
  _("maemopad_no"), CONFRESP_NO,
    NULL, NULL));
  response = gtk_dialog_run(GTK_DIALOG(hn));
  gtk_widget_destroy(GTK_WIDGET(hn));
  return response;
}			

Error

You must inform the end user if something went wrong with failing to save or open a file in maemoPad. Use hildon_note_new_information for this. MAEMOPAD_ERRORs are defined in interface.h.

 
void interface_error( MaemopadError me, MainView *main )
{
GtkWidget *dialog = NULL;
gchar *msg = NULL;
switch (me) {
case MAEMOPAD_ERROR_INVALID_URI:
msg = g_strdup(_("maemopad_ms_invalid_uri"));
break;
. . .
default:
g_assert_not_reached();
}
dialog = hildon_note_new_information
( GTK_WINDOW( main->data->program ), msg );
gtk_dialog_run( GTK_DIALOG( dialog ) );
gtk_widget_destroy( dialog );
g_free( msg );
}					

Functionality

This section describes different available callback functionalities.

Edit callbacks

Edit callbacks are quite simple to use. In every edit function, you need to connect parameter pointer data to your MainView struct. Then use standard GTK functions to cut, copy and paste to your buffer. Mainview->clipboard is used to store text from the cut and copy commands. You get the same text from clipboard when using the paste command.

Cut

Cut is implemented using a simple GTK command. It copies selected text and then deletes it if the last parameter is TRUE.

 
void callback_edit_cut( GtkAction * action, gpointer data )
{
. . .
/* do cut */
gtk_text_buffer_cut_clipboard( GTK_TEXT_BUFFER(mainview->buffer),
mainview->clipboard, TRUE);
}					

Copy

The GTK function for Copy needs only parameters for the buffer and clipboard used.

 
void callback_edit_copy( GtkAction * action, gpointer data )
{
. . .
/* do copy */
gtk_text_buffer_copy_clipboard( GTK_TEXT_BUFFER(mainview->buffer),
mainview->clipboard);
}

Paste

When pasting, the second latest parameter is set to NULL to paste it to the cursor position. The last parameter defines whether or not the buffer is editable.

 
void callback_edit_paste( GtkAction * action, gpointer data )
{
. . .
gtk_text_buffer_paste_clipboard( GTK_TEXT_BUFFER( mainview->buffer ),
mainview->clipboard, NULL, TRUE );
}

File callbacks

In file callback, you need verification from the users to as to whether they want to save unsaved changes. For this, use the file I/O functions.

New

When the new file callback is used, you need to ensure that changes are saved if end user wants. If the end user wants to save the content and you do not have a file name, you must also choose the name for the file.

 
void callback_file_new(GtkAction * action, gpointer data)
{
. . .
/* 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;

Open

In open file callback, the opening dialog appears and selects the right file.

 
void callback_file_open(GtkAction * action, gpointer data)
{
. . .
/* save changes note if file is edited */
if( mainview->file_edited ) {
answer = interface_save_changes_note( mainview );
if( answer == CONFRESP_YES ) {
/* check if we had a new file */
if( mainview->file_name == NULL ) {
mainview->file_name = interface_file_chooser ( mainview, GTK_FILE_CHOOSER_ACTION_SAVE );
}
write_buffer_to_file ( mainview );
}
}
/* open new file */
filename = interface_file_chooser ( mainview, GTK_FILE_CHOOSER_ACTION_OPEN );
/* if we got a file name from chooser -> open file */
if( filename != NULL ) {
mainview->file_name = filename;
read_file_to_buffer ( mainview );
mainview->file_edited = FALSE;
}
}

Save

The save file dialog saves the file. If there is no filename defined, you have to choose it first with file_chooser.

 
void callback_file_save(GtkAction * action, gpointer data)
{
. . .
/* check if we had a new file */
if( mainview->file_name != NULL ) {
write_buffer_to_file ( mainview );
} else { 
filename = interface_file_chooser
( mainview, GTK_FILE_CHOOSER_ACTION_SAVE );
/* if we got a file name from chooser -> save file */
if( filename != NULL ) {
mainview->file_name = filename;
write_buffer_to_file ( mainview );
mainview->file_edited = FALSE;
}
}
}		

Save As...

Save as... chooses first a filename with file_chooser and then saves the file.

 
void callback_file_saveas(GtkAction * action, gpointer data)
{
. . .
filename = interface_file_chooser
( mainview, GTK_FILE_CHOOSER_ACTION_SAVE );
/* if we got a file name from chooser -> save file */
if( filename != NULL ) {
mainview->file_name = filename;
write_buffer_to_file ( mainview );
mainview->file_edited = FALSE;
}
}

Font callback

In font callback, you use the interface's interface_font_chooser function to get new font. Then modify the screen font by using GTK gtk_widget_modify_font function .

 
void callback_font( GtkAction * action, gpointer data )
{
. . .
new_font = interface_font_chooser( mainview );
/* if we got a new font from chooser -> change font */
if( new_font != NULL ) {
mainview->font_desc = new_font;
gtk_widget_modify_font( mainview->textview, mainview->font_desc );
}    
}	  

Fullscreen callback

The fullscreen callback toggles the full screen mode on and off. This is easy to implement in a Hildon application.

 
void callback_fullscreen( GtkAction * action, gpointer data )
{
. . .
/* toggle fullscreen onoff */
mainview->fullscreen = !mainview->fullscreen;
if (mainview->fullscreen) {
gtk_window_fullscreen(GTK_WINDOW(mainview->data->window));
} else {
gtk_window_unfullscreen(GTK_WINDOW(mainview->data->window));
}
}

Buffer modified callback

The callback for buffer modifications updates the maemoPad text buffer to the edited state so that if the end user wants to open or create a new file, you need to ask for a save confirmation.

 
void callback_buffer_modified ( GtkAction * action, gpointer data )
{
. . .
/* change state */
mainview->file_edited = TRUE;
}

File I/O functions

The maemoPad needs to access files when opening or saving text under editing. The maemo platform uses GnomeVFS for file operations. For more information on GnomeVFS, see http://developer.gnome.org/doc/API/gnome-vfs/. You must read and write functions for your file callbacks.

Reading from file

Reading files is implemented with the GnomeVFS functions. First, get file information and create a handle for the file. Then open the file and read it to the text buffer. When the data has been read, the file is closed.

 
void read_file_to_buffer ( MainView* mainview )
{
. . .
/* try to get file info */
vfs_result = gnome_vfs_get_file_info
(mainview->file_name, &finfo, GNOME_VFS_FILE_INFO_DEFAULT);
if ( vfs_result != GNOME_VFS_OK ) {
interface_error( MAEMOPAD_ERROR_OPEN_FAILED, mainview );
return;
}
/* try to create handle to file */
vfs_result = gnome_vfs_open
(&handle, mainview->file_name, GNOME_VFS_OPEN_READ);
if ( vfs_result != GNOME_VFS_OK ) {
interface_error( MAEMOPAD_ERROR_OPEN_FAILED, mainview );
return;
}
/* allocate memory for temp_buffer */
temp_buffer = g_malloc(finfo.size + 1);
memset(temp_buffer, 0, finfo.size + 1);
/* read from file to buffer */
gnome_vfs_read(handle, temp_buffer, finfo.size, &in_bytes);
/* set text to screen */
gtk_text_buffer_set_text
( GTK_TEXT_BUFFER (mainview->buffer), temp_buffer, -1);
/* free temp, close file and return */
g_free(temp_buffer);
gnome_vfs_close(handle);
}		

Writing to file

Writing to a file is also done with GnomeVFS functions. First, create a handle for the file. Then copy the text from maemoPad to temp_buffer for saving and write temp_buffer to the file. Finally, close the handle.

 
void write_buffer_to_file ( MainView* mainview )
{
. . .
/* try to create handle to file */
vfs_result = gnome_vfs_create
(&handle, mainview->file_name, GNOME_VFS_OPEN_WRITE, 0, 0600);
if ( vfs_result != GNOME_VFS_OK ) {
interface_error( MAEMOPAD_ERROR_SAVE_FAILED, mainview );
return;
}
/* find start and end of text */
gtk_text_buffer_get_bounds
( GTK_TEXT_BUFFER (mainview->buffer), &start, &end);
/* copy all text from screen to temp_buffer */
temp_buffer = gtk_text_buffer_get_slice
( GTK_TEXT_BUFFER (mainview->buffer), &start, &end, TRUE);
/* write text to file */
gnome_vfs_write(handle, temp_buffer, strlen(temp_buffer), &out_bytes);
/* free temp, close file and return */
g_free(temp_buffer);
gnome_vfs_close(handle);
}

Localisation

Localisation means translating the application to different languages. Localisation needs a few files, which are stored in the po/directory. The following files are used for localisation:

 
Makefile.am	
POTFILES.in
en_GB.po

POTFILES.in contains the list of source code files which are localized. File en_GB.po includes translated text.

Creating en_GB.po

Creating po files is quite straightforward. In maemoPad you need only create one localisation file, en_GB.po, others such as fi_FI.po can be easily made based on this. The localisation file structure is simple, you have to define only the localisation ID and actual text string. A sample of this structure follows:

 
#: src/document.c:727 src/document.c:733
msgid "note_ib_autosave_recover_failed"
msgstr "Recovering autosaved file failed"

Here "msgid" is the original string (key) used in the code and "msgstr" defines the translated string for localisation.

First create a template file including all strings from the source files for translation. Use the GNU xgettext command [8] to extract the strings from sources:

"xgettext -f POTFILES.in -C -a -o template.po"

The "-f POTFILES.in" option uses POTFILES.in to retrieve the files to be localised, "-C" is for C-code type strings, "-a" is for ensuring that you get all strings from sources and "-o template.po" defines the output filename.

This may output some warnings, which usually are not serious, but it is recommended that you check them anyway.

Then edit the template.po with a text editor; the content must be similar to the example above. When you look at the strings you got from the sources, note that some of them, such as signal "clicked", empty strings "", font names and so forth, are not going to be translated. Those lines must be deleted or commented away. Following this, you can write translations of British English to each "msgstr" line.

After editing, save the file with name en_GB.po to the po/ directory and delete the template.po.

Adding Application to the menu

To add application icon to the maemo Task Navigator menu, two files are needed. These are the .desktop and .service files.

Creating .desktop file

It is recommended that you ensure that the .desktop file uses the same directory structure as the rest of the project. Next see what your maemoPad.desktop looks like:

 
[Desktop Entry]-->       

Creating .service file

A service file is also needed to launch applications from the Task Navigator. The service file in you data/ directory is following:

 
# Service description file
[D-BUS Service]--> 

Adding help

Applications have their own help files. Help files are XML files, which are located under the /usr/share/osso-help directory. Each language is located in its own subdirectory, such as /usr/share/osso-help/en_GB for British English help files. In maemoPad, there is a data/help/en_GB/MaemoPad.xml, which contains very simple help content. It is mandatory that the middle part of the contextUID middle is the same as the help file name (without suffix):

 
<?xml version="1.0" encoding="UTF-8"?>
<ossohelpsource>
<folder>
<title>Help MaemoPad Example</title>
<topic>
<topictitle>Main Topic</topictitle>
<context contextUID="Example_MaemoPad_Content" />
<para>
This is a help file with example content.
</para>
</topic>
</folder>
</ossohelpsource>

By using the ossohelp_show() function (see osso-helplib.h) you can display this help content in your application. You can create a "Help" menu item and connect this kind of callback function for it:

 
void callback_help( GtkAction * action, gpointer data )
{
    osso_return_t retval;

    /* connect pointer to our MainView struct */
    MainView *mainview = NULL;
    mainview = ( MainView * ) data;
    g_assert(mainview != NULL && mainview->data != NULL );
    retval = ossohelp_show(
    mainview->data->osso, /* osso_context */
    HELP_TOPIC_ID,        /* topic id */
    OSSO_HELP_SHOW_DIALOG);
}

For more information, see Help Framework.

Packaging application

A Debian package is an application packed into one file to make installing easy in the Debian-based operating systems like the maemo platform. For more information on creating a Debian package, see the official Maintainers' guide (http://www.debian.org/doc/maint-guide/).The purpose of this section is to create a Debian package of maemoPad, which can be installed in the maemo platform.

For information of how to create a package, which can be installed using the the Application Manager, see Making application packages.

Creating debian/directory

To create the package, some files are required. They are placed under the debian/directory. The following files are created:

 
changelog
control
copyright
maemoPad.install
maemoPad.links
rules

The 'rules' file is the file that defines how the Debian package is built. The 'rules' file indicates where the files are to be installed. You also need the 'control' file to define what kind of packages (often different language versions) you are going to create. The 'maemoPad.links' file defines the link to the Task Navigator. The 'maemoPad.install' file defines the localisation file used in maemoPad. The changelog and copyright files are also needed for building when the package does not work. The 'changelog' defines the version number of the package and carries a short description of changes as compared to older version. 'copyright' includes information in plain text about the package's copyright issues.

Most important lines in the rules file are:

 
# Add here commands to install the package into debian/tmp/<installation directory>
$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp/<installation directory>

These lines define where the package files are installed. The Debian/tmp directory is used as a temporary directory for package construction.

Creating package

The package is created using the following command:

"dpkg-buildpackage -rfakeroot -uc -us -sa -D"

The result must be these maemoPad files:

  • maemoPad_1.5.dsc
  • maemoPad_1.5 .tar.gz
  • maemoPad_1.5_i386.changes
  • maemoPad_1.5_i386.deb

There is now a .deb file. This package can be installed using the "fakeroot dpkg -i maemoPad_1.1_i386.deb" command. The icon for the application is now in the maemo task navigator menu and you can launch it from there. The package can be removed with the command "fakeroot dpkg -r maemoPad".

Download source

The full source code of maemoPad can be downloaded from Downloads.



Improve this page