Documentation/Maemo 5 Developer Guide/Using Generic Platform Components/GnomeVFS File System

m (GnomeVFS Example: better link for pango markup)
m (use ambox)
 
(3 intermediate revisions not shown)
Line 1: Line 1:
-
= File System - GnomeVFS =
 
-
 
Maemo includes a powerful file system framework, GnomeVFS. This framework enables applications to use a vast number of different file access protocols without having to know anything about the underlying details. Some examples of the supported protocols are: local file system, HTTP, FTP and OBEX over Bluetooth.
Maemo includes a powerful file system framework, GnomeVFS. This framework enables applications to use a vast number of different file access protocols without having to know anything about the underlying details. Some examples of the supported protocols are: local file system, HTTP, FTP and OBEX over Bluetooth.
Line 8: Line 6:
A good hands-on starting point is taking a look at the GnomeVFS example in maemo-examples package. Detailed API information can be found in the [http://library.gnome.org/devel/gnome-vfs-2.0/stable/ GnomeVFS API reference]
A good hands-on starting point is taking a look at the GnomeVFS example in maemo-examples package. Detailed API information can be found in the [http://library.gnome.org/devel/gnome-vfs-2.0/stable/ GnomeVFS API reference]
-
 
==GnomeVFS Example==
==GnomeVFS Example==
Line 15: Line 12:
* [https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/support-libraries/hildon_helloworld-8.c hildon_helloworld-8.c]
* [https://vcs.maemo.org/svn/maemoexamples/tags/maemo_5.0/support-libraries/hildon_helloworld-8.c hildon_helloworld-8.c]
 +
In Maemo, GnomeVFS also provides some filename case insensitivity support, so that the end users do not have to care about the UNIX filename conventions, which are case-sensitive.
-
In maemo, GnomeVFS also provides some filename case insensitivity support, so that the end users do not have to care about the UNIX filename conventions, which are case-sensitive.
+
The GnomeVFS interface attempts to provide a POSIX-like interface, so that where with POSIX you would use <code>open()</code>, in GnomeVFS you can use <code>gnome_vfs_open()</code>; and instead of <code>write()</code>, you can use <code>gnome_vfs_write()</code>, etc. The GnomeVFS function names are sometimes a bit more verbose, but otherwise they attempt to implement the basic API. Some POSIX functions, such as <code>mmap()</code>, are impossible to implement in the user space, but normally this is not a big problem. Also some functions fail to work over network connections and outside the local filesystem.
-
 
+
-
The GnomeVFS interface attempts to provide a POSIX-like interface, so that where with POSIX you would use open(), in GnomeVFS you can use gnome_vfs_open; and instead of write(), you can use gnome_vfs_write, etc. The GnomeVFS function names are sometimes a bit more verbose, but otherwise they attempt to implement the basic API. Some POSIX functions, such as mmap(), are impossible to implement in the user space, but normally this is not a big problem. Also some functions fail to work over network connections and outside the local filesystem.
+
For a simple example of using the GnomeVFS interface functions, see below.
For a simple example of using the GnomeVFS interface functions, see below.
Line 24: Line 20:
In order to save and load data, at least the following functions are needed:
In order to save and load data, at least the following functions are needed:
-
* '''gnome_vfs_init()'''<nowiki>: initializes the GnomeVFS library. Needs to be done once at an early stage at program startup. </nowiki>
+
* <code>gnome_vfs_init()</code>: initializes the GnomeVFS library. Needs to be done once at an early stage at program startup.
-
* '''gnome_vfs_shutdown()'''<nowiki>: frees up resources inside the library and closes it down. </nowiki>
+
* <code>gnome_vfs_shutdown()</code>: frees up resources inside the library and closes it down.
-
* '''gnome_vfs_open()'''<nowiki>: opens the given URI (explained below) and returns a file handle for that if successful. </nowiki>
+
* <code>gnome_vfs_open()</code>: opens the given URI (explained below) and returns a file handle for that if successful.
-
* '''gnome_vfs_get_file_info()'''<nowiki>: get information about a file (similar to, but with broader scope than fstat). </nowiki>
+
* <code>gnome_vfs_get_file_info()</code>: get information about a file (similar to, but with broader scope than fstat).
-
* '''gnome_vfs_read()'''<nowiki>: read data from an opened file. </nowiki>
+
* <code>gnome_vfs_read()</code>: read data from an opened file.
-
* '''gnome_vfs_write()'''<nowiki>: write data into an opened file. </nowiki>
+
* <code>gnome_vfs_write()</code>: write data into an opened file.
-
In order to differentiate between different protocols, GnomeVFS uses Uniform Resource 2 syntax when accessing resources. For example in '''file:///tmp/somefile.txt''', the file:// is the protocol to use, and the rest is the location within that protocol space for the resource or file to manipulate. Protocols can be stacked inside a single URI, and the URI also supports username and password combinations (these are best demonstrated in the GnomeVFS API documentation).
+
In order to differentiate between different protocols, GnomeVFS uses Uniform Resource 2 syntax when accessing resources. For example in <code>file:///tmp/somefile.txt</code>, the <code>file://</code> is the protocol to use, and the rest is the location within that protocol space for the resource or file to manipulate. Protocols can be stacked inside a single URI, and the URI also supports username and password combinations (these are best demonstrated in the GnomeVFS API documentation).
The following simple demonstration will be using local files.
The following simple demonstration will be using local files.
Line 38: Line 34:
* Implement the "Open" command by using GnomeVFS with full error checking.
* Implement the "Open" command by using GnomeVFS with full error checking.
-
* The memory will be allocated and freed with g_malloc0() and g_free(), when loading the contents of the file that the user has selected.
+
* The memory will be allocated and freed with <code>g_malloc0()</code> and <code>g_free()</code>, when loading the contents of the file that the user has selected.
-
* Data loaded through "Open" will replace the text in the GtkLabel that is in the center area of the HildonWindow. The label will be switched to support  [http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html Pango simple text markup], which looks a lot like simple HTML.
+
* Data loaded through "Open" will replace the text in the <code>GtkLabel</code> that is in the center area of the <code>HildonWindow</code>. The label will be switched to support  [http://library.gnome.org/devel/pango/stable/PangoMarkupFormat.html Pango simple text markup], which looks a lot like simple HTML.
-
* Notification about loading success and failures will be communicated to the user by using a widget called HildonBanner, which will float a small notification dialog (with an optional icon) in the top-right corner for a while, without blocking the application.
+
* Notification about loading success and failures will be communicated to the user by using a widget called <code>HildonBanner</code>, which will float a small notification dialog (with an optional icon) in the top-right corner for a while, without blocking the application.
* N.B. Saving into a file is not implemented in this code, as it is a lab exercise (and it is simpler than opening).
* N.B. Saving into a file is not implemented in this code, as it is a lab exercise (and it is simpler than opening).
-
* File loading failures can be simulated by attempting to load an empty file. Since empty files are not wanted, the code will turn this into an error as well. If no empty file is available, one can easily be created with the touch command (under '''MyDocs''', so that the open dialog can find it). It is also possible to attempt to load a file larger than 100 KiB, since the code limits the file size (artificially), and will refuse to load large files.
+
* File loading failures can be simulated by attempting to load an empty file. Since empty files are not wanted, the code will turn this into an error as well. If no empty file is available, one can easily be created with the touch command (under <code>MyDocs</code>, so that the open dialog can find it). It is also possible to attempt to load a file larger than 100 KiB, since the code limits the file size (artificially), and will refuse to load large files.
-
* The goto statement should normally be avoided. Team coding guidelines should be checked to see, whether this is an allowed practice. Note how it is used in this example to cut down the possibility of leaked resources (and typing). Another option for this would be using variable finalizers, but not many people know how to use them, or even that they exist. They are gcc extensions into the C language, and you can find more about them by reading gcc info pages (look for variable attributes).
+
* The <code>goto</code> statement should normally be avoided. Team coding guidelines should be checked to see, whether this is an allowed practice. Note how it is used in this example to cut down the possibility of leaked resources (and typing). Another option for this would be using variable finalizers, but not many people know how to use them, or even that they exist. They are gcc extensions into the C language, and you can find more about them by reading gcc info pages (look for variable attributes).
* Simple GnomeVFS functions are used here. They are all synchronous, which means that if loading the file takes a long time, the application will remain unresponsive during that time. For small files residing in local storage, this is a risk that is taken knowingly. Synchronous API should not be used when loading files over network due to the greater uncertainty.
* Simple GnomeVFS functions are used here. They are all synchronous, which means that if loading the file takes a long time, the application will remain unresponsive during that time. For small files residing in local storage, this is a risk that is taken knowingly. Synchronous API should not be used when loading files over network due to the greater uncertainty.
* I/O in most cases will be slightly slower than using a controlled approach with POSIX I/O API (controlled meaning that one should know what to use and how). This is a price that has to be paid in order to enable easy switching to other protocols later.
* I/O in most cases will be slightly slower than using a controlled approach with POSIX I/O API (controlled meaning that one should know what to use and how). This is a price that has to be paid in order to enable easy switching to other protocols later.
-
N.B. Since GnomeVFS is a separate library from GLib, you will have to add the flags and library options that it requires. The pkg-config package name for the library is <code>gnome-vfs-2.0</code>.
+
{ambox|text=Since GnomeVFS is a separate library from GLib, you will have to add the flags and library options that it requires. The pkg-config package name for the library is <code>gnome-vfs-2.0</code>.}}
 +
 
 +
<source lang="c">
 +
/**
 +
* hildon_helloworld-8.c
 +
*
 +
* This maemo code example is licensed under a MIT-style license,
 +
* that can be found in the file called "License" in the same
 +
* directory as this file.
 +
* Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
 +
*
 +
* We add file loading support using GnomeVFS. Saving files using
 +
* GnomeVFS is left as an exercise. We also add a small notification
 +
* widget (HildonBanner).
 +
*
 +
* Look for lines with "NEW" or "MODIFIED" in them.
 +
*/
 +
 
 +
#include <stdlib.h>
 +
#include <hildon/hildon-program.h>
 +
#include <hildon/hildon-color-button.h>
 +
#include <hildon/hildon-find-toolbar.h>
 +
#include <hildon/hildon-file-chooser-dialog.h>
 +
/* A small notification window widget (NEW). */
 +
#include <hildon/hildon-banner.h>
 +
/* Pull in the GnomeVFS headers (NEW). */
 +
#include <libgnomevfs/gnome-vfs.h>
 +
 
 +
/* Declare the two slant styles. */
 +
enum {
 +
  STYLE_SLANT_NORMAL = 0,
 +
  STYLE_SLANT_ITALIC
 +
};
 +
 
 +
/**
 +
* The application state.
 +
*/
 +
typedef struct {
 +
  gboolean styleUseUnderline;
 +
  gboolean styleSlant;
 +
 
 +
  GdkColor currentColor;
 +
 
 +
  /* Pointer to the label so that we can modify its contents when a
 +
    file is loaded by the user (NEW). */
 +
  GtkWidget* textLabel;
 +
 
 +
  gboolean fullScreen;
 +
 
 +
  GtkWidget* findToolbar;
 +
  GtkWidget* mainToolbar;
 +
  gboolean findToolbarIsVisible;
 +
  gboolean mainToolbarIsVisible;
 +
 
 +
  HildonProgram* program;
 +
  HildonWindow* window;
 +
} ApplicationState;
 +
 
 +
  /*... Listing cut for brevity ...*/
 +
 
 +
/**
 +
* Utility function to print a GnomeVFS I/O related error message to
 +
* standard error (not seen by the user in graphical mode) (NEW).
 +
*/
 +
static void dbgFileError(GnomeVFSResult errCode, const gchar* uri) {
 +
  g_printerr("Error while accessing '%s': %s\n", uri,
 +
            gnome_vfs_result_to_string(errCode));
 +
}
 +
 
 +
/**
 +
* MODIFIED (A LOT)
 +
*
 +
* We read in the file selected by the user if possible and set the
 +
* contents of the file as the new Label content.
 +
*
 +
* If reading the file fails, the label will be left unchanged.
 +
*/
 +
static void cbActionOpen(GtkWidget* widget, ApplicationState* app) {
 +
 
 +
  gchar* filename = NULL;
 +
  /* We need to use URIs with GnomeVFS, so declare one here. */
 +
  gchar* uri = NULL;
 +
 
 +
  g_assert(app != NULL);
 +
  /* Bad things will happen if these two widgets don't exist. */
 +
  g_assert(GTK_IS_LABEL(app->textLabel));
 +
  g_assert(GTK_IS_WINDOW(app->window));
 +
 
 +
  g_print("cbActionOpen invoked\n");
 +
 
 +
  /* Ask the user to select a file to open. */
 +
  filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_OPEN);
 +
  if (filename) {
 +
    /* This will point to loaded data buffer. */
 +
    gchar* buffer = NULL;
 +
    /* Pointer to a structure describing an open GnomeVFS "file". */
 +
    GnomeVFSHandle* fileHandle = NULL;
 +
    /* Structure to hold information about a "file", initialized to
 +
      zero. */
 +
    GnomeVFSFileInfo fileInfo = {};
 +
    /* Result code from the GnomeVFS operations. */
 +
    GnomeVFSResult result;
 +
    /* Size of the file (in bytes) that we'll read in. */
 +
    GnomeVFSFileSize fileSize = 0;
 +
    /* Number of bytes that were read in successfully. */
 +
    GnomeVFSFileSize readCount = 0;
 +
 
 +
    g_print("  you chose to load file '%s'\n", filename);
 +
 
 +
    /* Convert the filename into an GnomeVFS URI. */
 +
    uri = gnome_vfs_get_uri_from_local_path(filename);
 +
    /* We don't need the original filename anymore. */
 +
    g_free(filename);
 +
    filename = NULL;
 +
    /* Should not happen since we got a filename before. */
 +
    g_assert(uri != NULL);
 +
    /* Attempt to get file size first. We need to get information
 +
      about the file and aren't interested in other than the very
 +
      basic information, so we'll use the INFO_DEFAULT setting. */
 +
    result = gnome_vfs_get_file_info(uri, &fileInfo,
 +
                                    GNOME_VFS_FILE_INFO_DEFAULT);
 +
    if (result != GNOME_VFS_OK) {
 +
      /* There was a failure. Print a debug error message and break
 +
        out into error handling. */
 +
      dbgFileError(result, uri);
 +
      goto error;
 +
    }
 +
 
 +
    /* We got the information (maybe). Let's check whether it
 +
      contains the data that we need. */
 +
    if (fileInfo.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
 +
      /* Yes, we got the file size. */
 +
      fileSize = fileInfo.size;
 +
    } else {
 +
      g_printerr("Couldn't get the size of file!\n");
 +
      goto error;
 +
    }
 +
 
 +
    /* By now we have the file size to read in. Check for some limits
 +
      first. */
 +
    if (fileSize > 1024*100) {
 +
      g_printerr("Loading over 100KiB files is not supported!\n");
 +
      goto error;
 +
    }
 +
    /* Refuse to load empty files. */
 +
    if (fileSize == 0) {
 +
      g_printerr("Refusing to load an empty file\n");
 +
      goto error;
 +
    }
 +
 
 +
    /* Allocate memory for the contents and fill it with zeroes.
 +
      NOTE:
 +
        We leave space for the terminating zero so that we can pass
 +
        this buffer as gchar to string functions and it is
 +
        guaranteed to be terminated, even if the file doesn't end
 +
        with binary zero (odds of that happening are small). */
 +
    buffer = g_malloc0(fileSize+1);
 +
    if (buffer == NULL) {
 +
      g_printerr("Failed to allocate %u bytes for buffer\n",
 +
                (guint)fileSize);
 +
      goto error;
 +
    }
 +
 
 +
    /* Open the file.
 +
 
 +
      Parameters:
 +
      - A pointer to the location where to store the address of the
 +
        new GnomeVFS file handle (created internally in open).
 +
      - uri: What to open (needs to be GnomeVFS URI).
 +
      - open-flags: Flags that tell what we plan to use the handle
 +
        for. This will affect how permissions are checked by the
 +
        Linux kernel. */
 +
 
 +
    result = gnome_vfs_open(&fileHandle, uri, GNOME_VFS_OPEN_READ);
 +
    if (result != GNOME_VFS_OK) {
 +
      dbgFileError(result, uri);
 +
      goto error;
 +
    }
 +
    /* File opened succesfully, read its contents in. */
 +
    result = gnome_vfs_read(fileHandle, buffer, fileSize,
 +
                            &readCount);
 +
    if (result != GNOME_VFS_OK) {
 +
      dbgFileError(result, uri);
 +
      goto error;
 +
    }
 +
    /* Verify that we got the amount of data that we requested.
 +
      NOTE:
 +
        With URIs it won't be an error to get less bytes than you
 +
        requested. Getting zero bytes will however signify an
 +
        End-of-File condition. */
 +
    if (fileSize != readCount) {
 +
      g_printerr("Failed to load the requested amount\n");
 +
      /* We could also attempt to read the missing data until we have
 +
        filled our buffer, but for simplicity, we'll flag this
 +
        condition as an error. */
 +
      goto error;
 +
    }
 +
 
 +
    /* Whew, if we got this far, it means that we actually managed to
 +
      load the file into memory. Let's set the buffer contents as
 +
      the new label now. */
 +
    gtk_label_set_markup(GTK_LABEL(app->textLabel), buffer);
 +
 
 +
    /* That's it! Display a message of great joy. For this we'll use
 +
      a dialog (non-modal) designed for displaying short
 +
      informational messages. It will linger around on the screen
 +
      for a while and then disappear (in parallel to our program
 +
      continuing). */
 +
    hildon_banner_show_information(GTK_WIDGET(app->window),
 +
      NULL, /* Use the default icon (info). */
 +
      "File loaded successfully");
 +
 
 +
    /* Jump to the resource releasing phase. */
 +
    goto release;
 +
 
 +
  error:
 +
    /* Display a failure message with a stock icon.
 +
      Please see http://maemo.org/api_refs/4.0/gtk/gtk-Stock-Items.html
 +
      for a full listing of stock items. */
 +
    hildon_banner_show_information(GTK_WIDGET(app->window),
 +
      GTK_STOCK_DIALOG_ERROR, /* Use the stock error icon. */
 +
      "Failed to load the file");
 +
 
 +
  release:
 +
    /* Close and free all resources that were allocated. */
 +
    if (fileHandle) gnome_vfs_close(fileHandle);
 +
    if (filename) g_free(filename);
 +
    if (uri) g_free(uri);
 +
    if (buffer) g_free(buffer);
 +
    /* Zero them all out to prevent stack-reuse-bugs. */
 +
    fileHandle = NULL;
 +
    filename = NULL;
 +
    uri = NULL;
 +
    buffer = NULL;
 +
 
 +
    return;
 +
  } else {
 +
    g_print("  you didn't choose any file to open\n");
 +
  }
 +
}
 +
 
 +
/**
 +
* MODIFIED (kind of)
 +
*
 +
* Function to save the contents of the label (although it doesn't
 +
* actually save the contents, on purpose). Use gtk_label_get_label
 +
* to get a gchar pointer into the application label contents
 +
* (including current markup), then use gnome_vfs_create and
 +
* gnome_vfs_write to create the file (left as an exercise).
 +
*/
 +
static void cbActionSave(GtkWidget* widget, ApplicationState* app) {
 +
  gchar* filename = NULL;
 +
 
 +
  g_assert(app != NULL);
 +
 
 +
  g_print("cbActionSave invoked\n");
 +
 
 +
  filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_SAVE);
 +
  if (filename) {
 +
    g_print("  you chose to save into '%s'\n", filename);
 +
    /* Process saving .. */
 +
 
 +
    g_free(filename);
 +
    filename = NULL;
 +
  } else {
 +
    g_print("  you didn't choose a filename to save to\n");
 +
  }
 +
}
 +
 
 +
  /*... Listing cut for brevity ...*/
 +
 
 +
/**
 +
* MODIFIED
 +
*
 +
* Add support for GnomeVFS (it needs to be initialized before use)
 +
* and add support for the Pango markup feature of the GtkLabel
 +
* widget.
 +
*/
 +
int main(int argc, char** argv) {
 +
 
 +
  ApplicationState aState = {};
 +
 
 +
  GtkWidget* label = NULL;
 +
  GtkWidget* vbox = NULL;
 +
  GtkWidget* mainToolbar = NULL;
 +
  GtkWidget* findToolbar = NULL;
 +
 
 +
  /* Initialize the GnomeVFS (NEW). */
 +
  if(!gnome_vfs_init()) {
 +
    g_error("Failed to initialize GnomeVFS-libraries, exiting\n");
 +
  }
 +
 
 +
  /* Initialize the GTK+ */
 +
  gtk_init(&argc, &argv);
 +
 
 +
  /* Setup the HildonProgram, HildonWindow and application name. */
 +
  aState.program = HILDON_PROGRAM(hildon_program_get_instance());
 +
  g_set_application_name("Hello Hildon!");
 +
  aState.window = HILDON_WINDOW(hildon_window_new());
 +
  hildon_program_add_window(aState.program,
 +
                            HILDON_WINDOW(aState.window));
 +
 
 +
  /* Create the label widget, with Pango marked up content (NEW). */
 +
  label = gtk_label_new("<b>Hello</b> <i>Hildon</i> (with Hildon"
 +
                        "<sub>search</sub> <u>and</u> GnomeVFS "
 +
                        "and other tricks<sup>tm</sup>)!");
 +
 
 +
  /* Allow lines to wrap (NEW). */
 +
  gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
 +
 
 +
  /* Tell the GtkLabel widget to support the Pango markup (NEW). */
 +
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
 +
 
 +
  /* Store the widget pointer into the application state so that the
 +
    contents can be replaced when a file will be loaded (NEW). */
 +
  aState.textLabel = label;
 +
 
 +
  buildMenu(&aState);
 +
 
 +
  vbox = gtk_vbox_new(FALSE, 0);
 +
  gtk_container_add(GTK_CONTAINER(aState.window), vbox);
 +
  gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 +
 
 +
  mainToolbar = buildToolbar(&aState);
 +
  findToolbar = buildFindToolbar(&aState);
 +
 
 +
  aState.mainToolbar = mainToolbar;
 +
  aState.findToolbar = findToolbar;
 +
 
 +
  /* Connect the termination signals. */
 +
  g_signal_connect(G_OBJECT(aState.window), "delete-event",
 +
                  G_CALLBACK(cbEventDelete), &aState);
 +
  g_signal_connect(G_OBJECT(aState.window), "destroy",
 +
                  G_CALLBACK(cbActionTopDestroy), &aState);
 +
 
 +
  /* Show all widgets that are contained by the Window. */
 +
  gtk_widget_show_all(GTK_WIDGET(aState.window));
 +
 
 +
  /* Add the toolbars to the Hildon Window. */
 +
  hildon_window_add_toolbar(HILDON_WINDOW(aState.window),
 +
                            GTK_TOOLBAR(mainToolbar));
 +
  hildon_window_add_toolbar(HILDON_WINDOW(aState.window),
 +
                            GTK_TOOLBAR(findToolbar));
-
<tt><span>''<span><font color="#9A1900">/**</font></span>''</span>
+
  /* Register a callback to handle key presses. */
-
<span>''<span><font color="#9A1900"> * hildon_helloworld-8.c</font></span>''</span>
+
  g_signal_connect(G_OBJECT(aState.window), "key_press_event",
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
                  G_CALLBACK(cbKeyPressed), &aState);
-
<span>''<span><font color="#9A1900"> * This maemo code example is licensed under a MIT-style license,</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * that can be found in the file called "License" in the same</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * directory as this file.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * We add file loading support using GnomeVFS. Saving files using</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * GnomeVFS is left as an exercise. We also add a small notification</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * widget (HildonBanner).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Look for lines with "NEW" or "MODIFIED" in them.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;stdlib.h&gt;</font></span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-program.h&gt;</font></span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-color-button.h&gt;</font></span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-find-toolbar.h&gt;</font></span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-file-chooser-dialog.h&gt;</font></span>
+
-
<span>''<span><font color="#9A1900">/* A small notification window widget (NEW). */</font></span>''</span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;hildon/hildon-banner.h&gt;</font></span>
+
-
<span>''<span><font color="#9A1900">/* Pull in the GnomeVFS headers (NEW). */</font></span>''</span>
+
-
<span>'''<span><font color="#000080"><nowiki>#include</nowiki></font></span>'''</span> <span><font color="#FF0000">&lt;libgnomevfs/gnome-vfs.h&gt;</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/* Declare the two slant styles. */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">enum</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
  STYLE_SLANT_NORMAL <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000">,</font></span>
+
-
  STYLE_SLANT_ITALIC
+
-
<span><font color="#FF0000">}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * The application state.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">typedef</font></span>'''</span> <span>'''<span><font color="#0000FF">struct</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
  gboolean styleUseUnderline<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gboolean styleSlant<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  GdkColor currentColor<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Pointer to the label so that we can modify its contents when a</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    file is loaded by the user (NEW). */</font></span>''</span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> textLabel<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  gboolean fullScreen<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> findToolbar<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> mainToolbar<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gboolean findToolbarIsVisible<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  gboolean mainToolbarIsVisible<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  HildonProgram<span><font color="#990000"><nowiki>*</nowiki></font></span> program<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  HildonWindow<span><font color="#990000"><nowiki>*</nowiki></font></span> window<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span> ApplicationState<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Utility function to print a GnomeVFS I/O related error message to</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * standard error (not seen by the user in graphical mode) (NEW).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">dbgFileError</font></span>'''</span><span><font color="#990000">(</font></span>GnomeVFSResult errCode<span><font color="#990000">,</font></span> <span>'''<span><font color="#0000FF">const</font></span>'''</span> gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> uri<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
  <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Error while accessing '%s': %s</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> uri<span><font color="#990000">,</font></span>
+
-
              <span>'''<span><font color="#000000">gnome_vfs_result_to_string</font></span>'''</span><span><font color="#990000">(</font></span>errCode<span><font color="#990000">));</font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * MODIFIED (A LOT)</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * We read in the file selected by the user if possible and set the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * contents of the file as the new Label content.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * If reading the file fails, the label will be left unchanged.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">cbActionOpen</font></span>'''</span><span><font color="#990000">(</font></span>GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> widget<span><font color="#990000">,</font></span> ApplicationState<span><font color="#990000"><nowiki>*</nowiki></font></span> app<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> filename <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span>''<span><font color="#9A1900">/* We need to use URIs with GnomeVFS, so declare one here. */</font></span>''</span>
+
-
  gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> uri <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>app <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
  <span>''<span><font color="#9A1900">/* Bad things will happen if these two widgets don't exist. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_IS_LABEL</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">-&gt;</font></span>textLabel<span><font color="#990000">));</font></span>
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_IS_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">-&gt;</font></span>window<span><font color="#990000">));</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"cbActionOpen invoked</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Ask the user to select a file to open. */</font></span>''</span>
+
-
  filename <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">runFileChooser</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">,</font></span> GTK_FILE_CHOOSER_ACTION_OPEN<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>filename<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>''<span><font color="#9A1900">/* This will point to loaded data buffer. */</font></span>''</span>
+
-
    gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> buffer <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Pointer to a structure describing an open GnomeVFS "file". */</font></span>''</span>
+
-
    GnomeVFSHandle<span><font color="#990000"><nowiki>*</nowiki></font></span> fileHandle <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Structure to hold information about a "file", initialized to</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      zero. */</font></span>''</span>
+
-
    GnomeVFSFileInfo fileInfo <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Result code from the GnomeVFS operations. */</font></span>''</span>
+
-
    GnomeVFSResult result<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Size of the file (in bytes) that we'll read in. */</font></span>''</span>
+
-
    GnomeVFSFileSize fileSize <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Number of bytes that were read in successfully. */</font></span>''</span>
+
-
    GnomeVFSFileSize readCount <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"  you chose to load file '%s'</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> filename<span><font color="#990000">);</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Convert the filename into an GnomeVFS URI. */</font></span>''</span>
+
-
    uri <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gnome_vfs_get_uri_from_local_path</font></span>'''</span><span><font color="#990000">(</font></span>filename<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* We don't need the original filename anymore. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>filename<span><font color="#990000">);</font></span>
+
-
    filename <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Should not happen since we got a filename before. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>uri <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Attempt to get file size first. We need to get information</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      about the file and aren't interested in other than the very</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      basic information, so we'll use the INFO_DEFAULT setting. */</font></span>''</span>
+
-
    result <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gnome_vfs_get_file_info</font></span>'''</span><span><font color="#990000">(</font></span>uri<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>fileInfo<span><font color="#990000">,</font></span>
+
-
                                      GNOME_VFS_FILE_INFO_DEFAULT<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>result <span><font color="#990000"><nowiki>!=</nowiki></font></span> GNOME_VFS_OK<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>''<span><font color="#9A1900">/* There was a failure. Print a debug error message and break</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        out into error handling. */</font></span>''</span>
+
-
      <span>'''<span><font color="#000000">dbgFileError</font></span>'''</span><span><font color="#990000">(</font></span>result<span><font color="#990000">,</font></span> uri<span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* We got the information (maybe). Let's check whether it</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      contains the data that we need. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>fileInfo<span><font color="#990000">.</font></span>valid_fields <span><font color="#990000">&amp;</font></span> GNOME_VFS_FILE_INFO_FIELDS_SIZE<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>''<span><font color="#9A1900">/* Yes, we got the file size. */</font></span>''</span>
+
-
      fileSize <span><font color="#990000"><nowiki>=</nowiki></font></span> fileInfo<span><font color="#990000">.</font></span>size<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Couldn't get the size of file!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* By now we have the file size to read in. Check for some limits</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      first. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>fileSize <span><font color="#990000">&gt;</font></span> <span><font color="#993399">1024</font></span><span><font color="#990000"><nowiki>*</nowiki></font></span><span><font color="#993399">100</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Loading over 100KiB files is not supported!</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Refuse to load empty files. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>fileSize <span><font color="#990000"><nowiki>==</nowiki></font></span> <span><font color="#993399">0</font></span><span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Refusing to load an empty file</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Allocate memory for the contents and fill it with zeroes.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      NOTE:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        We leave space for the terminating zero so that we can pass</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        this buffer as gchar to string functions and it is</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        guaranteed to be terminated, even if the file doesn't end</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        with binary zero (odds of that happening are small). */</font></span>''</span>
+
-
    buffer <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">g_malloc0</font></span>'''</span><span><font color="#990000">(</font></span>fileSize<span><font color="#990000">+</font></span><span><font color="#993399">1</font></span><span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>buffer <span><font color="#990000"><nowiki>==</nowiki></font></span> NULL<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to allocate %u bytes for buffer</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span>
+
-
                  <span><font color="#990000">(</font></span>guint<span><font color="#990000">)</font></span>fileSize<span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Open the file.</font></span>''</span>
+
-
+
-
<span>''<span><font color="#9A1900">      Parameters:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      - A pointer to the location where to store the address of the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        new GnomeVFS file handle (created internally in open).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      - uri: What to open (needs to be GnomeVFS URI).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      - open-flags: Flags that tell what we plan to use the handle</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        for. This will affect how permissions are checked by the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        Linux kernel. */</font></span>''</span>
+
-
+
-
    result <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gnome_vfs_open</font></span>'''</span><span><font color="#990000">(&amp;</font></span>fileHandle<span><font color="#990000">,</font></span> uri<span><font color="#990000">,</font></span> GNOME_VFS_OPEN_READ<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>result <span><font color="#990000"><nowiki>!=</nowiki></font></span> GNOME_VFS_OK<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">dbgFileError</font></span>'''</span><span><font color="#990000">(</font></span>result<span><font color="#990000">,</font></span> uri<span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
    <span>''<span><font color="#9A1900">/* File opened succesfully, read its contents in. */</font></span>''</span>
+
-
    result <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gnome_vfs_read</font></span>'''</span><span><font color="#990000">(</font></span>fileHandle<span><font color="#990000">,</font></span> buffer<span><font color="#990000">,</font></span> fileSize<span><font color="#990000">,</font></span>
+
-
                            <span><font color="#990000">&amp;</font></span>readCount<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>result <span><font color="#990000"><nowiki>!=</nowiki></font></span> GNOME_VFS_OK<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">dbgFileError</font></span>'''</span><span><font color="#990000">(</font></span>result<span><font color="#990000">,</font></span> uri<span><font color="#990000">);</font></span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Verify that we got the amount of data that we requested.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      NOTE:</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        With URIs it won't be an error to get less bytes than you</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        requested. Getting zero bytes will however signify an</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        End-of-File condition. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>fileSize <span><font color="#990000"><nowiki>!=</nowiki></font></span> readCount<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
      <span>'''<span><font color="#000000">g_printerr</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to load the requested amount</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
      <span>''<span><font color="#9A1900">/* We could also attempt to read the missing data until we have</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        filled our buffer, but for simplicity, we'll flag this</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">        condition as an error. */</font></span>''</span>
+
-
      <span>'''<span><font color="#0000FF">goto</font></span>'''</span> error<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    <span><font color="#FF0000">}</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Whew, if we got this far, it means that we actually managed to</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      load the file into memory. Let's set the buffer contents as</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      the new label now. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">gtk_label_set_markup</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_LABEL</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">-&gt;</font></span>textLabel<span><font color="#990000">),</font></span> buffer<span><font color="#990000">);</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* That's it! Display a message of great joy. For this we'll use</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      a dialog (non-modal) designed for displaying short</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      informational messages. It will linger around on the screen</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      for a while and then disappear (in parallel to our program</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      continuing). */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">hildon_banner_show_information</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WIDGET</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">-&gt;</font></span>window<span><font color="#990000">),</font></span>
+
-
      NULL<span><font color="#990000">,</font></span> <span>''<span><font color="#9A1900">/* Use the default icon (info). */</font></span>''</span>
+
-
      <span><font color="#FF0000">"File loaded successfully"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
    <span>''<span><font color="#9A1900">/* Jump to the resource releasing phase. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">goto</font></span>'''</span> release<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  error<span><font color="#990000"><nowiki>:</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Display a failure message with a stock icon.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">      Please see </font></span>''</span><span><u><span><font color="#0000FF">http://maemo.org/api_refs/4.0/gtk/gtk-Stock-Items.html</font></span></u></span>
+
-
<span>''<span><font color="#9A1900">      for a full listing of stock items. */</font></span>''</span>
+
-
    <span>'''<span><font color="#000000">hildon_banner_show_information</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WIDGET</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">-&gt;</font></span>window<span><font color="#990000">),</font></span>
+
-
      GTK_STOCK_DIALOG_ERROR<span><font color="#990000">,</font></span> <span>''<span><font color="#9A1900">/* Use the stock error icon. */</font></span>''</span>
+
-
      <span><font color="#FF0000">"Failed to load the file"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  release<span><font color="#990000"><nowiki>:</nowiki></font></span>
+
-
    <span>''<span><font color="#9A1900">/* Close and free all resources that were allocated. */</font></span>''</span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>fileHandle<span><font color="#990000">)</font></span> <span>'''<span><font color="#000000">gnome_vfs_close</font></span>'''</span><span><font color="#990000">(</font></span>fileHandle<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>filename<span><font color="#990000">)</font></span> <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>filename<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>uri<span><font color="#990000">)</font></span> <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>uri<span><font color="#990000">);</font></span>
+
-
    <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>buffer<span><font color="#990000">)</font></span> <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>buffer<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Zero them all out to prevent stack-reuse-bugs. */</font></span>''</span>
+
-
    fileHandle <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    filename <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    uri <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
    buffer <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
    <span>'''<span><font color="#0000FF">return</font></span>'''</span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"  you didn't choose any file to open</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * MODIFIED (kind of)</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Function to save the contents of the label (although it doesn't</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * actually save the contents, on purpose). Use gtk_label_get_label</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * to get a gchar pointer into the application label contents</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * (including current markup), then use gnome_vfs_create and</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * gnome_vfs_write to create the file (left as an exercise).</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span>'''<span><font color="#0000FF">static</font></span>'''</span> <span><font color="#009900">void</font></span> <span>'''<span><font color="#000000">cbActionSave</font></span>'''</span><span><font color="#990000">(</font></span>GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> widget<span><font color="#990000">,</font></span> ApplicationState<span><font color="#990000"><nowiki>*</nowiki></font></span> app<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
  gchar<span><font color="#990000"><nowiki>*</nowiki></font></span> filename <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_assert</font></span>'''</span><span><font color="#990000">(</font></span>app <span><font color="#990000"><nowiki>!=</nowiki></font></span> NULL<span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"cbActionSave invoked</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  filename <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">runFileChooser</font></span>'''</span><span><font color="#990000">(</font></span>app<span><font color="#990000">,</font></span> GTK_FILE_CHOOSER_ACTION_SAVE<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span> <span><font color="#990000">(</font></span>filename<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"  you chose to save into '%s'</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">,</font></span> filename<span><font color="#990000">);</font></span>
+
-
    <span>''<span><font color="#9A1900">/* Process saving .. */</font></span>''</span>
+
-
+
-
    <span>'''<span><font color="#000000">g_free</font></span>'''</span><span><font color="#990000">(</font></span>filename<span><font color="#990000">);</font></span>
+
-
    filename <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  <span><font color="#FF0000">}</font></span> <span>'''<span><font color="#0000FF">else</font></span>'''</span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"  you didn't choose a filename to save to</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/*... Listing cut for brevity ...*/</font></span>''</span>
+
-
+
-
<span>''<span><font color="#9A1900">/**</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * MODIFIED</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> *</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * Add support for GnomeVFS (it needs to be initialized before use)</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * and add support for the Pango markup feature of the GtkLabel</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> * widget.</font></span>''</span>
+
-
<span>''<span><font color="#9A1900"> */</font></span>''</span>
+
-
<span><font color="#009900">int</font></span> <span>'''<span><font color="#000000">main</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#009900">int</font></span> argc<span><font color="#990000">,</font></span> <span><font color="#009900">char</font></span><span><font color="#990000"><nowiki>**</nowiki></font></span> argv<span><font color="#990000">)</font></span> <span><font color="#FF0000">{</font></span>
+
-
+
-
  ApplicationState aState <span><font color="#990000"><nowiki>=</nowiki></font></span> <span><font color="#FF0000">{}</font></span><span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> label <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> vbox <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> mainToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  GtkWidget<span><font color="#990000"><nowiki>*</nowiki></font></span> findToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> NULL<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Initialize the GnomeVFS (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#0000FF">if</font></span>'''</span><span><font color="#990000">(!</font></span><span>'''<span><font color="#000000">gnome_vfs_init</font></span>'''</span><span><font color="#990000">())</font></span> <span><font color="#FF0000">{</font></span>
+
-
    <span>'''<span><font color="#000000">g_error</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Failed to initialize GnomeVFS-libraries, exiting</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span><font color="#FF0000">}</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Initialize the GTK+ */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">gtk_init</font></span>'''</span><span><font color="#990000">(&amp;</font></span>argc<span><font color="#990000">,</font></span> <span><font color="#990000">&amp;</font></span>argv<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Setup the HildonProgram, HildonWindow and application name. */</font></span>''</span>
+
-
  aState<span><font color="#990000">.</font></span>program <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">HILDON_PROGRAM</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">hildon_program_get_instance</font></span>'''</span><span><font color="#990000">());</font></span>
+
-
  <span>'''<span><font color="#000000">g_set_application_name</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"Hello Hildon!"</font></span><span><font color="#990000">);</font></span>
+
-
  aState<span><font color="#990000">.</font></span>window <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">hildon_window_new</font></span>'''</span><span><font color="#990000">());</font></span>
+
-
  <span>'''<span><font color="#000000">hildon_program_add_window</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>program<span><font color="#990000">,</font></span>
+
-
                            <span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">));</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Create the label widget, with Pango marked up content (NEW). */</font></span>''</span>
+
-
  label <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_label_new</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"&lt;b&gt;Hello&lt;/b&gt; &lt;i&gt;Hildon&lt;/i&gt; (with Hildon"</font></span>
+
-
                        <span><font color="#FF0000">"&lt;sub&gt;search&lt;/sub&gt; &lt;u&gt;and&lt;/u&gt; GnomeVFS "</font></span>
+
-
                        <span><font color="#FF0000">"and other tricks&lt;sup&gt;tm&lt;/sup&gt;)!"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Allow lines to wrap (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">gtk_label_set_line_wrap</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_LABEL</font></span>'''</span><span><font color="#990000">(</font></span>label<span><font color="#990000">),</font></span> TRUE<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Tell the GtkLabel widget to support the Pango markup (NEW). */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">gtk_label_set_use_markup</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_LABEL</font></span>'''</span><span><font color="#990000">(</font></span>label<span><font color="#990000">),</font></span> TRUE<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Store the widget pointer into the application state so that the</font></span>''</span>
+
-
<span>''<span><font color="#9A1900">    contents can be replaced when a file will be loaded (NEW). */</font></span>''</span>
+
-
  aState<span><font color="#990000">.</font></span>textLabel <span><font color="#990000"><nowiki>=</nowiki></font></span> label<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>'''<span><font color="#000000">buildMenu</font></span>'''</span><span><font color="#990000">(&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
+
-
  vbox <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">gtk_vbox_new</font></span>'''</span><span><font color="#990000">(</font></span>FALSE<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">gtk_container_add</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_CONTAINER</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span> vbox<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">gtk_box_pack_end</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_BOX</font></span>'''</span><span><font color="#990000">(</font></span>vbox<span><font color="#990000">),</font></span> label<span><font color="#990000">,</font></span> TRUE<span><font color="#990000">,</font></span> TRUE<span><font color="#990000">,</font></span> <span><font color="#993399">0</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  mainToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">buildToolbar</font></span>'''</span><span><font color="#990000">(&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
  findToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> <span>'''<span><font color="#000000">buildFindToolbar</font></span>'''</span><span><font color="#990000">(&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
+
-
  aState<span><font color="#990000">.</font></span>mainToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> mainToolbar<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
  aState<span><font color="#990000">.</font></span>findToolbar <span><font color="#990000"><nowiki>=</nowiki></font></span> findToolbar<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Connect the termination signals. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span> <span><font color="#FF0000">"delete-event"</font></span><span><font color="#990000">,</font></span>
+
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>cbEventDelete<span><font color="#990000">),</font></span> <span><font color="#990000">&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span> <span><font color="#FF0000">"destroy"</font></span><span><font color="#990000">,</font></span>
+
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>cbActionTopDestroy<span><font color="#990000">),</font></span> <span><font color="#990000">&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Show all widgets that are contained by the Window. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">gtk_widget_show_all</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">GTK_WIDGET</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">));</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Add the toolbars to the Hildon Window. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">hildon_window_add_toolbar</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span>
+
-
                            <span>'''<span><font color="#000000">GTK_TOOLBAR</font></span>'''</span><span><font color="#990000">(</font></span>mainToolbar<span><font color="#990000">));</font></span>
+
-
  <span>'''<span><font color="#000000">hildon_window_add_toolbar</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">HILDON_WINDOW</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span>
+
-
                            <span>'''<span><font color="#000000">GTK_TOOLBAR</font></span>'''</span><span><font color="#990000">(</font></span>findToolbar<span><font color="#990000">));</font></span>
+
-
+
-
  <span>''<span><font color="#9A1900">/* Register a callback to handle key presses. */</font></span>''</span>
+
-
  <span>'''<span><font color="#000000">g_signal_connect</font></span>'''</span><span><font color="#990000">(</font></span><span>'''<span><font color="#000000">G_OBJECT</font></span>'''</span><span><font color="#990000">(</font></span>aState<span><font color="#990000">.</font></span>window<span><font color="#990000">),</font></span> <span><font color="#FF0000">"key_press_event"</font></span><span><font color="#990000">,</font></span>
+
-
                    <span>'''<span><font color="#000000">G_CALLBACK</font></span>'''</span><span><font color="#990000">(</font></span>cbKeyPressed<span><font color="#990000">),</font></span> <span><font color="#990000">&amp;</font></span>aState<span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"main: calling gtk_main</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
  <span>'''<span><font color="#000000">gtk_main</font></span>'''</span><span><font color="#990000">();</font></span>
+
-
  <span>'''<span><font color="#000000">g_print</font></span>'''</span><span><font color="#990000">(</font></span><span><font color="#FF0000">"main: returned from gtk_main and exiting with success</font></span><span><font color="#CC33CC">\n</font></span><span><font color="#FF0000">"</font></span><span><font color="#990000">);</font></span>
+
-
+
-
  <span>'''<span><font color="#0000FF">return</font></span>'''</span> EXIT_SUCCESS<span><font color="#990000"><nowiki>;</nowiki></font></span>
+
-
<span><font color="#FF0000">}</font></span>
+
-
</tt>
+
 +
  g_print("main: calling gtk_main\n");
 +
  gtk_main();
 +
  g_print("main: returned from gtk_main and exiting with success\n");
 +
  return EXIT_SUCCESS;
 +
}
 +
</source>
-
In order to experiment with loading other content, a simple file can be created, containing Pango markup like this: '''echo "&lt;b&gt;Hello world&lt;/b&gt;" &gt; MyDocs/hello.txt''' , and then loading hello.txt.
+
In order to experiment with loading other content, a simple file can be created, containing Pango markup like this:
 +
<source lang="bash">
 +
echo "<b>Hello world</b>" > MyDocs/hello.txt
 +
</source>
 +
and then loading <code>hello.txt</code>.
As can be imagined, these examples have only scratched the surface of GnomeVFS that is quite a rich library, and contains a broad API and a large amount of plug-ins. Many things have been completely avoided, such as directory content iteration, the asynchronous interface, callback signaling on directory content changes etc. Please see [http://library.gnome.org/devel/gnome-vfs-2.0/stable/ GnomeVFS API reference] for more information. The API also contains some mini tutorials on various GnomeVFS topics, so it is well worth the time spent reading. It will also show that GnomeVFS has been overloaded with functions, which are not even file operation related (such as ZeroConf and creating TCP/IP connections etc.).
As can be imagined, these examples have only scratched the surface of GnomeVFS that is quite a rich library, and contains a broad API and a large amount of plug-ins. Many things have been completely avoided, such as directory content iteration, the asynchronous interface, callback signaling on directory content changes etc. Please see [http://library.gnome.org/devel/gnome-vfs-2.0/stable/ GnomeVFS API reference] for more information. The API also contains some mini tutorials on various GnomeVFS topics, so it is well worth the time spent reading. It will also show that GnomeVFS has been overloaded with functions, which are not even file operation related (such as ZeroConf and creating TCP/IP connections etc.).

Latest revision as of 13:16, 12 November 2010

Maemo includes a powerful file system framework, GnomeVFS. This framework enables applications to use a vast number of different file access protocols without having to know anything about the underlying details. Some examples of the supported protocols are: local file system, HTTP, FTP and OBEX over Bluetooth.

In practice, this means that all GnomeVFS file access methods are transparently available for both developer and end user just by using the framework for file operations. The API for file handling is also much more flexible than the standard platform offerings. It features, for example, asynchronous reading and writing, MIME type support and file monitoring.

All user-file access should be done with GnomeVFS in maemo applications, because file access can be remote. In fact, many applications that come with the operating system on the Maemo compatible device do make use of GnomeVFS. Access to files not visible to the user should be done directly for performance reasons.

A good hands-on starting point is taking a look at the GnomeVFS example in maemo-examples package. Detailed API information can be found in the GnomeVFS API reference

[edit] GnomeVFS Example

This section uses the following example:

In Maemo, GnomeVFS also provides some filename case insensitivity support, so that the end users do not have to care about the UNIX filename conventions, which are case-sensitive.

The GnomeVFS interface attempts to provide a POSIX-like interface, so that where with POSIX you would use open(), in GnomeVFS you can use gnome_vfs_open(); and instead of write(), you can use gnome_vfs_write(), etc. The GnomeVFS function names are sometimes a bit more verbose, but otherwise they attempt to implement the basic API. Some POSIX functions, such as mmap(), are impossible to implement in the user space, but normally this is not a big problem. Also some functions fail to work over network connections and outside the local filesystem.

For a simple example of using the GnomeVFS interface functions, see below.

In order to save and load data, at least the following functions are needed:

  • gnome_vfs_init(): initializes the GnomeVFS library. Needs to be done once at an early stage at program startup.
  • gnome_vfs_shutdown(): frees up resources inside the library and closes it down.
  • gnome_vfs_open(): opens the given URI (explained below) and returns a file handle for that if successful.
  • gnome_vfs_get_file_info(): get information about a file (similar to, but with broader scope than fstat).
  • gnome_vfs_read(): read data from an opened file.
  • gnome_vfs_write(): write data into an opened file.

In order to differentiate between different protocols, GnomeVFS uses Uniform Resource 2 syntax when accessing resources. For example in file:///tmp/somefile.txt, the file:// is the protocol to use, and the rest is the location within that protocol space for the resource or file to manipulate. Protocols can be stacked inside a single URI, and the URI also supports username and password combinations (these are best demonstrated in the GnomeVFS API documentation).

The following simple demonstration will be using local files.

A simple application will be extended in the following ways:

  • Implement the "Open" command by using GnomeVFS with full error checking.
  • The memory will be allocated and freed with g_malloc0() and g_free(), when loading the contents of the file that the user has selected.
  • Data loaded through "Open" will replace the text in the GtkLabel that is in the center area of the HildonWindow. The label will be switched to support Pango simple text markup, which looks a lot like simple HTML.
  • Notification about loading success and failures will be communicated to the user by using a widget called HildonBanner, which will float a small notification dialog (with an optional icon) in the top-right corner for a while, without blocking the application.
  • N.B. Saving into a file is not implemented in this code, as it is a lab exercise (and it is simpler than opening).
  • File loading failures can be simulated by attempting to load an empty file. Since empty files are not wanted, the code will turn this into an error as well. If no empty file is available, one can easily be created with the touch command (under MyDocs, so that the open dialog can find it). It is also possible to attempt to load a file larger than 100 KiB, since the code limits the file size (artificially), and will refuse to load large files.
  • The goto statement should normally be avoided. Team coding guidelines should be checked to see, whether this is an allowed practice. Note how it is used in this example to cut down the possibility of leaked resources (and typing). Another option for this would be using variable finalizers, but not many people know how to use them, or even that they exist. They are gcc extensions into the C language, and you can find more about them by reading gcc info pages (look for variable attributes).
  • Simple GnomeVFS functions are used here. They are all synchronous, which means that if loading the file takes a long time, the application will remain unresponsive during that time. For small files residing in local storage, this is a risk that is taken knowingly. Synchronous API should not be used when loading files over network due to the greater uncertainty.
  • I/O in most cases will be slightly slower than using a controlled approach with POSIX I/O API (controlled meaning that one should know what to use and how). This is a price that has to be paid in order to enable easy switching to other protocols later.

{ambox|text=Since GnomeVFS is a separate library from GLib, you will have to add the flags and library options that it requires. The pkg-config package name for the library is gnome-vfs-2.0.}}

/**
 * hildon_helloworld-8.c
 *
 * This maemo code example is licensed under a MIT-style license,
 * that can be found in the file called "License" in the same
 * directory as this file.
 * Copyright (c) 2007-2008 Nokia Corporation. All rights reserved.
 *
 * We add file loading support using GnomeVFS. Saving files using
 * GnomeVFS is left as an exercise. We also add a small notification
 * widget (HildonBanner).
 *
 * Look for lines with "NEW" or "MODIFIED" in them.
 */
 
#include <stdlib.h>
#include <hildon/hildon-program.h>
#include <hildon/hildon-color-button.h>
#include <hildon/hildon-find-toolbar.h>
#include <hildon/hildon-file-chooser-dialog.h>
/* A small notification window widget (NEW). */
#include <hildon/hildon-banner.h>
/* Pull in the GnomeVFS headers (NEW). */
#include <libgnomevfs/gnome-vfs.h>
 
/* Declare the two slant styles. */
enum {
  STYLE_SLANT_NORMAL = 0,
  STYLE_SLANT_ITALIC
};
 
/**
 * The application state.
 */
typedef struct {
  gboolean styleUseUnderline;
  gboolean styleSlant;
 
  GdkColor currentColor;
 
  /* Pointer to the label so that we can modify its contents when a
     file is loaded by the user (NEW). */
  GtkWidget* textLabel;
 
  gboolean fullScreen;
 
  GtkWidget* findToolbar;
  GtkWidget* mainToolbar;
  gboolean findToolbarIsVisible;
  gboolean mainToolbarIsVisible;
 
  HildonProgram* program;
  HildonWindow* window;
} ApplicationState;
 
  /*... Listing cut for brevity ...*/
 
/**
 * Utility function to print a GnomeVFS I/O related error message to
 * standard error (not seen by the user in graphical mode) (NEW).
 */
static void dbgFileError(GnomeVFSResult errCode, const gchar* uri) {
  g_printerr("Error while accessing '%s': %s\n", uri,
             gnome_vfs_result_to_string(errCode));
}
 
/**
 * MODIFIED (A LOT)
 *
 * We read in the file selected by the user if possible and set the
 * contents of the file as the new Label content.
 *
 * If reading the file fails, the label will be left unchanged.
 */
static void cbActionOpen(GtkWidget* widget, ApplicationState* app) {
 
  gchar* filename = NULL;
  /* We need to use URIs with GnomeVFS, so declare one here. */
  gchar* uri = NULL;
 
  g_assert(app != NULL);
  /* Bad things will happen if these two widgets don't exist. */
  g_assert(GTK_IS_LABEL(app->textLabel));
  g_assert(GTK_IS_WINDOW(app->window));
 
  g_print("cbActionOpen invoked\n");
 
  /* Ask the user to select a file to open. */
  filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_OPEN);
  if (filename) {
    /* This will point to loaded data buffer. */
    gchar* buffer = NULL;
    /* Pointer to a structure describing an open GnomeVFS "file". */
    GnomeVFSHandle* fileHandle = NULL;
    /* Structure to hold information about a "file", initialized to
       zero. */
    GnomeVFSFileInfo fileInfo = {};
    /* Result code from the GnomeVFS operations. */
    GnomeVFSResult result;
    /* Size of the file (in bytes) that we'll read in. */
    GnomeVFSFileSize fileSize = 0;
    /* Number of bytes that were read in successfully. */
    GnomeVFSFileSize readCount = 0;
 
    g_print("  you chose to load file '%s'\n", filename);
 
    /* Convert the filename into an GnomeVFS URI. */
    uri = gnome_vfs_get_uri_from_local_path(filename);
    /* We don't need the original filename anymore. */
    g_free(filename);
    filename = NULL;
    /* Should not happen since we got a filename before. */
    g_assert(uri != NULL);
    /* Attempt to get file size first. We need to get information
       about the file and aren't interested in other than the very
       basic information, so we'll use the INFO_DEFAULT setting. */
    result = gnome_vfs_get_file_info(uri, &fileInfo,
                                     GNOME_VFS_FILE_INFO_DEFAULT);
    if (result != GNOME_VFS_OK) {
      /* There was a failure. Print a debug error message and break
         out into error handling. */
      dbgFileError(result, uri);
      goto error;
    }
 
    /* We got the information (maybe). Let's check whether it
       contains the data that we need. */
    if (fileInfo.valid_fields & GNOME_VFS_FILE_INFO_FIELDS_SIZE) {
      /* Yes, we got the file size. */
      fileSize = fileInfo.size;
    } else {
      g_printerr("Couldn't get the size of file!\n");
      goto error;
    }
 
    /* By now we have the file size to read in. Check for some limits
       first. */
    if (fileSize > 1024*100) {
      g_printerr("Loading over 100KiB files is not supported!\n");
      goto error;
    }
    /* Refuse to load empty files. */
    if (fileSize == 0) {
      g_printerr("Refusing to load an empty file\n");
      goto error;
    }
 
    /* Allocate memory for the contents and fill it with zeroes.
       NOTE:
         We leave space for the terminating zero so that we can pass
         this buffer as gchar to string functions and it is
         guaranteed to be terminated, even if the file doesn't end
         with binary zero (odds of that happening are small). */
    buffer = g_malloc0(fileSize+1);
    if (buffer == NULL) {
      g_printerr("Failed to allocate %u bytes for buffer\n",
                 (guint)fileSize);
      goto error;
    }
 
    /* Open the file.
 
       Parameters:
       - A pointer to the location where to store the address of the
         new GnomeVFS file handle (created internally in open).
       - uri: What to open (needs to be GnomeVFS URI).
       - open-flags: Flags that tell what we plan to use the handle
         for. This will affect how permissions are checked by the
         Linux kernel. */
 
    result = gnome_vfs_open(&fileHandle, uri, GNOME_VFS_OPEN_READ);
    if (result != GNOME_VFS_OK) {
      dbgFileError(result, uri);
      goto error;
    }
    /* File opened succesfully, read its contents in. */
    result = gnome_vfs_read(fileHandle, buffer, fileSize,
                            &readCount);
    if (result != GNOME_VFS_OK) {
      dbgFileError(result, uri);
      goto error;
    }
    /* Verify that we got the amount of data that we requested.
       NOTE:
         With URIs it won't be an error to get less bytes than you
         requested. Getting zero bytes will however signify an
         End-of-File condition. */
    if (fileSize != readCount) {
      g_printerr("Failed to load the requested amount\n");
      /* We could also attempt to read the missing data until we have
         filled our buffer, but for simplicity, we'll flag this
         condition as an error. */
      goto error;
    }
 
    /* Whew, if we got this far, it means that we actually managed to
       load the file into memory. Let's set the buffer contents as
       the new label now. */
    gtk_label_set_markup(GTK_LABEL(app->textLabel), buffer);
 
    /* That's it! Display a message of great joy. For this we'll use
       a dialog (non-modal) designed for displaying short
       informational messages. It will linger around on the screen
       for a while and then disappear (in parallel to our program
       continuing). */
    hildon_banner_show_information(GTK_WIDGET(app->window),
      NULL, /* Use the default icon (info). */
      "File loaded successfully");
 
    /* Jump to the resource releasing phase. */
    goto release;
 
  error:
    /* Display a failure message with a stock icon.
       Please see http://maemo.org/api_refs/4.0/gtk/gtk-Stock-Items.html
       for a full listing of stock items. */
    hildon_banner_show_information(GTK_WIDGET(app->window),
      GTK_STOCK_DIALOG_ERROR, /* Use the stock error icon. */
      "Failed to load the file");
 
  release:
    /* Close and free all resources that were allocated. */
    if (fileHandle) gnome_vfs_close(fileHandle);
    if (filename) g_free(filename);
    if (uri) g_free(uri);
    if (buffer) g_free(buffer);
    /* Zero them all out to prevent stack-reuse-bugs. */
    fileHandle = NULL;
    filename = NULL;
    uri = NULL;
    buffer = NULL;
 
    return;
  } else {
    g_print("  you didn't choose any file to open\n");
  }
}
 
/**
 * MODIFIED (kind of)
 *
 * Function to save the contents of the label (although it doesn't
 * actually save the contents, on purpose). Use gtk_label_get_label
 * to get a gchar pointer into the application label contents
 * (including current markup), then use gnome_vfs_create and
 * gnome_vfs_write to create the file (left as an exercise).
 */
static void cbActionSave(GtkWidget* widget, ApplicationState* app) {
  gchar* filename = NULL;
 
  g_assert(app != NULL);
 
  g_print("cbActionSave invoked\n");
 
  filename = runFileChooser(app, GTK_FILE_CHOOSER_ACTION_SAVE);
  if (filename) {
    g_print("  you chose to save into '%s'\n", filename);
    /* Process saving .. */
 
    g_free(filename);
    filename = NULL;
  } else {
    g_print("  you didn't choose a filename to save to\n");
  }
}
 
  /*... Listing cut for brevity ...*/
 
/**
 * MODIFIED
 *
 * Add support for GnomeVFS (it needs to be initialized before use)
 * and add support for the Pango markup feature of the GtkLabel
 * widget.
 */
int main(int argc, char** argv) {
 
  ApplicationState aState = {};
 
  GtkWidget* label = NULL;
  GtkWidget* vbox = NULL;
  GtkWidget* mainToolbar = NULL;
  GtkWidget* findToolbar = NULL;
 
  /* Initialize the GnomeVFS (NEW). */
  if(!gnome_vfs_init()) {
    g_error("Failed to initialize GnomeVFS-libraries, exiting\n");
  }
 
  /* Initialize the GTK+ */
  gtk_init(&argc, &argv);
 
  /* Setup the HildonProgram, HildonWindow and application name. */
  aState.program = HILDON_PROGRAM(hildon_program_get_instance());
  g_set_application_name("Hello Hildon!");
  aState.window = HILDON_WINDOW(hildon_window_new());
  hildon_program_add_window(aState.program,
                            HILDON_WINDOW(aState.window));
 
  /* Create the label widget, with Pango marked up content (NEW). */
  label = gtk_label_new("<b>Hello</b> <i>Hildon</i> (with Hildon"
                        "<sub>search</sub> <u>and</u> GnomeVFS "
                        "and other tricks<sup>tm</sup>)!");
 
  /* Allow lines to wrap (NEW). */
  gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
 
  /* Tell the GtkLabel widget to support the Pango markup (NEW). */
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
 
  /* Store the widget pointer into the application state so that the
     contents can be replaced when a file will be loaded (NEW). */
  aState.textLabel = label;
 
  buildMenu(&aState);
 
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(aState.window), vbox);
  gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
 
  mainToolbar = buildToolbar(&aState);
  findToolbar = buildFindToolbar(&aState);
 
  aState.mainToolbar = mainToolbar;
  aState.findToolbar = findToolbar;
 
  /* Connect the termination signals. */
  g_signal_connect(G_OBJECT(aState.window), "delete-event",
                   G_CALLBACK(cbEventDelete), &aState);
  g_signal_connect(G_OBJECT(aState.window), "destroy",
                   G_CALLBACK(cbActionTopDestroy), &aState);
 
  /* Show all widgets that are contained by the Window. */
  gtk_widget_show_all(GTK_WIDGET(aState.window));
 
  /* Add the toolbars to the Hildon Window. */
  hildon_window_add_toolbar(HILDON_WINDOW(aState.window),
                            GTK_TOOLBAR(mainToolbar));
  hildon_window_add_toolbar(HILDON_WINDOW(aState.window),
                            GTK_TOOLBAR(findToolbar));
 
  /* Register a callback to handle key presses. */
  g_signal_connect(G_OBJECT(aState.window), "key_press_event",
                   G_CALLBACK(cbKeyPressed), &aState);
 
  g_print("main: calling gtk_main\n");
  gtk_main();
  g_print("main: returned from gtk_main and exiting with success\n");
 
  return EXIT_SUCCESS;
}

In order to experiment with loading other content, a simple file can be created, containing Pango markup like this:

echo "<b>Hello world</b>" > MyDocs/hello.txt

and then loading hello.txt.

As can be imagined, these examples have only scratched the surface of GnomeVFS that is quite a rich library, and contains a broad API and a large amount of plug-ins. Many things have been completely avoided, such as directory content iteration, the asynchronous interface, callback signaling on directory content changes etc. Please see GnomeVFS API reference for more information. The API also contains some mini tutorials on various GnomeVFS topics, so it is well worth the time spent reading. It will also show that GnomeVFS has been overloaded with functions, which are not even file operation related (such as ZeroConf and creating TCP/IP connections etc.).

GTK+ does not have to be used in order to use GnomeVFS. One such example program is Midnight Commander (a Norton Commander clone, but better), which is a menu-based "text" mode program. GnomeVFS uses GLib though, so if using GnomeVFS, one should think about using GLib as well, as it will be loaded anyway.