Previously, the GNOME libraries primarily contained GTK+ extensions. However, GTK+ 2 integrated several GNOME capabilities. GNOME still contains a few widgets that serve as
To build modern graphical
A GNOME application may use these libraries:
libgnomecanvas-2: GNOME canvas widget.
libgnomevfs-2: GNOME file access abstraction layer (see Chapter 8).
libbonobo-2: Graphics-independent component object model (not covered in this book).
libbonoboui-2: Graphics-dependent component object model.
libbonobo-activation: Bonobo's object activation system.
These libraries depend on a number of other libraries, but you won't normally need to directly access these extras.
GNOME developers use the functionality in these libraries whenever possible so that they don't have to reinvent the wheel and require even more memory or libraries. GNOME applications should behave well with each other and have a consistent look and feel. You can achieve this with the following:
Adherence to the GNOME user interface guidelines (see Section 3.1).
Stock items for uniform icon, label, and translation appearance in
Help buttons and menus to activate documentation in the ScrollKeeper database (see Section 6.4.1).
Smooth integration with the GNOME main menu (see Section 6.3).
Interaction with the GNOME Session Manager (see Section 4.3.16).
Configuration with GConf (see Chapter 7).
File access with
Among GTK+ developers is a small but vocal minority that wants no truck with the GNOME libraries. The reasons are somewhat unclear, but the most frequent argument is that linking with GNOME causes bloat in "lean" GTK+ programs.
On the surface, this sentiment appears to have some truth, because GNOME applications usually link against more than 30 libraries. However, many of these "pure" programs wander into GNOME territory anyway. One
This
You may take some
The
libgnome
library contains
Other libraries reflect this separation between graphical and nongraphical modules. For example, Bonobo and Gnome-Print exhibit the same division. One of the consequences of the split in the core library, though, is that libgnome doesn't do a whole lot.
Recall from Chapter 3 that all pure GTK+ programs share a common gtk_init() call and run gtk_main() to start the main event loop. GNOME applications also use gtk_main() , but their initialization is different because they must initialize every piece of the GNOME system.
To set up a GNOME application, create an object of the GnomeProgram ( GNOME_TYPE_PROGRAM ) class. You do this with a generator function:
#include <gnome.h> GnomeProgram * app ; app = gnome_program_init( application_id , application_version , module_info , argc , argv , [property list,] NULL);
The parameters are as
application_id
(
char *
): The
application identifier
string. The identifier is normally the name of the executable file. Later, this string serves as a directory
application_version ( char * ): The application's version number, as a string.
module_info ( GnomeModuleInfo * ): The GNOME module identifier. The most useful values are LIBGNOMEUI_MODULE for graphical applications and LIBGNOME_MODULE for other programs.
argc ( int ): Command-line argument count; normally argc from main() .
argv ( char ** ): Command-line arguments; normally argc from main() .
property list : A list of key-value properties. The important keys and values are
GNOME_PARAM_POPT_TABLE : A table of command-line parameters for the popt library (GNOME uses popt to parse parameters). If you have application-specific parameters, you can recognize them with the popt API. See [Johnson] for more information.
GNOME_PARAM_POPT_FLAGS : Options for popt .
GNOME_PARAM_POPT_CONTEXT : Context for popt .
GNOME_PARAM_ENABLE_SOUND ( gboolean ): If TRUE , the application can make noise.
GNOME_PARAM_HUMAN_READABLE_NAME
(
gchararray
): The familiar name of the application, that is, something that
GNOME_PARAM_SM_CONNECT ( gboolean ): Set this to FALSE if you want this program to ignore the session manager. Normally, you want to be able to respond, so the default for this property is TRUE .
You should always specify
GNOME_PARAM_STANDARD_PROPERTIES
in the list in addition to the
PREFIX : Installation prefix (for example, /opt/gnome or /usr/local ).
SYSCONFDIR : Installation file directory (for example, $(PREFIX)/etc ).
DATADIR : Application data directory (for example, $(PREFIX)/share ).
LIBDIR : Library directory (for example, $(PREFIX)/lib ).
The GNU autotools (see Section 6.2) can set these macros for you.
Here is a simple program skeleton:
#include <gnome.h>
int main(int argc, char **argv)
{
GnomeProgram *program;
program = gnome_program_init("skeleton",
"0.1",
LIBGNOMEUI_MODULE,
argc, argv,
GNOME_PROGRAM_STANDARD_PROPERTIES,
GNOME_PARAM_HUMAN_READABLE_NAME, "Skeleton",
GNOME_PARAM_ENABLE_SOUND, FALSE,
NULL);
/* rest of main program goes here */
gtk_main();
exit(0);
}
Without GNU autotools, the Makefile looks something like this:
PREFIX = /opt/gnome
CFLAGS = `pkg-config --cflags libgnomeui-2.0` -ansi -Wall \
-DPREFIX=\""$(PREFIX)"\" \
-DDATADIR=\""$(PREFIX)/share"\" \
-DSYSCONFDIR=\""$(PREFIX)/etc"\" \
-DLIBDIR=\""$(PREFIX)/lib"\"
LIBS = `pkg-config --libs libgnomeui-2.0`
skeleton: skeleton.c
gcc -o skeleton skeleton.c $(CFLAGS) $(LIBS)
One of the most important functions in the GNOME libraries
char *path ; path = gnome_program_locate_file( program, domain, file_name, if_exists, ret_locations );
The return value is a newly allocated string pointer containing a file pathname if any file matched the criteria:
program ( GnomeProgram * ): Your program object.
domain ( GnomeFileDomain ): Determines the file type. Possible values include
GNOME_FILE_DOMAIN_LIBDIR : GNOME libraries.
GNOME_FILE_DOMAIN_DATADIR : GNOME data files.
GNOME_FILE_DOMAIN_SOUND : GNOME sound files.
GNOME_FILE_DOMAIN_PIXMAP : GNOME images.
GNOME_FILE_DOMAIN_CONFIG : GNOME configuration files.
GNOME_FILE_DOMAIN_HELP : GNOME help files.
GNOME_FILE_DOMAIN_APP_LIBDIR : Application libraries.
GNOME_FILE_DOMAIN_APP_DATADIR : Application data files.
GNOME_FILE_DOMAIN_APP_SOUND : Application sound files.
GNOME_FILE_DOMAIN_APP_PIXMAP : Application images.
GNOME_FILE_DOMAIN_APP_CONFIG : Application configuration files.
GNOME_FILE_DOMAIN_HELP : Application files.
Each of these
file_name ( char * ): The desired filename.
if_exists ( gboolean ): If TRUE , GNOME returns the full pathname if there is actually a file in that location (and NULL otherwise). However, if you set if_exists to FALSE , a full pathname comes back even if the file doesn't exist.
ret_locations ( GSList ** ): If you need to know if there are multiple files matching file_name , pass the address of a GSList pointer here; GNOME creates a new list containing all matching pathnames at that pointer. You need to free the list when you're done. You can specify NULL here if you don't care.
The
gnome_program_file_locate()
function is the GNOME-approved method of finding application configuration and data files because it
Here is an example:
char *path;
/* find the green apple image; it should be in the standard
GNOME pixmap directory */
path = gnome_program_locate_file(program,
GNOME_FILE_DOMAIN_PIXMAP,
"apple-green.png",
TRUE,
NULL);
g_print("Path for green apple: %s\n", path);
g_free(path);
/* look for an application-specific sound file --
do not verify that it exists */
path = gnome_program_locate_file(program,
GNOME_FILE_DOMAIN_APP_SOUND,
"plop.wav",
FALSE,
NULL);
g_print("Alleged path for plop sound: %s\n", path);
g_free(path);
/* look for the same application-specific sound file --
this time, return a path only if the file exists */
path = gnome_program_locate_file(program,
GNOME_FILE_DOMAIN_APP_SOUND,
"plop.wav",
TRUE,
NULL);
if (path)
{
g_print(" ... the actual location is %s.\n", path);
g_free(path);
} else {
g_print(" ... however, plop.wav isn't there.\n");
}
Another useful function is
gnome_util_prepend_user_home( relative_path )
This returns a newly allocated string with the current
gnome_util_home_file( relative_path )
As usual, you have to free up the return value:
/* directory prepend functions */
path = gnome_util_prepend_user_home("pictures/house.png");
g_print("Path for house.png: %s\n", path);
g_free(path);
path = gnome_util_home_file("mega-app/config");
g_print("Path for mega-app configuration: %s\n", path);
g_free(path);
| Note |
You will likely call
gnome_util_home_file()
more than its
|
If you need to find a file extension in a path, use the strangely named
g_extension_pointer( path )
If path ends with an extension, this function returns a pointer to that extension, after the period. This utility does not allocate new memory. If there is no extension, a pointer to the end of path comes back as the return value.
The GNOME libraries include several functions for running external programs. Among these are asynchronous calls that fork off a program and let it run in the background.
These functions look somewhat similar to Unix system calls that use an array of command-line arguments ( argv ). The array's first element is a string containing the program name, followed by the program's parameters. You must provide the total count of program and parameters names separately with argc .
| Note |
Many of the following functions partially duplicate GLib GSpawn facilities (and are thus around for compatibility). You may want to look at the GLib reference documentation before using one of these. |
int gnome_execute_async(const char * dir , int argc , char *const argv [])
Runs the new process in dir . For the current directory, use NULL for dir . The return value is the process ID (PID) of the new program. If the program fails to start at all (that is, if it doesn't exist or some other problem occurs), the return value is -1.
int gnome_execute_async_fds(const char * dir , int argc , char *const argv [], gboolean close_fds )
Like the preceding function, but if close_fds is TRUE , the new process does not share any file descriptors with its parent other than the standard input, output, and error.
int gnome_execute_async_with_env(const char * dir , int argc , char *const argv [], int envc , char *const envv [])
Like
gnome_execute_async()
, but adds the environment
int gnome_execute_async_with_env_fds(const char * dir , int argc , char *const argv [], int envc , char *const envv [], gboolean close_fds )
Like the preceding function, but with the file description behavior in gnome_execute_async_fds() .
If you want to run a process in a terminal window with one of the preceding functions, you can alter your argc and argv with this function:
int gnome_prepend_terminal_to_vector(int * argc , char *** argv )
This function
| Warning |
You must create *argv with g_malloc() , and you must also individually create each of this array's component strings with g_malloc() . This function deallocates your original vector, so you can expect a core dump if you give it some nondynamic memory or try to use your original argument vector after the call. |
If you would rather start your new process with a shell, use one of these functions:
int gnome_execute_shell(const char * dir , const char * command )
Runs command in the directory dir with the user's default shell. Use NULL for the current directory. As with all other functions in this section, the command is forked off; the return value is the new process ID, or -1 if the command does not execute properly.
int gnome_execute_terminal_shell(const char * dir , const char * command )
Like the preceding function, but runs the command in a terminal window.
int gnome_execute_shell_fds(const char * dir , const char * command , gboolean close_fds )
Like gnome_execute_shell() , but does not pass any file descriptors other than standard input, output, and error if close_fds is TRUE .
int gnome_execute_terminal_shell_fds(const char * dir , const char * command , gboolean close_fds )
Like the preceding function, but runs the command in a terminal window.
| Warning |
For any function in this section, be careful if some of your arguments come from the network. It's far too easy to
|
You can make GNOME show a URL without worrying about the program responsible for viewing the content behind the URL. There is a central configuration mapping; if you want to show a website or FTP index to the user, you can do it with a single function:
gboolean gnome_url_show(const char * url , GError ** error )
Here, url is a string containing the target URL, and error is a GError pointer (error class: GNOME_URL_ERROR ). This function attempts to interpret the URL and send it to the appropriate helper program. Upon success, this function returns TRUE .
At the moment, the only error code you might get in error if there's a problem is GNOME_ERROR_URL_PARSE if a syntax error occurs in the URL.
Here is a short example:
GError *error;
gnome_url_show("http://www.gnome.org/" &error);
if (error != NULL)
{
g_printerr("Can't open URL: %s\n", error->message);
g_error_free(error);
}
| Note |
For the most part, your applications should display a URL in response to user input. In this case, you shouldn't use gnome_url_show() . GnomeHRef widgets work better. See Section 4.3.11 for more information. |
If you need to change the environment of the helper program (for example, to send to a different display), put the new environment variables in an array of strings envp (each string should be in the form VAR=value ) and call this function:
gboolean gnome_url_show_with_env(const char *url , char **envp , GError **error )
Help files use a special URI protocol ( ghelp: ), separate from other URL mechanisms. GNOME sends help data to a browser such as Yelp rather than a generic web browser.
To view a help file, use
gnome_help_display( filename , link_id , error )
If GNOME finds a help file matching filename , it displays the content, skipping to the section named link_id (this can be NULL if there is no section). Upon success, this function returns TRUE . The GError class for error is GNOME_HELP_ERROR , with one of these codes:
GNOME_HELP_ERROR_NOT_FOUND : There is no such help file.
GNOME_HELP_ERROR_INTERNAL : Unspecified help system error.
If you tell gnome_help_display() to look for foo , GNOME goes to the help directory for the current application to look for a matching file, including foo.xml , foo.docbook , foo.sgml , and foo.html . Therefore, if you install your help documents according to the conventions in Section 6.4.1, you do not need to worry about a subdirectory or extension.
Your program's help file ( filename ) should match the application identifier string because this makes it especially easy to jump to the main help index page. For example, assume that miracletext is your application ID and you choose miracletext.xml as the help filename.
Here are a few gnome_help_display() examples:
/* show main help page */
gnome_help_display("miracletext", NULL, NULL);
/* show main_window section of the same help page */
gnome_help_display("miracletext", "main_window", NULL);
GNOME has a primitive API for attaching sounds to events. On systems that do not support sound, these functions are harmless.
| Note |
Although you can decorate events with sound in your application, don't expect
|
If you just want to play a sound somewhere in your code, call
gnome_sound_play( sound_file )
where sound_file is the name of a sound file.
It's more likely that you want to attach a sound to an event in your application. Follow these steps:
Install the default sound file for the event in the GNOME sound directory (normally $(PREFIX)/share/sounds ). You should try to put the file in a new directory that matches the name of your application (for example, /opt/gnome/share/sounds/miracletext ).
Create a file called
Call gnome_triggers_do() in your event handlers to play the sound (more on this shortly).
| Note |
Use a .wav file for your sound, not an esoteric format. The user should find the sound comfortable ” not too shrill, not too low, not too loud, not too quiet, and most important, not too long. Five seconds is the extreme maximum, but that can be an eternity when the user repeatedly encounters the sound. |
Here's a sample .soundlist file (encode this in UTF-8):
[__section_info__] description=MiracleText [miracle] file=miracletext/miracle.wav description=Miracle description[de]=Wunder description[es]=Milagro [miracle_big] file=miracletext/miracle_big.wav description=Big Miracle description[de]=Wunderbares Wunder description[es]=Milagro Grande
This file breaks down into several sections, denoted with square brackets ( [] ). In the first section, __section_info__ is a special identifier; this section contains global definitions for the following sections. In the second and third sections, [miracle] and [miracle_big] define specific sound events.
The keys and settings in the sections are as follows:
description describes the sound event. In __section_info__ , this is the program description.
file is the filename relative to $(PREFIX)/share/sounds .
| Note |
You can override a setting for a particular language by placing the language code within square brackets after the key. |
If all files are in the right locations, the user can also change the sound events with the Sound preference tool.
To connect the sounds to events, call
gnome_triggers_do( message , "program", app_id , event_id , NULL)
Here,
message
is an optional additional message,
app_id
is your application identifier, and
event_id
is the event identifier from the
.soundlist
file (
gnome_triggers_do()
does much more than this, so that is why the syntax looks a little
/* event that tells you that a miracle happened */
gnome_triggers_do("A miracle occurred!",
"program", "miracletext", "miracle", NULL);
/* for really big miracles, you don't need to put it in writing */
gnome_triggers_do("", "program", "miracletext", "miracle_big", NULL);
If your GNOME application happens to be a game, you will inevitably run into the problem of how to maintain a high score table. Maintaining a high score table with
When you initialize your main program, call gint gnome_score_init(const char *game_name ) . Set game_name to your application identifier. This function returns 0 upon success and -1 if something goes wrong.
At the end of each game, call gint gnome_score_log(gfloat score , const gchar *score , gboolean descending ) to enter score and score into the high score table. Set descending to TRUE if a lower score is better in your game (a golf game, for example). This function returns the new score's rank in the table (starting at 1), or 0 if an error occurs.
If you need to get the data in a high score table, use gint gnome_score_get_notable(const gchar *game_name , const gchar *level , gchar ***names , gfloat **scores , time_t **score_times ) to fill arrays for the high scores, the player names, and times of the scores for game_name at level . This function returns the number of entries in the high score table. All of the the new information goes into newly allocated arrays; you must free everything inside with g_free() .
| Note |
It may ease your mind to know that you do not normally need to bother with the terrifying gnome_score_get_notable() . You can create a GnomeScores widget to display a high score list (see Section 4.3.12). |