如何创建Maemo应用程序
From Forum Nokia Wiki
本文是一篇对Maemo.org开源社区官方文档How to Write New Applications in maemo 4.0的非正式简体中文翻译版本。希望本文对广大中国开发者了解Maemo平台以及开发Maemo平台应用程序带来一些帮助:)
Contents[hide] |
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
简介
这是一篇为maemo平台创建新的应用程序的指南。
当开始编写新的应用程序时,第一步首先是配置开发环境, Maemo Tutorial [1]介绍了这一步骤。该文档介绍实际的代码编写过程。
作为范例,我们使用了只有少数重要特性的简易纯文本编辑。对这种纯文本编辑器来说最好的范例是gtk2edit和gpe-edit。从现在开始,此应用程序称为"MaemoPad"。
MaemoPad有几个基本特性,比如"新建", "打开", "保存", "另存为...", "剪切", "复制", "粘贴", "字体", "全屏", "全屏"硬件设备按键处理, "发送-经电子邮件/蓝牙"和 "关闭"。 为了简易化,这里没有诸如"撤销", "重做",同一文中不同字体,图片等特性。
图1是MaemoPad的一个截图。如我们所见,MaemoPad应用程序在屏幕底端有它自己的工具栏,左上方角有下拉菜单。左边区域是用来显示文本的。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
创建应用程序文件结构
首先要创建MaemoPad的文件结构。这里显示的文件结构非常普遍而且它可被用在maemo SDK平台上各种应用程序。
项目目录有四个子目录:
- src/: 包含源文件。
在src/目录里,main.c是MaemoPad代码的最高层。文件appdata.h包含应用程序所需的数据。下一节将介绍如何编写main.c代码和appdata.h。在src/ui/目录下的interface.c文件用来创建图形用户接口。callbacks.c里的代码用来处理MaemoPad和maemo平台间的消息。 - debian/: 包含和debian包相关的文件。
- data/: 包含应用程序运行时使用的数据文件;比如图标和帮助文件; 添加MaemoPad到菜单的.desktop文件, 一个DBUS .service文件。这里能找到更多关于.service文件的详情。
- po/: 包括本地化文件。
本地化目录po/有一个POTFILES.in文件,列出了所需本地化文件的名称,另外翻译文件en_GB.po包含了应用程序使用的英国英语的本地化字符串,更多信息请查阅本地化一部分。
除了上述4项子目录之外,项目的主目录仅包含3个脚本文件,使用GNU autoconf和<tt>automake工具来配置和编译该项目:
- autogen.sh 是使其它脚本文件生成项目的微脚本文件。
- configure.ac 包含如何为项目产生配置脚步的定义。
- Makefile.am 包含创建应用程序的<tt>src/, po/ ,data/目录及其内所有的Makefile所需的文件和子目录。
编译项目常用的方法是
$ ./autogen.sh && ./configure && make;
有关GNU autoconf和automake的内容该文档将不做介绍,更多内容请查阅参考文献[2]和[3]。 MaemoPad应用程序的文档结构应该是这样:
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
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
应用程序代码主体
在MaemoPad的src/目录,有main.c和appdata.h.
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
appdata.h
appdata.h定义了AppData结构以获取应用程序数据。即使这些数据会因应用程序的不同而有差异,一个AppData结构样式应如下:
struct _AppData
{
AppUIData *ui; /* handle to app's UI */
HildonProgram *program; /* handle to application */
osso_context_t *osso; /* handle to osso*/
AppConfData *conf; /*handle to app's Gconf data */
};
这里
- AppUIData一个包含所有UI对象指针的结构;如Hildon窗口,菜单项,工具栏; 和UI相关数据, 例如由一个布尔变量确定应用程序是否为全屏模式。
- HildonProgram在Maemo教程中被介绍。它能够在AppUIData结构里被有选择地声明。
- osso_context_tMaemo教程介绍。
- AppConfData是应用程序里包含Gconf相关数据的结构。
每个应用程序都将创建起自己的AppData变量,而且这些变量在函数里通常被当作参数传递,特别是在回调函数里,以使其能提取这些应用程序数据。
许多应用程序将这些AppData变量声明为全局变量。
MaemoPad's AppData结构如下:
struct _AppData
{
HildonProgram * program; /* handle to application */
HildonWindow * window; /* handle to app's window */
osso_context_t *osso; /* handle to osso */
};
MaemoPad的N.B. AppUIData指向AppData, 代替了AppData指向AppUIData。这并不是显著的差别,因为最终的目的是让函数能提取这些应用程序数据。
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 *open_item;
GtkWidget *save_item;
GtkWidget *saveas_item;
GtkWidget *edit_item;
GtkWidget *cut_item;
GtkWidget *copy_item;
GtkWidget *paste_item;
/*....more truncated .....*/
}
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
main.c
main.c通常实现以下功能:
- 初始化GTK
- 初始本地化资源
- 创建Hildon应用程序范例
- 调用函数,通常在interface.c被定义,创建主视图。
- 连接主视图窗口和已创建的HildonProgram。
- 运行gtk_main()
- 连接主视图的"delete_event"和回调函数,以处理适当的应用程序退出,比如销毁主视图窗口,释放已使用的内存及保存退出时的应用程序状态。
- 调用gtk_main_quit()
以下为带有注释的MaemoPad主函数:
int main( int argc, char* argv[] )
{
AppData* data;
HildonProgram* program;
MainView* main_view;
/* Initialize the locale stuff */
setlocale ( LC_ALL, "" );
bindtextdomain ( GETTEXT_PACKAGE, LOCALEDIR );
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain ( GETTEXT_PACKAGE );
/* Init the gtk - must be called before any hildon stuff */
gtk_init( &argc, &argv );
/* Create the hildon application and setup the title */
program = HILDON_PROGRAM ( hildon_program_get_instance () );
g_set_application_name ( _("MaemoPad") );
/* Create the data and views for our application */
data = create_data ();
data->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;
}
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
用户界面接口
图形用户接口被放在./src/ui/中,有两个.c文件: interface.c和callbacks.c。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
interface.c
该文件将创建图形用户接口(GUI)并连接信号和事件到callbacks.c所定义的正确处理。查阅MaemoTutorial的GUI部分以了解更多如何在maemo里创建GUI的信息。如果熟悉GTK,那么建议从GTK+参考手册[4]开始。
常用的一种方式是在创建GUI时创建AppUIData结构变量。然后用不同的函数创建HildonWindow和小组件,比如create_menu(), create_toolbar().
当创建每个组件时,AppUIData应该涉及同时创建的各种必需的UI对象。 下述摘录代码显示了AppUIData是如何创建的以及它是如何指向工具栏和工具栏上的“新建”按钮。
/* 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);
}
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
callbacks.c
callbacks.c定义了处理所有可能由UI激发的信号和事件的函数。当在interface.c里创建不同的UI对象时,操作如下:
/* 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 );
}
callback_file_new函数在callbacks.c被使用,当需要时可保存当前文件,并打开另一个需编辑的新文件。
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;
注意AppUIData结构变量mainview是如何被获取的,从而使用户可以有目的地高效操作。
MaemoPad还有很多其他函数, 这些在Maemo教程里有介绍:
- 文件保存/另存为/打开: 这些使用HildonFileChooserDialog。
- 编辑剪切/复制/粘贴: 这些使用Clipboard。
- 硬件按键: MaemoPad能够识别"全屏"按键(按F6)。它能把应用程序从全屏切换到正常模式,并可相反操作。许多其它的硬件按键事件都可被添加到MaemoPad。查阅Hardware keys。
- 字体/颜色选择: 这些在HildonFontSelectionDialog和HildonColorChooser里有介绍。
- 发送-经电子邮件/蓝牙: 参考Send Via functionality所介绍的的如何实现路径发送功能。
更多有关GTK Widgets的内容可从GTK+参考手册获取。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
interface.h
在接口头文件interface.h中, 共有函数为main.c和callbacks.c所定义. 在MaemoPad上, 保存修改提示的确认响应,Hildon错误提示的MaemopadError枚举类型和AppUIData都在此被定义。在一些其他的应用程序中. AppUIData也能在appdata.h里被定义。
MaemoPad的interface.h如下:
#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 );
....
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
本地化
本地化是指把应用程序翻译成不同的语言。在maemo里,简单的做法是把所有需要翻译的字符串放到一个.po文件,各个分配id, 然后在代码里使用这些id,而不是硬编码字符串。
在maemo里用来处理翻译的字符串的函数是标准GNU gettext().
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
初始化
当应用程序运行时,根据系统设置的本地语言,gettext()将把这些id翻译成正确的语言。应用程序应按以下方式初始化文本内容。
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 );
........
}
更多关于本地化的内容请查阅Maemo Localization How-to
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
文件结构
本地化文件被保存在po/目录。MaemoPad的本地化将用到以下文件:
Makefile.am
POTFILES.in
en_GB.po
POTFILES.in包含了将被本地化的源代码文件列表。在MaemoPad里, 仅有main.c和interface.c包含需要本地化的字符串。
# MaemoPad 里需要被本地化的源文件列表
../src/main.c
../src/ui/interface.c
en_GB.po文件包含了翻译成英国英语的文本.他包含如下几对 id/string:
msgid "maemopad_yes"
msgstr "Yes"
在.po文件中,注释总是以"#"(井号)开头的。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
使用en_GB.po
使用en_GB.po msgid作为一个参数被传送到GNU gettext()函数以生成翻译字符串。在maemo里,推荐的方式是:
#define _(String) gettext(String)
因此,在MaemoPad里, 菜单->文件->打开 菜单的字符串按如下方式创建:
main->open_item = gtk_menu_item_new_with_label ( _("Open") );
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
从源代码中创建.po文件
有时候初始化并不是从开始就被执行时,从应用程序中本地化代码是很有必要的。通过从源文件中提取所有字符串到模板.po文件的方式,使用GNU xgettext创建.po files是有可能的。
xgettext -f POTFILES.in -C -a -o template.po"
从man page索取更多有关xgettext的信息,简述如下:
- "-f POTFILES.in" 使用POTFILES.in以获取需要翻译的文件
- "-C" 是C-code类型的字符串
- "-a" 确保我们可以从指定的文件中获取所有的字符串
- "-o template.po" 定义了输出文件名
接下来复制template.po到./po/en_GB.po, 并添加或编辑所有英国英语的字符串。其他语言的处理同上述类似。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
在菜单项中加入应用程序
查阅Maemo教程有关如何操作此步骤地信息。简述之,就是将maemopad.desktop和com.nokia.maemopad.service文件保存在./data目录里,它们分别如下所示:
[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=maemopad
X-Osso-Type=application/x-executable
(注释,行尾不能有任何空白字符)
# Service description file
[D-BUS Service]
Name=com.nokia.maemopad
Exec=/usr/bin/maemopad
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
连接maemo菜单
当Debian包被装到maemo平台, .desktop和.service文件就会把MaemoPad放置在Task Navigator里.更多信息查阅下面Debian打包部分。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
添加帮助
应用程序可以有其自己的帮助文件。帮助文件是被放在/usr/share/osso-help目录下的XML文件。比如英国英语的帮助文件/usr/share/osso-help/en_GB。在MaemoPad里,有一个data/help/en_GB/MaemoPad.xml目录下的文件,有很多简单的帮助内容。它规定了contextUID的中间部分和帮助文件名相同(不带后缀):
<?xml version="1.0" encoding="UTF-8"?>
<ossohelpsource>
<folder>
<h2>Help MaemoPad Example</h2>
<topic>
<topich2>Main Topic</topich2>
<context contextUID="Example_MaemoPad_Content" />
<para>This is a help file with example content.</para>
</topic>
</folder>
</ossohelpsource>
通过使用ossohelp_show()函数(查阅osso-helplib.h), 帮助文件就能被显示在应用程序中。创建帮助菜单项之后,会连接一个回调函数:
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);
}
更多信息请查阅Help Framework HOWTO.
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
应用程序安装包
Debian包是应用程序封装在一个文件里用来在基于Debian操作系统(如Maemo平台里完成简单安装的应用程序安装包。更多关于创建Debian包的信息可从Creating a Debian package中获取。此节我们的目的是创建可安装在Maemo平台上的MaemoPad程序的Debian包。
如果要创建可使用应用程序管理器安装的安装包,请查阅Making Package for Application Manager.
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
创建debian/ Directory
创建安装包需要一些文件。它们被放在debian/目录下。下列文件将被创建:
changelog
control
copyright
maemopad.install
maemopad.links
rules
'rules' 文件定义了Debian包如何被生产。'rules'文件告诉我们文件应安装在什么地方。还有一个'control'文件用来定义即将被创建的包类型(通 常是不同语言版本)。'maemopad.links'文件定义了到达 Task Navigator的链接。'maemopad.install'定义了MaemoPad里使用的本地化文件. Changelog 和 copyright files也是需要的,不然生成的安装包无法正常使用。'changelog'文件由安装包的版本数组成,简要记录了各个版本间的差别。 'copyright'文件包含了安装包版权的纯文本信息。
'rules'文件中最重要的几行是:
# Add here commands to install the package into
debian/tmp/<installation directory>
$(MAKE) install DESTDIR=$(CURDIR)/debian/tmp/<installation directory>
上述几行代码表明了安装包将在哪里安装。Debian/tmp是生成安装包的临时目录。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
创建并生成安装包
使用以下指令生成安装包:
"dpkg-buildpackage -rfakeroot -uc -us -sa -D"
其结果应该是这些MaemoPad文件:
maemopad_2.1.dsc
maemopad_2.1 .tar.gz
maemopad_2.1_i386.changes
maemopad_2.1_i386.deb
现在有一个.deb file.安装包可使用"fakeroot dpkg -i maemopad_2.1_i386.deb"指令进行安装。应用程序的图标此时应该在Maemo Task Navigator菜单, 同时它应该在那里显示出来。安装包可由"fakeroot dpkg -r maemopad"指令移除。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
下载
MaemoPad所有的源代码可从SVN下载。
![](https://www.cnblogs.com/CuteSoft_Client/CuteEditor/Images/anchor.gif)
参考文献
[2] http://www.gnu.org/software/autoconf
[3] http://www.gnu.org/software/automake