GtkFileChooser Extension Interface

Federico Mena Quintero

Novell, Inc.
Revision History
Revision 0.0September 22, 2004

Initial draft.


Table of Contents

Current state of GtkFileChooser extensibility
Desired Extensibility Features
Scenario: Global Search
Scenario: Lock-down
Scenario: Metadata System
General Extension Mechanism
GtkFileChooserExtensionIface
New methods for GtkFileChooser
GtkFileChooserModelIface
Loading Extensions
Open Issues
Extension Bookmarks
Save Mode
Extension Priorities
References

Abstract

Currently, GtkFileChooser as it exists in GTK+ 2.4.x is not extensible except for simple application-level hooks to add extra widgets to a file chooser dialogs and custom shortcuts. However, more sophisticated extensions could be possible. Global functionality such as a desktop-wide search system requires that all file choosers present the user with a user interface suitable for searching. A locked-down system requires that the system administrator be able to specify which files and folders are visible by users or not. This article describes an extension interface to be used for GtkFileChooser.

Current state of GtkFileChooser extensibility

As of GTK+ 2.4.x, the default implementation of the GtkFileChooser interface ([GtkFileChooser]) supports only very simple extensions that make sense in the context of a single application. The only available facilities are these:

  • Add an "extra" widget to the file chooser.  For example, a word processor may want to add a check box to let the user select whether the file is to be opened in read-only mode.

  • Add application-specific shortcut folders.  For example, a paint program may want to add a "Clipart" shortcut for its /usr/share/paint_app/Clipart folder. Unlike user-defined bookmarks, these shortcuts are not shared among applications.

Preview widgets and filters for the file list are not considered extensions; they are simply optional features that a file chooser can have.

Desired Extensibility Features

To design the set of extensibility features we require, we'll present a number of useful scenarios than cannot be implemented with GtkFileChooser as of GTK+ 2.4.x.

Scenario: Global Search

A desktop-wide search system like Beagle ([Beagle]) may want to allow the user to search for files directly from file choosers. For example, the user may select File->Open, and immedately realize that he doesn't actually know where the file is. In that case, it's convenient to embed the user interface for searching directly in the file chooser so that the user does not have to invoke a separate search utility. As a prototype, let us consider the following figures. In Figure 1, “File open dialog with a Search shortcut”, we see a GtkFileChooserDialog augmented with a "Search..." shortcut.

Figure 1. File open dialog with a Search shortcut

File open dialog with a Search shortcut

When selected, this shortcut would activate an extension that would in turn cause the following actions to happen. First, it would instruct the file chooser to clear the list of files. Second, it would replace the path bar at the top of the dialog with an entry widget and a button, used to let the user type in his search terms. This is shown in Figure 2, “File open dialog with a search entry”.

Figure 2. File open dialog with a search entry

File open dialog with a search entry

Finally, after the user types his search terms and clicks on the "Find" button, the extension populates the file list with the results of the search. This is shown in Figure 3, “File open dialog with search results”.

Figure 3. File open dialog with search results

File open dialog with search results

This process points to several requirements for the extension mechanism:

  • Extensions should be able to define their own items for the shortcuts bar. When an extension-defined shortcut is activated, the extension should be notified so that it can take the appropriate actions.

  • Extensions should be able to temporarily replace the path bar at the top of the file chooser with another widget.

  • Extensions should be able to populate the file list. Right now the file chooser is constrained to showing files that live all under the same folder. This is insufficient for the results of a global search, as the found files may reside in different folders.

Scenario: Lock-down

The system administrator of a locked-down installation may want to constrain file system browsers in general to a few folders, such as a particular user's home directory. The original design for GtkFileChooser called for such lock-down features to be implemented at the GtkFileSystem level, but this is impractical in practice.

For the file chooser's purposes, lock-down can be implemented conveniently in an extension that can do the following:

  • Set a starting location for the file chooser, or startup folder. This can be a user's home directory, for example.

  • Be able to veto which files and folders are shown to the user. The file chooser's internals would simply generate normal lists of files and folders, but run them through the extension's filter before displaying them to the user. This is similar to the CDN_INCLUDEITEM message in the Win32 API ([Win32OpenDialog].

  • Be able to veto which volumes are displayed, if at all. For example, an Internet cafe may want to let users save their documents to floppies or to a local temporary directory. The lock-down extension would make all file choosers display only the shortcut for the floppy volume, and a shortcut for the temporary directory. As another example, a user's desktop within a company may show only two locations in the file chooser's shortcuts bar: "My files" and "Company-wide files", pointing to the user's home directory and a mounted share, respectively. No other volumes would be shown in the shortcuts bar.

Scenario: Metadata System

A metadata layer for file systems may want to let the user annotate the files he saves with extra information. For example, when saving a file, the metadata system may want to prompt the user for a description for that file. When opening files, it would be useful to present those descriptions to the user.

Both of these actions, prompting the user for a description when saving files, and displaying the description when opening files, can be accomplished if an extension is allowed to install extra widgets in file choosers. In GTK_FILE_CHOOSER_ACTION_SAVE mode, the extra widget can be an editable text area; in GTK_FILE_CHOOSER_ACTION_OPEN mode, it can be a non-editable label or text area. Figure 4, “Save dialog with a description field” shows a save dialog with a description field for a metadata engine.

Although currently GtkFileChooser allows applications to set a single extra widget, it does not have a facility to automatically create extension widgets that should be present in all file choosers.

Figure 4. Save dialog with a description field

Save dialog with a description field

General Extension Mechanism

An extension gets registered as a factory for objects who in turn implement a GtkFileChooserExtensionIface interface. These objects get created, one per file chooser, and get bound to the corresponding file chooser.

GtkFileChooserExtensionIface

Extension objects implement the GtkFileChooserExtensionIface interface, which contains the following methods:

struct _GtkFileChooserExtensionIface
{
  GTypeInterface base_iface;

  /* Methods */

  void (* setup)     (GtkFileChooserExtension *extension,
		      GtkFileChooser          *chooser);
  void (* terminate) (GtkFileChooserExtension *extension);

  void (* shortcut_activated) (GtkFileChooserExtension *extension,
			       int                      shortcut_id);

  gboolean (* is_path_visible) (GtkFileChooserExtension *extension,
				const GtkFilePath       *path);

  gboolean (* is_volume_visible) (GtkFileChooserExtension *extension,
				  GtkFileSystemVolume     *volume);
}
      

When a file chooser is created, it asks the extension factory to create an object which implements the GtkFileChooserExtensionIface interface. The file chooser calls the ::setup() method on this object, to let the extension perform initialization tasks like connecting to the file chooser's signals, setting up extra widgets, and installing extension shortcuts.

The mechanism through which an extension installs shortcuts and extra widgets is described in the section called “New methods for GtkFileChooser”.

When a file chooser is about to be destroyed, it calls the ::terminate() method on its extensions. Each extension can then tear down whatever machinery it set up earlier.

An extension can install a special shortcut in the file chooser's shortcuts bar by using the gtk_file_chooser_add_shortcut_for_extension() function, described in the section called “New methods for GtkFileChooser”. If the user activates such a shortcut, then the ::shortcut_activated() method in the extension gets called.

Finally, an extension can request that the file chooser ask it first before entering or displaying a folder, file, or volume. An extension can set this by using the gtk_file_chooser_set_check_path_visibility_for_extension() function, described in the section called “New methods for GtkFileChooser”. Before the file chooser tries to display a file, folder, or volume, it will query the extension to see if it should be visible through the extension's ::is_path_visible() and ::is_volume_visible() methods.

New methods for GtkFileChooser

The following listing shows the new public methods that would be added to GtkFileChooser to be used from implementations of GtkFileChooserExtensionIface.

void gtk_file_chooser_add_shortcut_for_extension (GtkFileChooser          *chooser,
						  GtkFileChooserExtension *extension,
						  int                      pos,
						  GdkPixbuf               *icon,
						  const char              *text,
						  int                      shortcut_id);

void gtk_file_chooser_remove_shortcut_for_extension (GtkFileChooser          *chooser,
						     GtkFileChooserExtension *extension,
						     int                      shortcut_id);

void gtk_file_chooser_set_extra_widget_for_extension (GtkFileChooser          *chooser,
						      GtkFileChooserExtension *extension,
						      GtkWidget               *widget);
GtkWidget *gtk_file_chooser_get_extra_widget_for_extension (GtkFileChooser          *chooser,
							    GtkFileChooserExtension *extension);

void gtk_file_chooser_set_model_for_extension (GtkFileChooser          *chooser,
					       GtkFileChooserExtension *extension,
					       GtkFileChooserModel     *model,
					       GtkWidget               *path_bar_replacement);

void gtk_file_chooser_set_check_path_visibility_for_extension (GtkFileChooser          *chooser,
							       GtkFileChooserExtension *extension);
gboolean gtk_file_chooser_get_check_path_visibility_for_extension (GtkFileChooser *chooser,
								   GtkFileChooserExtension *extension);
      

Extensions can add and remove custom shortcuts by using the gtk_file_chooser_add_shortcut_for_extension() and gtk_file_chooser_remove_shortcut_for_extension() functions, respectively. When one of those shortcuts gets activated by the user, the file chooser will call the ::shortcut_activated() method on the corresponding GtkFileChooserExtension.

Note

In terms of the user interface, the idea is to put extension shortcuts in the shortcuts bar above the separator that divides volumes from bookmarks. In turn, extension shortcuts would be separated from volumes by another separator. See Figure 1, “File open dialog with a Search shortcut” for an example.

An extension can install an extra widget by using the gtk_file_chooser_set_extra_widget_for_extension() function. This would get placed above application-level extra widgets. See Figure 4, “Save dialog with a description field” for an example of this placement.

An extension may present its own list of files and install a replacement for the path bar widget by using the gtk_file_chooser_set_model_for_extension() function. The GtkFileChooserModel, described in the section called “GtkFileChooserModelIface”, would get bound to the GtkTreeView that displays the file list via an internal adapter.

An extension may tell the file chooser that it wants to veto the display of files, folders, or volumes by calling the gtk_file_chooser_set_check_path_visibility_for_extension(). Before displaying a file or folder in the file list, or a shortcut for a folder or a volume in the shortcuts list, the file chooser will call the ::is_path_visible() or ::is_volume_visible() method of the extension, respectively. If they return FALSE, then that path or volume will not be displayed and the user will not be able to access it from the file chooser.

GtkFileChooserModelIface

The following listing shows the GtkFileChooserModelIface interface with its methods and signals. An object that implements this interface gets used to feed data to the GtkTreeView which displays the file list in the file chooser.

Note that this is not a GtkTreeModel, but rather just a provider for file paths — an actual implementation of GtkTreeModel would sit between the tree view and the GtkFileChooserModel. This middle-man would be provided by the GtkFileChooser internals.

Also, note that this interface does not provide a method to get the file information for a path in the form of a GtkFileInfo structure. Instead, the file chooser would use its underlying GtkFileSystem to fetch this information from the path.

struct _GtkFileChooserModelIface
{
  GTypeInterface base_iface;

  /* Methods */

  gboolean (* is_finished_loading) (GtkFileChooserModel *model);

  gboolean (* list_paths)         (GtkFileChooserModel *model,
				   GSList             **paths,
				   GError             **error);

  /* Signals */

  void (* finished_loading) (GtkFileChooserModel *model);

  void (* paths_added)   (GtkFileChooserModel *model,
			  GSList              *paths);
  void (* paths_removed) (GtkFileChooserModel *model,
			  GSList              *paths);
  void (* paths_changed) (GtkFileChooserModel *model,
			  GSList              *paths);
};
      

Loading Extensions

Extensions would be GTypeModules, placed in a well-known location in the file system. For example, they could be loaded from $(prefix)/lib/gtk-2.0/2.6.0/file-chooser-extensions. Once loaded, creating a file chooser would result in extension objects being created and bound to the file chooser.

See the section called “Extension Priorities” for some open issues about loading extensions.

Open Issues

Extension Bookmarks

If the file chooser is showing a folder with no files selected and the user clicks on the Add button in the file chooser, then the current folder gets added to his bookmarks. Should the Add be disabled while an extension has overriden the file list with a GtkFileChooserModel and there are no selected files? Or should we have a way to let extensions define their own bookmarks? We would need something like this:

  • A method in GtkFileChooser to add a bookmark specific to an extension. This would be specified by a human-readable name for the bookmark, and a string that describes the bookmark's contents in the context of the extension. For a global search system, such a string could be as simple as the search terms that the user typed ("paper"), or something more complex such as an S-expression with search criteria ("(and (text-contains "paper") (> (file-size) 524288))").

  • A method in GtkFileChooserExtensionIface to visit a bookmark that was registered by that extension. The file chooser would pass the string mentioned above to the extension, and the extension would then restore its state. For example, a global search system could re-execute the search specified by the string and then populate the file list.

This has the disadvantage of complicating the format of the .gtk-bookmarks, and of making it harder to interoperate with other desktops through a share bookmarks specification in the future.

Save Mode

What do we do in GTK_FILE_CHOOSER_ACTION_SAVE mode? Do we list extension shortcuts only in the "Browse for other folders" shortcuts bar? Wouldn't that make them hard to discover? Or do we show them in the normal shortcuts drop-down and forcifully expand the "Browser for other folders" section when the extension wants to install its replacement for the path bar and file list model?

Extension Priorities

If we drop extension modules in $(prefix)/lib/gtk-2.0/2.6.0/file-chooser-extensions, and these get all loaded when a file chooser gets created, do we need a mechanism to indicate the order in which extensions get loaded?

Say you want to have both a global search system and a locked-down setup: this would restrict the user to a section of the file system, but still allow him to find his files easily. Since extensions do not communicate with each other, the lock-down extension would have to filter out the results of the search extension:

  1. The user activates the "Search..." shortcut, and the search extension replaces the path bar and the file list with a prompt for search terms and an empty GtkFileChooserModel.

  2. The user types his search terms, and the search extension populates its GtkFileChooserModel with the results from the search.

  3. The file chooser calls the lock-down extension's ::is_path_visible() upon on each of the paths from the GtkFileChooserModel to see if it should be shown to the user.

  4. The file chooser presents the constrained search results to the user in the GtkTreeView.

If we have this requirement, then we probably need a gtk-register-file-chooser-extension script to help maintain the order of extensions. A distribution package for an extension could call a command like gtk-register-file-chooser-extension --add --priority=50 some-extension.so. When uninstalled, it could call gtk-register-file-chooser-extension --remove some-extension.so.

References