Porting an existing application to maemo 2.2

This document is released under the GPL license.

Introduction

This document describes the porting process of an application to the maemo platform. When starting to port an application to the maemo platform, the first phase is to set up the development environment as described in the 2nd section of the Maemo 2.2 Tutorial. The actual porting process after that is described in the present document.

Gaim, which is a GTK+ based instant messaging client with a graphical user interface, is used here as an example of an application for porting. It allows the user to simultaneously log on multiple IM networks and chat with contacts. Supported protocols include, for example, AIM, ICQ, IRC, Jabber and MSN messenger.

Gaim's user interface mainly consists of two dialogs: the contacts dialog and the conversation dialog.

Contacts dialog

Figure 1. Contacts dialog

Figure 1 is a screenshot of the main dialog of the Gaim, which shows the end user's contacts. Contacts can be grouped in end user defined groups despite the protocol that contacts are using. By double-clicking a contact's name the end user opens a new conversation.

Conversation dialog

Figure 2. Conversation dialog

Figure 2 is a screenshot of the conversation dialog, which is used when communicating with contacts. When you have a conversation opened with several contacts simultaneously, they are located in their own tabs on the same dialog window.

Application file structure

Gaim already has a well- structured source tree and has not been modified. Documents can be found from the doc directory. Internationalisation is implemented in the intl directory. Each of the plugins are found under the plugins directory in their own subdirectories: perl, win32, docklet, gevolution, gaim-remote, gestures, ssl, tcl and ticker. Sounds are located in the sounds directory and images in pixmaps directory. Localisation files are placed under the po directory. Source code files are located in the src directory, which includes subdirectories for different protocols.

Gaim directory tree

The Gaim directory tree is illustrated below.

	  ./debian
	  ./doc
	  ./intl
	  ./plugins
	  ./plugins/perl
	  ./plugins/perl/common
	  ./plugins/perl/common/fallback
	  ./plugins/win32
	  ./plugins/win32/transparency
	  ./plugins/win32/winprefs
	  ./plugins/docklet
	  ./plugins/gevolution
	  ./plugins/gaim-remote
	  ./plugins/gestures
	  ./plugins/ssl
	  ./plugins/tcl
	  ./plugins/ticker
	  ./po
	  ./pixmaps
	  ./pixmaps/smileys
	  ./pixmaps/smileys/default
	  ./pixmaps/smileys/none
	  ./pixmaps/status
	  ./pixmaps/status/default
	  ./sounds
	  ./src
	  ./src/protocols
	  ./src/protocols/gg
	  ./src/protocols/irc
	  ./src/protocols/jabber/win32
	  ./src/protocols/jabber
	  ./src/protocols/msn
	  ./src/protocols/napster
	  ./src/protocols/novell
	  ./src/protocols/oscar
	  ./src/protocols/rendezvous
	  ./src/protocols/silc
	  ./src/protocols/toc
	  ./src/protocols/trepia
	  ./src/protocols/yahoo
	  ./src/protocols/zephyr
	  ./src/win32
	  ./src/win32/IdleTracker
	  ./src/win32/mingw_plus
	  ./src/win32/nsis/translations
	  ./src/win32/nsis

Gaim uses already GNU autotools so the required Makefile.am and configure.ac files were brought up with Gaim. Only autogen.sh was missing so it was created. For more information about GNU autotools usage, see the GNU development tools tutorial4. Chapters 5, 6 and 7 are dedicated to the specified changes needed in Makefile.am and configure.ac files.

autogen.sh

The following example illustrates the contents of the autogen.sh file.

	  #!/bin/sh
	  set -x
	  glib-gettextize --copy --force
	  libtoolize --automake
	  intltoolize --copy --force --automake
	  aclocal-1.7
	  autoconf
	  autoheader
	  automake-1.7 --add-missing --foreign

User interface changes

Gaim's basic UI has several different windows and therefore needed modification to make it to fit neatly into the screen. The buddy list and conversation windows are put together so that only one main view needs to be used. In addition to those two, there are also, for example, a few dialogs for accounts and preferences.

src/Makefile.am

Hildon library needs to be added as well as libosso to gaim_LDADD:

	  -lhildonlwidgets -losso

src/gtkdialogs.h

The structure for holding menu and toolbar items is as follows:

	  typedef struct _MenuToolBar MenuToolBar;
	  struct _MenuToolBar
	  {
	  /* items for menu */
	  GtkWidget *im_main_menu_conversation;
	  GtkWidget *im_main_menu_buddy;
	  GtkWidget *im_main_menu_tools;
	  GtkWidget *im_main_menu_view;
	  
	  GtkWidget *im_menu_conversation_details;
	  GtkWidget *im_menu_conversation_set_alias;
	  GtkWidget *im_menu_conversation_list_people;
	  GtkWidget *im_menu_conversation_close;
	  
	  GtkWidget *im_menu_buddy_send_im;
	  GtkWidget *im_menu_buddy_remove_buddy;
	  GtkWidget *im_menu_buddy_add_buddy;
	  GtkWidget *im_menu_buddy_add_chat;
	  
	  GtkWidget *im_menu_tools_accounts;
	  GtkWidget *im_menu_tools_room_list;
	  GtkWidget *im_menu_tools_preferences;
	  
	  GtkWidget *im_menu_view_formatting_toolbar;
	  GtkWidget *im_menu_view_buddy_list;
	  
	  GtkWidget *im_main_menu_close;
	  
	  /* items for toolbar */
	  GtkWidget *im_toolbar_add_buddy;
	  GtkWidget *im_toolbar_add_chat;
	  GtkWidget *im_toolbar_remove_buddy;
	  GtkWidget *im_toolbar_buddy_list;
	  GtkWidget *im_toolbar_preferences;
	  GtkWidget *im_toolbar_accounts;
	  GtkWidget *sep1, *sep2;
	  };

src/main.c

To create a Hildon application, you need hildon-program.h and hildon-window.h as well as libosso.h:

	  #include "hildon-widgets/hildon-program.h"
	  #include "hildon-widgets/hildon-window.h"
          #include "libosso.h"

The variables for the application's main view are as follows:

	  HildonProgram *program = NULL;
	  HildonWindow *hildonmainwindow = NULL;
	  GtkWidget *mainhbox = NULL;

The modified ui_main() function creates program, hildonmainwindow, menubar and toolbar:

	  static int ui_main()
	  {
	  #ifndef _WIN32
	  GList *icons = NULL;
	  GdkPixbuf *icon = NULL;
	  char *icon_path;
	  #endif
	  GtkToolbar *toolbar = NULL;
	  GtkMenu *mainmenu = NULL;
	  GtkWidget *buddymenu = NULL, *convmenu = NULL,
	  *toolsmenu = NULL, *viewmenu = NULL;
          osso_context_t *osso;


          /* Initialize libosso. PACKAGE should contain the same
           * name that is used in the gaim.desktop file. */
          osso = osso_initialize ( PACKAGE, VERSION, TRUE, NULL );

	  /* Creating Hildonized main view.. */
	  program = HILDON_PROGRAM(hildon_program_get_instance());
	  hildonmainwindow = HILDON_WINDOW(hildon_window_new());

	  g_signal_connect(G_OBJECT(hildonmainwindow), "delete_event", gtk_main_quit, NULL);

	  hildon_program_add_window(program, hildonmainwindow);
	  g_set_application_name(_("IM"));  

	  mainhbox = gtk_hbox_new(FALSE, 0);
	  gtk_container_add(GTK_CONTAINER(hildonmainwindow), mainhbox);

	  mainmenubar = g_new0(MenuToolBar, 1);

          mainmenu = GTK_MENU(gtk_menu_new());
	  hildon_program_set_common_menu(program, mainmenu);
	  buddymenu = gtk_menu_new();
	  convmenu = gtk_menu_new();
	  toolsmenu = gtk_menu_new();
	  viewmenu = gtk_menu_new();

	  /* Create and append menu items to menu */
	  mainmenubar->im_main_menu_conversation = gtk_menu_item_new_with_label(_("Conversation"));
	  mainmenubar->im_main_menu_buddy = gtk_menu_item_new_with_label(_("Buddy"));
	  mainmenubar->im_main_menu_tools = gtk_menu_item_new_with_label(_("Tools"));
	  mainmenubar->im_main_menu_view = gtk_menu_item_new_with_label(_("View"));
	  gtk_menu_append(mainmenu, mainmenubar->im_main_menu_conversation);
	  gtk_menu_append(mainmenu, mainmenubar->im_main_menu_buddy);
	  gtk_menu_append(mainmenu, mainmenubar->im_main_menu_tools);
	  gtk_menu_append(mainmenu, mainmenubar->im_main_menu_view);
	  
	  mainmenubar->im_menu_conversation_details = gtk_menu_item_new_with_label(_("Details..."));
	  mainmenubar->im_menu_conversation_set_alias = gtk_menu_item_new_with_label(_("Set alias..."));
	  mainmenubar->im_menu_conversation_list_people = gtk_menu_item_new_with_label(_("List people"));
	  mainmenubar->im_menu_conversation_close = gtk_menu_item_new_with_label(_("Close"));
	  gtk_menu_append(convmenu, mainmenubar->im_menu_conversation_details);
	  gtk_menu_append(convmenu, mainmenubar->im_menu_conversation_set_alias);
	  gtk_menu_append(convmenu, mainmenubar->im_menu_conversation_list_people);
	  gtk_menu_append(convmenu, mainmenubar->im_menu_conversation_close);
	  
	  mainmenubar->im_menu_buddy_send_im = gtk_menu_item_new_with_label(_("Send IM..."));
	  mainmenubar->im_menu_buddy_remove_buddy = gtk_menu_item_new_with_label(_("Remove buddy"));
	  mainmenubar->im_menu_buddy_add_buddy = gtk_menu_item_new_with_label(_("Add buddy..."));
	  mainmenubar->im_menu_buddy_add_chat = gtk_menu_item_new_with_label(_("Add chat..."));
	  gtk_menu_append(buddymenu, mainmenubar->im_menu_buddy_send_im);
	  gtk_menu_append(buddymenu, mainmenubar->im_menu_buddy_remove_buddy);
	  gtk_menu_append(buddymenu, mainmenubar->im_menu_buddy_add_buddy);
	  gtk_menu_append(buddymenu, mainmenubar->im_menu_buddy_add_chat);
	  
	  mainmenubar->im_menu_tools_accounts = gtk_menu_item_new_with_label(_("Accounts..."));
	  mainmenubar->im_menu_tools_room_list = gtk_menu_item_new_with_label(_("Room list..."));
	  mainmenubar->im_menu_tools_preferences = gtk_menu_item_new_with_label(_("Preferences..."));
	  gtk_menu_append(toolsmenu, mainmenubar->im_menu_tools_accounts);
	  gtk_menu_append(toolsmenu, mainmenubar->im_menu_tools_room_list);
	  gtk_menu_append(toolsmenu, mainmenubar->im_menu_tools_preferences);
	  
	  mainmenubar->im_menu_view_formatting_toolbar = gtk_check_menu_item_new_with_label(_("Formatting toolbar"));
	  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mainmenubar->im_menu_view_formatting_toolbar), TRUE);
	  mainmenubar->im_menu_view_buddy_list = gtk_check_menu_item_new_with_label(_("Buddy list"));
	  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mainmenubar->im_menu_view_buddy_list), TRUE);
	  gtk_menu_append(viewmenu, mainmenubar->im_menu_view_formatting_toolbar);
	  gtk_menu_append(viewmenu, mainmenubar->im_menu_view_buddy_list);
	  
	  mainmenubar->im_main_menu_close = gtk_menu_item_new_with_label(_("Close"));
	  gtk_menu_append(mainmenu, mainmenubar->im_main_menu_close);
	  
	  /* Set submenus */
	  gtk_menu_item_set_submenu(GTK_MENU_ITEM(mainmenubar->im_main_menu_conversation), convmenu);
	  gtk_menu_item_set_submenu(GTK_MENU_ITEM(mainmenubar->im_main_menu_buddy), buddymenu);
	  gtk_menu_item_set_submenu(GTK_MENU_ITEM(mainmenubar->im_main_menu_tools), toolsmenu);
	  gtk_menu_item_set_submenu(GTK_MENU_ITEM(mainmenubar->im_main_menu_view), viewmenu);

	  /* Connect callbacks */
	  /* Buddy and Conversation menus' callbacks are elsewhere */
	  g_signal_connect(G_OBJECT(mainmenubar->im_main_menu_close), "activate", gtk_main_quit, NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_tools_accounts), "activate", G_CALLBACK(gaim_gtk_accounts_window_show), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_tools_room_list), "activate", G_CALLBACK(gaim_gtk_roomlist_dialog_show), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_tools_preferences), "activate", G_CALLBACK(gaim_gtk_prefs_show), NULL);

	  gtk_widget_show_all(GTK_WIDGET(mainmenu));

	  /* Toolbar */
	  toolbar = GTK_TOOLBAR(gtk_toolbar_new());
	  mainmenubar->im_toolbar_add_buddy = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_ADD));
	  mainmenubar->im_toolbar_add_chat = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_ADD));
	  mainmenubar->im_toolbar_remove_buddy = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_REMOVE));
	  mainmenubar->im_toolbar_buddy_list = GTK_WIDGET(gtk_toggle_tool_button_new());
	  mainmenubar->im_toolbar_preferences = GTK_WIDGET(gtk_tool_button_new_from_stock(GTK_STOCK_PREFERENCES));
	  mainmenubar->im_toolbar_accounts = GTK_WIDGET(gtk_tool_button_new_from_stock(GAIM_STOCK_ACCOUNTS));
	  mainmenubar->sep1 = GTK_WIDGET(gtk_separator_tool_item_new());
	  mainmenubar->sep2 = GTK_WIDGET(gtk_separator_tool_item_new());

	  gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(mainmenubar->im_toolbar_buddy_list), TRUE);

	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_add_buddy), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_add_chat), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_remove_buddy), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->sep1), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_buddy_list), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->sep2), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_preferences), -1);
	  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(mainmenubar->im_toolbar_accounts), -1);

	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_accounts), "clicked", G_CALLBACK(gaim_gtk_accounts_window_show), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_preferences), "clicked", G_CALLBACK(gaim_gtk_prefs_show), NULL);

	  g_assert(toolbar);
	  gtk_toolbar_set_orientation(toolbar, GTK_ORIENTATION_HORIZONTAL);
	  gtk_toolbar_set_style(toolbar, GTK_TOOLBAR_ICONS);
	  hildon_window_add_toolbar(hildonmainwindow, toolbar);

	  if (current_smiley_theme == NULL) {
	  	smiley_theme_probe();
	  	if (smiley_themes) {
	  		struct smiley_theme *smile = smiley_themes->data;
	  		load_smiley_theme(smile->path, TRUE);
	  	}
	  }

	  gaim_gtk_blist_setup_sort_methods();

	  #ifndef _WIN32
	  /* use the nice PNG icon for all the windows */
	  icon_path = g_build_filename(DATADIR, "pixmaps", "gaim", "icons", "online.png", NULL);
	  icon = gdk_pixbuf_new_from_file(icon_path, NULL);
	  g_free(icon_path);
	  if (icon) {
	  icons = g_list_append(icons,icon);
	  gtk_window_set_default_icon_list(icons);
	  g_object_unref(G_OBJECT(icon));
	  g_list_free(icons);
	  } else {
	  gaim_debug(GAIM_DEBUG_ERROR, "ui_main", "Failed to load the default window icon!\n");
	  }
	  #endif

	  return 0;
	  }

To handle the startup in a more straightforward manner, see the following command:

	  /*
	  if (!opt_acct && !opt_nologin)
	  	gaim_accounts_auto_login(GAIM_GTK_UI);

	  if (opt_acct) {
	  	gaim_gtk_accounts_window_show();
	  } else if ((dologin_ret == -1) && !gaim_connections_get_all())
	  	show_login();
	  */

	  /* Let's auto login to ALL accounts and jump straight to main view */
	  gaim_accounts_auto_login("");

src/account.c

This modified the auto login function so that it does not check accounts' auto login flags. It tries to connect every account that is not already connected:

	  void gaim_accounts_auto_login(const char *ui)
	  {
	  GaimAccount *account;
	  GList *l;

	  /* We don't need this as we don't care about auto login flags..
	  g_return_if_fail(ui != NULL);
	  */

	  for (l = gaim_accounts_get_all(); l != NULL; l = l->next) {
	  account = l->data;

	  /* We don't care about auto login flags. Let's connect if disconnected..
	  if (gaim_account_get_auto_login(account, ui))
	  */
	  if (!gaim_account_is_connected(account))
	  gaim_account_connect(account);
	  }
	  }

src/gtkaccount.c

This removes online and auto login columns:

	  enum
	  {
	  COLUMN_ICON,
	  COLUMN_SCREENNAME,
	  /*COLUMN_ONLINE,
	  COLUMN_AUTOLOGIN,*/
	  COLUMN_PROTOCOL,
	  COLUMN_DATA,
	  COLUMN_PULSE_DATA,
	  NUM_COLUMNS
	  };

	  ...

	  /* Online?
	  renderer = gtk_cell_renderer_toggle_new();

	  g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(online_cb), dialog);

	  gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview),
		-1, _("Online"), renderer, "active", COLUMN_ONLINE, NULL);
	  column = gtk_tree_view_get_column(GTK_TREE_VIEW(treeview), 1);
	  gtk_tree_view_column_set_resizable(column, TRUE); */

	  /* Auto-login?
	  renderer = gtk_cell_renderer_toggle_new();

	  g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(autologin_cb), dialog);

	  column = gtk_tree_view_column_new_with_attributes(_("Auto-login"), renderer,
		"active", COLUMN_AUTOLOGIN, NULL);

	  gtk_tree_view_insert_column(GTK_TREE_VIEW(treeview), column, -1);
	  gtk_tree_view_column_set_resizable(column, TRUE); */

	  ...

	  gtk_list_store_set(store, iter,
	  COLUMN_ICON, scale,
	  COLUMN_SCREENNAME, gaim_account_get_username(account),
	  /* COLUMN_ONLINE, gaim_account_is_connected(account),
	  COLUMN_AUTOLOGIN, gaim_account_get_auto_login(account, GAIM_GTK_UI), */
	  COLUMN_PROTOCOL, gaim_account_get_protocol_name(account),
	  COLUMN_DATA, account,
	  -1);

	  ...

	  dialog->model = gtk_list_store_new(NUM_COLUMNS,
	  GDK_TYPE_PIXBUF, G_TYPE_STRING,
	  /*G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,*/
	  G_TYPE_STRING, G_TYPE_POINTER,
	  G_TYPE_POINTER);

To auto log into disconnected accounts when closing account dialog:

	  static void close_accounts_cb(GtkWidget *w, AccountsWindow *dialog)
	  {
	  gtk_widget_destroy(dialog->window);

	  gaim_gtk_accounts_window_hide();

	  /* Let's auto login immediately (on disconnected accounts) when closing */
	  gaim_accounts_auto_login(GAIM_GTK_UI);
	  }
	  

To change gtk_window_new() to gtk_dialog_new():

	  /*
	  dialog->window = win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	  gtk_window_set_default_size(GTK_WINDOW(win), width, height);
	  gtk_window_set_role(GTK_WINDOW(win), "accounts");
	  gtk_window_set_title(GTK_WINDOW(win), _("Accounts"));
	  gtk_container_set_border_width(GTK_CONTAINER(win), 12);
	  */
	  dialog->window = win = gtk_dialog_new();
	  gtk_window_set_title(GTK_WINDOW(win), _("Accounts"));
	  
	  g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(accedit_win_destroy_cb), accounts_window);
	  g_signal_connect(G_OBJECT(win), "configure_event", G_CALLBACK(configure_cb), accounts_window);

	  /* Setup the vbox */
	  vbox = gtk_vbox_new(FALSE, 12);
	  /*gtk_container_add(GTK_CONTAINER(win), vbox);*/
	  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(win)->vbox), vbox);
	  gtk_widget_show(vbox);

To build the login options frame without showing or using space for frame title:

	  /*frame = gaim_gtk_make_frame(parent, _("Login Options"));*/
	  vbox = gtk_vbox_new(FALSE, 6);
	  gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0);
	  gtk_widget_show(vbox);

	  hbox = gtk_hbox_new(FALSE, 6);
	  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
	  gtk_widget_show(hbox);

	  vbox = gtk_vbox_new(FALSE, 6);
	  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
	  gtk_widget_show(vbox);
	  
	  frame = vbox;

To check but not display the remember password box:

	  dialog->remember_pass_check = gtk_check_button_new_with_label(_("Remember password"));
	  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dialog->remember_pass_check), TRUE);
	  /*gtk_widget_show(dialog->remember_pass_check);*/

To remove thre auto-login checkbox:

	  /* Auto-Login
	  dialog->auto_login_check = gtk_check_button_new_with_label(_("Auto-login"));
	  gtk_box_pack_start(GTK_BOX(vbox), dialog->auto_login_check, FALSE, FALSE, 0);
	  gtk_widget_show(dialog->auto_login_check); */

To remove some unnecessary options. Some of these are dispalyed in the advanced options dialog:

	  #if (0)
	  add_user_options(dialog, vbox);

	  /* Add the disclosure */
	  disclosure = gaim_disclosure_new(_("Show more options"), _("Show fewer options"));
	  gtk_box_pack_start(GTK_BOX(vbox), disclosure, FALSE, FALSE, 0);
	  gtk_widget_show(disclosure);

	  /* Setup the box that the disclosure will cover. */
	  dialog->bottom_vbox = dbox = gtk_vbox_new(FALSE, 18);
	  gtk_box_pack_start(GTK_BOX(vbox), dbox, FALSE, FALSE, 0);

	  gaim_disclosure_set_container(GAIM_DISCLOSURE(disclosure), dbox);

	  /** Setup the bottom frames. */
	  add_protocol_options(dialog, dbox);
	  add_proxy_options(dialog, dbox);

	  /* Separator... */
	  sep = gtk_hseparator_new();
	  gtk_box_pack_start(GTK_BOX(main_vbox), sep, FALSE, FALSE, 0);
	  gtk_widget_show(sep);
	  #endif /* (0) */

To create dialog and display buttons for advanced options:

	  if (NULL == dialog->advanced_win)
	  dialog->advanced_win = gtk_dialog_new_with_buttons(_("Advanced account settings"),
	  GTK_WINDOW(dialog->window),
	  GTK_DIALOG_MODAL
	  | GTK_DIALOG_DESTROY_WITH_PARENT
	  | GTK_DIALOG_NO_SEPARATOR,
	  NULL);

	  add_protocol_options(dialog, GTK_DIALOG(dialog->advanced_win)->vbox);
	  add_proxy_options(dialog, GTK_DIALOG(dialog->advanced_win)->vbox);

	  gtk_dialog_add_button(GTK_DIALOG(dialog->advanced_win), _("OK"), GTK_RESPONSE_OK);
	  gtk_dialog_add_button(GTK_DIALOG(dialog->advanced_win), _("Cancel"), GTK_RESPONSE_CANCEL);

	  ...

	  /* Advanced button */
	  button = gtk_button_new_with_label("Advanced...");
	  gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
	  gtk_widget_show(button);

	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(show_advanced_cb), dialog);

The callback for showing advanced options is as follows:

	  static void
	  show_advanced_cb(GtkWidget *b, AccountPrefsDialog *dialog)
	  {
	  gint res;
	  res = gtk_dialog_run(GTK_DIALOG(dialog->advanced_win));

	  /*if (GTK_RESPONSE_OK != res) {
	  dialog->proxy_dropdown = NULL;
	  // ...
	  }*/

	  gtk_widget_hide(GTK_WIDGET(dialog->advanced_win));
	  }

src/gtkblist.c

To remove the toolbar:

	  /*
	  button = gaim_pixbuf_button_from_stock(_("I_M"), GAIM_STOCK_IM, GAIM_BUTTON_VERTICAL);
	  gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
	  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	  gtk_size_group_add_widget(sg, button);
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_im_cb), gtkblist->treeview);
	  gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Send a message to the selected buddy"), NULL);
	  g_object_set_data(G_OBJECT(button), "button_name", "im");
	  gtk_widget_show(button);

	  button = gaim_pixbuf_button_from_stock(_("Get _Info"), GAIM_STOCK_INFO, GAIM_BUTTON_VERTICAL);
	  gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
	  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	  gtk_size_group_add_widget(sg, button);
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_info_cb), gtkblist->treeview);
	  gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Get information on the selected buddy"), NULL);
	  g_object_set_data(G_OBJECT(button), "button_name", "info");
	  gtk_widget_show(button);

	  button = gaim_pixbuf_button_from_stock(_("_Chat"), GAIM_STOCK_CHAT, GAIM_BUTTON_VERTICAL);
	  gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
	  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	  gtk_size_group_add_widget(sg, button);
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_chat_cb), gtkblist->treeview);
	  gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Join a chat room"), NULL);
	  gtk_widget_set_sensitive(button, gaim_gtk_blist_joinchat_is_showable());
	  g_object_set_data(G_OBJECT(button), "button_name", "chat");
	  gtk_widget_show(button);

	  button = gaim_pixbuf_button_from_stock(_("_Away"), GAIM_STOCK_ICON_AWAY, GAIM_BUTTON_VERTICAL);
	  gtk_box_pack_start(GTK_BOX(gtkblist->bbox), button, FALSE, FALSE, 0);
	  gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
	  gtk_size_group_add_widget(sg, button);
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(gtk_blist_button_away_cb), NULL);
	  gtk_tooltips_set_tip(GTK_TOOLTIPS(gtkblist->tooltips), button, _("Set an away message"), NULL);
	  g_object_set_data(G_OBJECT(button), "button_name", "away");
	  gtk_widget_show(button);
	  */

To set the buddy list inside appview:

	  /*gtkblist->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	  gtk_window_set_role(GTK_WINDOW(gtkblist->window), "buddy_list");
	  gtk_window_set_title(GTK_WINDOW(gtkblist->window), _("Buddy List"));*/

	  gtkblist->vbox = gtk_vbox_new(FALSE, 0);
	  /*gtk_container_add(GTK_CONTAINER(gtkblist->window), gtkblist->vbox);*/
	  gtk_box_pack_start(GTK_BOX(mainhbox), gtkblist->vbox, TRUE, TRUE, 0);

To remove the menubar:

 	  /******************************* Menu bar ************************************
	  accel_group = gtk_accel_group_new();
	  gtk_window_add_accel_group(GTK_WINDOW (gtkblist->window), accel_group);
	  g_object_unref(accel_group);
	  gtkblist->ift = gtk_item_factory_new(GTK_TYPE_MENU_BAR, "", accel_group);
	  gtk_item_factory_set_translate_func(gtkblist->ift, item_factory_translate_func, NULL, NULL);
	  gtk_item_factory_create_items(gtkblist->ift, sizeof(blist_menu) / sizeof(*blist_menu), blist_menu, NULL);
	  gaim_gtk_load_accels();
	  g_signal_connect(G_OBJECT(accel_group), "accel-changed", G_CALLBACK(gaim_gtk_save_accels_cb), NULL);
	  gtk_box_pack_start(GTK_BOX(gtkblist->vbox), gtk_item_factory_get_widget(gtkblist->ift, ""), FALSE, FALSE, 0);

	  awaymenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Away"));
	  do_away_menu();

	  gtkblist->bpmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Buddy Pounce"));
	  gaim_gtkpounce_menu_build(gtkblist->bpmenu);

	  protomenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Account Actions"));
	  gaim_gtk_blist_update_protocol_actions();

	  pluginmenu = gtk_item_factory_get_widget(gtkblist->ift, N_("/Tools/Plugin Actions"));
	  gaim_gtk_blist_update_plugin_actions();*/

To connect relative callbacks to the menu and toolbar:

          g_signal_connect(G_OBJECT(mainmenubar->im_menu_buddy_send_im), "activate", G_CALLBACK(gtk_blist_button_im_cb), gtkblist->treeview);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_buddy_remove_buddy), "activate", G_CALLBACK(gtk_blist_button_remove_cb), gtkblist->treeview);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_buddy_add_buddy), "activate", G_CALLBACK(gaim_gtk_blist_add_buddy_cb), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_buddy_add_chat), "activate", G_CALLBACK(gaim_gtk_blist_add_chat_cb), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_view_buddy_list), "toggled", G_CALLBACK(view_blist_cb), gtkblist->vbox);
	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_remove_buddy), "clicked", G_CALLBACK(gtk_blist_button_remove_cb), gtkblist->treeview);
	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_add_buddy), "clicked", G_CALLBACK(gaim_gtk_blist_add_buddy_cb), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_add_chat), "clicked", G_CALLBACK(gaim_gtk_blist_add_chat_cb), NULL);
	  g_signal_connect(G_OBJECT(mainmenubar->im_toolbar_buddy_list), "toggled", G_CALLBACK(view_blist_cb), gtkblist->vbox);

The callback for 'Send IM' menu item, and joining chat are handled by the following callback:

	  void gtk_blist_button_im_cb(GtkWidget *w, GtkTreeView *tv)
	  {
	  GtkTreeIter iter;
	  GtkTreeModel *model = gtk_tree_view_get_model(tv);
	  GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);

	  if(gtk_tree_selection_get_selected(sel, &model, &iter)){
	  GaimBlistNode *node;

	  gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
	  if (GAIM_BLIST_NODE_IS_CHAT(node)) {
	  gtk_blist_button_chat_cb(w, tv);
	  return;
	  } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
	  gaim_gtkdialogs_im_with_user(((GaimBuddy*)node)->account, ((GaimBuddy*)node)->name);
	  return;
	  } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) {
	  GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)node);
	  gaim_gtkdialogs_im_with_user(buddy->account, buddy->name);
	  return;
	  }
	  }
	  gaim_gtkdialogs_im();
	  }

This is the callback for 'Remove buddy' menu item. Chat removing is also handled by this callback:

	  void gtk_blist_button_remove_cb(GtkWidget *w, GtkTreeView *tv)
	  {
	  GtkTreeIter iter;
	  GtkTreeModel *model = gtk_tree_view_get_model(tv);
	  GtkTreeSelection *sel = gtk_tree_view_get_selection(tv);

	  if(gtk_tree_selection_get_selected(sel, &model, &iter)){
	  GaimBlistNode *node;

	  gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
	  if (GAIM_BLIST_NODE_IS_CHAT(node)) {
	  gaim_gtkdialogs_remove_chat((GaimChat*)node);
	  return;
	  } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) {
	  gaim_gtkdialogs_remove_buddy((GaimBuddy*)node);
	  return;
	  } else if(GAIM_BLIST_NODE_IS_CONTACT(node)) {
	  GaimBuddy *buddy = gaim_contact_get_priority_buddy((GaimContact*)node);
	  gaim_gtkdialogs_remove_buddy(buddy);
	  return;
	  }
	  }
	  }

This small status icon is used to save space:

	  status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy, GAIM_STATUS_ICON_SMALL);
	  /* (gaim_prefs_get_bool("/gaim/gtk/blist/show_buddy_icons")
	  ? GAIM_STATUS_ICON_LARGE : GAIM_STATUS_ICON_SMALL)); */

To hide the avatar and idle time:

 	  avatar = NULL;
	  mark = gaim_gtk_blist_get_name_markup(buddy, selected);
	  idle = NULL;
	  /*
	  avatar = gaim_gtk_blist_get_buddy_icon(buddy);
	  mark = gaim_gtk_blist_get_name_markup(buddy, selected);

	  if (buddy->idle > 0) {
	  time_t t;
	  int ihrs, imin;
	  time(&t);
	  ihrs = (t - buddy->idle) / 3600;
	  imin = ((t - buddy->idle) / 60) % 60;
	  if(ihrs > 0)
	  idle = g_strdup_printf("(%d:%02d)", ihrs, imin);
	  else
	  idle = g_strdup_printf("(%d)", imin);
	  } */

The callback to menu item for showing and hiding the buddy list:

	  static void view_blist_cb(GtkCheckMenuItem *c, gpointer data)
	  {
	  if (gtk_check_menu_item_get_active(c))
	  gtk_widget_show_all(GTK_WIDGET(data));
	  else
	  gtk_widget_hide_all(GTK_WIDGET(data));
	  }

	  ...

	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_view_buddy_list), "toggled", 
		G_CALLBACK(view_blist_cb), gtkblist->vbox);

src/gtkconn.c

To change the gtk_window_new() to gtk_dialog_new():

	  /*
	  meter_win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	  gtk_window_set_resizable(GTK_WINDOW(meter_win->window), FALSE);
	  gtk_window_set_role(GTK_WINDOW(meter_win->window), "signon");
	  gtk_container_set_border_width(GTK_CONTAINER(meter_win->window), 5);
	  gtk_window_set_title(GTK_WINDOW(meter_win->window), _("Signon"));
	  */
	  meter_win->window = gtk_dialog_new();
	  gtk_window_set_title(GTK_WINDOW(meter_win->window), _("Signon"));

	  vbox = gtk_vbox_new (FALSE, 0);
	  /*gtk_container_add(GTK_CONTAINER(meter_win->window), vbox);*/
	  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(meter_win->window)->vbox), vbox);

src/gtkconv.c

This is the callback for showing or hiding all formatting toolbars:

	  static void
	  /*menu_toolbar_cb(gpointer data, guint action, GtkWidget *widget)*/
	  menu_toolbar_cb(GtkWidget *widget, gpointer data)
	  {
	  GaimConvWindow *win = (GaimConvWindow *)data;
	  GaimConversation *conv;
	  GaimGtkConversation *gtkconv;
	  gint i;

	  for (i = 0; i < gaim_conv_window_get_conversation_count(win); i++) {
	  /*conv = gaim_conv_window_get_active_conversation(win);*/
	  conv = gaim_conv_window_get_conversation_at(win, i);

	  if (conv == NULL)
	  return;

	  gtkconv = GAIM_GTK_CONVERSATION(conv);

	  gtkconv->show_formatting_toolbar = 
		gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));

	  if (gtkconv->show_formatting_toolbar)
	  gtk_widget_show(gtkconv->toolbar);
	  else
	  gtk_widget_hide(gtkconv->toolbar);
	  }
	  }

Remove toolbar:

	  /*gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->bbox, FALSE, FALSE, 0);*/

	 To remove the menubar:
	  /*
	  gtk_box_pack_start(GTK_BOX(testidea), menubar, FALSE, TRUE, 0);

	  gtk_box_pack_start(GTK_BOX(testidea), gtkwin->notebook, TRUE, TRUE, 0);

	  gtk_container_add(GTK_CONTAINER(gtkwin->window), testidea);

	  gtk_widget_show(testidea);
	  */

To set conversation inside the appview:

	  gtk_box_pack_end(GTK_BOX(mainhbox), gtkwin->notebook, TRUE, TRUE, 0);
	  

To rewrite callback declarations used for the conversation submenu:

	  /*menu_alias_cb(gpointer data, guint action, GtkWidget *widget)*/
	  menu_alias_cb(GtkWidget *widget, gpointer data)

	  ...

	  /*menu_get_info_cb(gpointer data, guint action, GtkWidget *widget)*/
	  menu_get_info_cb(GtkWidget *widget, gpointer data)

	  ...

	  /*menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget)*/
	  menu_close_conv_cb(GtkWidget *widget, gpointer data)

	  ...

	  /*menu_toolbar_cb(gpointer data, guint action, GtkWidget *widget)*/
	  menu_toolbar_cb(GtkWidget *widget, gpointer data)

To remove the horizontal pane and buttonbox and correct the focus chain:

	  /* Setup the horizontal pane.
	  hpaned = gtk_hpaned_new();
	  gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
	  gtk_widget_show(hpaned); */

	  ...

	  /*gtk_paned_pack1(GTK_PANED(hpaned), gtkconv->sw, TRUE, TRUE);*/
	  gtk_box_pack_start(GTK_BOX(vbox), gtkconv->sw, TRUE, TRUE, 0);

	  ...

	  /*gtk_paned_pack2(GTK_PANED(hpaned), lbox, FALSE, TRUE);*/

	  ...

	  /*gtk_box_pack_start(GTK_BOX(vbox), gtkconv->bbox, FALSE, FALSE, 0);*/

	  ...

	  /*focus_chain = g_list_prepend(focus_chain, gtkconv->bbox);*/

To modify items in chat menu:

	  static GtkWidget *
	  create_chat_menu(GaimConversation *conv, const char *who,
		GaimPluginProtocolInfo *prpl_info, GaimConnection *gc)
	  {
	  static GtkWidget *menu = NULL;
	  GtkWidget *button;

	  /*
	  * If a menu already exists, destroy it before creating a new one,
	  * thus freeing-up the memory it occupied.
	  */
	  if (menu)
	  gtk_widget_destroy(menu);

	  menu = gtk_menu_new();

	  button = gtk_menu_item_new_with_label(_("Add to buddy list"));
	  g_signal_connect(G_OBJECT(button), "activate",
		G_CALLBACK(menu_chat_add_buddy_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);

	  button = gtk_menu_item_new_with_label(_("Send message"));
	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_im_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);

	  /*if (gc && prpl_info && prpl_info->send_file
	  && (!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who))) {
	  button = gtk_menu_item_new_with_label(_("Send File"));
	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_send_file_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);
	  }

	  if (gaim_conv_chat_is_user_ignored(GAIM_CONV_CHAT(conv), who))
	  button = gtk_menu_item_new_with_label(_("Un-Ignore"));
	  else
	  button = gtk_menu_item_new_with_label(_("Ignore"));

	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(ignore_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);*/

	  if (gc && (prpl_info->get_info || prpl_info->get_cb_info)) {
	  button = gtk_menu_item_new_with_label(_("Show details"));
	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_info_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);
	  }

	  /*if (gc && prpl_info->get_cb_away) {
	  button = gtk_menu_item_new_with_label(_("Get Away Msg"));
	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_get_away_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);
	  }*/

	  /* XXX: jabber can only add buddies from here in certain circumstances */
	  /* Added by Jonas  */
	  /*if (gc) {
	  if (gaim_find_buddy(gc->account, who))
	  button = gtk_menu_item_new_with_label(_("Remove"));
	  else
	  button = gtk_menu_item_new_with_label(_("Add"));

	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_add_remove_cb), conv);

	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);
	  }*/
	  /* End Jonas */

	  return menu;
	  }

The callback to the context-sensitive menu for adding a person to buddy list is as follows:

	  static void
	  menu_chat_add_buddy_cb(GtkWidget *w, GaimConversation *conv)
	  {
	  GaimAccount *account;
	  char *name;

	  account = gaim_conversation_get_account(conv);
	  name = g_object_get_data(G_OBJECT(w), "user_data");

	  if (account != NULL && gaim_account_is_connected(account))
	  gaim_blist_request_add_buddy(account, name, NULL, NULL);
	  }

	  ...

	  button = gtk_menu_item_new_with_label(_("Add to buddy list"));
	  g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(menu_chat_add_buddy_cb), conv);
	  g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
	  gtk_menu_shell_append(GTK_MENU_SHELL(menu), button);
	  gtk_widget_show(button);
	 

Use only a single click (no double or right clicks):

	  /*if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) {
	  chat_do_im(conv, who);
	  } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {*/
	  GtkWidget *menu = create_chat_menu (conv, who, prpl_info, gc);
	  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
	  /*}*/

The callbacks for hiding and showing the people list are as follows:

	  static void
	  hide_list_people_cb(GtkWidget *w, gpointer data)
	  {
	  GtkWidget *dialog = GTK_WIDGET(data);

	  gtk_widget_hide_all(dialog);
	  }

	  static void
	  show_list_people_cb(GtkWidget *w, gpointer data)
	  {
	  GaimConvWindow *win;
	  GtkWidget *dialog;
	  GtkWidget *button;
	  GtkWidget *sep;
	  GaimConvChat *chat;
	  GaimConversation *conv;
	  GaimGtkConversation *gtkconv;
	  GaimGtkChatPane *gtkchat;

	  win = (GaimConvWindow *)data;
	  conv = gaim_conv_window_get_active_conversation(win);

	  if (gaim_conversation_get_type(conv) != GAIM_CONV_CHAT)
	  return;

	  dialog = gtk_dialog_new();
	  gtk_window_set_title(GTK_WINDOW(dialog), _("People in conversation"));
	  gtk_dialog_set_has_separator(dialog, FALSE);

	  chat = GAIM_CONV_CHAT(conv);
	  gtkconv = GAIM_GTK_CONVERSATION(conv);
	  gtkchat = gtkconv->u.chat;

	  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), gtkchat->lbox, FALSE, FALSE, 0);

	  sep = gtk_hseparator_new();
	  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), sep, FALSE, FALSE, 0);

	  button = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
	  gtk_widget_show(button);
	  gtk_box_pack_end(GTK_BOX(GTK_DIALOG(dialog)->vbox), button, FALSE, FALSE, 0);
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(hide_list_people_cb), dialog);

	  gtk_widget_show(dialog);
	  }

To connect signals to menu items:

g_signal_connect(G_OBJECT(mainmenubar->im_menu_view_formatting_toolbar), "toggled", G_CALLBACK(menu_toolbar_cb), win);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_conversation_set_alias), "activate", G_CALLBACK(menu_alias_cb), win);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_conversation_close), "activate", G_CALLBACK(menu_close_conv_cb), win);
	  g_signal_connect(G_OBJECT(mainmenubar->im_menu_conversation_list_people), "activate", G_CALLBACK(show_list_people_cb), win);

src/gtkprefs.c

The following is a structure for the sound page's checkboxes:

	  struct sound_cb {
	  GtkWidget *im_recv;
	  GtkWidget *login;
	  GtkWidget *logout;
	  GtkWidget *chat_msg_recv;
	  GtkWidget *nick_said;
	  };

The callback to checkbox for disabling all sounds is as follows:

	  static void
	  prefs_sound_click(GtkWidget *cb, struct sound_cb *checkbox) {
	  gint i;

	  /* Disabling all sounds */
	  if (TRUE == gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb))) {
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/im_recv", FALSE);
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/login", FALSE);
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/logout", FALSE);
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/chat_msg_recv", FALSE);
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/nick_said", FALSE);
	  gtk_widget_set_sensitive(checkbox->im_recv, FALSE);
	  gtk_widget_set_sensitive(checkbox->login, FALSE);
	  gtk_widget_set_sensitive(checkbox->logout, FALSE);
	  gtk_widget_set_sensitive(checkbox->chat_msg_recv, FALSE);
	  gtk_widget_set_sensitive(checkbox->nick_said, FALSE);
	  } else {
	  gtk_widget_set_sensitive(checkbox->im_recv, TRUE);
	  gtk_widget_set_sensitive(checkbox->login, TRUE);
	  gtk_widget_set_sensitive(checkbox->logout, TRUE);
	  gtk_widget_set_sensitive(checkbox->chat_msg_recv, TRUE);
	  gtk_widget_set_sensitive(checkbox->nick_said, TRUE);
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/im_recv", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox->im_recv)));
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/login", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox->login)));
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/logout", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox->logout)));
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/chat_msg_recv", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox->chat_msg_recv)));
	  gaim_prefs_set_bool("/gaim/gtk/sound/enabled/nick_said", gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox->nick_said)));
	  }
	  }

The new ports preferences page is as follows:

	  GtkWidget *ports_page() {
	  GtkWidget *ret;
	  GtkWidget *vbox, *hbox, *entry;
	  GtkWidget *table, *label, *auto_ip_checkbox, *ports_checkbox, *spin_button;
	  GtkSizeGroup *sg;
	  GaimProxyInfo *proxy_info;

	  ret = gtk_vbox_new(FALSE, 18);
	  gtk_container_set_border_width (GTK_CONTAINER (ret), 12);

	  vbox = gaim_gtk_make_frame (ret, _("Ports"));
	  sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

	  ports_checkbox = gaim_gtk_prefs_checkbox(_("_Manually specify range of ports to listen on"), "/core/network/ports_range_use", vbox);

	  spin_button = gaim_gtk_prefs_labeled_spin_button(vbox, _("_Start Port:"),
	  "/core/network/ports_range_start", 0, 65535, sg);
	  if (!gaim_prefs_get_bool("/core/network/ports_range_use"))
	  gtk_widget_set_sensitive(GTK_WIDGET(spin_button), FALSE);
	  g_signal_connect(G_OBJECT(ports_checkbox), "clicked", G_CALLBACK(gaim_gtk_toggle_sensitive), spin_button);

	  spin_button = gaim_gtk_prefs_labeled_spin_button(vbox, _("_End Port:"), "/core/network/ports_range_end", 0, 65535, sg);
	  if (!gaim_prefs_get_bool("/core/network/ports_range_use"))
	  gtk_widget_set_sensitive(GTK_WIDGET(spin_button), FALSE);
	  g_signal_connect(G_OBJECT(ports_checkbox), "clicked", G_CALLBACK(gaim_gtk_toggle_sensitive), spin_button);

	  gtk_widget_show_all(ret);
	  return ret;
	  }

The new proxy preferences page is as follows:

	  GtkWidget *proxy_page() {
	  GtkWidget *ret;
	  GtkWidget *vbox, *hbox, *entry;
	  GtkWidget *table, *label, *auto_ip_checkbox, *ports_checkbox, *spin_button;
	  GtkSizeGroup *sg;
	  GaimProxyInfo *proxy_info;

	  ret = gtk_vbox_new(FALSE, 18);
	  gtk_container_set_border_width (GTK_CONTAINER (ret), 12);

	  vbox = gaim_gtk_make_frame(ret, _("Proxy Server"));
	  prefs_proxy_frame = gtk_vbox_new(FALSE, 0);
	  gaim_gtk_prefs_dropdown(vbox, _("Proxy _type:"), GAIM_PREF_STRING,
	  "/core/proxy/type",
	  _("No proxy"), "none",
	  "SOCKS 4", "socks4",
	  "SOCKS 5", "socks5",
	  "HTTP", "http",
	  _("Use Environmental Settings"), "envvar",
	  NULL);
	  gtk_box_pack_start(GTK_BOX(vbox), prefs_proxy_frame, 0, 0, 0);
	  proxy_info = gaim_global_proxy_get_info();

	  if (proxy_info == NULL ||
	  gaim_proxy_info_get_type(proxy_info) == GAIM_PROXY_NONE ||
	  gaim_proxy_info_get_type(proxy_info) == GAIM_PROXY_USE_ENVVAR) {

	  gtk_widget_set_sensitive(GTK_WIDGET(prefs_proxy_frame), FALSE);
	  }
	  proxy_pref_id = gaim_prefs_connect_callback("/core/proxy/type",
	  proxy_changed_cb, prefs_proxy_frame);

	  table = gtk_table_new(4, 2, FALSE);
	  gtk_container_set_border_width(GTK_CONTAINER(table), 5);
	  gtk_table_set_col_spacings(GTK_TABLE(table), 5);
	  gtk_table_set_row_spacings(GTK_TABLE(table), 10);
	  gtk_container_add(GTK_CONTAINER(prefs_proxy_frame), table);

	  label = gtk_label_new_with_mnemonic(_("_Host:"));
	  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0);

	  entry = gtk_entry_new();
	  gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
	  gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 0, 1, GTK_FILL, 0, 0, 0);
	  g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(proxy_print_option), (void *)PROXYHOST);

	  if (proxy_info != NULL && gaim_proxy_info_get_host(proxy_info))
	  gtk_entry_set_text(GTK_ENTRY(entry), gaim_proxy_info_get_host(proxy_info));

	  hbox = gtk_hbox_new(TRUE, 5);
	  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	  gaim_set_accessible_label (entry, label);

	  label = gtk_label_new_with_mnemonic(_("_Port:"));
	  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	  gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0);

	  entry = gtk_entry_new();
	  gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
	  gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 0, 1, GTK_FILL, 0, 0, 0);
	  g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(proxy_print_option), (void *)PROXYPORT);

	  if (proxy_info != NULL && gaim_proxy_info_get_port(proxy_info) != 0) {
	  char buf[128];
	  g_snprintf(buf, sizeof(buf), "%d", gaim_proxy_info_get_port(proxy_info));

	  gtk_entry_set_text(GTK_ENTRY(entry), buf);
	  }
	  gaim_set_accessible_label (entry, label);

	  label = gtk_label_new_with_mnemonic(_("_User:"));
	  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	  gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);

	  entry = gtk_entry_new();
	  gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
	  gtk_table_attach(GTK_TABLE(table), entry, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
	  g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(proxy_print_option), (void *)PROXYUSER);

	  if (proxy_info != NULL && gaim_proxy_info_get_username(proxy_info) != NULL)
	  gtk_entry_set_text(GTK_ENTRY(entry), gaim_proxy_info_get_username(proxy_info));

	  hbox = gtk_hbox_new(TRUE, 5);
	  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	  gaim_set_accessible_label (entry, label);

	  label = gtk_label_new_with_mnemonic(_("Pa_ssword:"));
	  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
	  gtk_table_attach(GTK_TABLE(table), label, 2, 3, 1, 2, GTK_FILL, 0, 0, 0);

	  entry = gtk_entry_new();
	  gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
	  gtk_table_attach(GTK_TABLE(table), entry, 3, 4, 1, 2, GTK_FILL , 0, 0, 0);
	  gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE);
	  g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(proxy_print_option), (void *)PROXYPASS);

	  if (proxy_info != NULL && gaim_proxy_info_get_password(proxy_info) != NULL)
	  gtk_entry_set_text(GTK_ENTRY(entry), gaim_proxy_info_get_password(proxy_info));
	  gaim_set_accessible_label (entry, label);

	  gtk_widget_show_all(ret);
	  return ret;
	  }

The modified sound preferences page is as follows:

	  GtkWidget *sound_page() {
	  GtkWidget *ret;
	  GtkWidget *vbox, *sw, *button;
	  GtkSizeGroup *sg;
	  GtkTreeIter iter;
	  GtkWidget *event_view;
	  GtkListStore *event_store;
	  GtkCellRenderer *rend;
	  GtkTreeViewColumn *col;
	  GtkTreeSelection *sel;
	  GtkTreePath *path;
	  GtkWidget *hbox;
	  int j;
	  const char *file;
	  char *pref;
	  GtkWidget *cb;
	  struct sound_cb *checkbox;
	  #ifndef _WIN32
	  GtkWidget *dd;
	  GtkWidget *label;
	  GtkWidget *entry;
	  const char *cmd;
	  #endif

	  ret = gtk_vbox_new(FALSE, 18);
	  gtk_container_set_border_width (GTK_CONTAINER (ret), 12);

	  sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

	  /*vbox = gaim_gtk_make_frame (ret, _("Sound Options"));*/
	  /* Build without showing or using space for frame title */
	  vbox = gtk_vbox_new(FALSE, 6);
	  gtk_box_pack_start(GTK_BOX(ret), vbox, FALSE, FALSE, 0);

	  hbox = gtk_hbox_new(FALSE, 6);
	  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

	  vbox = gtk_vbox_new(FALSE, 6);
	  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);

	  cb = gtk_check_button_new_with_label(_("Disable all sounds"));
	  gtk_box_pack_start(GTK_BOX(vbox), cb, FALSE, FALSE, 0);

	  /* We don't need these checkboxes or sound method..
	  gaim_gtk_prefs_checkbox(_("Sounds when conversation has _focus"), "/gaim/gtk/sound/conv_focus", vbox);
	  gaim_gtk_prefs_checkbox(_("_Sounds while away"), "/core/sound/while_away", vbox);

	  #ifndef _WIN32
	  vbox = gaim_gtk_make_frame (ret, _("Sound Method"));
	  dd = gaim_gtk_prefs_dropdown(vbox, _("_Method:"), GAIM_PREF_STRING,
	  "/gaim/gtk/sound/method",
	  _("Console beep"), "beep",
	  #ifdef USE_AO
	  _("Automatic"), "automatic",
	  "ESD", "esd",
	  "Arts", "arts",
	  #endif
	  #ifdef USE_NAS_AUDIO
	  "NAS", "nas",
	  #endif
	  _("Command"), "custom",
	  NULL);
	  gtk_size_group_add_widget(sg, dd);
	  gtk_misc_set_alignment(GTK_MISC(dd), 0, 0);

	  hbox = gtk_hbox_new(FALSE, 5);
	  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);

	  label = gtk_label_new_with_mnemonic(_("Sound c_ommand:\n(%s for filename)"));
	  gtk_size_group_add_widget(sg, label);
	  gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
	  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);

	  entry = gtk_entry_new();
	  gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);

	  gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
	  cmd = gaim_prefs_get_string("/gaim/gtk/sound/command");
	  if(cmd)
	  gtk_entry_set_text(GTK_ENTRY(entry), cmd);
	  gtk_widget_set_size_request(entry, 75, -1);

	  gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 5);
	  g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(sound_cmd_yeah), NULL);

	  gtk_widget_set_sensitive(hbox, !strcmp(gaim_prefs_get_string("/gaim/gtk/sound/method"), "custom"));
	  sound_pref_id = gaim_prefs_connect_callback("/gaim/gtk/sound/method", sound_changed_cb, hbox);

	  gaim_set_accessible_label (entry, label);
	  */
	  //#endif /* _WIN32 */

	  vbox = gaim_gtk_make_frame(ret, _("Sound Events"));

	  /* Adding sound checkboxes */
	  checkbox = g_new(struct sound_cb, 1);
	  checkbox->im_recv = gaim_gtk_prefs_checkbox("Message received", "/gaim/gtk/sound/enabled/im_recv", vbox);
	  checkbox->login = gaim_gtk_prefs_checkbox("Buddy logs in", "/gaim/gtk/sound/enabled/login", vbox);
	  checkbox->logout = gaim_gtk_prefs_checkbox("Buddy logs out", "/gaim/gtk/sound/enabled/logout", vbox);
	  checkbox->chat_msg_recv = gaim_gtk_prefs_checkbox("Someone talks in chat", "/gaim/gtk/sound/enabled/chat_msg_recv", vbox);
	  checkbox->nick_said = gaim_gtk_prefs_checkbox("Someone says your name", "/gaim/gtk/sound/enabled/nick_said", vbox);

	  g_signal_connect(G_OBJECT (cb), "clicked", G_CALLBACK (prefs_sound_click), checkbox);

	  /* The following is an ugly hack to make the frame expand so the
	  * sound events list is big enough to be usable */
	  /* Adding wanted checkboxes above..
	  gtk_box_set_child_packing(GTK_BOX(vbox->parent), vbox, TRUE, TRUE, 0, GTK_PACK_START);
	  gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent), vbox->parent, TRUE, TRUE, 0, GTK_PACK_START);
	  gtk_box_set_child_packing(GTK_BOX(vbox->parent->parent->parent), vbox->parent->parent, TRUE, TRUE, 0, GTK_PACK_START);

	  sw = gtk_scrolled_window_new(NULL,NULL);
	  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);

	  gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
	  event_store = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);

	  for (j=0; j < GAIM_NUM_SOUNDS; j++) {
	  char *pref = g_strdup_printf("/gaim/gtk/sound/enabled/%s", gaim_gtk_sound_get_event_option(j));
	  const char *label = gaim_gtk_sound_get_event_label(j);

	  if (label == NULL) {
	  g_free(pref);
	  continue;
	  }

	  gtk_list_store_append (event_store, &iter);
	  gtk_list_store_set(event_store, &iter,
	  0, gaim_prefs_get_bool(pref),
	  1, _(label),
	  2, pref,
	  3, j,
	  -1);
	  g_free(pref);
	  }

	  event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(event_store));

	  rend = gtk_cell_renderer_toggle_new();
	  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
	  g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_sound_sel), NULL);
	  g_signal_connect (G_OBJECT(rend), "toggled", G_CALLBACK(event_toggled), event_store);
	  path = gtk_tree_path_new_first();
	  gtk_tree_selection_select_path(sel, path);
	  gtk_tree_path_free(path);

	  col = gtk_tree_view_column_new_with_attributes (_("Play"),
	  rend,
	  "active", 0,
	  NULL);
	  gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);

	  rend = gtk_cell_renderer_text_new();
	  col = gtk_tree_view_column_new_with_attributes (_("Event"),
	  rend,
	  "text", 1,
	  NULL);
	  gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
	  g_object_unref(G_OBJECT(event_store));
	  gtk_container_add(GTK_CONTAINER(sw), event_view);*/
	  /* We don't need sound testing, etc..
	  hbox = gtk_hbox_new(FALSE, 6);
	  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
	  sound_entry = gtk_entry_new();
	  pref = g_strdup_printf("/gaim/gtk/sound/file/%s", gaim_gtk_sound_get_event_option(0));
	  file = gaim_prefs_get_string(pref);
	  g_free(pref);
	  gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : "(default)");
	  gtk_editable_set_editable(GTK_EDITABLE(sound_entry), FALSE);
	  gtk_box_pack_start(GTK_BOX(hbox), sound_entry, FALSE, FALSE, 5);

	  button = gtk_button_new_with_label(_("Test"));
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(test_sound), NULL);
	  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);

	  button = gtk_button_new_with_label(_("Reset"));
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(reset_sound), NULL);
	  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);

	  button = gtk_button_new_with_label(_("Choose..."));
	  g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(select_sound), NULL);
	  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
	  */
	  gtk_widget_show_all(ret);

	  return ret;
	  }

To removing some preferences pages and use some new ones:

	  void prefs_notebook_init() {
	  GtkTreeIter p, c, c2;
	  GList *l;
	  GaimPlugin *plug;
	  
	  /* Let's drop unneeded pages
	  prefs_notebook_add_page(_("Interface"), NULL, interface_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Buddy List"), NULL, list_page(), &c, &p, notebook_page++);
	  prefs_notebook_add_page(_("Conversations"), NULL, conv_page(), &c, &p, notebook_page++);
	  prefs_notebook_add_page(_("Message Text"), NULL, messages_page(), &c2, &c, notebook_page++);
	  prefs_notebook_add_page(_("Shortcuts"), NULL, hotkeys_page(), &c2, &c, notebook_page++);
	  prefs_notebook_add_page(_("Smiley Themes"), NULL, theme_page(), &c2, &c, notebook_page++);
	  prefs_notebook_add_page(_("Network"), NULL, network_page(), &p, NULL, notebook_page++); */
	  prefs_notebook_add_page(_("IP address"), NULL, network_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Ports"), NULL, ports_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Proxy"), NULL, proxy_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Sounds"), NULL, sound_page(), &p, NULL, notebook_page++);
	  #ifndef _WIN32
	  /* We use the registered default browser in windows */
	  /* if the user is running gnome 2.x, hide the browsers tab */
	  /* Let's drop unneeded pages
	  if (gaim_running_gnome() == FALSE) {
	  prefs_notebook_add_page(_("Browser"), NULL, browser_page(), &p, NULL, notebook_page++);
	  } */
	  #endif
	  /* Let's drop unneeded pages
	  prefs_notebook_add_page(_("Logging"), NULL, logging_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Away / Idle"), NULL, away_page(), &p, NULL, notebook_page++);
	  prefs_notebook_add_page(_("Away Messages"), NULL, away_message_page(), &c, &p, notebook_page++);

	  if (gaim_plugins_enabled()) {
	  prefs_notebook_add_page(_("Plugins"), NULL, plugin_page(), &plugin_iter, NULL, notebook_page++);

	  for (l = gaim_plugins_get_loaded(); l != NULL; l = l->next) {
	  plug = (GaimPlugin *)l->data;

	  if (GAIM_IS_GTK_PLUGIN(plug)) {
	  GtkWidget *config_frame;
	  GaimGtkPluginUiInfo *ui_info;

	  ui_info = GAIM_GTK_PLUGIN_UI_INFO(plug);
	  config_frame = gaim_gtk_plugin_get_config_frame(plug);

	  if (config_frame != NULL) {
	  ui_info->iter = g_new0(GtkTreeIter, 1);
	  prefs_notebook_add_page(_(plug->info->name), NULL, config_frame, ui_info->iter, &plugin_iter, notebook_page++);
	  }
	  }

	  if(GAIM_PLUGIN_HAS_PREF_FRAME(plug)) {
	  GtkWidget *gtk_frame;
	  GaimPluginUiInfo *prefs_info;

	  prefs_info = GAIM_PLUGIN_UI_INFO(plug);
	  prefs_info->frame = prefs_info->get_plugin_pref_frame(plug);
	  gtk_frame = gaim_gtk_plugin_pref_create_frame(prefs_info->frame);
	  
	  if(GTK_IS_WIDGET(gtk_frame)) {
	  prefs_info->iter = g_new0(GtkTreeIter, 1);
	  prefs_notebook_add_page(_(plug->info->name), NULL, gtk_frame, prefs_info->iter,
		(plug->info->type == GAIM_PLUGIN_PROTOCOL) ?  NULL : &plugin_iter, notebook_page++);
	  } else if(prefs_info->frame) {
	  */
	  /* in the event that there is a pref frame and we can
	  * not make a widget out of it, we free the
	  * pluginpref frame --Gary
	  */
	  /* Let's drop unneeded pages
	  gaim_plugin_pref_frame_destroy(prefs_info->frame);
	  }
	  }
	  }
	  }
	  */
	  }

To use the gtk_dialog instead of gtk_window:

	  /* Create the window */
	  /* Let's change this to a popup dialog..
	  prefs = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	  gtk_window_set_role(GTK_WINDOW(prefs), "preferences");
	  gtk_window_set_title(GTK_WINDOW(prefs), _("Preferences"));
	  gtk_window_set_resizable (GTK_WINDOW(prefs), FALSE);
	  gtk_container_set_border_width(GTK_CONTAINER(prefs), 12);
	  */
	  prefs = gtk_dialog_new();
	  gtk_window_set_title(GTK_WINDOW(prefs), _("Preferences"));
	  g_signal_connect(G_OBJECT(prefs), "destroy", G_CALLBACK(delete_prefs), NULL);

	  ...

	  gtk_container_add(GTK_CONTAINER(GTK_DIALOG(prefs)->vbox), vbox);

To remove space that is consuming frame structure:

	  #if (0) /* Don't use this frame structure */
	  hbox = gtk_hbox_new (FALSE, 6);
	  gtk_container_add (GTK_CONTAINER(vbox), hbox);
	  gtk_widget_show (hbox);

	  frame = gtk_frame_new (NULL);
	  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
	  gtk_widget_show (frame);

	  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
	  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
	  gtk_container_add(GTK_CONTAINER(frame), scrolled_window);
	  gtk_widget_show(scrolled_window);

	  /* The tree -- much inspired by the Gimp */
	  prefstree = gtk_tree_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
	  tree_v = gtk_tree_view_new_with_model (GTK_TREE_MODEL (prefstree));
	  gtk_container_add(GTK_CONTAINER(scrolled_window), tree_v);

	  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_v), FALSE);
	  gtk_widget_show(tree_v);
	  /* icons */
	  /* XXX: to be used at a later date
	  cell = gtk_cell_renderer_pixbuf_new ();
	  column = gtk_tree_view_column_new_with_attributes ("icons", cell, "pixbuf", 0, NULL);
	  */

	  /* text */
	  cell = gtk_cell_renderer_text_new ();
	  column = gtk_tree_view_column_new_with_attributes ("text", cell, "text", 1, NULL);

	  gtk_tree_view_append_column (GTK_TREE_VIEW (tree_v), column);

	  /* The right side */
	  frame = gtk_frame_new (NULL);
	  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
	  gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
	  gtk_widget_show (frame);

	  vbox2 = gtk_vbox_new (FALSE, 4);
	  gtk_container_add (GTK_CONTAINER (frame), vbox2);
	  gtk_widget_show (vbox2);

	  frame = gtk_frame_new (NULL);
	  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
	  gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, TRUE, 0);
	  gtk_widget_show (frame);

	  hbox = gtk_hbox_new (FALSE, 4);
	  gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
	  gtk_container_add (GTK_CONTAINER (frame), hbox);
	  gtk_widget_show (hbox);

	  preflabel = gtk_label_new(NULL);
	  gtk_box_pack_end (GTK_BOX (hbox), preflabel, FALSE, FALSE, 0);
	  gtk_widget_show (preflabel);
	  #endif /* (0) */

To display the notebook's tabs:

	  gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE);

To add the notebook straight to tthe dialog's main vbox:

	  /* gtk_box_pack_start (GTK_BOX (vbox2), notebook, FALSE, FALSE, 0); */
	  gtk_container_add (GTK_CONTAINER(vbox), notebook);

Screenshots of the changes

Signon windows become overstretched on the screen, as is seen in Figure 3.

Signon window before modification

Figure 3. Signon window before modification

Figure 4 shows the Signon window following modification.

Signon dialog after modification

Figure 4. Signon dialog after modification

The original buddy list seen in Figure 5 has many removable parts. The buddy list's own menubar and toolbar are not needed. Also the users' avatars and user status texts are not very useful and are space consuming.

Buddy list before modifications

Figure 5. Buddy list before modifications

The modified buddy list is shown in Figure 6. The whole buddy list window is Hildonised by placing it inside the Appview. The window's menu is reconstructed in the buddy submenu in the Hildon menubar, taking most of the menu options away. However, some most vital actions are added in the Hildon toolbar.

Buddy list after modifications

Figure 6. Buddy list after modifications

The conversation window seen in Figure 7 is totally separated from the buddy list and has its own menubar and toolbar like the buddy list.

Conversation window before modifications

Figure 7. Conversation window before modifications

A modified conversation window is shown in Figure 8, where it is located inside the Appview on side of the buddy list. The window's menu is reconstructed to the Conversation submenu in Hildon menubar, taking most of the menu options away.

Conversation window after modifications

Figure 8. Conversation window after modifications

The menus presented in Figures 9 and 10 were removed and a new menu with the most important menu options was created, as shown in Figure 11.

Buddy list's old menu

Figure 9. Buddy list's old menu

Conversation window's old menu

Figure 10. Conversation window's old menu

New Hildon menubar

Figure 11. New Hildon menubar

Networking changes

To acquire a network connection on the target device all you have to do is to load libosso-ic-preload.so before starting the application. That is done by setting LD_PRELOAD environment variable before execution. To do that, create a src/gaim.sh startup script, which is used in .desktop file specified in Section Adding to menu.

src/gaim.sh

The following example illustrates the contents of the src/gaim.sh script.

	  #!/bin/sh
	  case `uname -m` in
	  armel*) export LD_PRELOAD=/usr/lib/libosso-ic-preload.so;;   # armel
	  *) ;;                                                      # some other architecture
	  esac

	  /usr/bin/gaim

The script is installed in same directory as the binary when you add it to src/Makefile.am.

src/Makefile.am

The following example illustrates the contents of the src/Makefile.am script.

	  bin_SCRIPTS = gaim.sh

Other changes

At first, Gaim did not succeed in compiling in Scratchbox and required two changes.

src/util.c

Gaim cannot compile without the following change:

	  /*tzoff += t->tm_gmtoff;*/
	  tzoff += t->__tm_gmtoff;

configure.ac

Perl, TCL and Tk were disabled as they are not required and at least some of them have some dependencies that are not met. The following are needed to modify configure.ac:

	  AC_ARG_ENABLE(perl,    [  --disable-perl   compile without perl scripting],,enable_perl=no)
	  AC_ARG_ENABLE(tcl,     [  --disable-tcl    compile without Tcl scripting],,enable_tcl=no)
	  AC_ARG_ENABLE(tk,      [  --disable-tk     compile without Tcl support for Tk],,enable_tk=no)	

Adding to menu

Adding an application to menu requires desktop and service files and a link to the installed desktop file. The required additions and changes are described below.

  1. Gaim has already a gaim.desktop file so it does not need to be created. Only Exec needs to be modified to include full path to the following startup script:
    	Exec=/usr/bin/gaim.sh
    
  2. To create a service file com.nokia.gaim.service:
    	[D-BUS Service]
    	Name=com.nokia.gaim
    	Exec=/usr/bin/gaim
    
  3. To add section of service file in Makefile.am:
    	dbusdir=$(prefix)/lib/dbus-1.0/services
    	dbus_DATA=com.nokia.gaim.service
    
  4. The correct path for desktop file in Makefile.am is:
    	appsdir = $(datadir)/applications/hildon
    	apps_DATA = gaim.desktop
    
  5. A link to a desktop file must be added to /etc/others-menu/extra_applications to display it in the menu. The link is opened by creating a debian/maemo-gaim.links file with the following contents:
    	usr/share/applications/hildon/gaim.desktop etc/others-menu/extra_applications/gaim.desktop
    
  6. All these three files need to be placed in Makefile.am's EXTRA_DIST variable. gaim.desktop is there already so only the other two need to be added:
    	gaim.desktop \
    	com.nokia.gaim.service \
    	debian/maemo-gaim.links
    

Application packaging

This section describes how Gaim sources were modified to make .deb package building possible. The actual packaging operation after that is specified in another document. To modify Gaim sources:

  1. Rename Gaim's source dir to maemo-gaim-0.0.1:
    	mv gaim-1.1.4 maemo-gaim-0.0.1
    
  2. The package source is dir maemo-gaim-0.0.1/ in maemo-gaim-0.0.1.tar.gz:
    	tar -czvvf maemo-gaim-0.0.1.tar.gz maemo-gaim-0.0.1
    
  3. Go to src dir:
    	cd maemo-gaim-0.0.1
    	
  4. Set full name environment variable:
    	export DEBFULLNAME=Mr Maemo
    

    You are now ready to start the actual work. For more information about this phase, see the Debian New Maintainers' Guide5.

  5. Make the initial debianisation:
    	dh_make -e xxxxxxx.xxxxxx@maemo.org -f ../maemo-gaim-0.0.1.tar.gz
    
  6. dh_make asks a question and prints a summary of the package:
    	Type of package: single binary, multiple binary, library, or kernel module?
    	[s/m/l/k] s
    
    	Maintainer name : Mr Maemo
    	Email-Address   : xxxxxxx.xxxxxx@maemo.org
    	Date            : Thu, 11 May 2006 14:47:39 +0300
    	Package Name    : maemo-gaim
    	Version         : 0.0.1
    	Type of Package : Single
    	Hit  to confirm:
    
  7. Modify debian dir. Only the changelog, compat, control, copyright, docs and rules files are needed. In the control file packages, hildon-libs0 (>= 0.9.50) must to be added. You must also set the Section field into the user/internet to make the package compatible with Application Manager. When this is done, the package structure is correct. To be able to build the package, you now need to start making changes in configuration files.
  8. Add the following lines to configure.ac:
    	AC_PROG_INTLTOOL([0.23])
    	GETTEXT_PACKAGE=gaim
    	AC_SUBST(GETTEXT_PACKAGE)
    	AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Name of gettext package])
    	AM_GLIB_GNU_GETTEXT
    
    	PKG_CHECK_MODULES(HILDON, hildon-libs >= 0.9.51)
    	AC_SUBST(HILDON_LIBS)
    	AC_SUBST(HILDON_CFLAGS)
    
  9. Make the following two changes in Makefile.am.

    Firstly, add items in EXTRA_DIST:

    	autogen.sh \
    	intltool-extract.in \
    	intltool-merge.in \
    	intltool-update.in \
    	debian/changelog \
    	debian/compat \
    	debian/copyright \
    	debian/control \
    	debian/rules \
    	debian/docs
    

    Then add the deb rule:

    	deb: dist
    	-mkdir $(top_builddir)/debian-build
    	cd $(top_builddir)/debian-build && tar zxf ../$(top_builddir)/$(PACKAGE)-$(VERSION).tar.gz
    	cd $(top_builddir)/debian-build/$(PACKAGE)-$(VERSION) && dpkg-buildpackage -rfakeroot
    	-rm -rf $(top_builddir)/debian-build/$(PACKAGE)-$(VERSION)
    
  10. You still need to fix some plugins/perl/Makefile.am. If these are not commented out, the "make distclean" used in dpkg-buildpackage command makes false symlinks in the plugins/perl/common directory and package building fails:
    	common/Makefile: common/Makefile.PL
    	#       @if test "x${top_srcdir}" != "x${top_builddir}"; then \
    	#               for f in ${common_sources}; do \
    	#                       ${LN_S} -f ../${srcdir}/$$f $$f; \
    	#               done; \
    	#       fi
            @cd common && $(perlpath) Makefile.PL $(PERL_MM_PARAMS)
    	...
    	#       @if test "x${top_srcdir}" != "x${top_builddir}"; then \
    	#               for f in ${common_sources}; do \
    	#                       ${LN_S} -f ../${srcdir}/$$f $$f; \
    	#               done; \
    	#       fi
    

The source dir is now ready for packaging. For more information of this process, see the Maemo 2.2 Tutorial6.

If you want to create a package that is useable with the Application Manager, see Making application packages.



Improve this page